From 7095b3b3b28031708ab74a65c32e84e231bc9d27 Mon Sep 17 00:00:00 2001 From: Sandor Yu Date: Wed, 11 Sep 2019 17:18:29 +0800 Subject: [PATCH] drm: imx: add imx8mq hdmi support add struct imx_mhdp_device for imx specific. add imx8mq hdmi support. move imx8qm specific functions to plat_data uniform variable name. Signed-off-by: Sandor Yu --- drivers/gpu/drm/imx/Makefile | 2 +- drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c | 8 +- drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c | 7 +- drivers/gpu/drm/imx/cdn-mhdp-imx8mq.c | 163 ------------ drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c | 437 ++++++++++---------------------- drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c | 202 +++++++++++++++ drivers/gpu/drm/imx/cdn-mhdp-phy.h | 12 +- drivers/gpu/drm/imx/cdns-mhdp-imx.h | 80 ++++++ 8 files changed, 421 insertions(+), 490 deletions(-) delete mode 100644 drivers/gpu/drm/imx/cdn-mhdp-imx8mq.c create mode 100644 drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c create mode 100644 drivers/gpu/drm/imx/cdns-mhdp-imx.h --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o -obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdn-mhdp-imx8qm.o cdn-mhdp-imx8mq.o cdn-mhdp-dp-phy.o cdn-mhdp-hdmi-phy.o +obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdn-mhdp-imxdrv.o cdn-mhdp-dp-phy.o cdn-mhdp-hdmi-phy.o cdn-mhdp-imx8qm.o --- a/drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c +++ b/drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c @@ -12,7 +12,6 @@ #include #include #include - #include #include "cdn-mhdp-phy.h" @@ -477,9 +476,8 @@ static int dp_phy_power_up(struct cdns_m return 0; } -int cdns_dp_phy_init_imx8mq(struct imx_mhdp_device *hdp) +int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *mhdp) { - struct cdns_mhdp_device *mhdp = &hdp->mhdp; int ret; /* Disable phy clock if PHY in power up state */ @@ -504,10 +502,8 @@ int cdns_dp_phy_init_imx8mq(struct imx_m return ret; } - -int cdns_dp_phy_init_imx8qm(struct imx_mhdp_device *hdp) +int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) { - struct cdns_mhdp_device *mhdp = &hdp->mhdp; int ret; /* Disable phy clock if PHY in power up state */ --- a/drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c +++ b/drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c @@ -624,9 +624,8 @@ static int hdmi_phy_power_up(struct cdns return 0; } -int cdns_hdmi_phy_set_imx8mq(struct imx_mhdp_device *hdp) +int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp) { - struct cdns_mhdp_device *mhdp = &hdp->mhdp; struct drm_display_mode *mode = &mhdp->mode; int ret; @@ -653,9 +652,8 @@ int cdns_hdmi_phy_set_imx8mq(struct imx_ return true; } -int cdns_hdmi_phy_set_imx8qm(struct imx_mhdp_device *hdp) +int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *mhdp) { - struct cdns_mhdp_device *mhdp = &hdp->mhdp; struct drm_display_mode *mode = &mhdp->mode; int ret; @@ -681,4 +679,3 @@ int cdns_hdmi_phy_set_imx8qm(struct imx_ return true; } - --- a/drivers/gpu/drm/imx/cdn-mhdp-imx8mq.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2019 NXP Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "cdn-mhdp-phy.h" -#include "imx-drm.h" - -struct imx_hdmi { - struct device *dev; - struct drm_encoder encoder; -}; - -static void cdns_hdmi_imx_encoder_disable(struct drm_encoder *encoder) -{ -} - -static void cdns_hdmi_imx_encoder_enable(struct drm_encoder *encoder) -{ -} - -static int cdns_hdmi_imx_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - return 0; -} - -static const struct drm_encoder_helper_funcs cdns_hdmi_imx_encoder_helper_funcs = { - .enable = cdns_hdmi_imx_encoder_enable, - .disable = cdns_hdmi_imx_encoder_disable, - .atomic_check = cdns_hdmi_imx_atomic_check, -}; - -static const struct drm_encoder_funcs cdns_hdmi_imx_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -static struct cdn_plat_data imx8mq_hdmi_drv_data = { - .bind = cdns_hdmi_bind, - .unbind = cdns_hdmi_unbind, - .phy_init = cdns_hdmi_phy_set_imx8mq, -}; - -static struct cdn_plat_data imx8mq_dp_drv_data = { - .bind = cdns_dp_bind, - .unbind = cdns_dp_unbind, - .phy_init = cdns_dp_phy_init_imx8mq, -}; - -static const struct of_device_id cdns_hdmi_imx_dt_ids[] = { - { .compatible = "cdn,imx8mq-hdmi", - .data = &imx8mq_hdmi_drv_data - }, - { .compatible = "cdn,imx8mq-dp", - .data = &imx8mq_dp_drv_data - }, - {}, -}; -MODULE_DEVICE_TABLE(of, cdns_hdmi_imx_dt_ids); - -static int cdns_hdmi_imx_bind(struct device *dev, struct device *master, - void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - const struct cdn_plat_data *plat_data; - const struct of_device_id *match; - struct drm_device *drm = data; - struct drm_encoder *encoder; - struct imx_hdmi *hdmi; - int ret; - - if (!pdev->dev.of_node) - return -ENODEV; - - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; - - match = of_match_node(cdns_hdmi_imx_dt_ids, pdev->dev.of_node); - plat_data = match->data; - hdmi->dev = &pdev->dev; - encoder = &hdmi->encoder; - - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (encoder->possible_crtcs == 0) - return -EPROBE_DEFER; - - drm_encoder_helper_add(encoder, &cdns_hdmi_imx_encoder_helper_funcs); - drm_encoder_init(drm, encoder, &cdns_hdmi_imx_encoder_funcs, - DRM_MODE_ENCODER_TMDS, NULL); - - ret = plat_data->bind(pdev, encoder, plat_data); - - /* - * If cdns_hdmi_bind() fails we'll never call cdns_hdmi_unbind(), - * which would have called the encoder cleanup. Do it manually. - */ - if (ret) - drm_encoder_cleanup(encoder); - - return ret; -} - -static void cdns_hdmi_imx_unbind(struct device *dev, struct device *master, - void *data) -{ - struct imx_mhdp_device *hdp = dev_get_drvdata(dev); - - hdp->plat_data->unbind(dev); -} - -static const struct component_ops cdns_hdmi_imx_ops = { - .bind = cdns_hdmi_imx_bind, - .unbind = cdns_hdmi_imx_unbind, -}; - -static int cdns_hdmi_imx_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &cdns_hdmi_imx_ops); -} - -static int cdns_hdmi_imx_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &cdns_hdmi_imx_ops); - - return 0; -} - -static struct platform_driver cdns_hdmi_imx_platform_driver = { - .probe = cdns_hdmi_imx_probe, - .remove = cdns_hdmi_imx_remove, - .driver = { - .name = "cdn-hdp-imx8mq", - .of_match_table = cdns_hdmi_imx_dt_ids, - }, -}; - -module_platform_driver(cdns_hdmi_imx_platform_driver); - -MODULE_AUTHOR("Sandor YU "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:cdnhdmi-imx"); --- a/drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c +++ b/drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c @@ -1,54 +1,40 @@ /* - * Copyright (C) 2019 NXP Semiconductor, Inc. + * copyright (c) 2019 nxp semiconductor, inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as + * published by the free software foundation. */ #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include +#include +#include -#include -#include "cdn-mhdp-phy.h" -#include "imx-drm.h" +#include "cdns-mhdp-imx.h" #define CSR_PIXEL_LINK_MUX_CTL 0x00 -#define PL_MUX_CTL_VCP_OFFSET 5 -#define PL_MUX_CTL_HCP_OFFSET 4 +#define CSR_PIXEL_LINK_MUX_VCP_OFFSET 5 +#define CSR_PIXEL_LINK_MUX_HCP_OFFSET 4 #define PLL_800MHZ (800000000) -struct imx_hdmi { - struct device *dev; - struct drm_encoder encoder; -}; - -static void imx8qm_pixel_link_mux(struct imx_mhdp_device *hdp) +static void imx8qm_pixel_link_mux(struct imx_mhdp_device *imx_mhdp) { - struct drm_display_mode *mode = &hdp->mhdp.mode; + struct drm_display_mode *mode = &imx_mhdp->mhdp.mode; u32 val; val = 0x4; /* RGB */ - if (hdp->dual_mode) + if (imx_mhdp->dual_mode) val |= 0x2; /* pixel link 0 and 1 are active */ if (mode->flags & DRM_MODE_FLAG_PVSYNC) - val |= 1 << PL_MUX_CTL_VCP_OFFSET; + val |= 1 << CSR_PIXEL_LINK_MUX_VCP_OFFSET; if (mode->flags & DRM_MODE_FLAG_PHSYNC) - val |= 1 << PL_MUX_CTL_HCP_OFFSET; + val |= 1 << CSR_PIXEL_LINK_MUX_HCP_OFFSET; if (mode->flags & DRM_MODE_FLAG_INTERLACE) val |= 0x2; - regmap_write(hdp->regmap_csr, hdp->csr_pxl_mux_reg, val); + writel(val, imx_mhdp->mhdp.regs_sec); } static void imx8qm_pixel_link_valid(u32 dual_mode) @@ -120,10 +106,10 @@ static void imx8qm_clk_mux(u8 is_dp) imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_MODE, 0); } -int imx8qm_clocks_init(struct imx_mhdp_device *hdp) +int imx8qm_clocks_init(struct imx_mhdp_device *imx_mhdp) { - struct device *dev = hdp->mhdp.dev; - struct imx_hdp_clks *clks = &hdp->clks; + struct device *dev = imx_mhdp->mhdp.dev; + struct imx_hdp_clks *clks = &imx_mhdp->clks; clks->dig_pll = devm_clk_get(dev, "dig_pll"); if (IS_ERR(clks->dig_pll)) { @@ -229,10 +215,10 @@ int imx8qm_clocks_init(struct imx_mhdp_d return true; } -static int imx8qm_pixel_clk_enable(struct imx_mhdp_device *hdp) +static int imx8qm_pixel_clk_enable(struct imx_mhdp_device *imx_mhdp) { - struct imx_hdp_clks *clks = &hdp->clks; - struct device *dev = hdp->mhdp.dev; + struct imx_hdp_clks *clks = &imx_mhdp->clks; + struct device *dev = imx_mhdp->mhdp.dev; int ret; ret = clk_prepare_enable(clks->av_pll); @@ -273,12 +259,11 @@ static int imx8qm_pixel_clk_enable(struc return ret; } return ret; - } -static void imx8qm_pixel_clk_disable(struct imx_mhdp_device *hdp) +static void imx8qm_pixel_clk_disable(struct imx_mhdp_device *imx_mhdp) { - struct imx_hdp_clks *clks = &hdp->clks; + struct imx_hdp_clks *clks = &imx_mhdp->clks; clk_disable_unprepare(clks->lpcg_pxl); clk_disable_unprepare(clks->lpcg_hdp); @@ -289,14 +274,14 @@ static void imx8qm_pixel_clk_disable(str clk_disable_unprepare(clks->av_pll); } -static void imx8qm_pixel_clk_set_rate(struct imx_mhdp_device *hdp, u32 pclock) +static void imx8qm_pixel_clk_set_rate(struct imx_mhdp_device *imx_mhdp, u32 pclock) { - struct imx_hdp_clks *clks = &hdp->clks; + struct imx_hdp_clks *clks = &imx_mhdp->clks; /* pixel clock for HDMI */ clk_set_rate(clks->av_pll, pclock); - if (hdp->dual_mode == true) { + if (imx_mhdp->dual_mode == true) { clk_set_rate(clks->clk_pxl, pclock/2); clk_set_rate(clks->clk_pxl_link, pclock/2); } else { @@ -306,24 +291,11 @@ static void imx8qm_pixel_clk_set_rate(st clk_set_rate(clks->clk_pxl_mux, pclock); } -static void imx8qm_pixel_clk_rate_change(struct imx_mhdp_device *hdp) -{ - /* set pixel clock before video mode setup */ - imx8qm_pixel_clk_disable(hdp); - - imx8qm_pixel_clk_set_rate(hdp, hdp->mhdp.mode.clock * 1000); - - imx8qm_pixel_clk_enable(hdp); - - /* Config pixel link mux */ - imx8qm_pixel_link_mux(hdp); -} - -static int imx8qm_ipg_clk_enable(struct imx_mhdp_device *hdp) +static int imx8qm_ipg_clk_enable(struct imx_mhdp_device *imx_mhdp) { int ret; - struct imx_hdp_clks *clks = &hdp->clks; - struct device *dev = hdp->mhdp.dev; + struct imx_hdp_clks *clks = &imx_mhdp->clks; + struct device *dev = imx_mhdp->mhdp.dev; ret = clk_prepare_enable(clks->dig_pll); if (ret < 0) { @@ -381,25 +353,9 @@ static int imx8qm_ipg_clk_enable(struct return ret; } -static void imx8qm_ipg_clk_disable(struct imx_mhdp_device *hdp) -{ - struct imx_hdp_clks *clks = &hdp->clks; - - clk_disable_unprepare(clks->clk_i2s_bypass); - clk_disable_unprepare(clks->lpcg_i2s); - clk_disable_unprepare(clks->lpcg_apb_ctrl); - clk_disable_unprepare(clks->lpcg_apb_csr); - clk_disable_unprepare(clks->lpcg_msi); - clk_disable_unprepare(clks->lpcg_lis); - clk_disable_unprepare(clks->lpcg_apb); - clk_disable_unprepare(clks->clk_core); - clk_disable_unprepare(clks->clk_ipg); - clk_disable_unprepare(clks->dig_pll); -} - -static void imx8qm_ipg_clk_set_rate(struct imx_mhdp_device *hdp) +static void imx8qm_ipg_clk_set_rate(struct imx_mhdp_device *imx_mhdp) { - struct imx_hdp_clks *clks = &hdp->clks; + struct imx_hdp_clks *clks = &imx_mhdp->clks; /* ipg/core clock */ clk_set_rate(clks->dig_pll, PLL_800MHZ); @@ -407,308 +363,171 @@ static void imx8qm_ipg_clk_set_rate(stru clk_set_rate(clks->clk_ipg, PLL_800MHZ/8); } -static void imx8qm_detach_pm_domains(struct imx_mhdp_device *hdp) +static void imx8qm_detach_pm_domains(struct imx_mhdp_device *imx_mhdp) { - if (hdp->pd_pll1_link && !IS_ERR(hdp->pd_pll1_link)) - device_link_del(hdp->pd_pll1_link); - if (hdp->pd_pll1_dev && !IS_ERR(hdp->pd_pll1_dev)) - dev_pm_domain_detach(hdp->pd_pll1_dev, true); - - if (hdp->pd_pll0_link && !IS_ERR(hdp->pd_pll0_link)) - device_link_del(hdp->pd_pll0_link); - if (hdp->pd_pll0_dev && !IS_ERR(hdp->pd_pll0_dev)) - dev_pm_domain_detach(hdp->pd_pll0_dev, true); - - if (hdp->pd_mhdp_link && !IS_ERR(hdp->pd_mhdp_link)) - device_link_del(hdp->pd_mhdp_link); - if (hdp->pd_mhdp_dev && !IS_ERR(hdp->pd_mhdp_dev)) - dev_pm_domain_detach(hdp->pd_mhdp_dev, true); - - hdp->pd_mhdp_dev = NULL; - hdp->pd_mhdp_link = NULL; - hdp->pd_pll0_dev = NULL; - hdp->pd_pll0_link = NULL; - hdp->pd_pll1_dev = NULL; - hdp->pd_pll1_link = NULL; + if (imx_mhdp->pd_pll1_link && !IS_ERR(imx_mhdp->pd_pll1_link)) + device_link_del(imx_mhdp->pd_pll1_link); + if (imx_mhdp->pd_pll1_dev && !IS_ERR(imx_mhdp->pd_pll1_dev)) + dev_pm_domain_detach(imx_mhdp->pd_pll1_dev, true); + + if (imx_mhdp->pd_pll0_link && !IS_ERR(imx_mhdp->pd_pll0_link)) + device_link_del(imx_mhdp->pd_pll0_link); + if (imx_mhdp->pd_pll0_dev && !IS_ERR(imx_mhdp->pd_pll0_dev)) + dev_pm_domain_detach(imx_mhdp->pd_pll0_dev, true); + + if (imx_mhdp->pd_mhdp_link && !IS_ERR(imx_mhdp->pd_mhdp_link)) + device_link_del(imx_mhdp->pd_mhdp_link); + if (imx_mhdp->pd_mhdp_dev && !IS_ERR(imx_mhdp->pd_mhdp_dev)) + dev_pm_domain_detach(imx_mhdp->pd_mhdp_dev, true); + + imx_mhdp->pd_mhdp_dev = NULL; + imx_mhdp->pd_mhdp_link = NULL; + imx_mhdp->pd_pll0_dev = NULL; + imx_mhdp->pd_pll0_link = NULL; + imx_mhdp->pd_pll1_dev = NULL; + imx_mhdp->pd_pll1_link = NULL; } -static int imx8qm_attach_pm_domains(struct imx_mhdp_device *hdp) +static int imx8qm_attach_pm_domains(struct imx_mhdp_device *imx_mhdp) { - struct device *dev = hdp->mhdp.dev; + struct device *dev = imx_mhdp->mhdp.dev; u32 flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE; int ret = 0; - hdp->pd_mhdp_dev = dev_pm_domain_attach_by_name(dev, "hdmi"); - if (IS_ERR(hdp->pd_mhdp_dev)) { - ret = PTR_ERR(hdp->pd_mhdp_dev); + imx_mhdp->pd_mhdp_dev = dev_pm_domain_attach_by_name(dev, "hdmi"); + if (IS_ERR(imx_mhdp->pd_mhdp_dev)) { + ret = PTR_ERR(imx_mhdp->pd_mhdp_dev); dev_err(dev, "Failed to attach dc pd dev: %d\n", ret); goto fail; } - hdp->pd_mhdp_link = device_link_add(dev, hdp->pd_mhdp_dev, flags); - if (IS_ERR(hdp->pd_mhdp_link)) { - ret = PTR_ERR(hdp->pd_mhdp_link); + imx_mhdp->pd_mhdp_link = device_link_add(dev, imx_mhdp->pd_mhdp_dev, flags); + if (IS_ERR(imx_mhdp->pd_mhdp_link)) { + ret = PTR_ERR(imx_mhdp->pd_mhdp_link); dev_err(dev, "Failed to add device link to dc pd dev: %d\n", ret); goto fail; } - hdp->pd_pll0_dev = dev_pm_domain_attach_by_name(dev, "pll0"); - if (IS_ERR(hdp->pd_pll0_dev)) { - ret = PTR_ERR(hdp->pd_pll0_dev); + imx_mhdp->pd_pll0_dev = dev_pm_domain_attach_by_name(dev, "pll0"); + if (IS_ERR(imx_mhdp->pd_pll0_dev)) { + ret = PTR_ERR(imx_mhdp->pd_pll0_dev); dev_err(dev, "Failed to attach pll0 pd dev: %d\n", ret); goto fail; } - hdp->pd_pll0_link = device_link_add(dev, hdp->pd_pll0_dev, flags); - if (IS_ERR(hdp->pd_pll0_link)) { - ret = PTR_ERR(hdp->pd_pll0_link); + imx_mhdp->pd_pll0_link = device_link_add(dev, imx_mhdp->pd_pll0_dev, flags); + if (IS_ERR(imx_mhdp->pd_pll0_link)) { + ret = PTR_ERR(imx_mhdp->pd_pll0_link); dev_err(dev, "Failed to add device link to pll0 pd dev: %d\n", ret); goto fail; } - hdp->pd_pll1_dev = dev_pm_domain_attach_by_name(dev, "pll1"); - if (IS_ERR(hdp->pd_pll1_dev)) { - ret = PTR_ERR(hdp->pd_pll1_dev); + imx_mhdp->pd_pll1_dev = dev_pm_domain_attach_by_name(dev, "pll1"); + if (IS_ERR(imx_mhdp->pd_pll1_dev)) { + ret = PTR_ERR(imx_mhdp->pd_pll1_dev); dev_err(dev, "Failed to attach pll0 pd dev: %d\n", ret); goto fail; } - hdp->pd_pll1_link = device_link_add(dev, hdp->pd_pll1_dev, flags); - if (IS_ERR(hdp->pd_pll1_link)) { - ret = PTR_ERR(hdp->pd_pll1_link); + imx_mhdp->pd_pll1_link = device_link_add(dev, imx_mhdp->pd_pll1_dev, flags); + if (IS_ERR(imx_mhdp->pd_pll1_link)) { + ret = PTR_ERR(imx_mhdp->pd_pll1_link); dev_err(dev, "Failed to add device link to pll1 pd dev: %d\n", ret); goto fail; } fail: - imx8qm_detach_pm_domains(hdp); + imx8qm_detach_pm_domains(imx_mhdp); return ret; } -static int imx8qm_firmware_init(struct imx_mhdp_device *hdp) +static void imx8qm_mhdp_power_on(struct cdns_mhdp_device *mhdp) { - u32 rate; - int ret; - + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); /* Power on PM Domains */ - imx8qm_attach_pm_domains(hdp); + + imx8qm_attach_pm_domains(imx_mhdp); /* clock init and rate set */ - imx8qm_clocks_init(hdp); + imx8qm_clocks_init(imx_mhdp); - imx8qm_ipg_clk_set_rate(hdp); + imx8qm_ipg_clk_set_rate(imx_mhdp); /* Init pixel clock with 148.5MHz before FW init */ - imx8qm_pixel_clk_set_rate(hdp, 148500000); + imx8qm_pixel_clk_set_rate(imx_mhdp, 148500000); - imx8qm_ipg_clk_enable(hdp); + imx8qm_ipg_clk_enable(imx_mhdp); - imx8qm_clk_mux(hdp->plat_data->is_dp); + imx8qm_clk_mux(imx_mhdp->mhdp.plat_data->is_dp); - imx8qm_pixel_clk_enable(hdp); + imx8qm_pixel_clk_enable(imx_mhdp); imx8qm_phy_reset(1); - - hdp->csr_pxl_mux_reg = 0; - hdp->csr_ctrl0_reg = 0x8; - hdp->csr_ctrl0_sec = 0xc; - /* iMX8QM HDP register, Remap HPD memory address to low 4K */ - regmap_write(hdp->regmap_csr, hdp->csr_ctrl0_reg, 0); - - /* configure HDMI/DP core clock */ - rate = clk_get_rate(hdp->clks.clk_core); - cdns_mhdp_set_fw_clk(&hdp->mhdp, rate); - - /* un-reset ucpu */ - writel(0, (APB_CTRL << 2) + hdp->mhdp.regs); - DRM_INFO("Started firmware!\n"); - - ret = cdns_mhdp_check_alive(&hdp->mhdp); - if (ret == false) { - DRM_ERROR("NO HDMI FW running\n"); - return -ENXIO; - } - - /* turn on IP activity */ - cdns_mhdp_set_firmware_active(&hdp->mhdp, 1); - - DRM_INFO("HDP FW Version - ver %d verlib %d\n", - __raw_readb(VER_L + hdp->mhdp.regs) + (__raw_readb(VER_H + hdp->mhdp.regs) << 8), - __raw_readb(VER_LIB_L_ADDR + hdp->mhdp.regs) + (__raw_readb(VER_LIB_H_ADDR + hdp->mhdp.regs) << 8)); - - return 0; } -static void cdns_hdmi_imx_encoder_disable(struct drm_encoder *encoder) +void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp) { - struct imx_mhdp_device *hdp = encoder->bridge->driver_private; + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); - imx8qm_pixel_link_sync_disable(hdp->dual_mode); - imx8qm_pixel_link_invalid(hdp->dual_mode); + imx8qm_pixel_link_sync_disable(imx_mhdp->dual_mode); + imx8qm_pixel_link_invalid(imx_mhdp->dual_mode); } -static void cdns_hdmi_imx_encoder_enable(struct drm_encoder *encoder) +void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp) { - struct imx_mhdp_device *hdp = encoder->bridge->driver_private; + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); - imx8qm_pixel_link_valid(hdp->dual_mode); - imx8qm_pixel_link_sync_enable(hdp->dual_mode); + imx8qm_pixel_link_valid(imx_mhdp->dual_mode); + imx8qm_pixel_link_sync_enable(imx_mhdp->dual_mode); } -static int cdns_hdmi_imx_encoder_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp) { - struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); - - imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB101010_1X30; - return 0; -} + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); -static const struct drm_encoder_helper_funcs cdns_hdmi_imx_encoder_helper_funcs = { - .enable = cdns_hdmi_imx_encoder_enable, - .disable = cdns_hdmi_imx_encoder_disable, - .atomic_check = cdns_hdmi_imx_encoder_atomic_check, -}; - -static const struct drm_encoder_funcs cdns_hdmi_imx_encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -#if 0 -static struct cdn_plat_data imx8mq_hdmi_drv_data = { - .bind = cdns_hdmi_bind, - .unbind = cdns_hdmi_unbind, - .phy_init = cdns_hdmi_phy_set_imx8mq, -}; - -static struct cdn_plat_data imx8mq_dp_drv_data = { - .bind = cdns_dp_bind, - .unbind = cdns_dp_unbind, - .phy_init = cdns_dp_phy_init_imx8mq, -}; -#endif - -static struct cdn_plat_data imx8qm_hdmi_drv_data = { - .bind = cdns_hdmi_bind, - .unbind = cdns_hdmi_unbind, - .phy_init = cdns_hdmi_phy_set_imx8qm, - .fw_init = imx8qm_firmware_init, - .pclock_change = imx8qm_pixel_clk_rate_change, -}; - -static struct cdn_plat_data imx8qm_dp_drv_data = { - .bind = cdns_dp_bind, - .unbind = cdns_dp_unbind, - .phy_init = cdns_dp_phy_init_imx8qm, - .fw_init = imx8qm_firmware_init, - .pclock_change = imx8qm_pixel_clk_rate_change, - .is_dp = true, -}; - -static const struct of_device_id cdns_hdmi_imx_dt_ids[] = { -#if 0 - { .compatible = "cdn,imx8mq-hdmi", - .data = &imx8mq_hdmi_drv_data - }, - { .compatible = "cdn,imx8mq-dp", - .data = &imx8mq_dp_drv_data - }, -#endif - { .compatible = "cdn,imx8qm-hdmi", - .data = &imx8qm_hdmi_drv_data - }, - { .compatible = "cdn,imx8qm-dp", - .data = &imx8qm_dp_drv_data - }, - {}, -}; -MODULE_DEVICE_TABLE(of, cdns_hdmi_imx_dt_ids); - -static int cdns_hdmi_imx_bind(struct device *dev, struct device *master, - void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - const struct cdn_plat_data *plat_data; - const struct of_device_id *match; - struct drm_device *drm = data; - struct drm_encoder *encoder; - struct imx_hdmi *hdmi; - int ret; + /* set pixel clock before video mode setup */ + imx8qm_pixel_clk_disable(imx_mhdp); - if (!pdev->dev.of_node) - return -ENODEV; + imx8qm_pixel_clk_set_rate(imx_mhdp, imx_mhdp->mhdp.mode.clock * 1000); - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; - - match = of_match_node(cdns_hdmi_imx_dt_ids, pdev->dev.of_node); - plat_data = match->data; - hdmi->dev = &pdev->dev; - encoder = &hdmi->encoder; - - encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (encoder->possible_crtcs == 0) - return -EPROBE_DEFER; - - drm_encoder_helper_add(encoder, &cdns_hdmi_imx_encoder_helper_funcs); - drm_encoder_init(drm, encoder, &cdns_hdmi_imx_encoder_funcs, - DRM_MODE_ENCODER_TMDS, NULL); - - ret = plat_data->bind(pdev, encoder, plat_data); - - /* - * If cdns_hdmi_bind() fails we'll never call cdns_hdmi_unbind(), - * which would have called the encoder cleanup. Do it manually. - */ - if (ret) - drm_encoder_cleanup(encoder); + imx8qm_pixel_clk_enable(imx_mhdp); - return ret; + /* Config pixel link mux */ + imx8qm_pixel_link_mux(imx_mhdp); } -static void cdns_hdmi_imx_unbind(struct device *dev, struct device *master, - void *data) +int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp) { - struct imx_mhdp_device *hdp = dev_get_drvdata(dev); + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); + u32 rate; + int ret; - hdp->plat_data->unbind(dev); -} + imx8qm_mhdp_power_on(mhdp); -static const struct component_ops cdns_hdmi_imx8qm_ops = { - .bind = cdns_hdmi_imx_bind, - .unbind = cdns_hdmi_imx_unbind, -}; + /* configure HDMI/DP core clock */ + rate = clk_get_rate(imx_mhdp->clks.clk_core); + cdns_mhdp_set_fw_clk(&imx_mhdp->mhdp, rate); -static int cdns_hdmi_imx_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &cdns_hdmi_imx8qm_ops); -} + /* un-reset ucpu */ + cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL); + DRM_INFO("Started firmware!\n"); -static int cdns_hdmi_imx_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &cdns_hdmi_imx8qm_ops); + ret = cdns_mhdp_check_alive(&imx_mhdp->mhdp); + if (ret == false) { + DRM_ERROR("NO HDMI FW running\n"); + return -ENXIO; + } + + /* turn on IP activity */ + cdns_mhdp_set_firmware_active(&imx_mhdp->mhdp, 1); + + DRM_INFO("HDP FW Version - ver %d verlib %d\n", + cdns_mhdp_bus_read(mhdp, VER_L) + (cdns_mhdp_bus_read(mhdp, VER_H) << 8), + cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) + (cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) << 8)); return 0; } - -static struct platform_driver cdns_hdmi_imx_platform_driver = { - .probe = cdns_hdmi_imx_probe, - .remove = cdns_hdmi_imx_remove, - .driver = { - .name = "cdn-hdp-imx8qm", - .of_match_table = cdns_hdmi_imx_dt_ids, - }, -}; - -module_platform_driver(cdns_hdmi_imx_platform_driver); - -MODULE_AUTHOR("Sandor YU "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:cdnhdmi-imx"); --- /dev/null +++ b/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c @@ -0,0 +1,202 @@ +/* + * copyright (c) 2019 nxp semiconductor, inc. + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as + * published by the free software foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "cdns-mhdp-imx.h" +#include "cdn-mhdp-phy.h" +#include "imx-drm.h" + +static void cdns_mhdp_imx_encoder_disable(struct drm_encoder *encoder) +{ + struct cdns_mhdp_device *mhdp = encoder->bridge->driver_private; + + cdns_mhdp_plat_call(mhdp, plat_init); +} + +static void cdns_mhdp_imx_encoder_enable(struct drm_encoder *encoder) +{ + struct cdns_mhdp_device *mhdp = encoder->bridge->driver_private; + + cdns_mhdp_plat_call(mhdp, plat_deinit); +} + +static int cdns_mhdp_imx_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); + struct cdns_mhdp_device *mhdp = encoder->bridge->driver_private; + + if (mhdp->plat_data->video_format != 0) + imx_crtc_state->bus_format = mhdp->plat_data->video_format; + return 0; +} + +static const struct drm_encoder_helper_funcs cdns_mhdp_imx_encoder_helper_funcs = { + .enable = cdns_mhdp_imx_encoder_enable, + .disable = cdns_mhdp_imx_encoder_disable, + .atomic_check = cdns_mhdp_imx_encoder_atomic_check, +}; + +static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static struct cdns_plat_data imx8mq_hdmi_drv_data = { + .bind = cdns_hdmi_bind, + .unbind = cdns_hdmi_unbind, + .phy_set = cdns_hdmi_phy_set_imx8mq, + .bus_type = BUS_TYPE_NORMAL_APB, +}; + +static struct cdns_plat_data imx8mq_dp_drv_data = { + .bind = cdns_dp_bind, + .unbind = cdns_dp_unbind, + .phy_set = cdns_dp_phy_set_imx8mq, + .bus_type = BUS_TYPE_NORMAL_APB, +}; + +static struct cdns_plat_data imx8qm_hdmi_drv_data = { + .bind = cdns_hdmi_bind, + .unbind = cdns_hdmi_unbind, + .phy_set = cdns_hdmi_phy_set_imx8qm, + .firmware_init = cdns_mhdp_firmware_init_imx8qm, + .pclk_rate = cdns_mhdp_pclk_rate_imx8qm, + .plat_init = cdns_mhdp_plat_init_imx8qm, + .plat_deinit = cdns_mhdp_plat_deinit_imx8qm, + .bus_type = BUS_TYPE_LOW4K_APB, + .video_format = MEDIA_BUS_FMT_RGB101010_1X30, +}; + +static struct cdns_plat_data imx8qm_dp_drv_data = { + .bind = cdns_dp_bind, + .unbind = cdns_dp_unbind, + .phy_set = cdns_dp_phy_set_imx8qm, + .firmware_init = cdns_mhdp_firmware_init_imx8qm, + .pclk_rate = cdns_mhdp_pclk_rate_imx8qm, + .plat_init = cdns_mhdp_plat_init_imx8qm, + .plat_deinit = cdns_mhdp_plat_deinit_imx8qm, + .bus_type = BUS_TYPE_LOW4K_APB, + .video_format = MEDIA_BUS_FMT_RGB101010_1X30, + .is_dp = true, +}; + +static const struct of_device_id cdns_mhdp_imx_dt_ids[] = { + { .compatible = "cdn,imx8mq-hdmi", + .data = &imx8mq_hdmi_drv_data + }, + { .compatible = "cdn,imx8mq-dp", + .data = &imx8mq_dp_drv_data + }, + { .compatible = "cdn,imx8qm-hdmi", + .data = &imx8qm_hdmi_drv_data + }, + { .compatible = "cdn,imx8qm-dp", + .data = &imx8qm_dp_drv_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids); + +static int cdns_mhdp_imx_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct cdns_plat_data *plat_data; + const struct of_device_id *match; + struct drm_device *drm = data; + struct drm_encoder *encoder; + struct imx_mhdp_device *imx_mhdp; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + imx_mhdp = devm_kzalloc(&pdev->dev, sizeof(*imx_mhdp), GFP_KERNEL); + if (!imx_mhdp) + return -ENOMEM; + + match = of_match_node(cdns_mhdp_imx_dt_ids, pdev->dev.of_node); + plat_data = match->data; + encoder = &imx_mhdp->encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + drm_encoder_helper_add(encoder, &cdns_mhdp_imx_encoder_helper_funcs); + drm_encoder_init(drm, encoder, &cdns_mhdp_imx_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + + + imx_mhdp->mhdp.plat_data = plat_data; + imx_mhdp->mhdp.dev = dev; + imx_mhdp->mhdp.bus_type = plat_data->bus_type; + ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp); + /* + * If cdns_mhdp_bind() fails we'll never call cdns_mhdp_unbind(), + * which would have called the encoder cleanup. Do it manually. + */ + if (ret < 0) + drm_encoder_cleanup(encoder); + + imx_mhdp->dual_mode = false; + return ret; +} + +static void cdns_mhdp_imx_unbind(struct device *dev, struct device *master, + void *data) +{ + struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); + + imx_mhdp->mhdp.plat_data->unbind(dev); +} + +static const struct component_ops cdns_mhdp_imx_ops = { + .bind = cdns_mhdp_imx_bind, + .unbind = cdns_mhdp_imx_unbind, +}; + +static int cdns_mhdp_imx_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &cdns_mhdp_imx_ops); +} + +static int cdns_mhdp_imx_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &cdns_mhdp_imx_ops); + + return 0; +} + +static struct platform_driver cdns_mhdp_imx_platform_driver = { + .probe = cdns_mhdp_imx_probe, + .remove = cdns_mhdp_imx_remove, + .driver = { + .name = "cdn-hdp-imx8qm", + .of_match_table = cdns_mhdp_imx_dt_ids, + }, +}; + +module_platform_driver(cdns_mhdp_imx_platform_driver); + +MODULE_AUTHOR("Sandor YU "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cdnhdmi-imx"); --- a/drivers/gpu/drm/imx/cdn-mhdp-phy.h +++ b/drivers/gpu/drm/imx/cdn-mhdp-phy.h @@ -10,7 +10,7 @@ #ifndef _CDN_DP_PHY_H #define _CDN_DP_PHY_H -#include +#include #define CMN_SSM_BIAS_TMR 0x0022 #define CMN_PLLSM0_PLLEN_TMR 0x0029 @@ -146,8 +146,8 @@ #define PHY_PMA_ISO_RX_DATA_LO 0xCC16 #define PHY_PMA_ISO_RX_DATA_HI 0xCC17 -int cdns_dp_phy_init_imx8mq(struct imx_mhdp_device *hdp); -int cdns_dp_phy_init_imx8qm(struct imx_mhdp_device *hdp); -int cdns_hdmi_phy_set_imx8mq(struct imx_mhdp_device *hdp); -int cdns_hdmi_phy_set_imx8qm(struct imx_mhdp_device *hdp); -#endif /* _CDN_DP_PHY_H */ +int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp); +int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *hdp); +int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *hdp); +int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *hdp); +#endif /* _CDNS_MHDP_PHY_H */ --- /dev/null +++ b/drivers/gpu/drm/imx/cdns-mhdp-imx.h @@ -0,0 +1,80 @@ +/* + * Cadence High-Definition Multimedia Interface (HDMI) driver + * + * Copyright (C) 2019 NXP Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#ifndef CDNS_MHDP_IMX_H_ +#define CDNS_MHDP_IMX_H_ + +#include +#include + + +#define HDP_DUAL_MODE_MIN_PCLK_RATE 300000 /* KHz */ +#define HDP_SINGLE_MODE_MAX_WIDTH 1920 + +static inline bool video_is_dual_mode(const struct drm_display_mode *mode) +{ + return (mode->clock > HDP_DUAL_MODE_MIN_PCLK_RATE || + mode->hdisplay > HDP_SINGLE_MODE_MAX_WIDTH) ? true : false; +} + +struct imx_mhdp_device; + +struct imx_hdp_clks { + struct clk *av_pll; + struct clk *dig_pll; + struct clk *clk_ipg; + struct clk *clk_core; + struct clk *clk_pxl; + struct clk *clk_pxl_mux; + struct clk *clk_pxl_link; + + struct clk *lpcg_hdp; + struct clk *lpcg_msi; + struct clk *lpcg_pxl; + struct clk *lpcg_vif; + struct clk *lpcg_lis; + struct clk *lpcg_apb; + struct clk *lpcg_apb_csr; + struct clk *lpcg_apb_ctrl; + + struct clk *lpcg_i2s; + struct clk *clk_i2s_bypass; +}; + +struct imx_mhdp_device { + struct cdns_mhdp_device mhdp; + struct drm_encoder encoder; + + struct mutex audio_mutex; + spinlock_t audio_lock; + bool connected; + bool active; + bool suspended; + struct imx_hdp_clks clks; + + int bus_type; + + u32 dual_mode; + + struct device *pd_mhdp_dev; + struct device *pd_pll0_dev; + struct device *pd_pll1_dev; + struct device_link *pd_mhdp_link; + struct device_link *pd_pll0_link; + struct device_link *pd_pll1_link; + +// u32 phy_init; +}; +void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp); +void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp); +void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp); +int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp); +#endif /* CDNS_MHDP_IMX_H_ */