The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it as merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Signed-off-by: Rob Herring <robh@kernel.org> Acked-by: Sam Ravnborg <sam@ravnborg.org> Reviewed-by: Steven Price <steven.price@arm.com> Acked-by: Liviu Dudau <liviu.dudau@arm.com> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> Acked-by: Robert Foss <rfoss@kernel.org> Signed-off-by: Thierry Reding <treding@nvidia.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230714174545.4056287-1-robh@kernel.org
324 lines
6.4 KiB
C
324 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2019 NXP.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_graph.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
#include <drm/drm_bridge_connector.h>
|
|
#include <drm/drm_device.h>
|
|
#include <drm/drm_modeset_helper.h>
|
|
|
|
#include "dcss-dev.h"
|
|
#include "dcss-kms.h"
|
|
|
|
static void dcss_clocks_enable(struct dcss_dev *dcss)
|
|
{
|
|
clk_prepare_enable(dcss->axi_clk);
|
|
clk_prepare_enable(dcss->apb_clk);
|
|
clk_prepare_enable(dcss->rtrm_clk);
|
|
clk_prepare_enable(dcss->dtrc_clk);
|
|
clk_prepare_enable(dcss->pix_clk);
|
|
}
|
|
|
|
static void dcss_clocks_disable(struct dcss_dev *dcss)
|
|
{
|
|
clk_disable_unprepare(dcss->pix_clk);
|
|
clk_disable_unprepare(dcss->dtrc_clk);
|
|
clk_disable_unprepare(dcss->rtrm_clk);
|
|
clk_disable_unprepare(dcss->apb_clk);
|
|
clk_disable_unprepare(dcss->axi_clk);
|
|
}
|
|
|
|
static void dcss_disable_dtg_and_ss_cb(void *data)
|
|
{
|
|
struct dcss_dev *dcss = data;
|
|
|
|
dcss->disable_callback = NULL;
|
|
|
|
dcss_ss_shutoff(dcss->ss);
|
|
dcss_dtg_shutoff(dcss->dtg);
|
|
|
|
complete(&dcss->disable_completion);
|
|
}
|
|
|
|
void dcss_disable_dtg_and_ss(struct dcss_dev *dcss)
|
|
{
|
|
dcss->disable_callback = dcss_disable_dtg_and_ss_cb;
|
|
}
|
|
|
|
void dcss_enable_dtg_and_ss(struct dcss_dev *dcss)
|
|
{
|
|
if (dcss->disable_callback)
|
|
dcss->disable_callback = NULL;
|
|
|
|
dcss_dtg_enable(dcss->dtg);
|
|
dcss_ss_enable(dcss->ss);
|
|
}
|
|
|
|
static int dcss_submodules_init(struct dcss_dev *dcss)
|
|
{
|
|
int ret = 0;
|
|
u32 base_addr = dcss->start_addr;
|
|
const struct dcss_type_data *devtype = dcss->devtype;
|
|
|
|
dcss_clocks_enable(dcss);
|
|
|
|
ret = dcss_blkctl_init(dcss, base_addr + devtype->blkctl_ofs);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs);
|
|
if (ret)
|
|
goto ctxld_err;
|
|
|
|
ret = dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs);
|
|
if (ret)
|
|
goto dtg_err;
|
|
|
|
ret = dcss_ss_init(dcss, base_addr + devtype->ss_ofs);
|
|
if (ret)
|
|
goto ss_err;
|
|
|
|
ret = dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs);
|
|
if (ret)
|
|
goto dpr_err;
|
|
|
|
ret = dcss_scaler_init(dcss, base_addr + devtype->scaler_ofs);
|
|
if (ret)
|
|
goto scaler_err;
|
|
|
|
dcss_clocks_disable(dcss);
|
|
|
|
return 0;
|
|
|
|
scaler_err:
|
|
dcss_dpr_exit(dcss->dpr);
|
|
|
|
dpr_err:
|
|
dcss_ss_exit(dcss->ss);
|
|
|
|
ss_err:
|
|
dcss_dtg_exit(dcss->dtg);
|
|
|
|
dtg_err:
|
|
dcss_ctxld_exit(dcss->ctxld);
|
|
|
|
ctxld_err:
|
|
dcss_blkctl_exit(dcss->blkctl);
|
|
|
|
dcss_clocks_disable(dcss);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void dcss_submodules_stop(struct dcss_dev *dcss)
|
|
{
|
|
dcss_clocks_enable(dcss);
|
|
dcss_scaler_exit(dcss->scaler);
|
|
dcss_dpr_exit(dcss->dpr);
|
|
dcss_ss_exit(dcss->ss);
|
|
dcss_dtg_exit(dcss->dtg);
|
|
dcss_ctxld_exit(dcss->ctxld);
|
|
dcss_blkctl_exit(dcss->blkctl);
|
|
dcss_clocks_disable(dcss);
|
|
}
|
|
|
|
static int dcss_clks_init(struct dcss_dev *dcss)
|
|
{
|
|
int i;
|
|
struct {
|
|
const char *id;
|
|
struct clk **clk;
|
|
} clks[] = {
|
|
{"apb", &dcss->apb_clk},
|
|
{"axi", &dcss->axi_clk},
|
|
{"pix", &dcss->pix_clk},
|
|
{"rtrm", &dcss->rtrm_clk},
|
|
{"dtrc", &dcss->dtrc_clk},
|
|
};
|
|
|
|
for (i = 0; i < ARRAY_SIZE(clks); i++) {
|
|
*clks[i].clk = devm_clk_get(dcss->dev, clks[i].id);
|
|
if (IS_ERR(*clks[i].clk)) {
|
|
dev_err(dcss->dev, "failed to get %s clock\n",
|
|
clks[i].id);
|
|
return PTR_ERR(*clks[i].clk);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dcss_clks_release(struct dcss_dev *dcss)
|
|
{
|
|
devm_clk_put(dcss->dev, dcss->dtrc_clk);
|
|
devm_clk_put(dcss->dev, dcss->rtrm_clk);
|
|
devm_clk_put(dcss->dev, dcss->pix_clk);
|
|
devm_clk_put(dcss->dev, dcss->axi_clk);
|
|
devm_clk_put(dcss->dev, dcss->apb_clk);
|
|
}
|
|
|
|
struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
int ret;
|
|
struct resource *res;
|
|
struct dcss_dev *dcss;
|
|
const struct dcss_type_data *devtype;
|
|
|
|
devtype = of_device_get_match_data(dev);
|
|
if (!devtype) {
|
|
dev_err(dev, "no device match found\n");
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
dev_err(dev, "cannot get memory resource\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
dcss = kzalloc(sizeof(*dcss), GFP_KERNEL);
|
|
if (!dcss)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
dcss->dev = dev;
|
|
dcss->devtype = devtype;
|
|
dcss->hdmi_output = hdmi_output;
|
|
|
|
ret = dcss_clks_init(dcss);
|
|
if (ret) {
|
|
dev_err(dev, "clocks initialization failed\n");
|
|
goto err;
|
|
}
|
|
|
|
dcss->of_port = of_graph_get_port_by_id(dev->of_node, 0);
|
|
if (!dcss->of_port) {
|
|
dev_err(dev, "no port@0 node in %pOF\n", dev->of_node);
|
|
ret = -ENODEV;
|
|
goto clks_err;
|
|
}
|
|
|
|
dcss->start_addr = res->start;
|
|
|
|
ret = dcss_submodules_init(dcss);
|
|
if (ret) {
|
|
of_node_put(dcss->of_port);
|
|
dev_err(dev, "submodules initialization failed\n");
|
|
goto clks_err;
|
|
}
|
|
|
|
init_completion(&dcss->disable_completion);
|
|
|
|
pm_runtime_set_autosuspend_delay(dev, 100);
|
|
pm_runtime_use_autosuspend(dev);
|
|
pm_runtime_set_suspended(dev);
|
|
pm_runtime_allow(dev);
|
|
pm_runtime_enable(dev);
|
|
|
|
return dcss;
|
|
|
|
clks_err:
|
|
dcss_clks_release(dcss);
|
|
|
|
err:
|
|
kfree(dcss);
|
|
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
void dcss_dev_destroy(struct dcss_dev *dcss)
|
|
{
|
|
if (!pm_runtime_suspended(dcss->dev)) {
|
|
dcss_ctxld_suspend(dcss->ctxld);
|
|
dcss_clocks_disable(dcss);
|
|
}
|
|
|
|
of_node_put(dcss->of_port);
|
|
|
|
pm_runtime_disable(dcss->dev);
|
|
|
|
dcss_submodules_stop(dcss);
|
|
|
|
dcss_clks_release(dcss);
|
|
|
|
kfree(dcss);
|
|
}
|
|
|
|
static int dcss_dev_suspend(struct device *dev)
|
|
{
|
|
struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
|
|
struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
|
|
int ret;
|
|
|
|
drm_mode_config_helper_suspend(ddev);
|
|
|
|
if (pm_runtime_suspended(dev))
|
|
return 0;
|
|
|
|
ret = dcss_ctxld_suspend(dcss->ctxld);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dcss_clocks_disable(dcss);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dcss_dev_resume(struct device *dev)
|
|
{
|
|
struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
|
|
struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
|
|
|
|
if (pm_runtime_suspended(dev)) {
|
|
drm_mode_config_helper_resume(ddev);
|
|
return 0;
|
|
}
|
|
|
|
dcss_clocks_enable(dcss);
|
|
|
|
dcss_blkctl_cfg(dcss->blkctl);
|
|
|
|
dcss_ctxld_resume(dcss->ctxld);
|
|
|
|
drm_mode_config_helper_resume(ddev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dcss_dev_runtime_suspend(struct device *dev)
|
|
{
|
|
struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
|
|
int ret;
|
|
|
|
ret = dcss_ctxld_suspend(dcss->ctxld);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dcss_clocks_disable(dcss);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dcss_dev_runtime_resume(struct device *dev)
|
|
{
|
|
struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
|
|
|
|
dcss_clocks_enable(dcss);
|
|
|
|
dcss_blkctl_cfg(dcss->blkctl);
|
|
|
|
dcss_ctxld_resume(dcss->ctxld);
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_GPL_DEV_PM_OPS(dcss_dev_pm_ops) = {
|
|
RUNTIME_PM_OPS(dcss_dev_runtime_suspend, dcss_dev_runtime_resume, NULL)
|
|
SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume)
|
|
};
|