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 int mtk_mipi_tx_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1490f80d95SChun-Kuang Hu unsigned long parent_rate) 1590f80d95SChun-Kuang Hu { 1690f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); 1790f80d95SChun-Kuang Hu 1890f80d95SChun-Kuang Hu dev_dbg(mipi_tx->dev, "set rate: %lu Hz\n", rate); 1990f80d95SChun-Kuang Hu 2090f80d95SChun-Kuang Hu mipi_tx->data_rate = rate; 2190f80d95SChun-Kuang Hu 2290f80d95SChun-Kuang Hu return 0; 2390f80d95SChun-Kuang Hu } 2490f80d95SChun-Kuang Hu 2590f80d95SChun-Kuang Hu unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, 2690f80d95SChun-Kuang Hu unsigned long parent_rate) 2790f80d95SChun-Kuang Hu { 2890f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); 2990f80d95SChun-Kuang Hu 3090f80d95SChun-Kuang Hu return mipi_tx->data_rate; 3190f80d95SChun-Kuang Hu } 3290f80d95SChun-Kuang Hu 3390f80d95SChun-Kuang Hu static int mtk_mipi_tx_power_on(struct phy *phy) 3490f80d95SChun-Kuang Hu { 3590f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); 3690f80d95SChun-Kuang Hu int ret; 3790f80d95SChun-Kuang Hu 3890f80d95SChun-Kuang Hu /* Power up core and enable PLL */ 3994255d98SAngeloGioacchino Del Regno ret = clk_prepare_enable(mipi_tx->pll_hw.clk); 4090f80d95SChun-Kuang Hu if (ret < 0) 4190f80d95SChun-Kuang Hu return ret; 4290f80d95SChun-Kuang Hu 4390f80d95SChun-Kuang Hu /* Enable DSI Lane LDO outputs, disable pad tie low */ 4490f80d95SChun-Kuang Hu mipi_tx->driver_data->mipi_tx_enable_signal(phy); 4590f80d95SChun-Kuang Hu return 0; 4690f80d95SChun-Kuang Hu } 4790f80d95SChun-Kuang Hu 4890f80d95SChun-Kuang Hu static int mtk_mipi_tx_power_off(struct phy *phy) 4990f80d95SChun-Kuang Hu { 5090f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); 5190f80d95SChun-Kuang Hu 5290f80d95SChun-Kuang Hu /* Enable pad tie low, disable DSI Lane LDO outputs */ 5390f80d95SChun-Kuang Hu mipi_tx->driver_data->mipi_tx_disable_signal(phy); 5490f80d95SChun-Kuang Hu 5590f80d95SChun-Kuang Hu /* Disable PLL and power down core */ 5694255d98SAngeloGioacchino Del Regno clk_disable_unprepare(mipi_tx->pll_hw.clk); 5790f80d95SChun-Kuang Hu 5890f80d95SChun-Kuang Hu return 0; 5990f80d95SChun-Kuang Hu } 6090f80d95SChun-Kuang Hu 6190f80d95SChun-Kuang Hu static const struct phy_ops mtk_mipi_tx_ops = { 6290f80d95SChun-Kuang Hu .power_on = mtk_mipi_tx_power_on, 6390f80d95SChun-Kuang Hu .power_off = mtk_mipi_tx_power_off, 6490f80d95SChun-Kuang Hu .owner = THIS_MODULE, 6590f80d95SChun-Kuang Hu }; 6690f80d95SChun-Kuang Hu 6790f80d95SChun-Kuang Hu static void mtk_mipi_tx_get_calibration_datal(struct mtk_mipi_tx *mipi_tx) 6890f80d95SChun-Kuang Hu { 6990f80d95SChun-Kuang Hu struct nvmem_cell *cell; 7090f80d95SChun-Kuang Hu size_t len; 7190f80d95SChun-Kuang Hu u32 *buf; 7290f80d95SChun-Kuang Hu 7390f80d95SChun-Kuang Hu cell = nvmem_cell_get(mipi_tx->dev, "calibration-data"); 7490f80d95SChun-Kuang Hu if (IS_ERR(cell)) { 7590f80d95SChun-Kuang Hu dev_info(mipi_tx->dev, "can't get nvmem_cell_get, ignore it\n"); 7690f80d95SChun-Kuang Hu return; 7790f80d95SChun-Kuang Hu } 7890f80d95SChun-Kuang Hu buf = (u32 *)nvmem_cell_read(cell, &len); 7990f80d95SChun-Kuang Hu nvmem_cell_put(cell); 8090f80d95SChun-Kuang Hu 8190f80d95SChun-Kuang Hu if (IS_ERR(buf)) { 8290f80d95SChun-Kuang Hu dev_info(mipi_tx->dev, "can't get data, ignore it\n"); 8390f80d95SChun-Kuang Hu return; 8490f80d95SChun-Kuang Hu } 8590f80d95SChun-Kuang Hu 8690f80d95SChun-Kuang Hu if (len < 3 * sizeof(u32)) { 8790f80d95SChun-Kuang Hu dev_info(mipi_tx->dev, "invalid calibration data\n"); 8890f80d95SChun-Kuang Hu kfree(buf); 8990f80d95SChun-Kuang Hu return; 9090f80d95SChun-Kuang Hu } 9190f80d95SChun-Kuang Hu 9290f80d95SChun-Kuang Hu mipi_tx->rt_code[0] = ((buf[0] >> 6 & 0x1f) << 5) | 9390f80d95SChun-Kuang Hu (buf[0] >> 11 & 0x1f); 9490f80d95SChun-Kuang Hu mipi_tx->rt_code[1] = ((buf[1] >> 27 & 0x1f) << 5) | 9590f80d95SChun-Kuang Hu (buf[0] >> 1 & 0x1f); 9690f80d95SChun-Kuang Hu mipi_tx->rt_code[2] = ((buf[1] >> 17 & 0x1f) << 5) | 9790f80d95SChun-Kuang Hu (buf[1] >> 22 & 0x1f); 9890f80d95SChun-Kuang Hu mipi_tx->rt_code[3] = ((buf[1] >> 7 & 0x1f) << 5) | 9990f80d95SChun-Kuang Hu (buf[1] >> 12 & 0x1f); 10090f80d95SChun-Kuang Hu mipi_tx->rt_code[4] = ((buf[2] >> 27 & 0x1f) << 5) | 10190f80d95SChun-Kuang Hu (buf[1] >> 2 & 0x1f); 10290f80d95SChun-Kuang Hu kfree(buf); 10390f80d95SChun-Kuang Hu } 10490f80d95SChun-Kuang Hu 10590f80d95SChun-Kuang Hu static int mtk_mipi_tx_probe(struct platform_device *pdev) 10690f80d95SChun-Kuang Hu { 10790f80d95SChun-Kuang Hu struct device *dev = &pdev->dev; 10890f80d95SChun-Kuang Hu struct mtk_mipi_tx *mipi_tx; 10990f80d95SChun-Kuang Hu const char *ref_clk_name; 11090f80d95SChun-Kuang Hu struct clk *ref_clk; 11190f80d95SChun-Kuang Hu struct clk_init_data clk_init = { 11290f80d95SChun-Kuang Hu .num_parents = 1, 11390f80d95SChun-Kuang Hu .parent_names = (const char * const *)&ref_clk_name, 11490f80d95SChun-Kuang Hu .flags = CLK_SET_RATE_GATE, 11590f80d95SChun-Kuang Hu }; 11690f80d95SChun-Kuang Hu struct phy *phy; 11790f80d95SChun-Kuang Hu struct phy_provider *phy_provider; 11890f80d95SChun-Kuang Hu int ret; 11990f80d95SChun-Kuang Hu 12090f80d95SChun-Kuang Hu mipi_tx = devm_kzalloc(dev, sizeof(*mipi_tx), GFP_KERNEL); 12190f80d95SChun-Kuang Hu if (!mipi_tx) 12290f80d95SChun-Kuang Hu return -ENOMEM; 12390f80d95SChun-Kuang Hu 12490f80d95SChun-Kuang Hu mipi_tx->driver_data = of_device_get_match_data(dev); 125399c91c3SMiaoqian Lin if (!mipi_tx->driver_data) 126399c91c3SMiaoqian Lin return -ENODEV; 12790f80d95SChun-Kuang Hu 1287508d1e4SChunfeng Yun mipi_tx->regs = devm_platform_ioremap_resource(pdev, 0); 1297508d1e4SChunfeng Yun if (IS_ERR(mipi_tx->regs)) 130779fabf2SHe Ying return PTR_ERR(mipi_tx->regs); 13190f80d95SChun-Kuang Hu 13290f80d95SChun-Kuang Hu ref_clk = devm_clk_get(dev, NULL); 133b7b930f3SAngeloGioacchino Del Regno if (IS_ERR(ref_clk)) 134b7b930f3SAngeloGioacchino Del Regno return dev_err_probe(dev, PTR_ERR(ref_clk), 135b7b930f3SAngeloGioacchino Del Regno "Failed to get reference clock\n"); 13690f80d95SChun-Kuang Hu 13790f80d95SChun-Kuang Hu ret = of_property_read_u32(dev->of_node, "drive-strength-microamp", 13890f80d95SChun-Kuang Hu &mipi_tx->mipitx_drive); 13990f80d95SChun-Kuang Hu /* If can't get the "mipi_tx->mipitx_drive", set it default 0x8 */ 14090f80d95SChun-Kuang Hu if (ret < 0) 14190f80d95SChun-Kuang Hu mipi_tx->mipitx_drive = 4600; 14290f80d95SChun-Kuang Hu 14390f80d95SChun-Kuang Hu /* check the mipitx_drive valid */ 14490f80d95SChun-Kuang Hu if (mipi_tx->mipitx_drive > 6000 || mipi_tx->mipitx_drive < 3000) { 14590f80d95SChun-Kuang Hu dev_warn(dev, "drive-strength-microamp is invalid %d, not in 3000 ~ 6000\n", 14690f80d95SChun-Kuang Hu mipi_tx->mipitx_drive); 14790f80d95SChun-Kuang Hu mipi_tx->mipitx_drive = clamp_val(mipi_tx->mipitx_drive, 3000, 14890f80d95SChun-Kuang Hu 6000); 14990f80d95SChun-Kuang Hu } 15090f80d95SChun-Kuang Hu 15190f80d95SChun-Kuang Hu ref_clk_name = __clk_get_name(ref_clk); 15290f80d95SChun-Kuang Hu 15390f80d95SChun-Kuang Hu ret = of_property_read_string(dev->of_node, "clock-output-names", 15490f80d95SChun-Kuang Hu &clk_init.name); 155b7b930f3SAngeloGioacchino Del Regno if (ret < 0) 156b7b930f3SAngeloGioacchino Del Regno return dev_err_probe(dev, ret, "Failed to read clock-output-names\n"); 15790f80d95SChun-Kuang Hu 15890f80d95SChun-Kuang Hu clk_init.ops = mipi_tx->driver_data->mipi_tx_clk_ops; 15990f80d95SChun-Kuang Hu 16090f80d95SChun-Kuang Hu mipi_tx->pll_hw.init = &clk_init; 16194255d98SAngeloGioacchino Del Regno ret = devm_clk_hw_register(dev, &mipi_tx->pll_hw); 16294255d98SAngeloGioacchino Del Regno if (ret) 16394255d98SAngeloGioacchino Del Regno return dev_err_probe(dev, ret, "Failed to register PLL\n"); 16490f80d95SChun-Kuang Hu 16590f80d95SChun-Kuang Hu phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops); 166b7b930f3SAngeloGioacchino Del Regno if (IS_ERR(phy)) 167b7b930f3SAngeloGioacchino Del Regno return dev_err_probe(dev, PTR_ERR(phy), "Failed to create MIPI D-PHY\n"); 168b7b930f3SAngeloGioacchino Del Regno 16990f80d95SChun-Kuang Hu phy_set_drvdata(phy, mipi_tx); 17090f80d95SChun-Kuang Hu 17190f80d95SChun-Kuang Hu phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 17275203e79SChunfeng Yun if (IS_ERR(phy_provider)) 17375203e79SChunfeng Yun return PTR_ERR(phy_provider); 17490f80d95SChun-Kuang Hu 17590f80d95SChun-Kuang Hu mipi_tx->dev = dev; 17690f80d95SChun-Kuang Hu 17790f80d95SChun-Kuang Hu mtk_mipi_tx_get_calibration_datal(mipi_tx); 17890f80d95SChun-Kuang Hu 179*e90da3fcSAngeloGioacchino Del Regno return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mipi_tx->pll_hw); 18090f80d95SChun-Kuang Hu } 18190f80d95SChun-Kuang Hu 18290f80d95SChun-Kuang Hu static const struct of_device_id mtk_mipi_tx_match[] = { 18390f80d95SChun-Kuang Hu { .compatible = "mediatek,mt2701-mipi-tx", 18490f80d95SChun-Kuang Hu .data = &mt2701_mipitx_data }, 18590f80d95SChun-Kuang Hu { .compatible = "mediatek,mt8173-mipi-tx", 18690f80d95SChun-Kuang Hu .data = &mt8173_mipitx_data }, 18790f80d95SChun-Kuang Hu { .compatible = "mediatek,mt8183-mipi-tx", 18890f80d95SChun-Kuang Hu .data = &mt8183_mipitx_data }, 18990f80d95SChun-Kuang Hu { }, 19090f80d95SChun-Kuang Hu }; 1919a8b9434SBoris Brezillon MODULE_DEVICE_TABLE(of, mtk_mipi_tx_match); 19290f80d95SChun-Kuang Hu 1936d54623aSZou Wei static struct platform_driver mtk_mipi_tx_driver = { 19490f80d95SChun-Kuang Hu .probe = mtk_mipi_tx_probe, 19590f80d95SChun-Kuang Hu .driver = { 19690f80d95SChun-Kuang Hu .name = "mediatek-mipi-tx", 19790f80d95SChun-Kuang Hu .of_match_table = mtk_mipi_tx_match, 19890f80d95SChun-Kuang Hu }, 19990f80d95SChun-Kuang Hu }; 20090f80d95SChun-Kuang Hu module_platform_driver(mtk_mipi_tx_driver); 20190f80d95SChun-Kuang Hu 20290f80d95SChun-Kuang Hu MODULE_DESCRIPTION("MediaTek MIPI TX Driver"); 20390f80d95SChun-Kuang Hu MODULE_LICENSE("GPL v2"); 204