Message ID | 20181002081041.32629-8-bibby.hsieh@mediatek.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/mediatek: support hdmi output for mt2701 and mt7623 | expand |
Hi, Bibby: On Tue, 2018-10-02 at 16:10 +0800, Bibby Hsieh wrote: > From: chunhui dai <chunhui.dai@mediatek.com> > > Different IC has different phy setting of HDMI. > This patch separates the phy hardware relate part for mt8173. > Reviewed-by: CK Hu <ck.hu@mediatek> > Signed-off-by: chunhui dai <chunhui.dai@mediatek.com> > --- > drivers/gpu/drm/mediatek/Makefile | 6 +- > drivers/gpu/drm/mediatek/mtk_hdmi.c | 1 + > drivers/gpu/drm/mediatek/mtk_hdmi.h | 2 +- > drivers/gpu/drm/mediatek/mtk_hdmi_phy.c | 210 +++++++++++++++++++++++++ > drivers/gpu/drm/mediatek/mtk_hdmi_phy.h | 54 +++++++ > drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c | 204 +----------------------- > 6 files changed, 276 insertions(+), 201 deletions(-) > create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_phy.c > create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_phy.h > > diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile > index ce83c396a742..61cf0d2ab28a 100644 > --- a/drivers/gpu/drm/mediatek/Makefile > +++ b/drivers/gpu/drm/mediatek/Makefile > @@ -1,4 +1,5 @@ > # SPDX-License-Identifier: GPL-2.0 > + > mediatek-drm-y := mtk_disp_color.o \ > mtk_disp_ovl.o \ > mtk_disp_rdma.o \ > @@ -18,6 +19,7 @@ obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o > mediatek-drm-hdmi-objs := mtk_cec.o \ > mtk_hdmi.o \ > mtk_hdmi_ddc.o \ > - mtk_mt8173_hdmi_phy.o > + mtk_mt8173_hdmi_phy.o \ > + mtk_hdmi_phy.o > > -obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o > +obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o > \ No newline at end of file > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c > index 2d45d1dd9554..2ca9f6a64dab 100644 > --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c > @@ -233,6 +233,7 @@ static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black) > static void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable) > { > struct arm_smccc_res res; > + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(hdmi->phy); > > /* > * MT8173 HDMI hardware has an output control bit to enable/disable HDMI > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h > index 6371b3de1ff6..3e9fb8d19802 100644 > --- a/drivers/gpu/drm/mediatek/mtk_hdmi.h > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h > @@ -13,11 +13,11 @@ > */ > #ifndef _MTK_HDMI_CTRL_H > #define _MTK_HDMI_CTRL_H > +#include "mtk_hdmi_phy.h" > > struct platform_driver; > > extern struct platform_driver mtk_cec_driver; > extern struct platform_driver mtk_hdmi_ddc_driver; > -extern struct platform_driver mtk_hdmi_phy_driver; > > #endif /* _MTK_HDMI_CTRL_H */ > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.c > new file mode 100644 > index 000000000000..d2dc50db1feb > --- /dev/null > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.c > @@ -0,0 +1,210 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Jie Qiu <jie.qiu@mediatek.com> > + */ > + > +#include "mtk_hdmi_phy.h" > + > +static int mtk_hdmi_phy_power_on(struct phy *phy); > +static int mtk_hdmi_phy_power_off(struct phy *phy); > + > +static const struct phy_ops mtk_hdmi_phy_dev_ops = { > + .power_on = mtk_hdmi_phy_power_on, > + .power_off = mtk_hdmi_phy_power_off, > + .owner = THIS_MODULE, > +}; > + > +void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > + u32 bits) > +{ > + void __iomem *reg = hdmi_phy->regs + offset; > + u32 tmp; > + > + tmp = readl(reg); > + tmp &= ~bits; > + writel(tmp, reg); > +} > + > +void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > + u32 bits) > +{ > + void __iomem *reg = hdmi_phy->regs + offset; > + u32 tmp; > + > + tmp = readl(reg); > + tmp |= bits; > + writel(tmp, reg); > +} > + > +void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > + u32 val, u32 mask) > +{ > + void __iomem *reg = hdmi_phy->regs + offset; > + u32 tmp; > + > + tmp = readl(reg); > + tmp = (tmp & ~mask) | (val & mask); > + writel(tmp, reg); > +} > + > +inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) > +{ > + return container_of(hw, struct mtk_hdmi_phy, pll_hw); > +} > + > +static int mtk_hdmi_phy_power_on(struct phy *phy) > +{ > + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); > + int ret; > + > + ret = clk_prepare_enable(hdmi_phy->pll); > + if (ret < 0) > + return ret; > + > + hdmi_phy->conf->hdmi_phy_enable_tmds(hdmi_phy); > + return 0; > +} > + > +static int mtk_hdmi_phy_power_off(struct phy *phy) > +{ > + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); > + > + hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy); > + clk_disable_unprepare(hdmi_phy->pll); > + > + return 0; > +} > + > +static const struct phy_ops * > +mtk_hdmi_phy_dev_get_ops(const struct mtk_hdmi_phy *hdmi_phy) > +{ > + if (hdmi_phy && hdmi_phy->conf && > + hdmi_phy->conf->hdmi_phy_enable_tmds && > + hdmi_phy->conf->hdmi_phy_disable_tmds) > + return &mtk_hdmi_phy_dev_ops; > + > + dev_err(hdmi_phy->dev, "Failed to get dev ops of phy\n"); > + return NULL; > +} > + > +static void mtk_hdmi_phy_clk_get_ops(struct mtk_hdmi_phy *hdmi_phy, > + const struct clk_ops **ops) > +{ > + if (hdmi_phy && hdmi_phy->conf && hdmi_phy->conf->hdmi_phy_clk_ops) > + *ops = hdmi_phy->conf->hdmi_phy_clk_ops; > + else > + dev_err(hdmi_phy->dev, "Failed to get clk ops of phy\n"); > +} > + > +static int mtk_hdmi_phy_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct mtk_hdmi_phy *hdmi_phy; > + struct resource *mem; > + struct clk *ref_clk; > + const char *ref_clk_name; > + struct clk_init_data clk_init = { > + .num_parents = 1, > + .parent_names = (const char * const *)&ref_clk_name, > + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, > + }; > + > + struct phy *phy; > + struct phy_provider *phy_provider; > + int ret; > + > + hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); > + if (!hdmi_phy) > + return -ENOMEM; > + > + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + hdmi_phy->regs = devm_ioremap_resource(dev, mem); > + if (IS_ERR(hdmi_phy->regs)) { > + ret = PTR_ERR(hdmi_phy->regs); > + dev_err(dev, "Failed to get memory resource: %d\n", ret); > + return ret; > + } > + > + ref_clk = devm_clk_get(dev, "pll_ref"); > + if (IS_ERR(ref_clk)) { > + ret = PTR_ERR(ref_clk); > + dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n", > + ret); > + return ret; > + } > + ref_clk_name = __clk_get_name(ref_clk); > + > + ret = of_property_read_string(dev->of_node, "clock-output-names", > + &clk_init.name); > + if (ret < 0) { > + dev_err(dev, "Failed to read clock-output-names: %d\n", ret); > + return ret; > + } > + > + hdmi_phy->dev = dev; > + hdmi_phy->conf = > + (struct mtk_hdmi_phy_conf *)of_device_get_match_data(dev); > + mtk_hdmi_phy_clk_get_ops(hdmi_phy, &clk_init.ops); > + hdmi_phy->pll_hw.init = &clk_init; > + hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw); > + if (IS_ERR(hdmi_phy->pll)) { > + ret = PTR_ERR(hdmi_phy->pll); > + dev_err(dev, "Failed to register PLL: %d\n", ret); > + return ret; > + } > + > + ret = of_property_read_u32(dev->of_node, "mediatek,ibias", > + &hdmi_phy->ibias); > + if (ret < 0) { > + dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret); > + return ret; > + } > + > + ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", > + &hdmi_phy->ibias_up); > + if (ret < 0) { > + dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret); > + return ret; > + } > + > + dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n"); > + hdmi_phy->drv_imp_clk = 0x30; > + hdmi_phy->drv_imp_d2 = 0x30; > + hdmi_phy->drv_imp_d1 = 0x30; > + hdmi_phy->drv_imp_d0 = 0x30; > + > + phy = devm_phy_create(dev, NULL, mtk_hdmi_phy_dev_get_ops(hdmi_phy)); > + if (IS_ERR(phy)) { > + dev_err(dev, "Failed to create HDMI PHY\n"); > + return PTR_ERR(phy); > + } > + phy_set_drvdata(phy, hdmi_phy); > + > + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(phy_provider)) { > + dev_err(dev, "Failed to register HDMI PHY\n"); > + return PTR_ERR(phy_provider); > + } > + > + return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, > + hdmi_phy->pll); > +} > + > +static const struct of_device_id mtk_hdmi_phy_match[] = { > + { .compatible = "mediatek,mt8173-hdmi-phy", > + .data = &mtk_hdmi_phy_8173_conf, > + }, > + {}, > +}; > + > +struct platform_driver mtk_hdmi_phy_driver = { > + .probe = mtk_hdmi_phy_probe, > + .driver = { > + .name = "mediatek-hdmi-phy", > + .of_match_table = mtk_hdmi_phy_match, > + }, > +}; > + > +MODULE_DESCRIPTION("MediaTek HDMI PHY Driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_phy.h b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.h > new file mode 100644 > index 000000000000..e346fe319621 > --- /dev/null > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.h > @@ -0,0 +1,54 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2018 MediaTek Inc. > + * Author: Chunhui Dai <chunhui.dai@mediatek.com> > + */ > + > +#ifndef _MTK_HDMI_PHY_H > +#define _MTK_HDMI_PHY_H > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/delay.h> > +#include <linux/io.h> > +#include <linux/mfd/syscon.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/types.h> > + > +struct mtk_hdmi_phy; > + > +struct mtk_hdmi_phy_conf { > + const struct clk_ops *hdmi_phy_clk_ops; > + void (*hdmi_phy_enable_tmds)(struct mtk_hdmi_phy *hdmi_phy); > + void (*hdmi_phy_disable_tmds)(struct mtk_hdmi_phy *hdmi_phy); > +}; > + > +struct mtk_hdmi_phy { > + void __iomem *regs; > + struct device *dev; > + struct mtk_hdmi_phy_conf *conf; > + struct clk *pll; > + struct clk_hw pll_hw; > + unsigned long pll_rate; > + unsigned char drv_imp_clk; > + unsigned char drv_imp_d2; > + unsigned char drv_imp_d1; > + unsigned char drv_imp_d0; > + unsigned int ibias; > + unsigned int ibias_up; > +}; > + > +void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > + u32 bits); > +void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > + u32 bits); > +void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > + u32 val, u32 mask); > +struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw); > + > +extern struct platform_driver mtk_hdmi_phy_driver; > +extern struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf; > + > +#endif /* _MTK_HDMI_PHY_H */ > diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c > index 51cb9cfb6646..5dea03334042 100644 > --- a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c > +++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c > @@ -12,15 +12,7 @@ > * GNU General Public License for more details. > */ > > -#include <linux/clk.h> > -#include <linux/clk-provider.h> > -#include <linux/delay.h> > -#include <linux/io.h> > -#include <linux/mfd/syscon.h> > -#include <linux/module.h> > -#include <linux/phy/phy.h> > -#include <linux/platform_device.h> > -#include <linux/types.h> > +#include "mtk_hdmi_phy.h" > > #define HDMI_CON0 0x00 > #define RG_HDMITX_PLL_EN BIT(31) > @@ -123,20 +115,6 @@ > #define RGS_HDMITX_5T1_EDG (0xf << 4) > #define RGS_HDMITX_PLUG_TST BIT(0) > > -struct mtk_hdmi_phy { > - void __iomem *regs; > - struct device *dev; > - struct clk *pll; > - struct clk_hw pll_hw; > - unsigned long pll_rate; > - u8 drv_imp_clk; > - u8 drv_imp_d2; > - u8 drv_imp_d1; > - u8 drv_imp_d0; > - u32 ibias; > - u32 ibias_up; > -}; > - > static const u8 PREDIV[3][4] = { > {0x0, 0x0, 0x0, 0x0}, /* 27Mhz */ > {0x1, 0x1, 0x1, 0x1}, /* 74Mhz */ > @@ -185,44 +163,6 @@ static const u8 HTPLLBR[3][4] = { > {0x1, 0x2, 0x2, 0x1} /* 148Mhz */ > }; > > -static void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > - u32 bits) > -{ > - void __iomem *reg = hdmi_phy->regs + offset; > - u32 tmp; > - > - tmp = readl(reg); > - tmp &= ~bits; > - writel(tmp, reg); > -} > - > -static void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > - u32 bits) > -{ > - void __iomem *reg = hdmi_phy->regs + offset; > - u32 tmp; > - > - tmp = readl(reg); > - tmp |= bits; > - writel(tmp, reg); > -} > - > -static void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, > - u32 val, u32 mask) > -{ > - void __iomem *reg = hdmi_phy->regs + offset; > - u32 tmp; > - > - tmp = readl(reg); > - tmp = (tmp & ~mask) | (val & mask); > - writel(tmp, reg); > -} > - > -static inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) > -{ > - return container_of(hw, struct mtk_hdmi_phy, pll_hw); > -} > - > static int mtk_hdmi_pll_prepare(struct clk_hw *hw) > { > struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); > @@ -367,7 +307,7 @@ static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw, > return hdmi_phy->pll_rate; > } > > -static const struct clk_ops mtk_hdmi_pll_ops = { > +static const struct clk_ops mtk_hdmi_phy_pll_ops = { > .prepare = mtk_hdmi_pll_prepare, > .unprepare = mtk_hdmi_pll_unprepare, > .set_rate = mtk_hdmi_pll_set_rate, > @@ -390,142 +330,10 @@ static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) > RG_HDMITX_SER_EN); > } > > -static int mtk_hdmi_phy_power_on(struct phy *phy) > -{ > - struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); > - int ret; > - > - ret = clk_prepare_enable(hdmi_phy->pll); > - if (ret < 0) > - return ret; > - > - mtk_hdmi_phy_enable_tmds(hdmi_phy); > - > - return 0; > -} > - > -static int mtk_hdmi_phy_power_off(struct phy *phy) > -{ > - struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); > - > - mtk_hdmi_phy_disable_tmds(hdmi_phy); > - clk_disable_unprepare(hdmi_phy->pll); > - > - return 0; > -} > - > -static const struct phy_ops mtk_hdmi_phy_ops = { > - .power_on = mtk_hdmi_phy_power_on, > - .power_off = mtk_hdmi_phy_power_off, > - .owner = THIS_MODULE, > -}; > - > -static int mtk_hdmi_phy_probe(struct platform_device *pdev) > -{ > - struct device *dev = &pdev->dev; > - struct mtk_hdmi_phy *hdmi_phy; > - struct resource *mem; > - struct clk *ref_clk; > - const char *ref_clk_name; > - struct clk_init_data clk_init = { > - .ops = &mtk_hdmi_pll_ops, > - .num_parents = 1, > - .parent_names = (const char * const *)&ref_clk_name, > - .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, > - }; > - struct phy *phy; > - struct phy_provider *phy_provider; > - int ret; > - > - hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); > - if (!hdmi_phy) > - return -ENOMEM; > - > - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > - hdmi_phy->regs = devm_ioremap_resource(dev, mem); > - if (IS_ERR(hdmi_phy->regs)) { > - ret = PTR_ERR(hdmi_phy->regs); > - dev_err(dev, "Failed to get memory resource: %d\n", ret); > - return ret; > - } > - > - ref_clk = devm_clk_get(dev, "pll_ref"); > - if (IS_ERR(ref_clk)) { > - ret = PTR_ERR(ref_clk); > - dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n", > - ret); > - return ret; > - } > - ref_clk_name = __clk_get_name(ref_clk); > - > - ret = of_property_read_string(dev->of_node, "clock-output-names", > - &clk_init.name); > - if (ret < 0) { > - dev_err(dev, "Failed to read clock-output-names: %d\n", ret); > - return ret; > - } > - > - hdmi_phy->pll_hw.init = &clk_init; > - hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw); > - if (IS_ERR(hdmi_phy->pll)) { > - ret = PTR_ERR(hdmi_phy->pll); > - dev_err(dev, "Failed to register PLL: %d\n", ret); > - return ret; > - } > - > - ret = of_property_read_u32(dev->of_node, "mediatek,ibias", > - &hdmi_phy->ibias); > - if (ret < 0) { > - dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret); > - return ret; > - } > - > - ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", > - &hdmi_phy->ibias_up); > - if (ret < 0) { > - dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret); > - return ret; > - } > - > - dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n"); > - hdmi_phy->drv_imp_clk = 0x30; > - hdmi_phy->drv_imp_d2 = 0x30; > - hdmi_phy->drv_imp_d1 = 0x30; > - hdmi_phy->drv_imp_d0 = 0x30; > - > - phy = devm_phy_create(dev, NULL, &mtk_hdmi_phy_ops); > - if (IS_ERR(phy)) { > - dev_err(dev, "Failed to create HDMI PHY\n"); > - return PTR_ERR(phy); > - } > - phy_set_drvdata(phy, hdmi_phy); > - > - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > - if (IS_ERR(phy_provider)) > - return PTR_ERR(phy_provider); > - > - hdmi_phy->dev = dev; > - return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, > - hdmi_phy->pll); > -} > - > -static int mtk_hdmi_phy_remove(struct platform_device *pdev) > -{ > - return 0; > -} > - > -static const struct of_device_id mtk_hdmi_phy_match[] = { > - { .compatible = "mediatek,mt8173-hdmi-phy", }, > - {}, > -}; > - > -struct platform_driver mtk_hdmi_phy_driver = { > - .probe = mtk_hdmi_phy_probe, > - .remove = mtk_hdmi_phy_remove, > - .driver = { > - .name = "mediatek-hdmi-phy", > - .of_match_table = mtk_hdmi_phy_match, > - }, > +struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf = { > + .hdmi_phy_clk_ops = &mtk_hdmi_phy_pll_ops, > + .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds, > + .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds, > }; > > MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile index ce83c396a742..61cf0d2ab28a 100644 --- a/drivers/gpu/drm/mediatek/Makefile +++ b/drivers/gpu/drm/mediatek/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 + mediatek-drm-y := mtk_disp_color.o \ mtk_disp_ovl.o \ mtk_disp_rdma.o \ @@ -18,6 +19,7 @@ obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o mediatek-drm-hdmi-objs := mtk_cec.o \ mtk_hdmi.o \ mtk_hdmi_ddc.o \ - mtk_mt8173_hdmi_phy.o + mtk_mt8173_hdmi_phy.o \ + mtk_hdmi_phy.o -obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o +obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o \ No newline at end of file diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 2d45d1dd9554..2ca9f6a64dab 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -233,6 +233,7 @@ static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black) static void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable) { struct arm_smccc_res res; + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(hdmi->phy); /* * MT8173 HDMI hardware has an output control bit to enable/disable HDMI diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h index 6371b3de1ff6..3e9fb8d19802 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.h +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h @@ -13,11 +13,11 @@ */ #ifndef _MTK_HDMI_CTRL_H #define _MTK_HDMI_CTRL_H +#include "mtk_hdmi_phy.h" struct platform_driver; extern struct platform_driver mtk_cec_driver; extern struct platform_driver mtk_hdmi_ddc_driver; -extern struct platform_driver mtk_hdmi_phy_driver; #endif /* _MTK_HDMI_CTRL_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.c new file mode 100644 index 000000000000..d2dc50db1feb --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Jie Qiu <jie.qiu@mediatek.com> + */ + +#include "mtk_hdmi_phy.h" + +static int mtk_hdmi_phy_power_on(struct phy *phy); +static int mtk_hdmi_phy_power_off(struct phy *phy); + +static const struct phy_ops mtk_hdmi_phy_dev_ops = { + .power_on = mtk_hdmi_phy_power_on, + .power_off = mtk_hdmi_phy_power_off, + .owner = THIS_MODULE, +}; + +void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits) +{ + void __iomem *reg = hdmi_phy->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp &= ~bits; + writel(tmp, reg); +} + +void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits) +{ + void __iomem *reg = hdmi_phy->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp |= bits; + writel(tmp, reg); +} + +void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 val, u32 mask) +{ + void __iomem *reg = hdmi_phy->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp = (tmp & ~mask) | (val & mask); + writel(tmp, reg); +} + +inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_hdmi_phy, pll_hw); +} + +static int mtk_hdmi_phy_power_on(struct phy *phy) +{ + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(hdmi_phy->pll); + if (ret < 0) + return ret; + + hdmi_phy->conf->hdmi_phy_enable_tmds(hdmi_phy); + return 0; +} + +static int mtk_hdmi_phy_power_off(struct phy *phy) +{ + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); + + hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy); + clk_disable_unprepare(hdmi_phy->pll); + + return 0; +} + +static const struct phy_ops * +mtk_hdmi_phy_dev_get_ops(const struct mtk_hdmi_phy *hdmi_phy) +{ + if (hdmi_phy && hdmi_phy->conf && + hdmi_phy->conf->hdmi_phy_enable_tmds && + hdmi_phy->conf->hdmi_phy_disable_tmds) + return &mtk_hdmi_phy_dev_ops; + + dev_err(hdmi_phy->dev, "Failed to get dev ops of phy\n"); + return NULL; +} + +static void mtk_hdmi_phy_clk_get_ops(struct mtk_hdmi_phy *hdmi_phy, + const struct clk_ops **ops) +{ + if (hdmi_phy && hdmi_phy->conf && hdmi_phy->conf->hdmi_phy_clk_ops) + *ops = hdmi_phy->conf->hdmi_phy_clk_ops; + else + dev_err(hdmi_phy->dev, "Failed to get clk ops of phy\n"); +} + +static int mtk_hdmi_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_hdmi_phy *hdmi_phy; + struct resource *mem; + struct clk *ref_clk; + const char *ref_clk_name; + struct clk_init_data clk_init = { + .num_parents = 1, + .parent_names = (const char * const *)&ref_clk_name, + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, + }; + + struct phy *phy; + struct phy_provider *phy_provider; + int ret; + + hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); + if (!hdmi_phy) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi_phy->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(hdmi_phy->regs)) { + ret = PTR_ERR(hdmi_phy->regs); + dev_err(dev, "Failed to get memory resource: %d\n", ret); + return ret; + } + + ref_clk = devm_clk_get(dev, "pll_ref"); + if (IS_ERR(ref_clk)) { + ret = PTR_ERR(ref_clk); + dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n", + ret); + return ret; + } + ref_clk_name = __clk_get_name(ref_clk); + + ret = of_property_read_string(dev->of_node, "clock-output-names", + &clk_init.name); + if (ret < 0) { + dev_err(dev, "Failed to read clock-output-names: %d\n", ret); + return ret; + } + + hdmi_phy->dev = dev; + hdmi_phy->conf = + (struct mtk_hdmi_phy_conf *)of_device_get_match_data(dev); + mtk_hdmi_phy_clk_get_ops(hdmi_phy, &clk_init.ops); + hdmi_phy->pll_hw.init = &clk_init; + hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw); + if (IS_ERR(hdmi_phy->pll)) { + ret = PTR_ERR(hdmi_phy->pll); + dev_err(dev, "Failed to register PLL: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,ibias", + &hdmi_phy->ibias); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", + &hdmi_phy->ibias_up); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret); + return ret; + } + + dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n"); + hdmi_phy->drv_imp_clk = 0x30; + hdmi_phy->drv_imp_d2 = 0x30; + hdmi_phy->drv_imp_d1 = 0x30; + hdmi_phy->drv_imp_d0 = 0x30; + + phy = devm_phy_create(dev, NULL, mtk_hdmi_phy_dev_get_ops(hdmi_phy)); + if (IS_ERR(phy)) { + dev_err(dev, "Failed to create HDMI PHY\n"); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, hdmi_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "Failed to register HDMI PHY\n"); + return PTR_ERR(phy_provider); + } + + return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, + hdmi_phy->pll); +} + +static const struct of_device_id mtk_hdmi_phy_match[] = { + { .compatible = "mediatek,mt8173-hdmi-phy", + .data = &mtk_hdmi_phy_8173_conf, + }, + {}, +}; + +struct platform_driver mtk_hdmi_phy_driver = { + .probe = mtk_hdmi_phy_probe, + .driver = { + .name = "mediatek-hdmi-phy", + .of_match_table = mtk_hdmi_phy_match, + }, +}; + +MODULE_DESCRIPTION("MediaTek HDMI PHY Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_phy.h b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.h new file mode 100644 index 000000000000..e346fe319621 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_phy.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 MediaTek Inc. + * Author: Chunhui Dai <chunhui.dai@mediatek.com> + */ + +#ifndef _MTK_HDMI_PHY_H +#define _MTK_HDMI_PHY_H +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +struct mtk_hdmi_phy; + +struct mtk_hdmi_phy_conf { + const struct clk_ops *hdmi_phy_clk_ops; + void (*hdmi_phy_enable_tmds)(struct mtk_hdmi_phy *hdmi_phy); + void (*hdmi_phy_disable_tmds)(struct mtk_hdmi_phy *hdmi_phy); +}; + +struct mtk_hdmi_phy { + void __iomem *regs; + struct device *dev; + struct mtk_hdmi_phy_conf *conf; + struct clk *pll; + struct clk_hw pll_hw; + unsigned long pll_rate; + unsigned char drv_imp_clk; + unsigned char drv_imp_d2; + unsigned char drv_imp_d1; + unsigned char drv_imp_d0; + unsigned int ibias; + unsigned int ibias_up; +}; + +void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits); +void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 bits); +void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, + u32 val, u32 mask); +struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw); + +extern struct platform_driver mtk_hdmi_phy_driver; +extern struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf; + +#endif /* _MTK_HDMI_PHY_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c index 51cb9cfb6646..5dea03334042 100644 --- a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c +++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c @@ -12,15 +12,7 @@ * GNU General Public License for more details. */ -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/mfd/syscon.h> -#include <linux/module.h> -#include <linux/phy/phy.h> -#include <linux/platform_device.h> -#include <linux/types.h> +#include "mtk_hdmi_phy.h" #define HDMI_CON0 0x00 #define RG_HDMITX_PLL_EN BIT(31) @@ -123,20 +115,6 @@ #define RGS_HDMITX_5T1_EDG (0xf << 4) #define RGS_HDMITX_PLUG_TST BIT(0) -struct mtk_hdmi_phy { - void __iomem *regs; - struct device *dev; - struct clk *pll; - struct clk_hw pll_hw; - unsigned long pll_rate; - u8 drv_imp_clk; - u8 drv_imp_d2; - u8 drv_imp_d1; - u8 drv_imp_d0; - u32 ibias; - u32 ibias_up; -}; - static const u8 PREDIV[3][4] = { {0x0, 0x0, 0x0, 0x0}, /* 27Mhz */ {0x1, 0x1, 0x1, 0x1}, /* 74Mhz */ @@ -185,44 +163,6 @@ static const u8 HTPLLBR[3][4] = { {0x1, 0x2, 0x2, 0x1} /* 148Mhz */ }; -static void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, - u32 bits) -{ - void __iomem *reg = hdmi_phy->regs + offset; - u32 tmp; - - tmp = readl(reg); - tmp &= ~bits; - writel(tmp, reg); -} - -static void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, - u32 bits) -{ - void __iomem *reg = hdmi_phy->regs + offset; - u32 tmp; - - tmp = readl(reg); - tmp |= bits; - writel(tmp, reg); -} - -static void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, - u32 val, u32 mask) -{ - void __iomem *reg = hdmi_phy->regs + offset; - u32 tmp; - - tmp = readl(reg); - tmp = (tmp & ~mask) | (val & mask); - writel(tmp, reg); -} - -static inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) -{ - return container_of(hw, struct mtk_hdmi_phy, pll_hw); -} - static int mtk_hdmi_pll_prepare(struct clk_hw *hw) { struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); @@ -367,7 +307,7 @@ static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw, return hdmi_phy->pll_rate; } -static const struct clk_ops mtk_hdmi_pll_ops = { +static const struct clk_ops mtk_hdmi_phy_pll_ops = { .prepare = mtk_hdmi_pll_prepare, .unprepare = mtk_hdmi_pll_unprepare, .set_rate = mtk_hdmi_pll_set_rate, @@ -390,142 +330,10 @@ static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) RG_HDMITX_SER_EN); } -static int mtk_hdmi_phy_power_on(struct phy *phy) -{ - struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); - int ret; - - ret = clk_prepare_enable(hdmi_phy->pll); - if (ret < 0) - return ret; - - mtk_hdmi_phy_enable_tmds(hdmi_phy); - - return 0; -} - -static int mtk_hdmi_phy_power_off(struct phy *phy) -{ - struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); - - mtk_hdmi_phy_disable_tmds(hdmi_phy); - clk_disable_unprepare(hdmi_phy->pll); - - return 0; -} - -static const struct phy_ops mtk_hdmi_phy_ops = { - .power_on = mtk_hdmi_phy_power_on, - .power_off = mtk_hdmi_phy_power_off, - .owner = THIS_MODULE, -}; - -static int mtk_hdmi_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mtk_hdmi_phy *hdmi_phy; - struct resource *mem; - struct clk *ref_clk; - const char *ref_clk_name; - struct clk_init_data clk_init = { - .ops = &mtk_hdmi_pll_ops, - .num_parents = 1, - .parent_names = (const char * const *)&ref_clk_name, - .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, - }; - struct phy *phy; - struct phy_provider *phy_provider; - int ret; - - hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); - if (!hdmi_phy) - return -ENOMEM; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi_phy->regs = devm_ioremap_resource(dev, mem); - if (IS_ERR(hdmi_phy->regs)) { - ret = PTR_ERR(hdmi_phy->regs); - dev_err(dev, "Failed to get memory resource: %d\n", ret); - return ret; - } - - ref_clk = devm_clk_get(dev, "pll_ref"); - if (IS_ERR(ref_clk)) { - ret = PTR_ERR(ref_clk); - dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n", - ret); - return ret; - } - ref_clk_name = __clk_get_name(ref_clk); - - ret = of_property_read_string(dev->of_node, "clock-output-names", - &clk_init.name); - if (ret < 0) { - dev_err(dev, "Failed to read clock-output-names: %d\n", ret); - return ret; - } - - hdmi_phy->pll_hw.init = &clk_init; - hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw); - if (IS_ERR(hdmi_phy->pll)) { - ret = PTR_ERR(hdmi_phy->pll); - dev_err(dev, "Failed to register PLL: %d\n", ret); - return ret; - } - - ret = of_property_read_u32(dev->of_node, "mediatek,ibias", - &hdmi_phy->ibias); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret); - return ret; - } - - ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", - &hdmi_phy->ibias_up); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret); - return ret; - } - - dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n"); - hdmi_phy->drv_imp_clk = 0x30; - hdmi_phy->drv_imp_d2 = 0x30; - hdmi_phy->drv_imp_d1 = 0x30; - hdmi_phy->drv_imp_d0 = 0x30; - - phy = devm_phy_create(dev, NULL, &mtk_hdmi_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "Failed to create HDMI PHY\n"); - return PTR_ERR(phy); - } - phy_set_drvdata(phy, hdmi_phy); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - hdmi_phy->dev = dev; - return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, - hdmi_phy->pll); -} - -static int mtk_hdmi_phy_remove(struct platform_device *pdev) -{ - return 0; -} - -static const struct of_device_id mtk_hdmi_phy_match[] = { - { .compatible = "mediatek,mt8173-hdmi-phy", }, - {}, -}; - -struct platform_driver mtk_hdmi_phy_driver = { - .probe = mtk_hdmi_phy_probe, - .remove = mtk_hdmi_phy_remove, - .driver = { - .name = "mediatek-hdmi-phy", - .of_match_table = mtk_hdmi_phy_match, - }, +struct mtk_hdmi_phy_conf mtk_hdmi_phy_8173_conf = { + .hdmi_phy_clk_ops = &mtk_hdmi_phy_pll_ops, + .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds, + .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds, }; MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");