162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015 MediaTek Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "phy-mtk-mipi-dsi.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ciinline struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw) 962306a36Sopenharmony_ci{ 1062306a36Sopenharmony_ci return container_of(hw, struct mtk_mipi_tx, pll_hw); 1162306a36Sopenharmony_ci} 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciint mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1462306a36Sopenharmony_ci unsigned long parent_rate) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci mipi_tx->data_rate = rate; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci return 0; 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciunsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, 2662306a36Sopenharmony_ci unsigned long parent_rate) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return mipi_tx->data_rate; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int mtk_mipi_tx_power_on(struct phy *phy) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); 3662306a36Sopenharmony_ci int ret; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* Power up core and enable PLL */ 3962306a36Sopenharmony_ci ret = clk_prepare_enable(mipi_tx->pll_hw.clk); 4062306a36Sopenharmony_ci if (ret < 0) 4162306a36Sopenharmony_ci return ret; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* Enable DSI Lane LDO outputs, disable pad tie low */ 4462306a36Sopenharmony_ci mipi_tx->driver_data->mipi_tx_enable_signal(phy); 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int mtk_mipi_tx_power_off(struct phy *phy) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* Enable pad tie low, disable DSI Lane LDO outputs */ 5362306a36Sopenharmony_ci mipi_tx->driver_data->mipi_tx_disable_signal(phy); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Disable PLL and power down core */ 5662306a36Sopenharmony_ci clk_disable_unprepare(mipi_tx->pll_hw.clk); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct phy_ops mtk_mipi_tx_ops = { 6262306a36Sopenharmony_ci .power_on = mtk_mipi_tx_power_on, 6362306a36Sopenharmony_ci .power_off = mtk_mipi_tx_power_off, 6462306a36Sopenharmony_ci .owner = THIS_MODULE, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void mtk_mipi_tx_get_calibration_datal(struct mtk_mipi_tx *mipi_tx) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct nvmem_cell *cell; 7062306a36Sopenharmony_ci size_t len; 7162306a36Sopenharmony_ci u32 *buf; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci cell = nvmem_cell_get(mipi_tx->dev, "calibration-data"); 7462306a36Sopenharmony_ci if (IS_ERR(cell)) { 7562306a36Sopenharmony_ci dev_info(mipi_tx->dev, "can't get nvmem_cell_get, ignore it\n"); 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci buf = (u32 *)nvmem_cell_read(cell, &len); 7962306a36Sopenharmony_ci nvmem_cell_put(cell); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (IS_ERR(buf)) { 8262306a36Sopenharmony_ci dev_info(mipi_tx->dev, "can't get data, ignore it\n"); 8362306a36Sopenharmony_ci return; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (len < 3 * sizeof(u32)) { 8762306a36Sopenharmony_ci dev_info(mipi_tx->dev, "invalid calibration data\n"); 8862306a36Sopenharmony_ci kfree(buf); 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci mipi_tx->rt_code[0] = ((buf[0] >> 6 & 0x1f) << 5) | 9362306a36Sopenharmony_ci (buf[0] >> 11 & 0x1f); 9462306a36Sopenharmony_ci mipi_tx->rt_code[1] = ((buf[1] >> 27 & 0x1f) << 5) | 9562306a36Sopenharmony_ci (buf[0] >> 1 & 0x1f); 9662306a36Sopenharmony_ci mipi_tx->rt_code[2] = ((buf[1] >> 17 & 0x1f) << 5) | 9762306a36Sopenharmony_ci (buf[1] >> 22 & 0x1f); 9862306a36Sopenharmony_ci mipi_tx->rt_code[3] = ((buf[1] >> 7 & 0x1f) << 5) | 9962306a36Sopenharmony_ci (buf[1] >> 12 & 0x1f); 10062306a36Sopenharmony_ci mipi_tx->rt_code[4] = ((buf[2] >> 27 & 0x1f) << 5) | 10162306a36Sopenharmony_ci (buf[1] >> 2 & 0x1f); 10262306a36Sopenharmony_ci kfree(buf); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int mtk_mipi_tx_probe(struct platform_device *pdev) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 10862306a36Sopenharmony_ci struct mtk_mipi_tx *mipi_tx; 10962306a36Sopenharmony_ci const char *ref_clk_name; 11062306a36Sopenharmony_ci struct clk *ref_clk; 11162306a36Sopenharmony_ci struct clk_init_data clk_init = { 11262306a36Sopenharmony_ci .num_parents = 1, 11362306a36Sopenharmony_ci .parent_names = (const char * const *)&ref_clk_name, 11462306a36Sopenharmony_ci .flags = CLK_SET_RATE_GATE, 11562306a36Sopenharmony_ci }; 11662306a36Sopenharmony_ci struct phy *phy; 11762306a36Sopenharmony_ci struct phy_provider *phy_provider; 11862306a36Sopenharmony_ci int ret; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL); 12162306a36Sopenharmony_ci if (!mipi_tx) 12262306a36Sopenharmony_ci return -ENOMEM; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mipi_tx->driver_data = of_device_get_match_data(dev); 12562306a36Sopenharmony_ci if (!mipi_tx->driver_data) 12662306a36Sopenharmony_ci return -ENODEV; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci mipi_tx->regs = devm_platform_ioremap_resource(pdev, 0); 12962306a36Sopenharmony_ci if (IS_ERR(mipi_tx->regs)) 13062306a36Sopenharmony_ci return PTR_ERR(mipi_tx->regs); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ref_clk = devm_clk_get(dev, NULL); 13362306a36Sopenharmony_ci if (IS_ERR(ref_clk)) 13462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ref_clk), 13562306a36Sopenharmony_ci "Failed to get reference clock\n"); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "drive-strength-microamp", 13862306a36Sopenharmony_ci &mipi_tx->mipitx_drive); 13962306a36Sopenharmony_ci /* If can't get the "mipi_tx->mipitx_drive", set it default 0x8 */ 14062306a36Sopenharmony_ci if (ret < 0) 14162306a36Sopenharmony_ci mipi_tx->mipitx_drive = 4600; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* check the mipitx_drive valid */ 14462306a36Sopenharmony_ci if (mipi_tx->mipitx_drive > 6000 || mipi_tx->mipitx_drive < 3000) { 14562306a36Sopenharmony_ci dev_warn(dev, "drive-strength-microamp is invalid %d, not in 3000 ~ 6000\n", 14662306a36Sopenharmony_ci mipi_tx->mipitx_drive); 14762306a36Sopenharmony_ci mipi_tx->mipitx_drive = clamp_val(mipi_tx->mipitx_drive, 3000, 14862306a36Sopenharmony_ci 6000); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ref_clk_name = __clk_get_name(ref_clk); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ret = of_property_read_string(dev->of_node, "clock-output-names", 15462306a36Sopenharmony_ci &clk_init.name); 15562306a36Sopenharmony_ci if (ret < 0) 15662306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Failed to read clock-output-names\n"); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci clk_init.ops = mipi_tx->driver_data->mipi_tx_clk_ops; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mipi_tx->pll_hw.init = &clk_init; 16162306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &mipi_tx->pll_hw); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci return dev_err_probe(dev, ret, "Failed to register PLL\n"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops); 16662306a36Sopenharmony_ci if (IS_ERR(phy)) 16762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(phy), "Failed to create MIPI D-PHY\n"); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci phy_set_drvdata(phy, mipi_tx); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 17262306a36Sopenharmony_ci if (IS_ERR(phy_provider)) 17362306a36Sopenharmony_ci return PTR_ERR(phy_provider); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mipi_tx->dev = dev; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mtk_mipi_tx_get_calibration_datal(mipi_tx); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mipi_tx->pll_hw); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct of_device_id mtk_mipi_tx_match[] = { 18362306a36Sopenharmony_ci { .compatible = "mediatek,mt2701-mipi-tx", .data = &mt2701_mipitx_data }, 18462306a36Sopenharmony_ci { .compatible = "mediatek,mt8173-mipi-tx", .data = &mt8173_mipitx_data }, 18562306a36Sopenharmony_ci { .compatible = "mediatek,mt8183-mipi-tx", .data = &mt8183_mipitx_data }, 18662306a36Sopenharmony_ci { /* sentinel */ } 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_mipi_tx_match); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic struct platform_driver mtk_mipi_tx_driver = { 19162306a36Sopenharmony_ci .probe = mtk_mipi_tx_probe, 19262306a36Sopenharmony_ci .driver = { 19362306a36Sopenharmony_ci .name = "mediatek-mipi-tx", 19462306a36Sopenharmony_ci .of_match_table = mtk_mipi_tx_match, 19562306a36Sopenharmony_ci }, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_cimodule_platform_driver(mtk_mipi_tx_driver); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek MIPI TX Driver"); 20062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 201