18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019 NXP.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/of_device.h>
88c2ecf20Sopenharmony_ci#include <linux/of_graph.h>
98c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_bridge_connector.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "dcss-dev.h"
168c2ecf20Sopenharmony_ci#include "dcss-kms.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic void dcss_clocks_enable(struct dcss_dev *dcss)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	clk_prepare_enable(dcss->axi_clk);
218c2ecf20Sopenharmony_ci	clk_prepare_enable(dcss->apb_clk);
228c2ecf20Sopenharmony_ci	clk_prepare_enable(dcss->rtrm_clk);
238c2ecf20Sopenharmony_ci	clk_prepare_enable(dcss->dtrc_clk);
248c2ecf20Sopenharmony_ci	clk_prepare_enable(dcss->pix_clk);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic void dcss_clocks_disable(struct dcss_dev *dcss)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	clk_disable_unprepare(dcss->pix_clk);
308c2ecf20Sopenharmony_ci	clk_disable_unprepare(dcss->dtrc_clk);
318c2ecf20Sopenharmony_ci	clk_disable_unprepare(dcss->rtrm_clk);
328c2ecf20Sopenharmony_ci	clk_disable_unprepare(dcss->apb_clk);
338c2ecf20Sopenharmony_ci	clk_disable_unprepare(dcss->axi_clk);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void dcss_disable_dtg_and_ss_cb(void *data)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = data;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	dcss->disable_callback = NULL;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	dcss_ss_shutoff(dcss->ss);
438c2ecf20Sopenharmony_ci	dcss_dtg_shutoff(dcss->dtg);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	complete(&dcss->disable_completion);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid dcss_disable_dtg_and_ss(struct dcss_dev *dcss)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	dcss->disable_callback = dcss_disable_dtg_and_ss_cb;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_civoid dcss_enable_dtg_and_ss(struct dcss_dev *dcss)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	if (dcss->disable_callback)
568c2ecf20Sopenharmony_ci		dcss->disable_callback = NULL;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	dcss_dtg_enable(dcss->dtg);
598c2ecf20Sopenharmony_ci	dcss_ss_enable(dcss->ss);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int dcss_submodules_init(struct dcss_dev *dcss)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	int ret = 0;
658c2ecf20Sopenharmony_ci	u32 base_addr = dcss->start_addr;
668c2ecf20Sopenharmony_ci	const struct dcss_type_data *devtype = dcss->devtype;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	dcss_clocks_enable(dcss);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = dcss_blkctl_init(dcss, base_addr + devtype->blkctl_ofs);
718c2ecf20Sopenharmony_ci	if (ret)
728c2ecf20Sopenharmony_ci		return ret;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	ret = dcss_ctxld_init(dcss, base_addr + devtype->ctxld_ofs);
758c2ecf20Sopenharmony_ci	if (ret)
768c2ecf20Sopenharmony_ci		goto ctxld_err;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ret = dcss_dtg_init(dcss, base_addr + devtype->dtg_ofs);
798c2ecf20Sopenharmony_ci	if (ret)
808c2ecf20Sopenharmony_ci		goto dtg_err;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	ret = dcss_ss_init(dcss, base_addr + devtype->ss_ofs);
838c2ecf20Sopenharmony_ci	if (ret)
848c2ecf20Sopenharmony_ci		goto ss_err;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = dcss_dpr_init(dcss, base_addr + devtype->dpr_ofs);
878c2ecf20Sopenharmony_ci	if (ret)
888c2ecf20Sopenharmony_ci		goto dpr_err;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	ret = dcss_scaler_init(dcss, base_addr + devtype->scaler_ofs);
918c2ecf20Sopenharmony_ci	if (ret)
928c2ecf20Sopenharmony_ci		goto scaler_err;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	dcss_clocks_disable(dcss);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciscaler_err:
998c2ecf20Sopenharmony_ci	dcss_dpr_exit(dcss->dpr);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cidpr_err:
1028c2ecf20Sopenharmony_ci	dcss_ss_exit(dcss->ss);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciss_err:
1058c2ecf20Sopenharmony_ci	dcss_dtg_exit(dcss->dtg);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cidtg_err:
1088c2ecf20Sopenharmony_ci	dcss_ctxld_exit(dcss->ctxld);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cictxld_err:
1118c2ecf20Sopenharmony_ci	dcss_blkctl_exit(dcss->blkctl);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	dcss_clocks_disable(dcss);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return ret;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void dcss_submodules_stop(struct dcss_dev *dcss)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	dcss_clocks_enable(dcss);
1218c2ecf20Sopenharmony_ci	dcss_scaler_exit(dcss->scaler);
1228c2ecf20Sopenharmony_ci	dcss_dpr_exit(dcss->dpr);
1238c2ecf20Sopenharmony_ci	dcss_ss_exit(dcss->ss);
1248c2ecf20Sopenharmony_ci	dcss_dtg_exit(dcss->dtg);
1258c2ecf20Sopenharmony_ci	dcss_ctxld_exit(dcss->ctxld);
1268c2ecf20Sopenharmony_ci	dcss_blkctl_exit(dcss->blkctl);
1278c2ecf20Sopenharmony_ci	dcss_clocks_disable(dcss);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int dcss_clks_init(struct dcss_dev *dcss)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int i;
1338c2ecf20Sopenharmony_ci	struct {
1348c2ecf20Sopenharmony_ci		const char *id;
1358c2ecf20Sopenharmony_ci		struct clk **clk;
1368c2ecf20Sopenharmony_ci	} clks[] = {
1378c2ecf20Sopenharmony_ci		{"apb",   &dcss->apb_clk},
1388c2ecf20Sopenharmony_ci		{"axi",   &dcss->axi_clk},
1398c2ecf20Sopenharmony_ci		{"pix",   &dcss->pix_clk},
1408c2ecf20Sopenharmony_ci		{"rtrm",  &dcss->rtrm_clk},
1418c2ecf20Sopenharmony_ci		{"dtrc",  &dcss->dtrc_clk},
1428c2ecf20Sopenharmony_ci	};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clks); i++) {
1458c2ecf20Sopenharmony_ci		*clks[i].clk = devm_clk_get(dcss->dev, clks[i].id);
1468c2ecf20Sopenharmony_ci		if (IS_ERR(*clks[i].clk)) {
1478c2ecf20Sopenharmony_ci			dev_err(dcss->dev, "failed to get %s clock\n",
1488c2ecf20Sopenharmony_ci				clks[i].id);
1498c2ecf20Sopenharmony_ci			return PTR_ERR(*clks[i].clk);
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void dcss_clks_release(struct dcss_dev *dcss)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	devm_clk_put(dcss->dev, dcss->dtrc_clk);
1598c2ecf20Sopenharmony_ci	devm_clk_put(dcss->dev, dcss->rtrm_clk);
1608c2ecf20Sopenharmony_ci	devm_clk_put(dcss->dev, dcss->pix_clk);
1618c2ecf20Sopenharmony_ci	devm_clk_put(dcss->dev, dcss->axi_clk);
1628c2ecf20Sopenharmony_ci	devm_clk_put(dcss->dev, dcss->apb_clk);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistruct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
1688c2ecf20Sopenharmony_ci	int ret;
1698c2ecf20Sopenharmony_ci	struct resource *res;
1708c2ecf20Sopenharmony_ci	struct dcss_dev *dcss;
1718c2ecf20Sopenharmony_ci	const struct dcss_type_data *devtype;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	devtype = of_device_get_match_data(dev);
1748c2ecf20Sopenharmony_ci	if (!devtype) {
1758c2ecf20Sopenharmony_ci		dev_err(dev, "no device match found\n");
1768c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1808c2ecf20Sopenharmony_ci	if (!res) {
1818c2ecf20Sopenharmony_ci		dev_err(dev, "cannot get memory resource\n");
1828c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	dcss = kzalloc(sizeof(*dcss), GFP_KERNEL);
1868c2ecf20Sopenharmony_ci	if (!dcss)
1878c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	dcss->dev = dev;
1908c2ecf20Sopenharmony_ci	dcss->devtype = devtype;
1918c2ecf20Sopenharmony_ci	dcss->hdmi_output = hdmi_output;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	ret = dcss_clks_init(dcss);
1948c2ecf20Sopenharmony_ci	if (ret) {
1958c2ecf20Sopenharmony_ci		dev_err(dev, "clocks initialization failed\n");
1968c2ecf20Sopenharmony_ci		goto err;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	dcss->of_port = of_graph_get_port_by_id(dev->of_node, 0);
2008c2ecf20Sopenharmony_ci	if (!dcss->of_port) {
2018c2ecf20Sopenharmony_ci		dev_err(dev, "no port@0 node in %s\n", dev->of_node->full_name);
2028c2ecf20Sopenharmony_ci		ret = -ENODEV;
2038c2ecf20Sopenharmony_ci		goto clks_err;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	dcss->start_addr = res->start;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	ret = dcss_submodules_init(dcss);
2098c2ecf20Sopenharmony_ci	if (ret) {
2108c2ecf20Sopenharmony_ci		of_node_put(dcss->of_port);
2118c2ecf20Sopenharmony_ci		dev_err(dev, "submodules initialization failed\n");
2128c2ecf20Sopenharmony_ci		goto clks_err;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	init_completion(&dcss->disable_completion);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 100);
2188c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
2198c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(dev);
2208c2ecf20Sopenharmony_ci	pm_runtime_allow(dev);
2218c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return dcss;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciclks_err:
2268c2ecf20Sopenharmony_ci	dcss_clks_release(dcss);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cierr:
2298c2ecf20Sopenharmony_ci	kfree(dcss);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_civoid dcss_dev_destroy(struct dcss_dev *dcss)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	if (!pm_runtime_suspended(dcss->dev)) {
2378c2ecf20Sopenharmony_ci		dcss_ctxld_suspend(dcss->ctxld);
2388c2ecf20Sopenharmony_ci		dcss_clocks_disable(dcss);
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	of_node_put(dcss->of_port);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	pm_runtime_disable(dcss->dev);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	dcss_submodules_stop(dcss);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	dcss_clks_release(dcss);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	kfree(dcss);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2538c2ecf20Sopenharmony_ciint dcss_dev_suspend(struct device *dev)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
2568c2ecf20Sopenharmony_ci	struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
2578c2ecf20Sopenharmony_ci	struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
2588c2ecf20Sopenharmony_ci	int ret;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	drm_bridge_connector_disable_hpd(kms->connector);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	drm_mode_config_helper_suspend(ddev);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(dev))
2658c2ecf20Sopenharmony_ci		return 0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ret = dcss_ctxld_suspend(dcss->ctxld);
2688c2ecf20Sopenharmony_ci	if (ret)
2698c2ecf20Sopenharmony_ci		return ret;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	dcss_clocks_disable(dcss);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ciint dcss_dev_resume(struct device *dev)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
2798c2ecf20Sopenharmony_ci	struct drm_device *ddev = dcss_drv_dev_to_drm(dev);
2808c2ecf20Sopenharmony_ci	struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(dev)) {
2838c2ecf20Sopenharmony_ci		drm_mode_config_helper_resume(ddev);
2848c2ecf20Sopenharmony_ci		return 0;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	dcss_clocks_enable(dcss);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	dcss_blkctl_cfg(dcss->blkctl);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	dcss_ctxld_resume(dcss->ctxld);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	drm_mode_config_helper_resume(ddev);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	drm_bridge_connector_enable_hpd(kms->connector);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
3028c2ecf20Sopenharmony_ciint dcss_dev_runtime_suspend(struct device *dev)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
3058c2ecf20Sopenharmony_ci	int ret;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	ret = dcss_ctxld_suspend(dcss->ctxld);
3088c2ecf20Sopenharmony_ci	if (ret)
3098c2ecf20Sopenharmony_ci		return ret;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	dcss_clocks_disable(dcss);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ciint dcss_dev_runtime_resume(struct device *dev)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	dcss_clocks_enable(dcss);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	dcss_blkctl_cfg(dcss->blkctl);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	dcss_ctxld_resume(dcss->ctxld);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
329