Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+270
View File
@@ -0,0 +1,270 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include "dsi.h"
bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi)
{
unsigned long host_flags = msm_dsi_host_get_mode_flags(msm_dsi->host);
return !(host_flags & MIPI_DSI_MODE_VIDEO);
}
struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi)
{
return msm_dsi_host_get_dsc_config(msm_dsi->host);
}
bool msm_dsi_wide_bus_enabled(struct msm_dsi *msm_dsi)
{
return msm_dsi_host_is_wide_bus_enabled(msm_dsi->host);
}
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, "phys", 0);
if (!phy_node) {
DRM_DEV_ERROR(&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);
msm_dsi->phy_dev = &phy_pdev->dev;
}
of_node_put(phy_node);
if (!phy_pdev) {
DRM_DEV_ERROR(&pdev->dev, "%s: phy driver is not ready\n", __func__);
return -EPROBE_DEFER;
}
if (!msm_dsi->phy) {
put_device(&phy_pdev->dev);
DRM_DEV_ERROR(&pdev->dev, "%s: phy driver is not ready\n", __func__);
return -EPROBE_DEFER;
}
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;
}
platform_set_drvdata(msm_dsi->pdev, NULL);
}
static struct msm_dsi *dsi_init(struct platform_device *pdev)
{
struct msm_dsi *msm_dsi;
int ret;
if (!pdev)
return ERR_PTR(-ENXIO);
msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL);
if (!msm_dsi)
return ERR_PTR(-ENOMEM);
DBG("dsi probed=%p", msm_dsi);
msm_dsi->id = -1;
msm_dsi->pdev = pdev;
platform_set_drvdata(pdev, msm_dsi);
/* Init dsi host */
ret = msm_dsi_host_init(msm_dsi);
if (ret)
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 destroy_dsi;
return msm_dsi;
destroy_dsi:
dsi_destroy(msm_dsi);
return ERR_PTR(ret);
}
static int dsi_bind(struct device *dev, struct device *master, void *data)
{
struct msm_drm_private *priv = dev_get_drvdata(master);
struct msm_dsi *msm_dsi = dev_get_drvdata(dev);
/*
* Next bridge doesn't exist for the secondary DSI host in a bonded
* pair.
*/
if (!msm_dsi_is_bonded_dsi(msm_dsi) ||
msm_dsi_is_master_dsi(msm_dsi)) {
struct drm_bridge *ext_bridge;
ext_bridge = devm_drm_of_get_bridge(&msm_dsi->pdev->dev,
msm_dsi->pdev->dev.of_node, 1, 0);
if (IS_ERR(ext_bridge))
return PTR_ERR(ext_bridge);
msm_dsi->next_bridge = ext_bridge;
}
priv->kms->dsi[msm_dsi->id] = msm_dsi;
return 0;
}
static void dsi_unbind(struct device *dev, struct device *master,
void *data)
{
struct msm_drm_private *priv = dev_get_drvdata(master);
struct msm_dsi *msm_dsi = dev_get_drvdata(dev);
msm_dsi_tx_buf_free(msm_dsi->host);
priv->kms->dsi[msm_dsi->id] = NULL;
}
static const struct component_ops dsi_ops = {
.bind = dsi_bind,
.unbind = dsi_unbind,
};
int dsi_dev_attach(struct platform_device *pdev)
{
return component_add(&pdev->dev, &dsi_ops);
}
void dsi_dev_detach(struct platform_device *pdev)
{
component_del(&pdev->dev, &dsi_ops);
}
static int dsi_dev_probe(struct platform_device *pdev)
{
struct msm_dsi *msm_dsi;
DBG("");
msm_dsi = dsi_init(pdev);
if (IS_ERR(msm_dsi)) {
/* Don't fail the bind if the dsi port is not connected */
if (PTR_ERR(msm_dsi) == -ENODEV)
return 0;
else
return PTR_ERR(msm_dsi);
}
return 0;
}
static void dsi_dev_remove(struct platform_device *pdev)
{
struct msm_dsi *msm_dsi = platform_get_drvdata(pdev);
DBG("");
dsi_destroy(msm_dsi);
}
static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,mdss-dsi-ctrl" },
/* Deprecated, don't use */
{ .compatible = "qcom,dsi-ctrl-6g-qcm2290" },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
static const struct dev_pm_ops dsi_pm_ops = {
SET_RUNTIME_PM_OPS(msm_dsi_runtime_suspend, msm_dsi_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver dsi_driver = {
.probe = dsi_dev_probe,
.remove = dsi_dev_remove,
.driver = {
.name = "msm_dsi",
.of_match_table = dt_match,
.pm = &dsi_pm_ops,
},
};
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);
}
int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
struct drm_encoder *encoder)
{
int ret;
msm_dsi->dev = dev;
ret = msm_dsi_host_modeset_init(msm_dsi->host, dev);
if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to modeset init host: %d\n", ret);
return ret;
}
if (msm_dsi_is_bonded_dsi(msm_dsi) &&
!msm_dsi_is_master_dsi(msm_dsi)) {
/*
* Do not return an eror here,
* Just skip creating encoder/connector for the slave-DSI.
*/
return 0;
}
ret = msm_dsi_manager_connector_init(msm_dsi, encoder);
if (ret) {
DRM_DEV_ERROR(dev->dev,
"failed to create dsi connector: %d\n", ret);
return ret;
}
return 0;
}
void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi)
{
msm_dsi_host_snapshot(disp_state, msm_dsi->host);
msm_dsi_phy_snapshot(disp_state, msm_dsi->phy);
}
+155
View File
@@ -0,0 +1,155 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#ifndef __DSI_CONNECTOR_H__
#define __DSI_CONNECTOR_H__
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include "msm_drv.h"
#include "disp/msm_disp_snapshot.h"
#define DSI_0 0
#define DSI_1 1
#define DSI_MAX 2
struct msm_dsi_phy_shared_timings;
struct msm_dsi_phy_clk_request;
enum msm_dsi_phy_usecase {
MSM_DSI_PHY_STANDALONE,
MSM_DSI_PHY_MASTER,
MSM_DSI_PHY_SLAVE,
};
#define DSI_BUS_CLK_MAX 4
struct msm_dsi {
struct drm_device *dev;
struct platform_device *pdev;
struct mipi_dsi_host *host;
struct msm_dsi_phy *phy;
const char *te_source;
struct drm_bridge *next_bridge;
struct device *phy_dev;
bool phy_enabled;
int id;
};
/* dsi manager */
int msm_dsi_manager_connector_init(struct msm_dsi *msm_dsi,
struct drm_encoder *encoder);
int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg);
bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len);
int msm_dsi_manager_register(struct msm_dsi *msm_dsi);
void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi);
void msm_dsi_manager_tpg_enable(void);
/* dsi host */
struct msm_dsi_host;
int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg);
void msm_dsi_host_xfer_restore(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg);
int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg);
int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg);
void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host,
u32 dma_base, u32 len);
int msm_dsi_host_enable(struct mipi_dsi_host *host);
int msm_dsi_host_disable(struct mipi_dsi_host *host);
void msm_dsi_host_enable_irq(struct mipi_dsi_host *host);
void msm_dsi_host_disable_irq(struct mipi_dsi_host *host);
int msm_dsi_host_power_on(struct mipi_dsi_host *host,
struct msm_dsi_phy_shared_timings *phy_shared_timings,
bool is_bonded_dsi, struct msm_dsi_phy *phy);
int msm_dsi_host_power_off(struct mipi_dsi_host *host);
int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host,
const struct drm_display_mode *mode);
enum drm_mode_status msm_dsi_host_check_dsc(struct mipi_dsi_host *host,
const struct drm_display_mode *mode);
unsigned long msm_dsi_host_get_mode_flags(struct mipi_dsi_host *host);
int msm_dsi_host_register(struct mipi_dsi_host *host);
void msm_dsi_host_unregister(struct mipi_dsi_host *host);
void msm_dsi_host_set_phy_mode(struct mipi_dsi_host *host,
struct msm_dsi_phy *src_phy);
int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
struct msm_dsi_phy *src_phy);
void msm_dsi_host_reset_phy(struct mipi_dsi_host *host);
void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host,
struct msm_dsi_phy_clk_request *clk_req,
bool is_bonded_dsi);
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);
int msm_dsi_host_init(struct msm_dsi *msm_dsi);
int msm_dsi_runtime_suspend(struct device *dev);
int msm_dsi_runtime_resume(struct device *dev);
int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host);
int dsi_link_clk_set_rate_6g_v2_9(struct msm_dsi_host *msm_host);
int dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host);
int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host);
int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host);
void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host);
void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host);
unsigned long dsi_byte_clk_get_rate(struct mipi_dsi_host *host, bool is_bonded_dsi,
const struct drm_display_mode *mode);
int dsi_tx_buf_alloc_6g(struct msm_dsi_host *msm_host, int size);
int dsi_tx_buf_alloc_v2(struct msm_dsi_host *msm_host, int size);
void *dsi_tx_buf_get_6g(struct msm_dsi_host *msm_host);
void *dsi_tx_buf_get_v2(struct msm_dsi_host *msm_host);
void dsi_tx_buf_put_6g(struct msm_dsi_host *msm_host);
void msm_dsi_tx_buf_free(struct mipi_dsi_host *mipi_host);
int dsi_dma_base_get_6g(struct msm_dsi_host *msm_host, uint64_t *iova);
int dsi_dma_base_get_v2(struct msm_dsi_host *msm_host, uint64_t *iova);
int dsi_clk_init_v2(struct msm_dsi_host *msm_host);
int dsi_clk_init_6g_v2(struct msm_dsi_host *msm_host);
int dsi_clk_init_6g_v2_9(struct msm_dsi_host *msm_host);
int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host);
void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host);
struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host);
bool msm_dsi_host_is_wide_bus_enabled(struct mipi_dsi_host *host);
/* dsi phy */
struct msm_dsi_phy;
struct msm_dsi_phy_shared_timings {
u32 clk_post;
u32 clk_pre;
bool clk_pre_inc_by_2;
bool byte_intf_clk_div_2;
};
struct msm_dsi_phy_clk_request {
unsigned long bitclk_rate;
unsigned long escclk_rate;
};
void msm_dsi_phy_driver_register(void);
void msm_dsi_phy_driver_unregister(void);
int msm_dsi_phy_enable(struct msm_dsi_phy *phy,
struct msm_dsi_phy_clk_request *clk_req,
struct msm_dsi_phy_shared_timings *shared_timings);
void msm_dsi_phy_disable(struct msm_dsi_phy *phy);
void msm_dsi_phy_set_usecase(struct msm_dsi_phy *phy,
enum msm_dsi_phy_usecase uc);
void msm_dsi_phy_pll_save_state(struct msm_dsi_phy *phy);
int msm_dsi_phy_pll_restore_state(struct msm_dsi_phy *phy);
void msm_dsi_phy_snapshot(struct msm_disp_state *disp_state, struct msm_dsi_phy *phy);
bool msm_dsi_phy_set_continuous_clock(struct msm_dsi_phy *phy, bool enable);
#endif /* __DSI_CONNECTOR_H__ */
@@ -0,0 +1,364 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include "dsi_cfg.h"
static const char * const dsi_v2_bus_clk_names[] = {
"core_mmss", "iface", "bus",
};
static const struct regulator_bulk_data apq8064_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 100000 }, /* 1.2 V */
{ .supply = "avdd", .init_load_uA = 10000 }, /* 3.0 V */
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
};
static const struct msm_dsi_config apq8064_dsi_cfg = {
.io_offset = 0,
.regulator_data = apq8064_dsi_regulators,
.num_regulators = ARRAY_SIZE(apq8064_dsi_regulators),
.bus_clk_names = dsi_v2_bus_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_bus_clk_names),
.io_start = {
{ 0x4700000, 0x5800000 },
},
};
static const char * const dsi_6g_bus_clk_names[] = {
"mdp_core", "iface", "bus", "core_mmss",
};
static const struct regulator_bulk_data msm8974_apq8084_regulators[] = {
{ .supply = "vdd", .init_load_uA = 150000 }, /* 3.0 V */
{ .supply = "vdda", .init_load_uA = 100000 }, /* 1.2 V */
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
};
static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = msm8974_apq8084_regulators,
.num_regulators = ARRAY_SIZE(msm8974_apq8084_regulators),
.bus_clk_names = dsi_6g_bus_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names),
.io_start = {
{ 0xfd922800, 0xfd922b00 },
},
};
static const char * const dsi_v1_3_1_clk_names[] = {
"mdp_core", "iface", "bus",
};
static const struct regulator_bulk_data dsi_v1_3_1_regulators[] = {
{ .supply = "vdda", .init_load_uA = 100000 }, /* 1.2 V */
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
};
static const struct msm_dsi_config msm8916_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = dsi_v1_3_1_regulators,
.num_regulators = ARRAY_SIZE(dsi_v1_3_1_regulators),
.bus_clk_names = dsi_v1_3_1_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v1_3_1_clk_names),
.io_start = {
{ 0x1a98000 },
},
};
static const struct msm_dsi_config msm8976_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = dsi_v1_3_1_regulators,
.num_regulators = ARRAY_SIZE(dsi_v1_3_1_regulators),
.bus_clk_names = dsi_v1_3_1_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v1_3_1_clk_names),
.io_start = {
{ 0x1a94000, 0x1a96000 },
},
};
static const struct regulator_bulk_data msm8994_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 100000 }, /* 1.25 V */
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
{ .supply = "vcca", .init_load_uA = 10000 }, /* 1.0 V */
{ .supply = "vdd", .init_load_uA = 100000 }, /* 1.8 V */
{ .supply = "lab_reg", .init_load_uA = -1 },
{ .supply = "ibb_reg", .init_load_uA = -1 },
};
static const struct msm_dsi_config msm8994_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = msm8994_dsi_regulators,
.num_regulators = ARRAY_SIZE(msm8994_dsi_regulators),
.bus_clk_names = dsi_6g_bus_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names),
.io_start = {
{ 0xfd998000, 0xfd9a0000 },
},
};
static const struct regulator_bulk_data msm8996_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 18160 }, /* 1.25 V */
{ .supply = "vcca", .init_load_uA = 17000 }, /* 0.925 V */
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
};
static const struct msm_dsi_config msm8996_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = msm8996_dsi_regulators,
.num_regulators = ARRAY_SIZE(msm8996_dsi_regulators),
.bus_clk_names = dsi_6g_bus_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names),
.io_start = {
{ 0x994000, 0x996000 },
},
};
static const char * const dsi_msm8998_bus_clk_names[] = {
"iface", "bus", "core",
};
static const struct regulator_bulk_data msm8998_dsi_regulators[] = {
{ .supply = "vdd", .init_load_uA = 367000 }, /* 0.9 V */
{ .supply = "vdda", .init_load_uA = 62800 }, /* 1.2 V */
};
static const struct msm_dsi_config msm8998_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = msm8998_dsi_regulators,
.num_regulators = ARRAY_SIZE(msm8998_dsi_regulators),
.bus_clk_names = dsi_msm8998_bus_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_msm8998_bus_clk_names),
.io_start = {
{ 0xc994000, 0xc996000 },
},
};
static const char * const dsi_sdm660_bus_clk_names[] = {
"iface", "bus", "core", "core_mmss",
};
static const struct regulator_bulk_data sdm660_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 12560 }, /* 1.2 V */
};
static const struct msm_dsi_config sdm660_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = sdm660_dsi_regulators,
.num_regulators = ARRAY_SIZE(sdm660_dsi_regulators),
.bus_clk_names = dsi_sdm660_bus_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_sdm660_bus_clk_names),
.io_start = {
{ 0xc994000, 0xc996000 },
},
};
static const char * const dsi_v2_4_clk_names[] = {
"iface", "bus",
};
static const struct regulator_bulk_data dsi_v2_4_regulators[] = {
{ .supply = "vdda", .init_load_uA = 21800 }, /* 1.2 V */
{ .supply = "refgen" },
};
static const struct msm_dsi_config sdm845_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = dsi_v2_4_regulators,
.num_regulators = ARRAY_SIZE(dsi_v2_4_regulators),
.bus_clk_names = dsi_v2_4_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_4_clk_names),
.io_start = {
{ 0xae94000, 0xae96000 }, /* SDM845 / SDM670 */
{ 0x5e94000 }, /* QCM2290 / SM6115 / SM6125 / SM6375 */
},
};
static const struct regulator_bulk_data sm8550_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 16800 }, /* 1.2 V */
};
static const struct msm_dsi_config sm8550_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = sm8550_dsi_regulators,
.num_regulators = ARRAY_SIZE(sm8550_dsi_regulators),
.bus_clk_names = dsi_v2_4_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_4_clk_names),
.io_start = {
{ 0xae94000, 0xae96000 },
},
};
static const struct regulator_bulk_data sm8650_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 16600 }, /* 1.2 V */
};
static const struct msm_dsi_config sm8650_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = sm8650_dsi_regulators,
.num_regulators = ARRAY_SIZE(sm8650_dsi_regulators),
.bus_clk_names = dsi_v2_4_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_4_clk_names),
.io_start = {
{ 0xae94000, 0xae96000 },
},
};
static const struct msm_dsi_config kaanapali_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = sm8650_dsi_regulators,
.num_regulators = ARRAY_SIZE(sm8650_dsi_regulators),
.bus_clk_names = dsi_v2_4_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_4_clk_names),
.io_start = {
{ 0x9ac0000, 0x9ac3000 },
},
};
static const struct regulator_bulk_data sc7280_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 8350 }, /* 1.2 V */
{ .supply = "refgen" },
};
static const struct msm_dsi_config sc7280_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = sc7280_dsi_regulators,
.num_regulators = ARRAY_SIZE(sc7280_dsi_regulators),
.bus_clk_names = dsi_v2_4_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_4_clk_names),
.io_start = {
{ 0xae94000, 0xae96000 },
},
};
static const struct regulator_bulk_data sa8775p_dsi_regulators[] = {
{ .supply = "vdda", .init_load_uA = 8300 }, /* 1.2 V */
{ .supply = "refgen" },
};
static const struct msm_dsi_config sa8775p_dsi_cfg = {
.io_offset = DSI_6G_REG_SHIFT,
.regulator_data = sa8775p_dsi_regulators,
.num_regulators = ARRAY_SIZE(sa8775p_dsi_regulators),
.bus_clk_names = dsi_v2_4_clk_names,
.num_bus_clks = ARRAY_SIZE(dsi_v2_4_clk_names),
.io_start = {
{ 0xae94000, 0xae96000 },
},
};
static const struct msm_dsi_host_cfg_ops msm_dsi_v2_host_ops = {
.link_clk_set_rate = dsi_link_clk_set_rate_v2,
.link_clk_enable = dsi_link_clk_enable_v2,
.link_clk_disable = dsi_link_clk_disable_v2,
.clk_init_ver = dsi_clk_init_v2,
.tx_buf_alloc = dsi_tx_buf_alloc_v2,
.tx_buf_get = dsi_tx_buf_get_v2,
.tx_buf_put = NULL,
.dma_base_get = dsi_dma_base_get_v2,
.calc_clk_rate = dsi_calc_clk_rate_v2,
};
static const struct msm_dsi_host_cfg_ops msm_dsi_6g_host_ops = {
.link_clk_set_rate = dsi_link_clk_set_rate_6g,
.link_clk_enable = dsi_link_clk_enable_6g,
.link_clk_disable = dsi_link_clk_disable_6g,
.clk_init_ver = NULL,
.tx_buf_alloc = dsi_tx_buf_alloc_6g,
.tx_buf_get = dsi_tx_buf_get_6g,
.tx_buf_put = dsi_tx_buf_put_6g,
.dma_base_get = dsi_dma_base_get_6g,
.calc_clk_rate = dsi_calc_clk_rate_6g,
};
static const struct msm_dsi_host_cfg_ops msm_dsi_6g_v2_host_ops = {
.link_clk_set_rate = dsi_link_clk_set_rate_6g,
.link_clk_enable = dsi_link_clk_enable_6g,
.link_clk_disable = dsi_link_clk_disable_6g,
.clk_init_ver = dsi_clk_init_6g_v2,
.tx_buf_alloc = dsi_tx_buf_alloc_6g,
.tx_buf_get = dsi_tx_buf_get_6g,
.tx_buf_put = dsi_tx_buf_put_6g,
.dma_base_get = dsi_dma_base_get_6g,
.calc_clk_rate = dsi_calc_clk_rate_6g,
};
static const struct msm_dsi_host_cfg_ops msm_dsi_6g_v2_9_host_ops = {
.link_clk_set_rate = dsi_link_clk_set_rate_6g_v2_9,
.link_clk_enable = dsi_link_clk_enable_6g,
.link_clk_disable = dsi_link_clk_disable_6g,
.clk_init_ver = dsi_clk_init_6g_v2_9,
.tx_buf_alloc = dsi_tx_buf_alloc_6g,
.tx_buf_get = dsi_tx_buf_get_6g,
.tx_buf_put = dsi_tx_buf_put_6g,
.dma_base_get = dsi_dma_base_get_6g,
.calc_clk_rate = dsi_calc_clk_rate_6g,
};
static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = {
{MSM_DSI_VER_MAJOR_V2, MSM_DSI_V2_VER_MINOR_8064,
&apq8064_dsi_cfg, &msm_dsi_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0,
&msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0_2,
&msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1,
&msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1_1,
&msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_2,
&msm8974_apq8084_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3,
&msm8994_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3_1,
&msm8916_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_4_1,
&msm8996_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_4_2,
&msm8976_dsi_cfg, &msm_dsi_6g_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_0_0,
&msm8998_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_1_0,
&sdm660_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_2_1,
&sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_3_0,
&sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_3_1,
&sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_4_0,
&sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_4_1,
&sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_5_0,
&sc7280_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_5_1,
&sa8775p_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_6_0,
&sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_7_0,
&sm8550_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_8_0,
&sm8650_dsi_cfg, &msm_dsi_6g_v2_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_9_0,
&sm8650_dsi_cfg, &msm_dsi_6g_v2_9_host_ops},
{MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_10_0,
&kaanapali_dsi_cfg, &msm_dsi_6g_v2_9_host_ops},
};
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;
}
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#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_0_2 0x10000002
#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 MSM_DSI_6G_VER_MINOR_V1_4_1 0x10040001
#define MSM_DSI_6G_VER_MINOR_V1_4_2 0x10040002
#define MSM_DSI_6G_VER_MINOR_V2_0_0 0x20000000
#define MSM_DSI_6G_VER_MINOR_V2_1_0 0x20010000
#define MSM_DSI_6G_VER_MINOR_V2_2_1 0x20020001
#define MSM_DSI_6G_VER_MINOR_V2_3_0 0x20030000
#define MSM_DSI_6G_VER_MINOR_V2_3_1 0x20030001
#define MSM_DSI_6G_VER_MINOR_V2_4_0 0x20040000
#define MSM_DSI_6G_VER_MINOR_V2_4_1 0x20040001
#define MSM_DSI_6G_VER_MINOR_V2_5_0 0x20050000
#define MSM_DSI_6G_VER_MINOR_V2_5_1 0x20050001
#define MSM_DSI_6G_VER_MINOR_V2_6_0 0x20060000
#define MSM_DSI_6G_VER_MINOR_V2_7_0 0x20070000
#define MSM_DSI_6G_VER_MINOR_V2_8_0 0x20080000
#define MSM_DSI_6G_VER_MINOR_V2_9_0 0x20090000
#define MSM_DSI_6G_VER_MINOR_V2_10_0 0x200a0000
#define MSM_DSI_V2_VER_MINOR_8064 0x0
#define DSI_6G_REG_SHIFT 4
/* Maximum number of configurations matched against the same hw revision */
#define VARIANTS_MAX 2
struct msm_dsi_config {
u32 io_offset;
const struct regulator_bulk_data *regulator_data;
int num_regulators;
const char * const *bus_clk_names;
const int num_bus_clks;
const resource_size_t io_start[VARIANTS_MAX][DSI_MAX];
};
struct msm_dsi_host_cfg_ops {
int (*link_clk_set_rate)(struct msm_dsi_host *msm_host);
int (*link_clk_enable)(struct msm_dsi_host *msm_host);
void (*link_clk_disable)(struct msm_dsi_host *msm_host);
int (*clk_init_ver)(struct msm_dsi_host *msm_host);
int (*tx_buf_alloc)(struct msm_dsi_host *msm_host, int size);
void* (*tx_buf_get)(struct msm_dsi_host *msm_host);
void (*tx_buf_put)(struct msm_dsi_host *msm_host);
int (*dma_base_get)(struct msm_dsi_host *msm_host, uint64_t *iova);
int (*calc_clk_rate)(struct msm_dsi_host *msm_host, bool is_bonded_dsi);
};
struct msm_dsi_cfg_handler {
u32 major;
u32 minor;
const struct msm_dsi_config *cfg;
const struct msm_dsi_host_cfg_ops *ops;
};
const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor);
#endif /* __MSM_DSI_CFG_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,616 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include "drm/drm_bridge_connector.h"
#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_bonded_dsi;
bool is_sync_needed;
int master_dsi_link_id;
};
static struct msm_dsi_manager msm_dsim_glb;
#define IS_BONDED_DSI() (msm_dsim_glb.is_bonded_dsi)
#define IS_SYNC_NEEDED() (msm_dsim_glb.is_sync_needed)
#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)
{
return msm_dsim_glb.dsi[id];
}
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_of(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 bonded dsi and
* sync-mode, and only one node specifies master in case of bonded mode.
*/
if (!msm_dsim->is_bonded_dsi)
msm_dsim->is_bonded_dsi = of_property_read_bool(np, "qcom,dual-dsi-mode");
if (msm_dsim->is_bonded_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-dsi");
}
return 0;
}
static int dsi_mgr_setup_components(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 *clk_slave_dsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
int ret;
if (!IS_BONDED_DSI()) {
/*
* Set the usecase before calling msm_dsi_host_register(), which would
* already program the PLL source mux based on a default usecase.
*/
msm_dsi_phy_set_usecase(msm_dsi->phy, MSM_DSI_PHY_STANDALONE);
msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy);
ret = msm_dsi_host_register(msm_dsi->host);
if (ret)
return ret;
} else if (other_dsi) {
struct msm_dsi *master_link_dsi = IS_MASTER_DSI_LINK(id) ?
msm_dsi : other_dsi;
struct msm_dsi *slave_link_dsi = IS_MASTER_DSI_LINK(id) ?
other_dsi : msm_dsi;
/*
* PLL0 is to drive both DSI link clocks in bonded DSI mode.
*
* Set the usecase before calling msm_dsi_host_register(), which would
* already program the PLL source mux based on a default usecase.
*/
msm_dsi_phy_set_usecase(clk_master_dsi->phy,
MSM_DSI_PHY_MASTER);
msm_dsi_phy_set_usecase(clk_slave_dsi->phy,
MSM_DSI_PHY_SLAVE);
msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy);
msm_dsi_host_set_phy_mode(other_dsi->host, other_dsi->phy);
/* 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(slave_link_dsi->host);
if (ret)
return ret;
ret = msm_dsi_host_register(master_link_dsi->host);
if (ret)
return ret;
}
return 0;
}
static int enable_phy(struct msm_dsi *msm_dsi,
struct msm_dsi_phy_shared_timings *shared_timings)
{
struct msm_dsi_phy_clk_request clk_req;
bool is_bonded_dsi = IS_BONDED_DSI();
msm_dsi_host_get_phy_clk_req(msm_dsi->host, &clk_req, is_bonded_dsi);
return msm_dsi_phy_enable(msm_dsi->phy, &clk_req, shared_timings);
}
static int
dsi_mgr_phy_enable(int id,
struct msm_dsi_phy_shared_timings shared_timings[DSI_MAX])
{
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
int ret;
/* In case of bonded DSI, some registers in PHY1 have been programmed
* during PLL0 clock's set_rate. The PHY1 reset called by host1 here
* will silently reset those PHY1 registers. Therefore we need to reset
* and enable both PHYs before any PLL clock operation.
*/
if (IS_BONDED_DSI() && mdsi && sdsi) {
if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
msm_dsi_host_reset_phy(mdsi->host);
msm_dsi_host_reset_phy(sdsi->host);
ret = enable_phy(mdsi,
&shared_timings[DSI_CLOCK_MASTER]);
if (ret)
return ret;
ret = enable_phy(sdsi,
&shared_timings[DSI_CLOCK_SLAVE]);
if (ret) {
msm_dsi_phy_disable(mdsi->phy);
return ret;
}
}
} else {
msm_dsi_host_reset_phy(msm_dsi->host);
ret = enable_phy(msm_dsi, &shared_timings[id]);
if (ret)
return ret;
}
msm_dsi->phy_enabled = true;
return 0;
}
static void dsi_mgr_phy_disable(int id)
{
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
/* disable DSI phy
* In bonded 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_BONDED_DSI() && mdsi && sdsi) {
if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
msm_dsi_phy_disable(sdsi->phy);
msm_dsi_phy_disable(mdsi->phy);
}
} else {
msm_dsi_phy_disable(msm_dsi->phy);
}
}
struct dsi_bridge {
struct drm_bridge base;
int id;
};
#define to_dsi_bridge(x) container_of(x, struct dsi_bridge, base)
static int dsi_mgr_bridge_get_id(struct drm_bridge *bridge)
{
struct dsi_bridge *dsi_bridge = to_dsi_bridge(bridge);
return dsi_bridge->id;
}
static int dsi_mgr_bridge_power_on(struct drm_bridge *bridge)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1);
struct mipi_dsi_host *host = msm_dsi->host;
struct msm_dsi_phy_shared_timings phy_shared_timings[DSI_MAX];
bool is_bonded_dsi = IS_BONDED_DSI();
int ret;
DBG("id=%d", id);
ret = dsi_mgr_phy_enable(id, phy_shared_timings);
if (ret)
goto phy_en_fail;
ret = msm_dsi_host_power_on(host, &phy_shared_timings[id], is_bonded_dsi, msm_dsi->phy);
if (ret) {
pr_err("%s: power on host %d failed, %d\n", __func__, id, ret);
goto host_on_fail;
}
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_power_on(msm_dsi1->host,
&phy_shared_timings[DSI_1], is_bonded_dsi, msm_dsi1->phy);
if (ret) {
pr_err("%s: power on host1 failed, %d\n",
__func__, ret);
goto host1_on_fail;
}
}
/*
* Enable before preparing the panel, disable after unpreparing, so
* that the panel can communicate over the DSI link.
*/
msm_dsi_host_enable_irq(host);
if (is_bonded_dsi && msm_dsi1)
msm_dsi_host_enable_irq(msm_dsi1->host);
return 0;
host1_on_fail:
msm_dsi_host_power_off(host);
host_on_fail:
dsi_mgr_phy_disable(id);
phy_en_fail:
return ret;
}
static void dsi_mgr_bridge_power_off(struct drm_bridge *bridge)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1);
struct mipi_dsi_host *host = msm_dsi->host;
bool is_bonded_dsi = IS_BONDED_DSI();
msm_dsi_host_disable_irq(host);
if (is_bonded_dsi && msm_dsi1) {
msm_dsi_host_disable_irq(msm_dsi1->host);
msm_dsi_host_power_off(msm_dsi1->host);
}
msm_dsi_host_power_off(host);
dsi_mgr_phy_disable(id);
}
static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1);
struct mipi_dsi_host *host = msm_dsi->host;
bool is_bonded_dsi = IS_BONDED_DSI();
int ret;
DBG("id=%d", id);
/* Do nothing with the host if it is slave-DSI in case of bonded DSI */
if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id))
return;
ret = dsi_mgr_bridge_power_on(bridge);
if (ret) {
dev_err(&msm_dsi->pdev->dev, "Power on failed: %d\n", ret);
return;
}
ret = msm_dsi_host_enable(host);
if (ret) {
pr_err("%s: enable host %d failed, %d\n", __func__, id, ret);
goto host_en_fail;
}
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_enable(msm_dsi1->host);
if (ret) {
pr_err("%s: enable host1 failed, %d\n", __func__, ret);
goto host1_en_fail;
}
}
return;
host1_en_fail:
msm_dsi_host_disable(host);
host_en_fail:
dsi_mgr_bridge_power_off(bridge);
}
void msm_dsi_manager_tpg_enable(void)
{
struct msm_dsi *m_dsi = dsi_mgr_get_dsi(DSI_0);
struct msm_dsi *s_dsi = dsi_mgr_get_dsi(DSI_1);
/* if dual dsi, trigger tpg on master first then slave */
if (m_dsi) {
msm_dsi_host_test_pattern_en(m_dsi->host);
if (IS_BONDED_DSI() && s_dsi)
msm_dsi_host_test_pattern_en(s_dsi->host);
}
}
static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1);
struct mipi_dsi_host *host = msm_dsi->host;
bool is_bonded_dsi = IS_BONDED_DSI();
int ret;
DBG("id=%d", id);
/*
* Do nothing with the host if it is slave-DSI in case of bonded DSI.
* It is safe to call dsi_mgr_phy_disable() here because a single PHY
* won't be diabled until both PHYs request disable.
*/
if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id))
goto disable_phy;
ret = msm_dsi_host_disable(host);
if (ret)
pr_err("%s: host %d disable failed, %d\n", __func__, id, ret);
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_disable(msm_dsi1->host);
if (ret)
pr_err("%s: host1 disable failed, %d\n", __func__, ret);
}
msm_dsi_host_disable_irq(host);
if (is_bonded_dsi && msm_dsi1)
msm_dsi_host_disable_irq(msm_dsi1->host);
/* Save PHY status if it is a clock source */
msm_dsi_phy_pll_save_state(msm_dsi->phy);
ret = msm_dsi_host_power_off(host);
if (ret)
pr_err("%s: host %d power off failed,%d\n", __func__, id, ret);
if (is_bonded_dsi && msm_dsi1) {
ret = msm_dsi_host_power_off(msm_dsi1->host);
if (ret)
pr_err("%s: host1 power off failed, %d\n",
__func__, ret);
}
disable_phy:
dsi_mgr_phy_disable(id);
}
static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
int id = dsi_mgr_bridge_get_id(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_bonded_dsi = IS_BONDED_DSI();
DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
if (is_bonded_dsi && !IS_MASTER_DSI_LINK(id))
return;
msm_dsi_host_set_display_mode(host, adjusted_mode);
if (is_bonded_dsi && other_dsi)
msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode);
}
static enum drm_mode_status dsi_mgr_bridge_mode_valid(struct drm_bridge *bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct mipi_dsi_host *host = msm_dsi->host;
struct platform_device *pdev = msm_dsi->pdev;
struct dev_pm_opp *opp;
unsigned long byte_clk_rate;
byte_clk_rate = dsi_byte_clk_get_rate(host, IS_BONDED_DSI(), mode);
opp = dev_pm_opp_find_freq_ceil(&pdev->dev, &byte_clk_rate);
if (!IS_ERR(opp)) {
dev_pm_opp_put(opp);
} else if (PTR_ERR(opp) == -ERANGE) {
/*
* An empty table is created by devm_pm_opp_set_clkname() even
* if there is none. Thus find_freq_ceil will still return
* -ERANGE in such case.
*/
if (dev_pm_opp_get_opp_count(&pdev->dev) != 0)
return MODE_CLOCK_RANGE;
} else {
return MODE_ERROR;
}
return msm_dsi_host_check_dsc(host, mode);
}
static int dsi_mgr_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
return drm_bridge_attach(encoder, msm_dsi->next_bridge,
bridge, flags);
}
static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = {
.attach = dsi_mgr_bridge_attach,
.pre_enable = dsi_mgr_bridge_pre_enable,
.post_disable = dsi_mgr_bridge_post_disable,
.mode_set = dsi_mgr_bridge_mode_set,
.mode_valid = dsi_mgr_bridge_mode_valid,
};
/* initialize bridge */
int msm_dsi_manager_connector_init(struct msm_dsi *msm_dsi,
struct drm_encoder *encoder)
{
struct drm_device *dev = msm_dsi->dev;
struct drm_bridge *bridge;
struct dsi_bridge *dsi_bridge;
struct drm_connector *connector;
int ret;
dsi_bridge = devm_drm_bridge_alloc(msm_dsi->dev->dev, struct dsi_bridge, base,
&dsi_mgr_bridge_funcs);
if (IS_ERR(dsi_bridge))
return PTR_ERR(dsi_bridge);
dsi_bridge->id = msm_dsi->id;
bridge = &dsi_bridge->base;
ret = devm_drm_bridge_add(msm_dsi->dev->dev, bridge);
if (ret)
return ret;
ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
connector = drm_bridge_connector_init(dev, encoder);
if (IS_ERR(connector)) {
DRM_ERROR("Unable to create bridge connector\n");
return PTR_ERR(connector);
}
return 0;
}
int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg)
{
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0);
struct mipi_dsi_host *host = msm_dsi->host;
bool is_read = (msg->rx_buf && msg->rx_len);
bool need_sync = (IS_SYNC_NEEDED() && !is_read);
int ret;
if (!msg->tx_buf || !msg->tx_len)
return 0;
/* In bonded master case, panel requires the same commands sent to
* both DSI links. Host issues the command trigger to both links
* when DSI_1 calls the cmd transfer function, no matter it happens
* before or after DSI_0 cmd transfer.
*/
if (need_sync && (id == DSI_0))
return is_read ? msg->rx_len : msg->tx_len;
if (need_sync && msm_dsi0) {
ret = msm_dsi_host_xfer_prepare(msm_dsi0->host, msg);
if (ret) {
pr_err("%s: failed to prepare non-trigger host, %d\n",
__func__, ret);
return ret;
}
}
ret = msm_dsi_host_xfer_prepare(host, msg);
if (ret) {
pr_err("%s: failed to prepare host, %d\n", __func__, ret);
goto restore_host0;
}
ret = is_read ? msm_dsi_host_cmd_rx(host, msg) :
msm_dsi_host_cmd_tx(host, msg);
msm_dsi_host_xfer_restore(host, msg);
restore_host0:
if (need_sync && msm_dsi0)
msm_dsi_host_xfer_restore(msm_dsi0->host, msg);
return ret;
}
bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len)
{
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0);
struct mipi_dsi_host *host = msm_dsi->host;
if (IS_SYNC_NEEDED() && (id == DSI_0))
return false;
if (IS_SYNC_NEEDED() && msm_dsi0)
msm_dsi_host_cmd_xfer_commit(msm_dsi0->host, dma_base, len);
msm_dsi_host_cmd_xfer_commit(host, dma_base, len);
return true;
}
int msm_dsi_manager_register(struct msm_dsi *msm_dsi)
{
struct msm_dsi_manager *msm_dsim = &msm_dsim_glb;
int id = msm_dsi->id;
int ret;
if (id >= DSI_MAX) {
pr_err("%s: invalid id %d\n", __func__, id);
return -EINVAL;
}
if (msm_dsim->dsi[id]) {
pr_err("%s: dsi%d already registered\n", __func__, id);
return -EBUSY;
}
msm_dsim->dsi[id] = msm_dsi;
ret = dsi_mgr_parse_of(msm_dsi->pdev->dev.of_node, id);
if (ret) {
pr_err("%s: failed to parse OF DSI info\n", __func__);
goto fail;
}
ret = dsi_mgr_setup_components(id);
if (ret) {
pr_err("%s: failed to register mipi dsi host for DSI %d: %d\n",
__func__, id, ret);
goto fail;
}
return 0;
fail:
msm_dsim->dsi[id] = NULL;
return ret;
}
void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi)
{
struct msm_dsi_manager *msm_dsim = &msm_dsim_glb;
if (msm_dsi->host)
msm_dsi_host_unregister(msm_dsi->host);
if (msm_dsi->id >= 0)
msm_dsim->dsi[msm_dsi->id] = NULL;
}
bool msm_dsi_is_bonded_dsi(struct msm_dsi *msm_dsi)
{
return IS_BONDED_DSI();
}
bool msm_dsi_is_master_dsi(struct msm_dsi *msm_dsi)
{
return IS_MASTER_DSI_LINK(msm_dsi->id);
}
const char *msm_dsi_get_te_source(struct msm_dsi *msm_dsi)
{
return msm_dsi->te_source;
}
@@ -0,0 +1,871 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#include <dt-bindings/phy/phy.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,
struct msm_dsi_phy_clk_request *clk_req)
{
const unsigned long bit_rate = clk_req->bitclk_rate;
const unsigned long esc_rate = clk_req->escclk_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->shared_timings.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->shared_timings.clk_pre = temp >> 1;
timing->shared_timings.clk_pre_inc_by_2 = true;
} else {
timing->shared_timings.clk_pre =
linear_inter(tmax, tmin, pcnt2, 0, false);
timing->shared_timings.clk_pre_inc_by_2 = 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, %d",
timing->shared_timings.clk_pre, timing->shared_timings.clk_post,
timing->shared_timings.clk_pre_inc_by_2, 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;
}
int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req)
{
const unsigned long bit_rate = clk_req->bitclk_rate;
const unsigned long esc_rate = clk_req->escclk_rate;
s32 ui, ui_x8;
s32 tmax, tmin;
s32 pcnt0 = 50;
s32 pcnt1 = 50;
s32 pcnt2 = 10;
s32 pcnt3 = 30;
s32 pcnt4 = 10;
s32 pcnt5 = 2;
s32 coeff = 1000; /* Precision, should avoid overflow */
s32 hb_en, hb_en_ckln, pd_ckln, pd;
s32 val, val_ckln;
s32 temp;
if (!bit_rate || !esc_rate)
return -EINVAL;
timing->hs_halfbyte_en = 0;
hb_en = 0;
timing->hs_halfbyte_en_ckln = 0;
hb_en_ckln = 0;
timing->hs_prep_dly_ckln = (bit_rate > 100000000) ? 0 : 3;
pd_ckln = timing->hs_prep_dly_ckln;
timing->hs_prep_dly = (bit_rate > 120000000) ? 0 : 1;
pd = timing->hs_prep_dly;
val = (hb_en << 2) + (pd << 1);
val_ckln = (hb_en_ckln << 2) + (pd_ckln << 1);
ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
ui_x8 = ui << 3;
temp = S_DIV_ROUND_UP(38 * coeff - val_ckln * ui, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (95 * coeff - val_ckln * ui) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false);
temp = 300 * coeff - ((timing->clk_prepare << 3) + val_ckln) * ui;
tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3;
tmax = (tmin > 255) ? 511 : 255;
timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp + 3 * ui) / ui_x8;
timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false);
temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui - val * ui, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (85 * coeff + 6 * ui - val * ui) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false);
temp = 145 * coeff + 10 * ui - ((timing->hs_prepare << 3) + val) * ui;
tmin = S_DIV_ROUND_UP(temp - 11 * ui, ui_x8) - 3;
tmax = 255;
timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 4 * ui + 3 * ui, ui_x8);
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp + 3 * ui) / ui_x8;
timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false);
temp = 50 * coeff + ((hb_en << 2) - 8) * ui;
timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);
tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;
tmax = 255;
timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false);
temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui;
timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8);
temp = 60 * coeff + 52 * ui - 43 * ui;
tmin = DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 63;
timing->shared_timings.clk_post =
linear_inter(tmax, tmin, pcnt2, 0, false);
temp = 8 * ui + ((timing->clk_prepare << 3) + val_ckln) * ui;
temp += (((timing->clk_zero + 3) << 3) + 11 - (pd_ckln << 1)) * ui;
temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) :
(((timing->hs_rqst_ckln << 3) + 8) * ui);
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 63;
if (tmin > tmax) {
temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false);
timing->shared_timings.clk_pre = temp >> 1;
timing->shared_timings.clk_pre_inc_by_2 = 1;
} else {
timing->shared_timings.clk_pre =
linear_inter(tmax, tmin, pcnt2, 0, false);
timing->shared_timings.clk_pre_inc_by_2 = 0;
}
timing->ta_go = 3;
timing->ta_sure = 0;
timing->ta_get = 4;
DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
timing->shared_timings.clk_pre, timing->shared_timings.clk_post,
timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero,
timing->clk_trail, timing->clk_prepare, timing->hs_exit,
timing->hs_zero, timing->hs_prepare, timing->hs_trail,
timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en,
timing->hs_halfbyte_en_ckln, timing->hs_prep_dly,
timing->hs_prep_dly_ckln);
return 0;
}
int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req)
{
const unsigned long bit_rate = clk_req->bitclk_rate;
const unsigned long esc_rate = clk_req->escclk_rate;
s32 ui, ui_x8;
s32 tmax, tmin;
s32 pcnt0 = 50;
s32 pcnt1 = 50;
s32 pcnt2 = 10;
s32 pcnt3 = 30;
s32 pcnt4 = 10;
s32 pcnt5 = 2;
s32 coeff = 1000; /* Precision, should avoid overflow */
s32 hb_en, hb_en_ckln;
s32 temp;
if (!bit_rate || !esc_rate)
return -EINVAL;
timing->hs_halfbyte_en = 0;
hb_en = 0;
timing->hs_halfbyte_en_ckln = 0;
hb_en_ckln = 0;
ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
ui_x8 = ui << 3;
temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (95 * coeff) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false);
temp = 300 * coeff - (timing->clk_prepare << 3) * ui;
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = (tmin > 255) ? 511 : 255;
timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp + 3 * ui) / ui_x8;
timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false);
temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (85 * coeff + 6 * ui) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false);
temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui;
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 255;
timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1;
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp / ui_x8) - 1;
timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false);
temp = 50 * coeff + ((hb_en << 2) - 8) * ui;
timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);
tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;
tmax = 255;
timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false);
temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui;
timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8);
temp = 60 * coeff + 52 * ui - 43 * ui;
tmin = DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 63;
timing->shared_timings.clk_post =
linear_inter(tmax, tmin, pcnt2, 0, false);
temp = 8 * ui + (timing->clk_prepare << 3) * ui;
temp += (((timing->clk_zero + 3) << 3) + 11) * ui;
temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) :
(((timing->hs_rqst_ckln << 3) + 8) * ui);
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 63;
if (tmin > tmax) {
temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false);
timing->shared_timings.clk_pre = temp >> 1;
timing->shared_timings.clk_pre_inc_by_2 = 1;
} else {
timing->shared_timings.clk_pre =
linear_inter(tmax, tmin, pcnt2, 0, false);
timing->shared_timings.clk_pre_inc_by_2 = 0;
}
timing->shared_timings.byte_intf_clk_div_2 = true;
timing->ta_go = 3;
timing->ta_sure = 0;
timing->ta_get = 4;
DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
timing->shared_timings.clk_pre, timing->shared_timings.clk_post,
timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero,
timing->clk_trail, timing->clk_prepare, timing->hs_exit,
timing->hs_zero, timing->hs_prepare, timing->hs_trail,
timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en,
timing->hs_halfbyte_en_ckln, timing->hs_prep_dly,
timing->hs_prep_dly_ckln);
return 0;
}
int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req)
{
const unsigned long bit_rate = clk_req->bitclk_rate;
const unsigned long esc_rate = clk_req->escclk_rate;
s32 ui, ui_x8;
s32 tmax, tmin;
s32 pcnt_clk_prep = 50;
s32 pcnt_clk_zero = 2;
s32 pcnt_clk_trail = 30;
s32 pcnt_hs_prep = 50;
s32 pcnt_hs_zero = 10;
s32 pcnt_hs_trail = 30;
s32 pcnt_hs_exit = 10;
s32 coeff = 1000; /* Precision, should avoid overflow */
s32 hb_en;
s32 temp;
if (!bit_rate || !esc_rate)
return -EINVAL;
hb_en = 0;
ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000);
ui_x8 = ui << 3;
/* TODO: verify these calculations against latest downstream driver
* everything except clk_post/clk_pre uses calculations from v3 based
* on the downstream driver having the same calculations for v3 and v4
*/
temp = S_DIV_ROUND_UP(38 * coeff, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (95 * coeff) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->clk_prepare = linear_inter(tmax, tmin, pcnt_clk_prep, 0, false);
temp = 300 * coeff - (timing->clk_prepare << 3) * ui;
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = (tmin > 255) ? 511 : 255;
timing->clk_zero = linear_inter(tmax, tmin, pcnt_clk_zero, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8);
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp + 3 * ui) / ui_x8;
timing->clk_trail = linear_inter(tmax, tmin, pcnt_clk_trail, 0, false);
temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8);
tmin = max_t(s32, temp, 0);
temp = (85 * coeff + 6 * ui) / ui_x8;
tmax = max_t(s32, temp, 0);
timing->hs_prepare = linear_inter(tmax, tmin, pcnt_hs_prep, 0, false);
temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui;
tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1;
tmax = 255;
timing->hs_zero = linear_inter(tmax, tmin, pcnt_hs_zero, 0, false);
tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1;
temp = 105 * coeff + 12 * ui - 20 * coeff;
tmax = (temp / ui_x8) - 1;
timing->hs_trail = linear_inter(tmax, tmin, pcnt_hs_trail, 0, false);
temp = 50 * coeff + ((hb_en << 2) - 8) * ui;
timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8);
tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1;
tmax = 255;
timing->hs_exit = linear_inter(tmax, tmin, pcnt_hs_exit, 0, false);
/* recommended min
* = roundup((mipi_min_ns + t_hs_trail_ns)/(16*bit_clk_ns), 0) - 1
*/
temp = 60 * coeff + 52 * ui + + (timing->hs_trail + 1) * ui_x8;
tmin = DIV_ROUND_UP(temp, 16 * ui) - 1;
tmax = 255;
timing->shared_timings.clk_post = linear_inter(tmax, tmin, 5, 0, false);
/* recommended min
* val1 = (tlpx_ns + clk_prepare_ns + clk_zero_ns + hs_rqst_ns)
* val2 = (16 * bit_clk_ns)
* final = roundup(val1/val2, 0) - 1
*/
temp = 52 * coeff + (timing->clk_prepare + timing->clk_zero + 1) * ui_x8 + 54 * coeff;
tmin = DIV_ROUND_UP(temp, 16 * ui) - 1;
tmax = 255;
timing->shared_timings.clk_pre = DIV_ROUND_UP((tmax - tmin) * 125, 10000) + tmin;
timing->shared_timings.byte_intf_clk_div_2 = true;
DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
timing->shared_timings.clk_pre, timing->shared_timings.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;
}
int msm_dsi_cphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req)
{
const unsigned long bit_rate = clk_req->bitclk_rate;
const unsigned long esc_rate = clk_req->escclk_rate;
s32 ui, ui_x7;
s32 tmax, tmin;
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);
ui_x7 = ui * 7;
temp = S_DIV_ROUND_UP(38 * coeff, ui_x7);
tmin = max_t(s32, temp, 0);
temp = (95 * coeff) / ui_x7;
tmax = max_t(s32, temp, 0);
timing->clk_prepare = linear_inter(tmax, tmin, 50, 0, false);
tmin = DIV_ROUND_UP(50 * coeff, ui_x7);
tmax = 255;
timing->hs_rqst = linear_inter(tmax, tmin, 1, 0, false);
tmin = DIV_ROUND_UP(100 * coeff, ui_x7) - 1;
tmax = 255;
timing->hs_exit = linear_inter(tmax, tmin, 10, 0, false);
tmin = 1;
tmax = 32;
timing->shared_timings.clk_post = linear_inter(tmax, tmin, 80, 0, false);
tmin = min_t(s32, 64, S_DIV_ROUND_UP(262 * coeff, ui_x7) - 1);
tmax = 64;
timing->shared_timings.clk_pre = linear_inter(tmax, tmin, 20, 0, false);
DBG("%d, %d, %d, %d, %d",
timing->shared_timings.clk_pre, timing->shared_timings.clk_post,
timing->clk_prepare, timing->hs_exit, timing->hs_rqst);
return 0;
}
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-hpm-fam-b",
.data = &dsi_phy_28nm_hpm_famb_cfgs },
{ .compatible = "qcom,dsi-phy-28nm-lp",
.data = &dsi_phy_28nm_lp_cfgs },
{ .compatible = "qcom,dsi-phy-28nm-8226",
.data = &dsi_phy_28nm_8226_cfgs },
{ .compatible = "qcom,dsi-phy-28nm-8937",
.data = &dsi_phy_28nm_8937_cfgs },
#endif
#ifdef CONFIG_DRM_MSM_DSI_20NM_PHY
{ .compatible = "qcom,dsi-phy-20nm",
.data = &dsi_phy_20nm_cfgs },
#endif
#ifdef CONFIG_DRM_MSM_DSI_28NM_8960_PHY
{ .compatible = "qcom,dsi-phy-28nm-8960",
.data = &dsi_phy_28nm_8960_cfgs },
#endif
#ifdef CONFIG_DRM_MSM_DSI_14NM_PHY
{ .compatible = "qcom,dsi-phy-14nm",
.data = &dsi_phy_14nm_cfgs },
{ .compatible = "qcom,dsi-phy-14nm-2290",
.data = &dsi_phy_14nm_2290_cfgs },
{ .compatible = "qcom,dsi-phy-14nm-660",
.data = &dsi_phy_14nm_660_cfgs },
{ .compatible = "qcom,dsi-phy-14nm-8953",
.data = &dsi_phy_14nm_8953_cfgs },
{ .compatible = "qcom,sm6125-dsi-phy-14nm",
.data = &dsi_phy_14nm_2290_cfgs },
{ .compatible = "qcom,sm6150-dsi-phy-14nm",
.data = &dsi_phy_14nm_6150_cfgs },
#endif
#ifdef CONFIG_DRM_MSM_DSI_10NM_PHY
{ .compatible = "qcom,dsi-phy-10nm",
.data = &dsi_phy_10nm_cfgs },
{ .compatible = "qcom,dsi-phy-10nm-8998",
.data = &dsi_phy_10nm_8998_cfgs },
#endif
#ifdef CONFIG_DRM_MSM_DSI_7NM_PHY
{ .compatible = "qcom,dsi-phy-7nm",
.data = &dsi_phy_7nm_cfgs },
{ .compatible = "qcom,dsi-phy-7nm-8150",
.data = &dsi_phy_7nm_8150_cfgs },
{ .compatible = "qcom,sa8775p-dsi-phy-5nm",
.data = &dsi_phy_5nm_8775p_cfgs },
{ .compatible = "qcom,sar2130p-dsi-phy-5nm",
.data = &dsi_phy_5nm_sar2130p_cfgs },
{ .compatible = "qcom,sc7280-dsi-phy-7nm",
.data = &dsi_phy_7nm_7280_cfgs },
{ .compatible = "qcom,sm6375-dsi-phy-7nm",
.data = &dsi_phy_7nm_6375_cfgs },
{ .compatible = "qcom,sm8350-dsi-phy-5nm",
.data = &dsi_phy_5nm_8350_cfgs },
{ .compatible = "qcom,sm8450-dsi-phy-5nm",
.data = &dsi_phy_5nm_8450_cfgs },
{ .compatible = "qcom,milos-dsi-phy-4nm",
.data = &dsi_phy_4nm_milos_cfgs },
{ .compatible = "qcom,sm8550-dsi-phy-4nm",
.data = &dsi_phy_4nm_8550_cfgs },
{ .compatible = "qcom,sm8650-dsi-phy-4nm",
.data = &dsi_phy_4nm_8650_cfgs },
{ .compatible = "qcom,sm8750-dsi-phy-3nm",
.data = &dsi_phy_3nm_8750_cfgs },
{ .compatible = "qcom,kaanapali-dsi-phy-3nm",
.data = &dsi_phy_3nm_kaanapali_cfgs },
#endif
{}
};
MODULE_DEVICE_TABLE(of, dsi_phy_dt_match);
/*
* Currently, we only support one SoC for each PHY type. When we have multiple
* SoCs for the same PHY, we can try to make the index searching a bit more
* clever.
*/
static int dsi_phy_get_id(struct msm_dsi_phy *phy)
{
struct platform_device *pdev = phy->pdev;
const struct msm_dsi_phy_cfg *cfg = phy->cfg;
struct resource *res;
int i;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_phy");
if (!res)
return -EINVAL;
for (i = 0; i < cfg->num_dsi_phy; i++) {
if (cfg->io_start[i] == res->start)
return i;
}
return -EINVAL;
}
static int dsi_phy_driver_probe(struct platform_device *pdev)
{
struct msm_dsi_phy *phy;
struct device *dev = &pdev->dev;
u32 phy_type;
int ret;
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
phy->provided_clocks = devm_kzalloc(dev,
struct_size(phy->provided_clocks, hws, NUM_PROVIDED_CLKS),
GFP_KERNEL);
if (!phy->provided_clocks)
return -ENOMEM;
phy->provided_clocks->num = NUM_PROVIDED_CLKS;
phy->cfg = of_device_get_match_data(&pdev->dev);
if (!phy->cfg)
return -ENODEV;
phy->pdev = pdev;
phy->id = dsi_phy_get_id(phy);
if (phy->id < 0)
return dev_err_probe(dev, phy->id,
"Couldn't identify PHY index\n");
phy->regulator_ldo_mode = of_property_read_bool(dev->of_node,
"qcom,dsi-phy-regulator-ldo-mode");
if (!of_property_read_u32(dev->of_node, "phy-type", &phy_type))
phy->cphy_mode = (phy_type == PHY_TYPE_CPHY);
phy->base = msm_ioremap_size(pdev, "dsi_phy", &phy->base_size);
if (IS_ERR(phy->base))
return dev_err_probe(dev, PTR_ERR(phy->base),
"Failed to map phy base\n");
phy->pll_base = msm_ioremap_size(pdev, "dsi_pll", &phy->pll_size);
if (IS_ERR(phy->pll_base))
return dev_err_probe(dev, PTR_ERR(phy->pll_base),
"Failed to map pll base\n");
if (phy->cfg->has_phy_lane) {
phy->lane_base = msm_ioremap_size(pdev, "dsi_phy_lane", &phy->lane_size);
if (IS_ERR(phy->lane_base))
return dev_err_probe(dev, PTR_ERR(phy->lane_base),
"Failed to map phy lane base\n");
}
if (phy->cfg->has_phy_regulator) {
phy->reg_base = msm_ioremap_size(pdev, "dsi_phy_regulator", &phy->reg_size);
if (IS_ERR(phy->reg_base))
return dev_err_probe(dev, PTR_ERR(phy->reg_base),
"Failed to map phy regulator base\n");
}
if (phy->cfg->ops.parse_dt_properties) {
ret = phy->cfg->ops.parse_dt_properties(phy);
if (ret)
return ret;
}
ret = devm_regulator_bulk_get_const(dev, phy->cfg->num_regulators,
phy->cfg->regulator_data,
&phy->supplies);
if (ret)
return ret;
platform_set_drvdata(pdev, phy);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
ret = devm_pm_clk_create(dev);
if (ret)
return ret;
ret = pm_clk_add(dev, "iface");
if (ret < 0)
return dev_err_probe(dev, ret, "Unable to get iface clk\n");
if (phy->cfg->ops.pll_init) {
ret = phy->cfg->ops.pll_init(phy);
if (ret)
return dev_err_probe(dev, ret,
"PLL init failed; need separate clk driver\n");
}
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
phy->provided_clocks);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register clk provider\n");
return 0;
}
static const struct dev_pm_ops dsi_phy_pm_ops = {
SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
};
static struct platform_driver dsi_phy_platform_driver = {
.probe = dsi_phy_driver_probe,
.driver = {
.name = "msm_dsi_phy",
.of_match_table = dsi_phy_dt_match,
.pm = &dsi_phy_pm_ops,
},
};
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,
struct msm_dsi_phy_clk_request *clk_req,
struct msm_dsi_phy_shared_timings *shared_timings)
{
struct device *dev;
int ret;
if (!phy || !phy->cfg->ops.enable)
return -EINVAL;
dev = &phy->pdev->dev;
ret = pm_runtime_resume_and_get(dev);
if (ret) {
DRM_DEV_ERROR(dev, "%s: resume failed, %d\n",
__func__, ret);
goto res_en_fail;
}
ret = regulator_bulk_enable(phy->cfg->num_regulators, phy->supplies);
if (ret) {
DRM_DEV_ERROR(dev, "%s: regulator enable failed, %d\n",
__func__, ret);
goto reg_en_fail;
}
ret = phy->cfg->ops.enable(phy, clk_req);
if (ret) {
DRM_DEV_ERROR(dev, "%s: phy enable failed, %d\n", __func__, ret);
goto phy_en_fail;
}
memcpy(shared_timings, &phy->timing.shared_timings,
sizeof(*shared_timings));
/*
* Resetting 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 (phy->usecase != MSM_DSI_PHY_SLAVE) {
ret = msm_dsi_phy_pll_restore_state(phy);
if (ret) {
DRM_DEV_ERROR(dev, "%s: failed to restore phy state, %d\n",
__func__, ret);
goto pll_restor_fail;
}
}
return 0;
pll_restor_fail:
if (phy->cfg->ops.disable)
phy->cfg->ops.disable(phy);
phy_en_fail:
regulator_bulk_disable(phy->cfg->num_regulators, phy->supplies);
reg_en_fail:
pm_runtime_put(dev);
res_en_fail:
return ret;
}
void msm_dsi_phy_disable(struct msm_dsi_phy *phy)
{
if (!phy || !phy->cfg->ops.disable)
return;
phy->cfg->ops.disable(phy);
regulator_bulk_disable(phy->cfg->num_regulators, phy->supplies);
pm_runtime_put(&phy->pdev->dev);
}
void msm_dsi_phy_set_usecase(struct msm_dsi_phy *phy,
enum msm_dsi_phy_usecase uc)
{
if (phy)
phy->usecase = uc;
}
/* Returns true if we have to clear DSI_LANE_CTRL.HS_REQ_SEL_PHY */
bool msm_dsi_phy_set_continuous_clock(struct msm_dsi_phy *phy, bool enable)
{
if (!phy || !phy->cfg->ops.set_continuous_clock)
return false;
return phy->cfg->ops.set_continuous_clock(phy, enable);
}
void msm_dsi_phy_pll_save_state(struct msm_dsi_phy *phy)
{
if (phy->cfg->ops.save_pll_state) {
phy->cfg->ops.save_pll_state(phy);
phy->state_saved = true;
}
}
int msm_dsi_phy_pll_restore_state(struct msm_dsi_phy *phy)
{
int ret;
if (phy->cfg->ops.restore_pll_state && phy->state_saved) {
ret = phy->cfg->ops.restore_pll_state(phy);
if (ret)
return ret;
phy->state_saved = false;
}
return 0;
}
void msm_dsi_phy_snapshot(struct msm_disp_state *disp_state, struct msm_dsi_phy *phy)
{
msm_disp_snapshot_add_block(disp_state,
phy->base_size, phy->base,
"dsi%d_phy", phy->id);
/* Do not try accessing PLL registers if it is switched off */
if (phy->pll_on)
msm_disp_snapshot_add_block(disp_state,
phy->pll_size, phy->pll_base,
"dsi%d_pll", phy->id);
if (phy->lane_base)
msm_disp_snapshot_add_block(disp_state,
phy->lane_size, phy->lane_base,
"dsi%d_lane", phy->id);
if (phy->reg_base)
msm_disp_snapshot_add_block(disp_state,
phy->reg_size, phy->reg_base,
"dsi%d_reg", phy->id);
}
@@ -0,0 +1,141 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#ifndef __DSI_PHY_H__
#define __DSI_PHY_H__
#include <dt-bindings/clock/qcom,dsi-phy-28nm.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include "dsi.h"
struct msm_dsi_phy_ops {
int (*pll_init)(struct msm_dsi_phy *phy);
int (*enable)(struct msm_dsi_phy *phy,
struct msm_dsi_phy_clk_request *clk_req);
void (*disable)(struct msm_dsi_phy *phy);
void (*save_pll_state)(struct msm_dsi_phy *phy);
int (*restore_pll_state)(struct msm_dsi_phy *phy);
bool (*set_continuous_clock)(struct msm_dsi_phy *phy, bool enable);
int (*parse_dt_properties)(struct msm_dsi_phy *phy);
};
struct msm_dsi_phy_cfg {
const struct regulator_bulk_data *regulator_data;
int num_regulators;
struct msm_dsi_phy_ops ops;
unsigned long min_pll_rate;
unsigned long max_pll_rate;
const resource_size_t io_start[DSI_MAX];
const int num_dsi_phy;
const int quirks;
bool has_phy_regulator;
bool has_phy_lane;
};
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_famb_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8226_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8937_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_6150_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_2290_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_14nm_8953_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_10nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_10nm_8998_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_6375_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_8150_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_7nm_7280_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_5nm_8350_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_5nm_8450_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_5nm_8775p_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_5nm_sar2130p_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_4nm_milos_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_4nm_8550_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_4nm_8650_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_3nm_8750_cfgs;
extern const struct msm_dsi_phy_cfg dsi_phy_3nm_kaanapali_cfgs;
struct msm_dsi_dphy_timing {
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_shared_timings shared_timings;
/* For PHY v2 only */
u32 hs_rqst_ckln;
u32 hs_prep_dly;
u32 hs_prep_dly_ckln;
u8 hs_halfbyte_en;
u8 hs_halfbyte_en_ckln;
};
#define NUM_PROVIDED_CLKS (DSI_PIXEL_PLL_CLK + 1)
#define DSI_LANE_MAX 5
struct msm_dsi_phy {
struct platform_device *pdev;
void __iomem *base;
void __iomem *pll_base;
void __iomem *reg_base;
void __iomem *lane_base;
phys_addr_t base_size;
phys_addr_t pll_size;
phys_addr_t reg_size;
phys_addr_t lane_size;
int id;
struct regulator_bulk_data *supplies;
struct msm_dsi_dphy_timing timing;
const struct msm_dsi_phy_cfg *cfg;
void *tuning_cfg;
enum msm_dsi_phy_usecase usecase;
bool regulator_ldo_mode;
bool cphy_mode;
struct clk_hw *vco_hw;
bool pll_on;
struct clk_hw_onecell_data *provided_clocks;
bool state_saved;
};
/*
* PHY internal functions
*/
int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
int msm_dsi_cphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing,
struct msm_dsi_phy_clk_request *clk_req);
#endif /* __DSI_PHY_H__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include "dsi_phy.h"
#include "dsi.xml.h"
#include "dsi_phy_20nm.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;
writel(DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero),
base + REG_DSI_20nm_PHY_TIMING_CTRL_0);
writel(DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail),
base + REG_DSI_20nm_PHY_TIMING_CTRL_1);
writel(DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare),
base + REG_DSI_20nm_PHY_TIMING_CTRL_2);
if (timing->clk_zero & BIT(8))
writel(DSI_20nm_PHY_TIMING_CTRL_3_CLK_ZERO_8,
base + REG_DSI_20nm_PHY_TIMING_CTRL_3);
writel(DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit),
base + REG_DSI_20nm_PHY_TIMING_CTRL_4);
writel(DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero),
base + REG_DSI_20nm_PHY_TIMING_CTRL_5);
writel(DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare),
base + REG_DSI_20nm_PHY_TIMING_CTRL_6);
writel(DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail),
base + REG_DSI_20nm_PHY_TIMING_CTRL_7);
writel(DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst),
base + REG_DSI_20nm_PHY_TIMING_CTRL_8);
writel(DSI_20nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) |
DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure),
base + REG_DSI_20nm_PHY_TIMING_CTRL_9);
writel(DSI_20nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get),
base + REG_DSI_20nm_PHY_TIMING_CTRL_10);
writel(DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0),
base + REG_DSI_20nm_PHY_TIMING_CTRL_11);
}
static void dsi_20nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable)
{
void __iomem *base = phy->reg_base;
if (!enable) {
writel(0, base + REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG);
return;
}
if (phy->regulator_ldo_mode) {
writel(0x1d, phy->base + REG_DSI_20nm_PHY_LDO_CNTRL);
return;
}
/* non LDO mode */
writel(0x03, base + REG_DSI_20nm_PHY_REGULATOR_CTRL_1);
writel(0x03, base + REG_DSI_20nm_PHY_REGULATOR_CTRL_2);
writel(0x00, base + REG_DSI_20nm_PHY_REGULATOR_CTRL_3);
writel(0x20, base + REG_DSI_20nm_PHY_REGULATOR_CTRL_4);
writel(0x01, base + REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG);
writel(0x00, phy->base + REG_DSI_20nm_PHY_LDO_CNTRL);
writel(0x03, base + REG_DSI_20nm_PHY_REGULATOR_CTRL_0);
}
static int dsi_20nm_phy_enable(struct msm_dsi_phy *phy,
struct msm_dsi_phy_clk_request *clk_req)
{
struct msm_dsi_dphy_timing *timing = &phy->timing;
int i;
void __iomem *base = phy->base;
u32 cfg_4[4] = {0x20, 0x40, 0x20, 0x00};
u32 val;
DBG("");
if (msm_dsi_dphy_timing_calc(timing, clk_req)) {
DRM_DEV_ERROR(&phy->pdev->dev,
"%s: D-PHY timing calculation failed\n", __func__);
return -EINVAL;
}
dsi_20nm_phy_regulator_ctrl(phy, true);
writel(0xff, base + REG_DSI_20nm_PHY_STRENGTH_0);
val = readl(base + REG_DSI_20nm_PHY_GLBL_TEST_CTRL);
if (phy->id == DSI_1 && phy->usecase == MSM_DSI_PHY_STANDALONE)
val |= DSI_20nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL;
else
val &= ~DSI_20nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL;
writel(val, base + REG_DSI_20nm_PHY_GLBL_TEST_CTRL);
for (i = 0; i < 4; i++) {
writel((i >> 1) * 0x40, base + REG_DSI_20nm_PHY_LN_CFG_3(i));
writel(0x01, base + REG_DSI_20nm_PHY_LN_TEST_STR_0(i));
writel(0x46, base + REG_DSI_20nm_PHY_LN_TEST_STR_1(i));
writel(0x02, base + REG_DSI_20nm_PHY_LN_CFG_0(i));
writel(0xa0, base + REG_DSI_20nm_PHY_LN_CFG_1(i));
writel(cfg_4[i], base + REG_DSI_20nm_PHY_LN_CFG_4(i));
}
writel(0x80, base + REG_DSI_20nm_PHY_LNCK_CFG_3);
writel(0x01, base + REG_DSI_20nm_PHY_LNCK_TEST_STR0);
writel(0x46, base + REG_DSI_20nm_PHY_LNCK_TEST_STR1);
writel(0x00, base + REG_DSI_20nm_PHY_LNCK_CFG_0);
writel(0xa0, base + REG_DSI_20nm_PHY_LNCK_CFG_1);
writel(0x00, base + REG_DSI_20nm_PHY_LNCK_CFG_2);
writel(0x00, base + REG_DSI_20nm_PHY_LNCK_CFG_4);
dsi_20nm_dphy_set_timing(phy, timing);
writel(0x00, base + REG_DSI_20nm_PHY_CTRL_1);
writel(0x06, base + REG_DSI_20nm_PHY_STRENGTH_1);
/* make sure everything is written before enable */
wmb();
writel(0x7f, base + REG_DSI_20nm_PHY_CTRL_0);
return 0;
}
static void dsi_20nm_phy_disable(struct msm_dsi_phy *phy)
{
writel(0, phy->base + REG_DSI_20nm_PHY_CTRL_0);
dsi_20nm_phy_regulator_ctrl(phy, false);
}
static const struct regulator_bulk_data dsi_phy_20nm_regulators[] = {
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
{ .supply = "vcca", .init_load_uA = 10000 }, /* 1.0 V */
};
const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_20nm_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_20nm_regulators),
.ops = {
.enable = dsi_20nm_phy_enable,
.disable = dsi_20nm_phy_disable,
},
.io_start = { 0xfd998500, 0xfd9a0500 },
.num_dsi_phy = 2,
};
@@ -0,0 +1,956 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*/
#include <dt-bindings/clock/qcom,dsi-phy-28nm.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include "dsi_phy.h"
#include "dsi.xml.h"
#include "dsi_phy_28nm.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 VCO_REF_CLK_RATE 19200000
#define VCO_MIN_RATE 350000000
#define VCO_MAX_RATE 750000000
/* v2.0.0 28nm LP implementation */
#define DSI_PHY_28NM_QUIRK_PHY_LP BIT(0)
#define DSI_PHY_28NM_QUIRK_PHY_8226 BIT(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 clk_hw clk_hw;
struct msm_dsi_phy *phy;
struct pll_28nm_cached_state cached_state;
};
#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, clk_hw)
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 = readl(pll_28nm->phy->pll_base + 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->phy->pll_base;
/*
* Add HW recommended delays after toggling the software
* reset bit off and back on.
*/
writel(DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET, base + REG_DSI_28nm_PHY_PLL_TEST_CFG);
udelay(1);
writel(0, base + REG_DSI_28nm_PHY_PLL_TEST_CFG);
udelay(1);
}
/*
* Clock Callbacks
*/
static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
struct device *dev = &pll_28nm->phy->pdev->dev;
void __iomem *base = pll_28nm->phy->pll_base;
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 */
writel(3, base + REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG);
/* 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) {
DRM_DEV_ERROR(dev, "unable to get loop filter resistance. vco=%lu\n",
rate);
return -EINVAL;
}
writel(lpfr_lut[i].resistance, base + REG_DSI_28nm_PHY_PLL_LPFR_CFG);
/* Loop filter capacitance values : c1 and c2 */
writel(0x70, base + REG_DSI_28nm_PHY_PLL_LPFC1_CFG);
writel(0x15, base + REG_DSI_28nm_PHY_PLL_LPFC2_CFG);
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 = readl(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);
writel(0x02, base + REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG);
writel(0x2b, base + REG_DSI_28nm_PHY_PLL_CAL_CFG3);
writel(0x06, base + REG_DSI_28nm_PHY_PLL_CAL_CFG4);
writel(0x0d, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
writel(sdm_cfg1, base + REG_DSI_28nm_PHY_PLL_SDM_CFG1);
writel(DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(sdm_cfg2),
base + REG_DSI_28nm_PHY_PLL_SDM_CFG2);
writel(DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(sdm_cfg3),
base + REG_DSI_28nm_PHY_PLL_SDM_CFG3);
writel(0, base + REG_DSI_28nm_PHY_PLL_SDM_CFG4);
/* Add hardware recommended delay for correct PLL configuration */
if (pll_28nm->phy->cfg->quirks & DSI_PHY_28NM_QUIRK_PHY_LP)
udelay(1000);
else
udelay(1);
writel(refclk_cfg, base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG);
writel(0x00, base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG);
writel(0x31, base + REG_DSI_28nm_PHY_PLL_VCOLPF_CFG);
writel(sdm_cfg0, base + REG_DSI_28nm_PHY_PLL_SDM_CFG0);
writel(0x12, base + REG_DSI_28nm_PHY_PLL_CAL_CFG0);
writel(0x30, base + REG_DSI_28nm_PHY_PLL_CAL_CFG6);
writel(0x00, base + REG_DSI_28nm_PHY_PLL_CAL_CFG7);
writel(0x60, base + REG_DSI_28nm_PHY_PLL_CAL_CFG8);
writel(0x00, base + REG_DSI_28nm_PHY_PLL_CAL_CFG9);
writel(cal_cfg10 & 0xff, base + REG_DSI_28nm_PHY_PLL_CAL_CFG10);
writel(cal_cfg11 & 0xff, base + REG_DSI_28nm_PHY_PLL_CAL_CFG11);
writel(0x20, base + REG_DSI_28nm_PHY_PLL_EFUSE_CFG);
return 0;
}
static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
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 dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
void __iomem *base = pll_28nm->phy->pll_base;
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 = readl(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 = readl(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0);
if (sdm0 & DSI_28nm_PHY_PLL_SDM_CFG0_BYP) {
/* integer mode */
sdm_byp_div = FIELD(
readl(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(
readl(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(readl(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2),
DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0);
sdm3 = FIELD(readl(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 int _dsi_pll_28nm_vco_prepare_hpm(struct dsi_pll_28nm *pll_28nm)
{
struct device *dev = &pll_28nm->phy->pdev->dev;
void __iomem *base = pll_28nm->phy->pll_base;
u32 max_reads = 5, timeout_us = 100;
bool locked;
u32 val;
int i;
DBG("id=%d", pll_28nm->phy->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;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(1);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(600);
for (i = 0; i < 2; i++) {
/* DSI Uniphy lock detect setting */
writel(0x0c, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
udelay(100);
writel(0x0d, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
/* 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;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(1);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(250);
val &= ~DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(600);
}
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL Lock success");
return locked ? 0 : -EINVAL;
}
static int dsi_pll_28nm_vco_prepare_hpm(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
int i, ret;
if (unlikely(pll_28nm->phy->pll_on))
return 0;
for (i = 0; i < 3; i++) {
ret = _dsi_pll_28nm_vco_prepare_hpm(pll_28nm);
if (!ret) {
pll_28nm->phy->pll_on = true;
return 0;
}
}
return ret;
}
static int dsi_pll_28nm_vco_prepare_8226(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
struct device *dev = &pll_28nm->phy->pdev->dev;
void __iomem *base = pll_28nm->phy->pll_base;
u32 max_reads = 5, timeout_us = 100;
bool locked;
u32 val;
int i;
DBG("id=%d", pll_28nm->phy->id);
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
writel(0x34, base + REG_DSI_28nm_PHY_PLL_CAL_CFG1);
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(200);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(600);
for (i = 0; i < 7; i++) {
/* DSI Uniphy lock detect setting */
writel(0x0d, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
writel(0x0c, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
udelay(100);
writel(0x0d, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
/* 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.
*/
writel(0x00, base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG);
udelay(50);
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(100);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
udelay(600);
}
if (unlikely(!locked))
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
else
DBG("DSI PLL Lock success");
return locked ? 0 : -EINVAL;
}
static int dsi_pll_28nm_vco_prepare_lp(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
struct device *dev = &pll_28nm->phy->pdev->dev;
void __iomem *base = pll_28nm->phy->pll_base;
bool locked;
u32 max_reads = 10, timeout_us = 50;
u32 val;
DBG("id=%d", pll_28nm->phy->id);
if (unlikely(pll_28nm->phy->pll_on))
return 0;
pll_28nm_software_reset(pll_28nm);
/*
* PLL power up sequence.
* Add necessary delays recommended by hardware.
*/
writel(0x34, base + REG_DSI_28nm_PHY_PLL_CAL_CFG1);
ndelay(500);
val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
ndelay(500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
ndelay(500);
val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B |
DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
writel(val, base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
ndelay(500);
/* DSI PLL toggle lock detect setting */
writel(0x04, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
ndelay(500);
writel(0x05, base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2);
udelay(512);
locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
if (unlikely(!locked)) {
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
return -EINVAL;
}
DBG("DSI PLL lock success");
pll_28nm->phy->pll_on = true;
return 0;
}
static void dsi_pll_28nm_vco_unprepare(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
DBG("id=%d", pll_28nm->phy->id);
if (unlikely(!pll_28nm->phy->pll_on))
return;
writel(0, pll_28nm->phy->pll_base + REG_DSI_28nm_PHY_PLL_GLB_CFG);
pll_28nm->phy->pll_on = false;
}
static int dsi_pll_28nm_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
req->rate = clamp_t(unsigned long, req->rate,
pll_28nm->phy->cfg->min_pll_rate,
pll_28nm->phy->cfg->max_pll_rate);
return 0;
}
static const struct clk_ops clk_ops_dsi_pll_28nm_vco_hpm = {
.determine_rate = dsi_pll_28nm_clk_determine_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = dsi_pll_28nm_vco_prepare_hpm,
.unprepare = dsi_pll_28nm_vco_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
static const struct clk_ops clk_ops_dsi_pll_28nm_vco_lp = {
.determine_rate = dsi_pll_28nm_clk_determine_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = dsi_pll_28nm_vco_prepare_lp,
.unprepare = dsi_pll_28nm_vco_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
static const struct clk_ops clk_ops_dsi_pll_28nm_vco_8226 = {
.determine_rate = dsi_pll_28nm_clk_determine_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = dsi_pll_28nm_vco_prepare_8226,
.unprepare = dsi_pll_28nm_vco_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
/*
* PLL Callbacks
*/
static void dsi_28nm_pll_save_state(struct msm_dsi_phy *phy)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(phy->vco_hw);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->phy->pll_base;
cached_state->postdiv3 =
readl(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG);
cached_state->postdiv1 =
readl(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG);
cached_state->byte_mux = readl(base + REG_DSI_28nm_PHY_PLL_VREG_CFG);
if (dsi_pll_28nm_clk_is_enabled(phy->vco_hw))
cached_state->vco_rate = clk_hw_get_rate(phy->vco_hw);
else
cached_state->vco_rate = 0;
}
static int dsi_28nm_pll_restore_state(struct msm_dsi_phy *phy)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(phy->vco_hw);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->phy->pll_base;
int ret;
ret = dsi_pll_28nm_clk_set_rate(phy->vco_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_28nm->phy->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
writel(cached_state->postdiv3, base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG);
writel(cached_state->postdiv1, base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG);
writel(cached_state->byte_mux, base + REG_DSI_28nm_PHY_PLL_VREG_CFG);
return 0;
}
static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm, struct clk_hw **provided_clocks)
{
char clk_name[32];
struct clk_init_data vco_init = {
.parent_data = &(const struct clk_parent_data) {
.fw_name = "ref", .name = "xo",
},
.num_parents = 1,
.name = clk_name,
.flags = CLK_IGNORE_UNUSED,
};
struct device *dev = &pll_28nm->phy->pdev->dev;
struct clk_hw *hw, *analog_postdiv, *indirect_path_div2, *byte_mux;
int ret;
DBG("%d", pll_28nm->phy->id);
if (pll_28nm->phy->cfg->quirks & DSI_PHY_28NM_QUIRK_PHY_LP)
vco_init.ops = &clk_ops_dsi_pll_28nm_vco_lp;
else if (pll_28nm->phy->cfg->quirks & DSI_PHY_28NM_QUIRK_PHY_8226)
vco_init.ops = &clk_ops_dsi_pll_28nm_vco_8226;
else
vco_init.ops = &clk_ops_dsi_pll_28nm_vco_hpm;
snprintf(clk_name, sizeof(clk_name), "dsi%dvco_clk", pll_28nm->phy->id);
pll_28nm->clk_hw.init = &vco_init;
ret = devm_clk_hw_register(dev, &pll_28nm->clk_hw);
if (ret)
return ret;
snprintf(clk_name, sizeof(clk_name), "dsi%danalog_postdiv_clk", pll_28nm->phy->id);
analog_postdiv = devm_clk_hw_register_divider_parent_hw(dev, clk_name,
&pll_28nm->clk_hw, CLK_SET_RATE_PARENT,
pll_28nm->phy->pll_base +
REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
0, 4, 0, NULL);
if (IS_ERR(analog_postdiv))
return PTR_ERR(analog_postdiv);
snprintf(clk_name, sizeof(clk_name), "dsi%dindirect_path_div2_clk", pll_28nm->phy->id);
indirect_path_div2 = devm_clk_hw_register_fixed_factor_parent_hw(dev,
clk_name, analog_postdiv, CLK_SET_RATE_PARENT, 1, 2);
if (IS_ERR(indirect_path_div2))
return PTR_ERR(indirect_path_div2);
snprintf(clk_name, sizeof(clk_name), "dsi%dpll", pll_28nm->phy->id);
hw = devm_clk_hw_register_divider_parent_hw(dev, clk_name,
&pll_28nm->clk_hw, 0, pll_28nm->phy->pll_base +
REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
0, 8, 0, NULL);
if (IS_ERR(hw))
return PTR_ERR(hw);
provided_clocks[DSI_PIXEL_PLL_CLK] = hw;
snprintf(clk_name, sizeof(clk_name), "dsi%dbyte_mux", pll_28nm->phy->id);
byte_mux = devm_clk_hw_register_mux_parent_hws(dev, clk_name,
((const struct clk_hw *[]){
&pll_28nm->clk_hw,
indirect_path_div2,
}), 2, CLK_SET_RATE_PARENT, pll_28nm->phy->pll_base +
REG_DSI_28nm_PHY_PLL_VREG_CFG, 1, 1, 0, NULL);
if (IS_ERR(byte_mux))
return PTR_ERR(byte_mux);
snprintf(clk_name, sizeof(clk_name), "dsi%dpllbyte", pll_28nm->phy->id);
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, clk_name,
byte_mux, CLK_SET_RATE_PARENT, 1, 4);
if (IS_ERR(hw))
return PTR_ERR(hw);
provided_clocks[DSI_BYTE_PLL_CLK] = hw;
return 0;
}
static int dsi_pll_28nm_init(struct msm_dsi_phy *phy)
{
struct platform_device *pdev = phy->pdev;
struct dsi_pll_28nm *pll_28nm;
int ret;
if (!pdev)
return -ENODEV;
pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
if (!pll_28nm)
return -ENOMEM;
pll_28nm->phy = phy;
ret = pll_28nm_register(pll_28nm, phy->provided_clocks->hws);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ret;
}
phy->vco_hw = &pll_28nm->clk_hw;
return 0;
}
static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy,
struct msm_dsi_dphy_timing *timing)
{
void __iomem *base = phy->base;
writel(DSI_28nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero),
base + REG_DSI_28nm_PHY_TIMING_CTRL_0);
writel(DSI_28nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail),
base + REG_DSI_28nm_PHY_TIMING_CTRL_1);
writel(DSI_28nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare),
base + REG_DSI_28nm_PHY_TIMING_CTRL_2);
if (timing->clk_zero & BIT(8))
writel(DSI_28nm_PHY_TIMING_CTRL_3_CLK_ZERO_8,
base + REG_DSI_28nm_PHY_TIMING_CTRL_3);
writel(DSI_28nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit),
base + REG_DSI_28nm_PHY_TIMING_CTRL_4);
writel(DSI_28nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero),
base + REG_DSI_28nm_PHY_TIMING_CTRL_5);
writel(DSI_28nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare),
base + REG_DSI_28nm_PHY_TIMING_CTRL_6);
writel(DSI_28nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail),
base + REG_DSI_28nm_PHY_TIMING_CTRL_7);
writel(DSI_28nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst),
base + REG_DSI_28nm_PHY_TIMING_CTRL_8);
writel(DSI_28nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) |
DSI_28nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure),
base + REG_DSI_28nm_PHY_TIMING_CTRL_9);
writel(DSI_28nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get),
base + REG_DSI_28nm_PHY_TIMING_CTRL_10);
writel(DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0),
base + REG_DSI_28nm_PHY_TIMING_CTRL_11);
}
static void dsi_28nm_phy_regulator_enable_dcdc(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->reg_base;
writel(0x0, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0);
writel(1, base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG);
writel(0, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5);
writel(0, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3);
writel(0x3, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2);
writel(0x9, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1);
writel(0x7, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0);
writel(0x20, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4);
writel(0x00, phy->base + REG_DSI_28nm_PHY_LDO_CNTRL);
}
static void dsi_28nm_phy_regulator_enable_ldo(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->reg_base;
writel(0x0, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0);
writel(0, base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG);
writel(0x7, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5);
writel(0, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3);
writel(0x1, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2);
writel(0x1, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1);
writel(0x20, base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4);
if (phy->cfg->quirks & DSI_PHY_28NM_QUIRK_PHY_LP)
writel(0x05, phy->base + REG_DSI_28nm_PHY_LDO_CNTRL);
else
writel(0x0d, phy->base + REG_DSI_28nm_PHY_LDO_CNTRL);
}
static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable)
{
if (!enable) {
writel(0, phy->reg_base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG);
return;
}
if (phy->regulator_ldo_mode)
dsi_28nm_phy_regulator_enable_ldo(phy);
else
dsi_28nm_phy_regulator_enable_dcdc(phy);
}
static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy,
struct msm_dsi_phy_clk_request *clk_req)
{
struct msm_dsi_dphy_timing *timing = &phy->timing;
int i;
void __iomem *base = phy->base;
u32 val;
DBG("");
if (msm_dsi_dphy_timing_calc(timing, clk_req)) {
DRM_DEV_ERROR(&phy->pdev->dev,
"%s: D-PHY timing calculation failed\n",
__func__);
return -EINVAL;
}
writel(0xff, base + REG_DSI_28nm_PHY_STRENGTH_0);
dsi_28nm_phy_regulator_ctrl(phy, true);
dsi_28nm_dphy_set_timing(phy, timing);
writel(0x00, base + REG_DSI_28nm_PHY_CTRL_1);
writel(0x5f, base + REG_DSI_28nm_PHY_CTRL_0);
writel(0x6, base + REG_DSI_28nm_PHY_STRENGTH_1);
for (i = 0; i < 4; i++) {
writel(0, base + REG_DSI_28nm_PHY_LN_CFG_0(i));
writel(0, base + REG_DSI_28nm_PHY_LN_CFG_1(i));
writel(0, base + REG_DSI_28nm_PHY_LN_CFG_2(i));
writel(0, base + REG_DSI_28nm_PHY_LN_CFG_3(i));
writel(0, base + REG_DSI_28nm_PHY_LN_CFG_4(i));
writel(0, base + REG_DSI_28nm_PHY_LN_TEST_DATAPATH(i));
writel(0, base + REG_DSI_28nm_PHY_LN_DEBUG_SEL(i));
writel(0x1, base + REG_DSI_28nm_PHY_LN_TEST_STR_0(i));
writel(0x97, base + REG_DSI_28nm_PHY_LN_TEST_STR_1(i));
}
writel(0, base + REG_DSI_28nm_PHY_LNCK_CFG_4);
writel(0xc0, base + REG_DSI_28nm_PHY_LNCK_CFG_1);
writel(0x1, base + REG_DSI_28nm_PHY_LNCK_TEST_STR0);
writel(0xbb, base + REG_DSI_28nm_PHY_LNCK_TEST_STR1);
writel(0x5f, base + REG_DSI_28nm_PHY_CTRL_0);
val = readl(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL);
if (phy->id == DSI_1 && phy->usecase == MSM_DSI_PHY_SLAVE)
val &= ~DSI_28nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL;
else
val |= DSI_28nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL;
writel(val, base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL);
return 0;
}
static void dsi_28nm_phy_disable(struct msm_dsi_phy *phy)
{
writel(0, phy->base + REG_DSI_28nm_PHY_CTRL_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();
}
static const struct regulator_bulk_data dsi_phy_28nm_regulators[] = {
{ .supply = "vddio", .init_load_uA = 100000 },
};
const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_28nm_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_28nm_regulators),
.ops = {
.enable = dsi_28nm_phy_enable,
.disable = dsi_28nm_phy_disable,
.pll_init = dsi_pll_28nm_init,
.save_pll_state = dsi_28nm_pll_save_state,
.restore_pll_state = dsi_28nm_pll_restore_state,
},
.min_pll_rate = VCO_MIN_RATE,
.max_pll_rate = VCO_MAX_RATE,
.io_start = { 0xfd922b00, 0xfd923100 },
.num_dsi_phy = 2,
};
const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_famb_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_28nm_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_28nm_regulators),
.ops = {
.enable = dsi_28nm_phy_enable,
.disable = dsi_28nm_phy_disable,
.pll_init = dsi_pll_28nm_init,
.save_pll_state = dsi_28nm_pll_save_state,
.restore_pll_state = dsi_28nm_pll_restore_state,
},
.min_pll_rate = VCO_MIN_RATE,
.max_pll_rate = VCO_MAX_RATE,
.io_start = { 0x1a94400, 0x1a96400 },
.num_dsi_phy = 2,
};
const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_28nm_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_28nm_regulators),
.ops = {
.enable = dsi_28nm_phy_enable,
.disable = dsi_28nm_phy_disable,
.pll_init = dsi_pll_28nm_init,
.save_pll_state = dsi_28nm_pll_save_state,
.restore_pll_state = dsi_28nm_pll_restore_state,
},
.min_pll_rate = VCO_MIN_RATE,
.max_pll_rate = VCO_MAX_RATE,
.io_start = { 0x1a98500 },
.num_dsi_phy = 1,
.quirks = DSI_PHY_28NM_QUIRK_PHY_LP,
};
const struct msm_dsi_phy_cfg dsi_phy_28nm_8226_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_28nm_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_28nm_regulators),
.ops = {
.enable = dsi_28nm_phy_enable,
.disable = dsi_28nm_phy_disable,
.pll_init = dsi_pll_28nm_init,
.save_pll_state = dsi_28nm_pll_save_state,
.restore_pll_state = dsi_28nm_pll_restore_state,
},
.min_pll_rate = VCO_MIN_RATE,
.max_pll_rate = VCO_MAX_RATE,
.io_start = { 0xfd922b00 },
.num_dsi_phy = 1,
.quirks = DSI_PHY_28NM_QUIRK_PHY_8226,
};
const struct msm_dsi_phy_cfg dsi_phy_28nm_8937_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_28nm_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_28nm_regulators),
.ops = {
.enable = dsi_28nm_phy_enable,
.disable = dsi_28nm_phy_disable,
.pll_init = dsi_pll_28nm_init,
.save_pll_state = dsi_28nm_pll_save_state,
.restore_pll_state = dsi_28nm_pll_restore_state,
},
.min_pll_rate = VCO_MIN_RATE,
.max_pll_rate = VCO_MAX_RATE,
.io_start = { 0x1a94400, 0x1a96400 },
.num_dsi_phy = 2,
.quirks = DSI_PHY_28NM_QUIRK_PHY_LP,
};
@@ -0,0 +1,648 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*/
#include <dt-bindings/clock/qcom,dsi-phy-28nm.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "dsi_phy.h"
#include "dsi.xml.h"
#include "dsi_phy_28nm_8960.xml.h"
/*
* DSI PLL 28nm (8960/A family) - clock diagram (eg: DSI1):
*
*
* +------+
* dsi1vco_clk ----o-----| DIV1 |---dsi1pllbit (not exposed as clock)
* F * byte_clk | +------+
* | bit clock divider (F / 8)
* |
* | +------+
* o-----| DIV2 |---dsi0pllbyte---o---> To byte RCG
* | +------+ | (sets parent rate)
* | byte clock divider (F) |
* | |
* | o---> To esc RCG
* | (doesn't set parent rate)
* |
* | +------+
* o-----| DIV3 |----dsi0pll------o---> To dsi RCG
* +------+ | (sets parent rate)
* dsi clock divider (F * magic) |
* |
* o---> To pixel rcg
* (doesn't set parent rate)
*/
#define POLL_MAX_READS 8000
#define POLL_TIMEOUT_US 1
#define VCO_REF_CLK_RATE 27000000
#define VCO_MIN_RATE 600000000
#define VCO_MAX_RATE 1200000000
#define VCO_PREF_DIV_RATIO 27
struct pll_28nm_cached_state {
unsigned long vco_rate;
u8 postdiv3;
u8 postdiv2;
u8 postdiv1;
};
struct clk_bytediv {
struct clk_hw hw;
void __iomem *reg;
};
struct dsi_pll_28nm {
struct clk_hw clk_hw;
struct msm_dsi_phy *phy;
struct pll_28nm_cached_state cached_state;
};
#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, clk_hw)
static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
int nb_tries, int timeout_us)
{
bool pll_locked = false;
u32 val;
while (nb_tries--) {
val = readl(pll_28nm->phy->pll_base + REG_DSI_28nm_8960_PHY_PLL_RDY);
pll_locked = !!(val & DSI_28nm_8960_PHY_PLL_RDY_PLL_RDY);
if (pll_locked)
break;
udelay(timeout_us);
}
DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
return pll_locked;
}
/*
* Clock Callbacks
*/
static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
void __iomem *base = pll_28nm->phy->pll_base;
u32 val, temp, fb_divider;
DBG("rate=%lu, parent's=%lu", rate, parent_rate);
temp = rate / 10;
val = VCO_REF_CLK_RATE / 10;
fb_divider = (temp * VCO_PREF_DIV_RATIO) / val;
fb_divider = fb_divider / 2 - 1;
writel(fb_divider & 0xff, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1);
val = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2);
val |= (fb_divider >> 8) & 0x07;
writel(val, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2);
val = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
val |= (VCO_PREF_DIV_RATIO - 1) & 0x3f;
writel(val, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
writel(0xf, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_6);
val = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
val |= 0x7 << 4;
writel(val, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
return 0;
}
static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
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 dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
void __iomem *base = pll_28nm->phy->pll_base;
unsigned long vco_rate;
u32 status, fb_divider, temp, ref_divider;
VERB("parent_rate=%lu", parent_rate);
status = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0);
if (status & DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE) {
fb_divider = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_1);
fb_divider &= 0xff;
temp = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_2) & 0x07;
fb_divider = (temp << 8) | fb_divider;
fb_divider += 1;
ref_divider = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_3);
ref_divider &= 0x3f;
ref_divider += 1;
/* multiply by 2 */
vco_rate = (parent_rate / ref_divider) * fb_divider * 2;
} else {
vco_rate = 0;
}
DBG("returning vco rate = %lu", vco_rate);
return vco_rate;
}
static int dsi_pll_28nm_vco_prepare(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
struct device *dev = &pll_28nm->phy->pdev->dev;
void __iomem *base = pll_28nm->phy->pll_base;
bool locked;
unsigned int bit_div, byte_div;
int max_reads = 1000, timeout_us = 100;
u32 val;
DBG("id=%d", pll_28nm->phy->id);
if (unlikely(pll_28nm->phy->pll_on))
return 0;
/*
* before enabling the PLL, configure the bit clock divider since we
* don't expose it as a clock to the outside world
* 1: read back the byte clock divider that should already be set
* 2: divide by 8 to get bit clock divider
* 3: write it to POSTDIV1
*/
val = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
byte_div = val + 1;
bit_div = byte_div / 8;
val = readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
val &= ~0xf;
val |= (bit_div - 1);
writel(val, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
/* enable the PLL */
writel(DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE,
base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0);
locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
if (unlikely(!locked)) {
DRM_DEV_ERROR(dev, "DSI PLL lock failed\n");
return -EINVAL;
}
DBG("DSI PLL lock success");
pll_28nm->phy->pll_on = true;
return 0;
}
static void dsi_pll_28nm_vco_unprepare(struct clk_hw *hw)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
DBG("id=%d", pll_28nm->phy->id);
if (unlikely(!pll_28nm->phy->pll_on))
return;
writel(0x00, pll_28nm->phy->pll_base + REG_DSI_28nm_8960_PHY_PLL_CTRL_0);
pll_28nm->phy->pll_on = false;
}
static int dsi_pll_28nm_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(hw);
req->rate = clamp_t(unsigned long, req->rate,
pll_28nm->phy->cfg->min_pll_rate, pll_28nm->phy->cfg->max_pll_rate);
return 0;
}
static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
.determine_rate = dsi_pll_28nm_clk_determine_rate,
.set_rate = dsi_pll_28nm_clk_set_rate,
.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
.prepare = dsi_pll_28nm_vco_prepare,
.unprepare = dsi_pll_28nm_vco_unprepare,
.is_enabled = dsi_pll_28nm_clk_is_enabled,
};
/*
* Custom byte clock divier clk_ops
*
* This clock is the entry point to configuring the PLL. The user (dsi host)
* will set this clock's rate to the desired byte clock rate. The VCO lock
* frequency is a multiple of the byte clock rate. The multiplication factor
* (shown as F in the diagram above) is a function of the byte clock rate.
*
* This custom divider clock ensures that its parent (VCO) is set to the
* desired rate, and that the byte clock postdivider (POSTDIV2) is configured
* accordingly
*/
#define to_clk_bytediv(_hw) container_of(_hw, struct clk_bytediv, hw)
static unsigned long clk_bytediv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_bytediv *bytediv = to_clk_bytediv(hw);
unsigned int div;
div = readl(bytediv->reg) & 0xff;
return parent_rate / (div + 1);
}
/* find multiplication factor(wrt byte clock) at which the VCO should be set */
static unsigned int get_vco_mul_factor(unsigned long byte_clk_rate)
{
unsigned long bit_mhz;
/* convert to bit clock in Mhz */
bit_mhz = (byte_clk_rate * 8) / 1000000;
if (bit_mhz < 125)
return 64;
else if (bit_mhz < 250)
return 32;
else if (bit_mhz < 600)
return 16;
else
return 8;
}
static int clk_bytediv_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
unsigned long best_parent;
unsigned int factor;
factor = get_vco_mul_factor(req->rate);
best_parent = req->rate * factor;
req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
req->rate = req->best_parent_rate / factor;
return 0;
}
static int clk_bytediv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_bytediv *bytediv = to_clk_bytediv(hw);
u32 val;
unsigned int factor;
factor = get_vco_mul_factor(rate);
val = readl(bytediv->reg);
val |= (factor - 1) & 0xff;
writel(val, bytediv->reg);
return 0;
}
/* Our special byte clock divider ops */
static const struct clk_ops clk_bytediv_ops = {
.determine_rate = clk_bytediv_determine_rate,
.set_rate = clk_bytediv_set_rate,
.recalc_rate = clk_bytediv_recalc_rate,
};
/*
* PLL Callbacks
*/
static void dsi_28nm_pll_save_state(struct msm_dsi_phy *phy)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(phy->vco_hw);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->phy->pll_base;
cached_state->postdiv3 =
readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10);
cached_state->postdiv2 =
readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
cached_state->postdiv1 =
readl(base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
cached_state->vco_rate = clk_hw_get_rate(phy->vco_hw);
}
static int dsi_28nm_pll_restore_state(struct msm_dsi_phy *phy)
{
struct dsi_pll_28nm *pll_28nm = to_pll_28nm(phy->vco_hw);
struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
void __iomem *base = pll_28nm->phy->pll_base;
int ret;
ret = dsi_pll_28nm_clk_set_rate(phy->vco_hw,
cached_state->vco_rate, 0);
if (ret) {
DRM_DEV_ERROR(&pll_28nm->phy->pdev->dev,
"restore vco rate failed. ret=%d\n", ret);
return ret;
}
writel(cached_state->postdiv3, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_10);
writel(cached_state->postdiv2, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9);
writel(cached_state->postdiv1, base + REG_DSI_28nm_8960_PHY_PLL_CTRL_8);
return 0;
}
static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm, struct clk_hw **provided_clocks)
{
char clk_name[32];
struct clk_init_data vco_init = {
.parent_data = &(const struct clk_parent_data) {
.fw_name = "ref",
},
.num_parents = 1,
.flags = CLK_IGNORE_UNUSED,
.ops = &clk_ops_dsi_pll_28nm_vco,
};
struct device *dev = &pll_28nm->phy->pdev->dev;
struct clk_hw *hw;
struct clk_bytediv *bytediv;
struct clk_init_data bytediv_init = { };
int ret;
DBG("%d", pll_28nm->phy->id);
bytediv = devm_kzalloc(dev, sizeof(*bytediv), GFP_KERNEL);
if (!bytediv)
return -ENOMEM;
snprintf(clk_name, sizeof(clk_name), "dsi%dvco_clk", pll_28nm->phy->id);
vco_init.name = clk_name;
pll_28nm->clk_hw.init = &vco_init;
ret = devm_clk_hw_register(dev, &pll_28nm->clk_hw);
if (ret)
return ret;
/* prepare and register bytediv */
bytediv->hw.init = &bytediv_init;
bytediv->reg = pll_28nm->phy->pll_base + REG_DSI_28nm_8960_PHY_PLL_CTRL_9;
snprintf(clk_name, sizeof(clk_name), "dsi%dpllbyte", pll_28nm->phy->id + 1);
bytediv_init.name = clk_name;
bytediv_init.ops = &clk_bytediv_ops;
bytediv_init.flags = CLK_SET_RATE_PARENT;
bytediv_init.parent_hws = (const struct clk_hw*[]){
&pll_28nm->clk_hw,
};
bytediv_init.num_parents = 1;
/* DIV2 */
ret = devm_clk_hw_register(dev, &bytediv->hw);
if (ret)
return ret;
provided_clocks[DSI_BYTE_PLL_CLK] = &bytediv->hw;
snprintf(clk_name, sizeof(clk_name), "dsi%dpll", pll_28nm->phy->id + 1);
/* DIV3 */
hw = devm_clk_hw_register_divider_parent_hw(dev, clk_name,
&pll_28nm->clk_hw, 0, pll_28nm->phy->pll_base +
REG_DSI_28nm_8960_PHY_PLL_CTRL_10,
0, 8, 0, NULL);
if (IS_ERR(hw))
return PTR_ERR(hw);
provided_clocks[DSI_PIXEL_PLL_CLK] = hw;
return 0;
}
static int dsi_pll_28nm_8960_init(struct msm_dsi_phy *phy)
{
struct platform_device *pdev = phy->pdev;
struct dsi_pll_28nm *pll_28nm;
int ret;
if (!pdev)
return -ENODEV;
pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
if (!pll_28nm)
return -ENOMEM;
pll_28nm->phy = phy;
ret = pll_28nm_register(pll_28nm, phy->provided_clocks->hws);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret);
return ret;
}
phy->vco_hw = &pll_28nm->clk_hw;
return 0;
}
static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy,
struct msm_dsi_dphy_timing *timing)
{
void __iomem *base = phy->base;
writel(DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_0);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_1);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_2);
writel(0, base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_3);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_4);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_5);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_6);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_7);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_8);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) |
DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_9);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_10);
writel(DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD(0),
base + REG_DSI_28nm_8960_PHY_TIMING_CTRL_11);
}
static void dsi_28nm_phy_regulator_init(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->reg_base;
writel(0x3, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_0);
writel(1, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_1);
writel(1, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_2);
writel(0, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_3);
writel(0x100, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_4);
}
static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->reg_base;
writel(0x3, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_0);
writel(0xa, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_1);
writel(0x4, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_2);
writel(0x0, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_3);
writel(0x20, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_4);
}
static void dsi_28nm_phy_calibration(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->reg_base;
u32 status;
int i = 5000;
writel(0x3, base + REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CAL_PWR_CFG);
writel(0x0, base + REG_DSI_28nm_8960_PHY_MISC_CAL_SW_CFG_2);
writel(0x5a, base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_1);
writel(0x10, base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_3);
writel(0x1, base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_4);
writel(0x1, base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_0);
writel(0x1, base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_TRIGGER);
usleep_range(5000, 6000);
writel(0x0, base + REG_DSI_28nm_8960_PHY_MISC_CAL_HW_TRIGGER);
do {
status = readl(base +
REG_DSI_28nm_8960_PHY_MISC_CAL_STATUS);
if (!(status & DSI_28nm_8960_PHY_MISC_CAL_STATUS_CAL_BUSY))
break;
udelay(1);
} while (--i > 0);
}
static void dsi_28nm_phy_lane_config(struct msm_dsi_phy *phy)
{
void __iomem *base = phy->base;
int i;
for (i = 0; i < 4; i++) {
writel(0x80, base + REG_DSI_28nm_8960_PHY_LN_CFG_0(i));
writel(0x45, base + REG_DSI_28nm_8960_PHY_LN_CFG_1(i));
writel(0x00, base + REG_DSI_28nm_8960_PHY_LN_CFG_2(i));
writel(0x00, base + REG_DSI_28nm_8960_PHY_LN_TEST_DATAPATH(i));
writel(0x01, base + REG_DSI_28nm_8960_PHY_LN_TEST_STR_0(i));
writel(0x66, base + REG_DSI_28nm_8960_PHY_LN_TEST_STR_1(i));
}
writel(0x40, base + REG_DSI_28nm_8960_PHY_LNCK_CFG_0);
writel(0x67, base + REG_DSI_28nm_8960_PHY_LNCK_CFG_1);
writel(0x0, base + REG_DSI_28nm_8960_PHY_LNCK_CFG_2);
writel(0x0, base + REG_DSI_28nm_8960_PHY_LNCK_TEST_DATAPATH);
writel(0x1, base + REG_DSI_28nm_8960_PHY_LNCK_TEST_STR0);
writel(0x88, base + REG_DSI_28nm_8960_PHY_LNCK_TEST_STR1);
}
static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy,
struct msm_dsi_phy_clk_request *clk_req)
{
struct msm_dsi_dphy_timing *timing = &phy->timing;
void __iomem *base = phy->base;
DBG("");
if (msm_dsi_dphy_timing_calc(timing, clk_req)) {
DRM_DEV_ERROR(&phy->pdev->dev,
"%s: D-PHY timing calculation failed\n",
__func__);
return -EINVAL;
}
dsi_28nm_phy_regulator_init(phy);
writel(0x04, base + REG_DSI_28nm_8960_PHY_LDO_CTRL);
/* strength control */
writel(0xff, base + REG_DSI_28nm_8960_PHY_STRENGTH_0);
writel(0x00, base + REG_DSI_28nm_8960_PHY_STRENGTH_1);
writel(0x06, base + REG_DSI_28nm_8960_PHY_STRENGTH_2);
/* phy ctrl */
writel(0x5f, base + REG_DSI_28nm_8960_PHY_CTRL_0);
writel(0x00, base + REG_DSI_28nm_8960_PHY_CTRL_1);
writel(0x00, base + REG_DSI_28nm_8960_PHY_CTRL_2);
writel(0x10, base + REG_DSI_28nm_8960_PHY_CTRL_3);
dsi_28nm_phy_regulator_ctrl(phy);
dsi_28nm_phy_calibration(phy);
dsi_28nm_phy_lane_config(phy);
writel(0x0f, base + REG_DSI_28nm_8960_PHY_BIST_CTRL_4);
writel(0x03, base + REG_DSI_28nm_8960_PHY_BIST_CTRL_1);
writel(0x03, base + REG_DSI_28nm_8960_PHY_BIST_CTRL_0);
writel(0x0, base + REG_DSI_28nm_8960_PHY_BIST_CTRL_4);
dsi_28nm_dphy_set_timing(phy, timing);
return 0;
}
static void dsi_28nm_phy_disable(struct msm_dsi_phy *phy)
{
writel(0x0, phy->base + REG_DSI_28nm_8960_PHY_CTRL_0);
/*
* Wait for the registers writes to complete in order to
* ensure that the phy is completely disabled
*/
wmb();
}
static const struct regulator_bulk_data dsi_phy_28nm_8960_regulators[] = {
{ .supply = "vddio", .init_load_uA = 100000 }, /* 1.8 V */
};
const struct msm_dsi_phy_cfg dsi_phy_28nm_8960_cfgs = {
.has_phy_regulator = true,
.regulator_data = dsi_phy_28nm_8960_regulators,
.num_regulators = ARRAY_SIZE(dsi_phy_28nm_8960_regulators),
.ops = {
.enable = dsi_28nm_phy_enable,
.disable = dsi_28nm_phy_disable,
.pll_init = dsi_pll_28nm_8960_init,
.save_pll_state = dsi_28nm_pll_save_state,
.restore_pll_state = dsi_28nm_pll_restore_state,
},
.min_pll_rate = VCO_MIN_RATE,
.max_pll_rate = VCO_MAX_RATE,
.io_start = { 0x4700300, 0x5800300 },
.num_dsi_phy = 2,
};
File diff suppressed because it is too large Load Diff