From 020578dd022e5d869db52e79c5aba95c1f1a84ec Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Wed, 11 Dec 2019 09:21:23 +0200 Subject: [PATCH] LF-794-3 gpu: cdn: imx8qm: Add firmware loading support This allows the HDP i.MX8QM driver to load the firmware on init and resume. In order to have backward compatibility, if there is no firmware-name property defined in the hdmi node, the driver probing sequence skips the firmware loading. Also, if u-boot has loaded already a firmware, we run with that but when probing the driver, the request_firmware_nowait is used to locate and keep safe the firmware for when suspend/resume happens. This leads to 4 possible scenarios: 1. u-boot loads the firmware, the kernel driver finds the firmware when rootfs is mounted. This is the most desirable scenario. Also this is the only scenario that allows the hdmi to work after resume. 2. u-boot loads the firmware, the kernel driver _doesn't_ find the firmware in rootfs. If there is no suspend ever happening, the kernel driver will keep using the firmware that was loaded by u-boot. On the first suspend/resume, the firmware is lost because the HDMI IP gets powered down. 3. u-boot doesn't load the firmare, the kernel driver probing tries to load the firmware, assuming this is available (see CONFIG_EXTRA_FIRMWARE). 4. u-boot doesn't load the firmware and the kernel driver is not able to find it either. The probing fails and there is no HDMI available in linux. Signed-off-by: Abel Vesa Reviewed-by: Sandor Yu Acked-by: Wen He Signed-off-by: Dong Aisheng --- drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c | 78 +++++++++++++++++++++++++++++++++-- drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c | 30 ++++++++++++++ drivers/gpu/drm/imx/cdns-mhdp-imx.h | 4 ++ include/drm/bridge/cdns-mhdp-common.h | 3 ++ 4 files changed, 111 insertions(+), 4 deletions(-) --- a/drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c +++ b/drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c @@ -7,12 +7,17 @@ */ #include #include +#include #include #include #include #include "cdns-mhdp-imx.h" +#define FW_IRAM_OFFSET 0x2000 +#define FW_IRAM_SIZE 0x10000 +#define FW_DRAM_SIZE 0x8000 + #define PLL_800MHZ (800000000) #define HDP_DUAL_MODE_MIN_PCLK_RATE 300000 /* KHz */ @@ -517,24 +522,69 @@ void cdns_mhdp_pclk_rate_imx8qm(struct c imx8qm_pixel_link_mux(imx_mhdp); } -int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp) +static void cdns_mhdp_firmware_load_cont(const struct firmware *fw, void *context) { - struct imx_mhdp_device *imx_mhdp = - container_of(mhdp, struct imx_mhdp_device, mhdp); + struct imx_mhdp_device *imx_mhdp = context; + + imx_mhdp->fw = fw; +} + +static int cdns_mhdp_load_firmware_imx8qm(struct imx_mhdp_device *imx_mhdp) +{ + const u8 *iram; + const u8 *dram; u32 rate; int ret; /* configure HDMI/DP core clock */ rate = clk_get_rate(imx_mhdp->clks.clk_core); - if (mhdp->is_ls1028a) + if (imx_mhdp->mhdp.is_ls1028a) rate = rate / 4; cdns_mhdp_set_fw_clk(&imx_mhdp->mhdp, rate); + /* skip fw loading if none is specified */ + if (!imx_mhdp->firmware_name) + goto out; + + if (!imx_mhdp->fw) { + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + imx_mhdp->firmware_name, + imx_mhdp->mhdp.dev, GFP_KERNEL, + imx_mhdp, + cdns_mhdp_firmware_load_cont); + if (ret < 0) { + DRM_ERROR("failed to load firmware\n"); + return -ENOENT; + } + } else { + iram = imx_mhdp->fw->data + FW_IRAM_OFFSET; + dram = iram + FW_IRAM_SIZE; + + cdns_mhdp_load_firmware(&imx_mhdp->mhdp, + (const u32 *) iram, FW_IRAM_SIZE, + (const u32 *) dram, FW_DRAM_SIZE); + } + +out: /* un-reset ucpu */ cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL); DRM_INFO("Started firmware!\n"); + return 0; +} + +int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp) +{ + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); + int ret; + + /* load firmware */ + ret = cdns_mhdp_load_firmware_imx8qm(imx_mhdp); + if (ret) + return ret; + ret = cdns_mhdp_check_alive(&imx_mhdp->mhdp); if (ret == false) { DRM_ERROR("NO HDMI FW running\n"); @@ -550,3 +600,23 @@ int cdns_mhdp_firmware_init_imx8qm(struc return 0; } + +int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp) +{ + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); + + imx8qm_pixel_clk_disable(imx_mhdp); + + return 0; +} + +int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp) +{ + struct imx_mhdp_device *imx_mhdp = + container_of(mhdp, struct imx_mhdp_device, mhdp); + + imx8qm_pixel_clk_enable(imx_mhdp); + + return cdns_mhdp_firmware_init_imx8qm(mhdp); +} --- a/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c +++ b/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c @@ -82,6 +82,8 @@ static struct cdns_plat_data imx8qm_hdmi .phy_video_valid = cdns_hdmi_phy_video_valid_imx8qm, .power_on = cdns_mhdp_power_on_imx8qm, .firmware_init = cdns_mhdp_firmware_init_imx8qm, + .resume = cdns_mhdp_resume_imx8qm, + .suspend = cdns_mhdp_suspend_imx8qm, .pclk_rate = cdns_mhdp_pclk_rate_imx8qm, .plat_init = cdns_mhdp_plat_init_imx8qm, .plat_deinit = cdns_mhdp_plat_deinit_imx8qm, @@ -96,6 +98,8 @@ static struct cdns_plat_data imx8qm_dp_d .phy_set = cdns_dp_phy_set_imx8qm, .power_on = cdns_mhdp_power_on_imx8qm, .firmware_init = cdns_mhdp_firmware_init_imx8qm, + .resume = cdns_mhdp_resume_imx8qm, + .suspend = cdns_mhdp_suspend_imx8qm, .pclk_rate = cdns_mhdp_pclk_rate_imx8qm, .plat_init = cdns_mhdp_plat_init_imx8qm, .plat_deinit = cdns_mhdp_plat_deinit_imx8qm, @@ -157,6 +161,9 @@ static int cdns_mhdp_imx_bind(struct dev encoder = &imx_mhdp->encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &imx_mhdp->firmware_name); /* * If we failed to find the CRTC(s) which this encoder is * supposed to be connected to, it's because the CRTC has @@ -198,6 +205,24 @@ static const struct component_ops cdns_m .unbind = cdns_mhdp_imx_unbind, }; +static int cdns_mhdp_imx_suspend(struct device *dev) +{ + struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); + + cdns_mhdp_plat_call(&imx_mhdp->mhdp, suspend); + + return 0; +} + +static int cdns_mhdp_imx_resume(struct device *dev) +{ + struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev); + + cdns_mhdp_plat_call(&imx_mhdp->mhdp, resume); + + return 0; +} + static int cdns_mhdp_imx_probe(struct platform_device *pdev) { return component_add(&pdev->dev, &cdns_mhdp_imx_ops); @@ -210,12 +235,17 @@ static int cdns_mhdp_imx_remove(struct p return 0; } +static const struct dev_pm_ops cdns_mhdp_imx_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(cdns_mhdp_imx_suspend, cdns_mhdp_imx_resume) +}; + static struct platform_driver cdns_mhdp_imx_platform_driver = { .probe = cdns_mhdp_imx_probe, .remove = cdns_mhdp_imx_remove, .driver = { .name = "cdns-mhdp-imx", .of_match_table = cdns_mhdp_imx_dt_ids, + .pm = &cdns_mhdp_imx_pm_ops, }, }; --- a/drivers/gpu/drm/imx/cdns-mhdp-imx.h +++ b/drivers/gpu/drm/imx/cdns-mhdp-imx.h @@ -50,6 +50,8 @@ struct imx_mhdp_device { bool active; bool suspended; struct imx_hdp_clks clks; + const struct firmware *fw; + const char *firmware_name; int bus_type; @@ -65,6 +67,8 @@ void cdns_mhdp_plat_init_imx8qm(struct c 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); +int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp); +int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp); int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp); int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp); void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp); --- a/include/drm/bridge/cdns-mhdp-common.h +++ b/include/drm/bridge/cdns-mhdp-common.h @@ -645,6 +645,9 @@ struct cdns_plat_data { int (*firmware_init)(struct cdns_mhdp_device *mhdp); void (*pclk_rate)(struct cdns_mhdp_device *mhdp); + int (*suspend)(struct cdns_mhdp_device *mhdp); + int (*resume)(struct cdns_mhdp_device *mhdp); + int (*power_on)(struct cdns_mhdp_device *mhdp); int (*power_off)(struct cdns_mhdp_device *mhdp);