mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 09:12:39 +00:00
269 lines
8.4 KiB
Diff
269 lines
8.4 KiB
Diff
|
From 020578dd022e5d869db52e79c5aba95c1f1a84ec Mon Sep 17 00:00:00 2001
|
||
|
From: Abel Vesa <abel.vesa@nxp.com>
|
||
|
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 <abel.vesa@nxp.com>
|
||
|
Reviewed-by: Sandor Yu <sandor.yu@nxp.com>
|
||
|
Acked-by: Wen He <wen.he_1@nxp.com>
|
||
|
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
|
||
|
---
|
||
|
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 <dt-bindings/firmware/imx/rsrc.h>
|
||
|
#include <linux/firmware/imx/sci.h>
|
||
|
+#include <linux/firmware.h>
|
||
|
#include <linux/pm_domain.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <drm/drmP.h>
|
||
|
|
||
|
#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);
|
||
|
|