18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ 48c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/console.h> 88c2ecf20Sopenharmony_ci#include <linux/of_device.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_irq.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_managed.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "tidss_dispc.h" 248c2ecf20Sopenharmony_ci#include "tidss_drv.h" 258c2ecf20Sopenharmony_ci#include "tidss_kms.h" 268c2ecf20Sopenharmony_ci#include "tidss_irq.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Power management */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint tidss_runtime_get(struct tidss_device *tidss) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int r; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci dev_dbg(tidss->dev, "%s\n", __func__); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci r = pm_runtime_get_sync(tidss->dev); 378c2ecf20Sopenharmony_ci WARN_ON(r < 0); 388c2ecf20Sopenharmony_ci return r < 0 ? r : 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid tidss_runtime_put(struct tidss_device *tidss) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int r; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci dev_dbg(tidss->dev, "%s\n", __func__); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci r = pm_runtime_put_sync(tidss->dev); 488c2ecf20Sopenharmony_ci WARN_ON(r < 0); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int __maybe_unused tidss_pm_runtime_suspend(struct device *dev) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct tidss_device *tidss = dev_get_drvdata(dev); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return dispc_runtime_suspend(tidss->dispc); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int __maybe_unused tidss_pm_runtime_resume(struct device *dev) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct tidss_device *tidss = dev_get_drvdata(dev); 638c2ecf20Sopenharmony_ci int r; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci r = dispc_runtime_resume(tidss->dispc); 688c2ecf20Sopenharmony_ci if (r) 698c2ecf20Sopenharmony_ci return r; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int __maybe_unused tidss_suspend(struct device *dev) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct tidss_device *tidss = dev_get_drvdata(dev); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return drm_mode_config_helper_suspend(&tidss->ddev); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int __maybe_unused tidss_resume(struct device *dev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct tidss_device *tidss = dev_get_drvdata(dev); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return drm_mode_config_helper_resume(&tidss->ddev); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tidss_pm_ops = { 958c2ecf20Sopenharmony_ci .runtime_suspend = tidss_pm_runtime_suspend, 968c2ecf20Sopenharmony_ci .runtime_resume = tidss_pm_runtime_resume, 978c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(tidss_suspend, tidss_resume) 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* DRM device Information */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void tidss_release(struct drm_device *ddev) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(ddev); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(tidss_fops); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic struct drm_driver tidss_driver = { 1128c2ecf20Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 1138c2ecf20Sopenharmony_ci .fops = &tidss_fops, 1148c2ecf20Sopenharmony_ci .release = tidss_release, 1158c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS_VMAP, 1168c2ecf20Sopenharmony_ci .name = "tidss", 1178c2ecf20Sopenharmony_ci .desc = "TI Keystone DSS", 1188c2ecf20Sopenharmony_ci .date = "20180215", 1198c2ecf20Sopenharmony_ci .major = 1, 1208c2ecf20Sopenharmony_ci .minor = 0, 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci .irq_preinstall = tidss_irq_preinstall, 1238c2ecf20Sopenharmony_ci .irq_postinstall = tidss_irq_postinstall, 1248c2ecf20Sopenharmony_ci .irq_handler = tidss_irq_handler, 1258c2ecf20Sopenharmony_ci .irq_uninstall = tidss_irq_uninstall, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int tidss_probe(struct platform_device *pdev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1318c2ecf20Sopenharmony_ci struct tidss_device *tidss; 1328c2ecf20Sopenharmony_ci struct drm_device *ddev; 1338c2ecf20Sopenharmony_ci int ret; 1348c2ecf20Sopenharmony_ci int irq; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci tidss = devm_drm_dev_alloc(&pdev->dev, &tidss_driver, 1398c2ecf20Sopenharmony_ci struct tidss_device, ddev); 1408c2ecf20Sopenharmony_ci if (IS_ERR(tidss)) 1418c2ecf20Sopenharmony_ci return PTR_ERR(tidss); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci ddev = &tidss->ddev; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci tidss->dev = dev; 1468c2ecf20Sopenharmony_ci tidss->feat = of_device_get_match_data(dev); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tidss); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = dispc_init(tidss); 1518c2ecf20Sopenharmony_ci if (ret) { 1528c2ecf20Sopenharmony_ci dev_err(dev, "failed to initialize dispc: %d\n", ret); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#ifndef CONFIG_PM 1598c2ecf20Sopenharmony_ci /* If we don't have PM, we need to call resume manually */ 1608c2ecf20Sopenharmony_ci dispc_runtime_resume(tidss->dispc); 1618c2ecf20Sopenharmony_ci#endif 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ret = tidss_modeset_init(tidss); 1648c2ecf20Sopenharmony_ci if (ret < 0) { 1658c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 1668c2ecf20Sopenharmony_ci dev_err(dev, "failed to init DRM/KMS (%d)\n", ret); 1678c2ecf20Sopenharmony_ci goto err_runtime_suspend; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 1718c2ecf20Sopenharmony_ci if (irq < 0) { 1728c2ecf20Sopenharmony_ci ret = irq; 1738c2ecf20Sopenharmony_ci goto err_runtime_suspend; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = drm_irq_install(ddev, irq); 1778c2ecf20Sopenharmony_ci if (ret) { 1788c2ecf20Sopenharmony_ci dev_err(dev, "drm_irq_install failed: %d\n", ret); 1798c2ecf20Sopenharmony_ci goto err_runtime_suspend; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(ddev); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci drm_mode_config_reset(ddev); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = drm_dev_register(ddev, 0); 1878c2ecf20Sopenharmony_ci if (ret) { 1888c2ecf20Sopenharmony_ci dev_err(dev, "failed to register DRM device\n"); 1898c2ecf20Sopenharmony_ci goto err_irq_uninstall; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(ddev, 32); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dev_dbg(dev, "%s done\n", __func__); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cierr_irq_uninstall: 1998c2ecf20Sopenharmony_ci drm_irq_uninstall(ddev); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cierr_runtime_suspend: 2028c2ecf20Sopenharmony_ci#ifndef CONFIG_PM 2038c2ecf20Sopenharmony_ci dispc_runtime_suspend(tidss->dispc); 2048c2ecf20Sopenharmony_ci#endif 2058c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int tidss_remove(struct platform_device *pdev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2138c2ecf20Sopenharmony_ci struct tidss_device *tidss = platform_get_drvdata(pdev); 2148c2ecf20Sopenharmony_ci struct drm_device *ddev = &tidss->ddev; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci dev_dbg(dev, "%s\n", __func__); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci drm_dev_unregister(ddev); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(ddev); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci drm_irq_uninstall(ddev); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#ifndef CONFIG_PM 2258c2ecf20Sopenharmony_ci /* If we don't have PM, we need to call suspend manually */ 2268c2ecf20Sopenharmony_ci dispc_runtime_suspend(tidss->dispc); 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* devm allocated dispc goes away with the dev so mark it NULL */ 2318c2ecf20Sopenharmony_ci dispc_remove(tidss); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci dev_dbg(dev, "%s done\n", __func__); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void tidss_shutdown(struct platform_device *pdev) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic const struct of_device_id tidss_of_table[] = { 2448c2ecf20Sopenharmony_ci { .compatible = "ti,k2g-dss", .data = &dispc_k2g_feats, }, 2458c2ecf20Sopenharmony_ci { .compatible = "ti,am65x-dss", .data = &dispc_am65x_feats, }, 2468c2ecf20Sopenharmony_ci { .compatible = "ti,j721e-dss", .data = &dispc_j721e_feats, }, 2478c2ecf20Sopenharmony_ci { } 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tidss_of_table); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic struct platform_driver tidss_platform_driver = { 2538c2ecf20Sopenharmony_ci .probe = tidss_probe, 2548c2ecf20Sopenharmony_ci .remove = tidss_remove, 2558c2ecf20Sopenharmony_ci .shutdown = tidss_shutdown, 2568c2ecf20Sopenharmony_ci .driver = { 2578c2ecf20Sopenharmony_ci .name = "tidss", 2588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2598c2ecf20Sopenharmony_ci .pm = &tidss_pm_ops, 2608c2ecf20Sopenharmony_ci#endif 2618c2ecf20Sopenharmony_ci .of_match_table = tidss_of_table, 2628c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 2638c2ecf20Sopenharmony_ci }, 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cimodule_platform_driver(tidss_platform_driver); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 2698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI Keystone DSS Driver"); 2708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 271