190f80d95SChun-Kuang Hu // SPDX-License-Identifier: GPL-2.0-only 290f80d95SChun-Kuang Hu /* 390f80d95SChun-Kuang Hu * Copyright (c) 2015 MediaTek Inc. 490f80d95SChun-Kuang Hu */ 590f80d95SChun-Kuang Hu 690f80d95SChun-Kuang Hu #include "phy-mtk-mipi-dsi.h" 790f80d95SChun-Kuang Hu 890f80d95SChun-Kuang Hu inline struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw) 990f80d95SChun-Kuang Hu { 1090f80d95SChun-Kuang Hu return container_of(hw, struct mtk_mipi_tx, pll_hw); 1190f80d95SChun-Kuang Hu } 1290f80d95SChun-Kuang Hu 1390f80d95SChun-Kuang Hu void mtk_mipi_tx_clear_bits(struct mtk_mipi_tx *mipi_tx, u32 offset, 1490f80d95SChun-Kuang Hu u32 bits) 1590f80d95SChun-Kuang Hu { 1690f80d95SChun-Kuang Hu u32 temp = readl(mipi_tx->regs + offset); 1790f80d95SChun-Kuang Hu 1890f80d95SChun-Kuang Hu writel(temp & ~bits, mipi_tx->regs + offset); 1990f80d95SChun-Kuang Hu } 2090f80d95SChun-Kuang Hu 2190f80d95SChun-Kuang Hu void mtk_mipi_tx_set_bits(struct mtk_mipi_tx *mipi_tx, u32 offset, 2290f80d95SChun-Kuang Hu u32 bits) 2390f80d95SChun-Kuang Hu { 2490f80d95SChun-Kuang Hu u32 temp = readl(mipi_tx->regs + offset); 2590f80d95SChun-Kuang Hu 2690f80d95SChun-Kuang Hu writel(temp | bits, mipi_tx->regs + offset); 2790f80d95SChun-Kuang Hu } 2890f80d95SChun-Kuang Hu 2990f80d95SChun-Kuang Hu void mtk_mipi_tx_update_bits(struct mtk_mipi_tx *mipi_tx, u32 offset, 3090f80d95SChun-Kuang Hu u32 mask, u32 data) 3190f80d95SChun-Kuang Hu { 3290f80d95SChun-Kuang Hu u32 temp = readl(mipi_tx->regs + offset); 3390f80d95SChun-Kuang Hu 3490f80d95SChun-Kuang Hu writel((temp & ~mask) | (data & mask), mipi_tx->regs + offset); 3590f80d95SChun-Kuang Hu } 3690f80d95SChun-Kuang Hu 3790f80d95SChun-Kuang Hu int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, 3890f80d95SChun-Kuang Hu unsigned long parent_rate) 3990f80d95SChun-Kuang Hu { 4090f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); 4190f80d95SChun-Kuang Hu 4290f80d95SChun-Kuang Hu dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate); 4390f80d95SChun-Kuang Hu 4490f80d95SChun-Kuang Hu mipi_tx->data_rate = rate; 4590f80d95SChun-Kuang Hu 4690f80d95SChun-Kuang Hu return 0; 4790f80d95SChun-Kuang Hu } 4890f80d95SChun-Kuang Hu 4990f80d95SChun-Kuang Hu unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, 5090f80d95SChun-Kuang Hu unsigned long parent_rate) 5190f80d95SChun-Kuang Hu { 5290f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); 5390f80d95SChun-Kuang Hu 5490f80d95SChun-Kuang Hu return mipi_tx->data_rate; 5590f80d95SChun-Kuang Hu } 5690f80d95SChun-Kuang Hu 5790f80d95SChun-Kuang Hu static int mtk_mipi_tx_power_on(struct phy *phy) 5890f80d95SChun-Kuang Hu { 5990f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); 6090f80d95SChun-Kuang Hu int ret; 6190f80d95SChun-Kuang Hu 6290f80d95SChun-Kuang Hu /* Power up core and enable PLL */ 6390f80d95SChun-Kuang Hu ret = clk_prepare_enable(mipi_tx->pll); 6490f80d95SChun-Kuang Hu if (ret < 0) 6590f80d95SChun-Kuang Hu return ret; 6690f80d95SChun-Kuang Hu 6790f80d95SChun-Kuang Hu /* Enable DSI Lane LDO outputs, disable pad tie low */ 6890f80d95SChun-Kuang Hu mipi_tx->driver_data->mipi_tx_enable_signal(phy); 6990f80d95SChun-Kuang Hu return 0; 7090f80d95SChun-Kuang Hu } 7190f80d95SChun-Kuang Hu 7290f80d95SChun-Kuang Hu static int mtk_mipi_tx_power_off(struct phy *phy) 7390f80d95SChun-Kuang Hu { 7490f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); 7590f80d95SChun-Kuang Hu 7690f80d95SChun-Kuang Hu /* Enable pad tie low, disable DSI Lane LDO outputs */ 7790f80d95SChun-Kuang Hu mipi_tx->driver_data->mipi_tx_disable_signal(phy); 7890f80d95SChun-Kuang Hu 7990f80d95SChun-Kuang Hu /* Disable PLL and power down core */ 8090f80d95SChun-Kuang Hu clk_disable_unprepare(mipi_tx->pll); 8190f80d95SChun-Kuang Hu 8290f80d95SChun-Kuang Hu return 0; 8390f80d95SChun-Kuang Hu } 8490f80d95SChun-Kuang Hu 8590f80d95SChun-Kuang Hu static const struct phy_ops mtk_mipi_tx_ops = { 8690f80d95SChun-Kuang Hu .power_on = mtk_mipi_tx_power_on, 8790f80d95SChun-Kuang Hu .power_off = mtk_mipi_tx_power_off, 8890f80d95SChun-Kuang Hu .owner = THIS_MODULE, 8990f80d95SChun-Kuang Hu }; 9090f80d95SChun-Kuang Hu 9190f80d95SChun-Kuang Hu static void mtk_mipi_tx_get_calibration_datal(struct mtk_mipi_tx *mipi_tx) 9290f80d95SChun-Kuang Hu { 9390f80d95SChun-Kuang Hu struct nvmem_cell *cell; 9490f80d95SChun-Kuang Hu size_t len; 9590f80d95SChun-Kuang Hu u32 *buf; 9690f80d95SChun-Kuang Hu 9790f80d95SChun-Kuang Hu cell = nvmem_cell_get(mipi_tx->dev, "calibration-data"); 9890f80d95SChun-Kuang Hu if (IS_ERR(cell)) { 9990f80d95SChun-Kuang Hu dev_info(mipi_tx->dev, "can't get nvmem_cell_get, ignore it\n"); 10090f80d95SChun-Kuang Hu return; 10190f80d95SChun-Kuang Hu } 10290f80d95SChun-Kuang Hu buf = (u32 *)nvmem_cell_read(cell, &len); 10390f80d95SChun-Kuang Hu nvmem_cell_put(cell); 10490f80d95SChun-Kuang Hu 10590f80d95SChun-Kuang Hu if (IS_ERR(buf)) { 10690f80d95SChun-Kuang Hu dev_info(mipi_tx->dev, "can't get data, ignore it\n"); 10790f80d95SChun-Kuang Hu return; 10890f80d95SChun-Kuang Hu } 10990f80d95SChun-Kuang Hu 11090f80d95SChun-Kuang Hu if (len < 3 * sizeof(u32)) { 11190f80d95SChun-Kuang Hu dev_info(mipi_tx->dev, "invalid calibration data\n"); 11290f80d95SChun-Kuang Hu kfree(buf); 11390f80d95SChun-Kuang Hu return; 11490f80d95SChun-Kuang Hu } 11590f80d95SChun-Kuang Hu 11690f80d95SChun-Kuang Hu mipi_tx->rt_code[0] = ((buf[0] >> 6 & 0x1f) << 5) | 11790f80d95SChun-Kuang Hu (buf[0] >> 11 & 0x1f); 11890f80d95SChun-Kuang Hu mipi_tx->rt_code[1] = ((buf[1] >> 27 & 0x1f) << 5) | 11990f80d95SChun-Kuang Hu (buf[0] >> 1 & 0x1f); 12090f80d95SChun-Kuang Hu mipi_tx->rt_code[2] = ((buf[1] >> 17 & 0x1f) << 5) | 12190f80d95SChun-Kuang Hu (buf[1] >> 22 & 0x1f); 12290f80d95SChun-Kuang Hu mipi_tx->rt_code[3] = ((buf[1] >> 7 & 0x1f) << 5) | 12390f80d95SChun-Kuang Hu (buf[1] >> 12 & 0x1f); 12490f80d95SChun-Kuang Hu mipi_tx->rt_code[4] = ((buf[2] >> 27 & 0x1f) << 5) | 12590f80d95SChun-Kuang Hu (buf[1] >> 2 & 0x1f); 12690f80d95SChun-Kuang Hu kfree(buf); 12790f80d95SChun-Kuang Hu } 12890f80d95SChun-Kuang Hu 12990f80d95SChun-Kuang Hu static int mtk_mipi_tx_probe(struct platform_device *pdev) 13090f80d95SChun-Kuang Hu { 13190f80d95SChun-Kuang Hu struct device *dev = &pdev->dev; 13290f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx; 13390f80d95SChun-Kuang Hu struct resource *mem; 13490f80d95SChun-Kuang Hu const char *ref_clk_name; 13590f80d95SChun-Kuang Hu struct clk *ref_clk; 13690f80d95SChun-Kuang Hu struct clk_init_data clk_init = { 13790f80d95SChun-Kuang Hu .num_parents = 1, 13890f80d95SChun-Kuang Hu .parent_names = (const char * const *)&ref_clk_name, 13990f80d95SChun-Kuang Hu .flags = CLK_SET_RATE_GATE, 14090f80d95SChun-Kuang Hu }; 14190f80d95SChun-Kuang Hu struct phy *phy; 14290f80d95SChun-Kuang Hu struct phy_provider *phy_provider; 14390f80d95SChun-Kuang Hu int ret; 14490f80d95SChun-Kuang Hu 14590f80d95SChun-Kuang Hu mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL); 14690f80d95SChun-Kuang Hu if (!mipi_tx) 14790f80d95SChun-Kuang Hu return -ENOMEM; 14890f80d95SChun-Kuang Hu 14990f80d95SChun-Kuang Hu mipi_tx->driver_data = of_device_get_match_data(dev); 15090f80d95SChun-Kuang Hu 15190f80d95SChun-Kuang Hu mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 15290f80d95SChun-Kuang Hu mipi_tx->regs = devm_ioremap_resource(dev, mem); 15390f80d95SChun-Kuang Hu if (IS_ERR(mipi_tx->regs)) { 154*779fabf2SHe Ying return PTR_ERR(mipi_tx->regs); 15590f80d95SChun-Kuang Hu } 15690f80d95SChun-Kuang Hu 15790f80d95SChun-Kuang Hu ref_clk = devm_clk_get(dev, NULL); 15890f80d95SChun-Kuang Hu if (IS_ERR(ref_clk)) { 15990f80d95SChun-Kuang Hu ret = PTR_ERR(ref_clk); 16090f80d95SChun-Kuang Hu dev_err(dev, "Failed to get reference clock: %d\n", ret); 16190f80d95SChun-Kuang Hu return ret; 16290f80d95SChun-Kuang Hu } 16390f80d95SChun-Kuang Hu 16490f80d95SChun-Kuang Hu ret = of_property_read_u32(dev->of_node, "drive-strength-microamp", 16590f80d95SChun-Kuang Hu &mipi_tx->mipitx_drive); 16690f80d95SChun-Kuang Hu /* If can't get the "mipi_tx->mipitx_drive", set it default 0x8 */ 16790f80d95SChun-Kuang Hu if (ret < 0) 16890f80d95SChun-Kuang Hu mipi_tx->mipitx_drive = 4600; 16990f80d95SChun-Kuang Hu 17090f80d95SChun-Kuang Hu /* check the mipitx_drive valid */ 17190f80d95SChun-Kuang Hu if (mipi_tx->mipitx_drive > 6000 || mipi_tx->mipitx_drive < 3000) { 17290f80d95SChun-Kuang Hu dev_warn(dev, "drive-strength-microamp is invalid %d, not in 3000 ~ 6000\n", 17390f80d95SChun-Kuang Hu mipi_tx->mipitx_drive); 17490f80d95SChun-Kuang Hu mipi_tx->mipitx_drive = clamp_val(mipi_tx->mipitx_drive, 3000, 17590f80d95SChun-Kuang Hu 6000); 17690f80d95SChun-Kuang Hu } 17790f80d95SChun-Kuang Hu 17890f80d95SChun-Kuang Hu ref_clk_name = __clk_get_name(ref_clk); 17990f80d95SChun-Kuang Hu 18090f80d95SChun-Kuang Hu ret = of_property_read_string(dev->of_node, "clock-output-names", 18190f80d95SChun-Kuang Hu &clk_init.name); 18290f80d95SChun-Kuang Hu if (ret < 0) { 18390f80d95SChun-Kuang Hu dev_err(dev, "Failed to read clock-output-names: %d\n", ret); 18490f80d95SChun-Kuang Hu return ret; 18590f80d95SChun-Kuang Hu } 18690f80d95SChun-Kuang Hu 18790f80d95SChun-Kuang Hu clk_init.ops = mipi_tx->driver_data->mipi_tx_clk_ops; 18890f80d95SChun-Kuang Hu 18990f80d95SChun-Kuang Hu mipi_tx->pll_hw.init = &clk_init; 19090f80d95SChun-Kuang Hu mipi_tx->pll = devm_clk_register(dev, &mipi_tx->pll_hw); 19190f80d95SChun-Kuang Hu if (IS_ERR(mipi_tx->pll)) { 19290f80d95SChun-Kuang Hu ret = PTR_ERR(mipi_tx->pll); 19390f80d95SChun-Kuang Hu dev_err(dev, "Failed to register PLL: %d\n", ret); 19490f80d95SChun-Kuang Hu return ret; 19590f80d95SChun-Kuang Hu } 19690f80d95SChun-Kuang Hu 19790f80d95SChun-Kuang Hu phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops); 19890f80d95SChun-Kuang Hu if (IS_ERR(phy)) { 19990f80d95SChun-Kuang Hu ret = PTR_ERR(phy); 20090f80d95SChun-Kuang Hu dev_err(dev, "Failed to create MIPI D-PHY: %d\n", ret); 20190f80d95SChun-Kuang Hu return ret; 20290f80d95SChun-Kuang Hu } 20390f80d95SChun-Kuang Hu phy_set_drvdata(phy, mipi_tx); 20490f80d95SChun-Kuang Hu 20590f80d95SChun-Kuang Hu phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 20690f80d95SChun-Kuang Hu if (IS_ERR(phy_provider)) { 20790f80d95SChun-Kuang Hu ret = PTR_ERR(phy_provider); 20890f80d95SChun-Kuang Hu return ret; 20990f80d95SChun-Kuang Hu } 21090f80d95SChun-Kuang Hu 21190f80d95SChun-Kuang Hu mipi_tx->dev = dev; 21290f80d95SChun-Kuang Hu 21390f80d95SChun-Kuang Hu mtk_mipi_tx_get_calibration_datal(mipi_tx); 21490f80d95SChun-Kuang Hu 21590f80d95SChun-Kuang Hu return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, 21690f80d95SChun-Kuang Hu mipi_tx->pll); 21790f80d95SChun-Kuang Hu } 21890f80d95SChun-Kuang Hu 21990f80d95SChun-Kuang Hu static int mtk_mipi_tx_remove(struct platform_device *pdev) 22090f80d95SChun-Kuang Hu { 22190f80d95SChun-Kuang Hu of_clk_del_provider(pdev->dev.of_node); 22290f80d95SChun-Kuang Hu return 0; 22390f80d95SChun-Kuang Hu } 22490f80d95SChun-Kuang Hu 22590f80d95SChun-Kuang Hu static const struct of_device_id mtk_mipi_tx_match[] = { 22690f80d95SChun-Kuang Hu { .compatible = "mediatek,mt2701-mipi-tx", 22790f80d95SChun-Kuang Hu .data = &mt2701_mipitx_data }, 22890f80d95SChun-Kuang Hu { .compatible = "mediatek,mt8173-mipi-tx", 22990f80d95SChun-Kuang Hu .data = &mt8173_mipitx_data }, 23090f80d95SChun-Kuang Hu { .compatible = "mediatek,mt8183-mipi-tx", 23190f80d95SChun-Kuang Hu .data = &mt8183_mipitx_data }, 23290f80d95SChun-Kuang Hu { }, 23390f80d95SChun-Kuang Hu }; 2349a8b9434SBoris Brezillon MODULE_DEVICE_TABLE(of, mtk_mipi_tx_match); 23590f80d95SChun-Kuang Hu 2366d54623aSZou Wei static struct platform_driver mtk_mipi_tx_driver = { 23790f80d95SChun-Kuang Hu .probe = mtk_mipi_tx_probe, 23890f80d95SChun-Kuang Hu .remove = mtk_mipi_tx_remove, 23990f80d95SChun-Kuang Hu .driver = { 24090f80d95SChun-Kuang Hu .name = "mediatek-mipi-tx", 24190f80d95SChun-Kuang Hu .of_match_table = mtk_mipi_tx_match, 24290f80d95SChun-Kuang Hu }, 24390f80d95SChun-Kuang Hu }; 24490f80d95SChun-Kuang Hu module_platform_driver(mtk_mipi_tx_driver); 24590f80d95SChun-Kuang Hu 24690f80d95SChun-Kuang Hu MODULE_DESCRIPTION("MediaTek MIPI TX Driver"); 24790f80d95SChun-Kuang Hu MODULE_LICENSE("GPL v2"); 248