Skip to content

Commit

Permalink
LF-794 gpu: cdn: imx8qm: Add firmware loading support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
abelvesa committed Feb 13, 2020
1 parent 4d3e8aa commit 0eef22c
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 4 deletions.
93 changes: 89 additions & 4 deletions drivers/gpu/drm/imx/cdn-mhdp-imx8qm.c
Expand Up @@ -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 */
Expand Down Expand Up @@ -517,24 +522,84 @@ void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp)
imx8qm_pixel_link_mux(imx_mhdp);
}

int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp)
int cdns_mhdp_firmware_write_section(struct imx_mhdp_device *imx_mhdp,
const u8 *data, int size, int addr)
{
struct imx_mhdp_device *imx_mhdp =
container_of(mhdp, struct imx_mhdp_device, mhdp);
int i;

for (i = 0; i < size; i += 4) {
u32 val = (unsigned int)data[i] << 0 |
(unsigned int)data[i + 1] << 8 |
(unsigned int)data[i + 2] << 16 |
(unsigned int)data[i + 3] << 24;
cdns_mhdp_bus_write(val, &imx_mhdp->mhdp, addr + i);
}

return 0;
}

static void cdns_mhdp_firmware_load_cont(const struct firmware *fw, void *context)
{
struct imx_mhdp_device *imx_mhdp = context;

imx_mhdp->fw = fw;
}

static int cdns_mhdp_firmware_load(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_firmware_write_section(imx_mhdp, iram, FW_IRAM_SIZE, ADDR_IMEM);
cdns_mhdp_firmware_write_section(imx_mhdp, dram, FW_DRAM_SIZE, ADDR_DMEM);
}

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_firmware_load(imx_mhdp);
if (ret)
return ret;

ret = cdns_mhdp_check_alive(&imx_mhdp->mhdp);
if (ret == false) {
DRM_ERROR("NO HDMI FW running\n");
Expand All @@ -550,3 +615,23 @@ int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp)

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);
}
30 changes: 30 additions & 0 deletions drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c
Expand Up @@ -82,6 +82,8 @@ static struct cdns_plat_data imx8qm_hdmi_drv_data = {
.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,
Expand All @@ -96,6 +98,8 @@ static struct cdns_plat_data imx8qm_dp_drv_data = {
.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,
Expand Down Expand Up @@ -157,6 +161,9 @@ static int cdns_mhdp_imx_bind(struct device *dev, struct device *master,
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
Expand Down Expand Up @@ -198,6 +205,24 @@ static const struct component_ops cdns_mhdp_imx_ops = {
.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);
Expand All @@ -210,12 +235,17 @@ static int cdns_mhdp_imx_remove(struct platform_device *pdev)
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,
},
};

Expand Down
4 changes: 4 additions & 0 deletions drivers/gpu/drm/imx/cdns-mhdp-imx.h
Expand Up @@ -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;

Expand All @@ -65,6 +67,8 @@ 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);
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);
Expand Down
3 changes: 3 additions & 0 deletions include/drm/bridge/cdns-mhdp-common.h
Expand Up @@ -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);

Expand Down

0 comments on commit 0eef22c

Please sign in to comment.