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