18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ARC PGU DRM driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_debugfs.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "arcpgu.h" 258c2ecf20Sopenharmony_ci#include "arcpgu_regs.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = { 288c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create, 298c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 308c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void arcpgu_setup_mode_config(struct drm_device *drm) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci drm_mode_config_init(drm); 368c2ecf20Sopenharmony_ci drm->mode_config.min_width = 0; 378c2ecf20Sopenharmony_ci drm->mode_config.min_height = 0; 388c2ecf20Sopenharmony_ci drm->mode_config.max_width = 1920; 398c2ecf20Sopenharmony_ci drm->mode_config.max_height = 1080; 408c2ecf20Sopenharmony_ci drm->mode_config.funcs = &arcpgu_drm_modecfg_funcs; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(arcpgu_drm_ops); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int arcpgu_load(struct drm_device *drm) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(drm->dev); 488c2ecf20Sopenharmony_ci struct arcpgu_drm_private *arcpgu; 498c2ecf20Sopenharmony_ci struct device_node *encoder_node = NULL, *endpoint_node = NULL; 508c2ecf20Sopenharmony_ci struct resource *res; 518c2ecf20Sopenharmony_ci int ret; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci arcpgu = devm_kzalloc(&pdev->dev, sizeof(*arcpgu), GFP_KERNEL); 548c2ecf20Sopenharmony_ci if (arcpgu == NULL) 558c2ecf20Sopenharmony_ci return -ENOMEM; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci drm->dev_private = arcpgu; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci arcpgu->clk = devm_clk_get(drm->dev, "pxlclk"); 608c2ecf20Sopenharmony_ci if (IS_ERR(arcpgu->clk)) 618c2ecf20Sopenharmony_ci return PTR_ERR(arcpgu->clk); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci arcpgu_setup_mode_config(drm); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 668c2ecf20Sopenharmony_ci arcpgu->regs = devm_ioremap_resource(&pdev->dev, res); 678c2ecf20Sopenharmony_ci if (IS_ERR(arcpgu->regs)) 688c2ecf20Sopenharmony_ci return PTR_ERR(arcpgu->regs); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci dev_info(drm->dev, "arc_pgu ID: 0x%x\n", 718c2ecf20Sopenharmony_ci arc_pgu_read(arcpgu, ARCPGU_REG_ID)); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Get the optional framebuffer memory resource */ 748c2ecf20Sopenharmony_ci ret = of_reserved_mem_device_init(drm->dev); 758c2ecf20Sopenharmony_ci if (ret && ret != -ENODEV) 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32))) 798c2ecf20Sopenharmony_ci return -ENODEV; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (arc_pgu_setup_crtc(drm) < 0) 828c2ecf20Sopenharmony_ci return -ENODEV; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * There is only one output port inside each device. It is linked with 868c2ecf20Sopenharmony_ci * encoder endpoint. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci endpoint_node = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); 898c2ecf20Sopenharmony_ci if (endpoint_node) { 908c2ecf20Sopenharmony_ci encoder_node = of_graph_get_remote_port_parent(endpoint_node); 918c2ecf20Sopenharmony_ci of_node_put(endpoint_node); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (encoder_node) { 958c2ecf20Sopenharmony_ci ret = arcpgu_drm_hdmi_init(drm, encoder_node); 968c2ecf20Sopenharmony_ci of_node_put(encoder_node); 978c2ecf20Sopenharmony_ci if (ret < 0) 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci dev_info(drm->dev, "no encoder found. Assumed virtual LCD on simulation platform\n"); 1018c2ecf20Sopenharmony_ci ret = arcpgu_drm_sim_init(drm, NULL); 1028c2ecf20Sopenharmony_ci if (ret < 0) 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 1078c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(drm); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, drm); 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int arcpgu_unload(struct drm_device *drm) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 1168c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 1178c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1238c2ecf20Sopenharmony_cistatic int arcpgu_show_pxlclock(struct seq_file *m, void *arg) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *)m->private; 1268c2ecf20Sopenharmony_ci struct drm_device *drm = node->minor->dev; 1278c2ecf20Sopenharmony_ci struct arcpgu_drm_private *arcpgu = drm->dev_private; 1288c2ecf20Sopenharmony_ci unsigned long clkrate = clk_get_rate(arcpgu->clk); 1298c2ecf20Sopenharmony_ci unsigned long mode_clock = arcpgu->crtc.mode.crtc_clock * 1000; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci seq_printf(m, "hw : %lu\n", clkrate); 1328c2ecf20Sopenharmony_ci seq_printf(m, "mode: %lu\n", mode_clock); 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct drm_info_list arcpgu_debugfs_list[] = { 1378c2ecf20Sopenharmony_ci { "clocks", arcpgu_show_pxlclock, 0 }, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void arcpgu_debugfs_init(struct drm_minor *minor) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci drm_debugfs_create_files(arcpgu_debugfs_list, 1438c2ecf20Sopenharmony_ci ARRAY_SIZE(arcpgu_debugfs_list), 1448c2ecf20Sopenharmony_ci minor->debugfs_root, minor); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci#endif 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct drm_driver arcpgu_drm_driver = { 1498c2ecf20Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 1508c2ecf20Sopenharmony_ci .name = "arcpgu", 1518c2ecf20Sopenharmony_ci .desc = "ARC PGU Controller", 1528c2ecf20Sopenharmony_ci .date = "20160219", 1538c2ecf20Sopenharmony_ci .major = 1, 1548c2ecf20Sopenharmony_ci .minor = 0, 1558c2ecf20Sopenharmony_ci .patchlevel = 0, 1568c2ecf20Sopenharmony_ci .fops = &arcpgu_drm_ops, 1578c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS, 1588c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1598c2ecf20Sopenharmony_ci .debugfs_init = arcpgu_debugfs_init, 1608c2ecf20Sopenharmony_ci#endif 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int arcpgu_probe(struct platform_device *pdev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct drm_device *drm; 1668c2ecf20Sopenharmony_ci int ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci drm = drm_dev_alloc(&arcpgu_drm_driver, &pdev->dev); 1698c2ecf20Sopenharmony_ci if (IS_ERR(drm)) 1708c2ecf20Sopenharmony_ci return PTR_ERR(drm); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = arcpgu_load(drm); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci goto err_unref; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 1778c2ecf20Sopenharmony_ci if (ret) 1788c2ecf20Sopenharmony_ci goto err_unload; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 16); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cierr_unload: 1858c2ecf20Sopenharmony_ci arcpgu_unload(drm); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cierr_unref: 1888c2ecf20Sopenharmony_ci drm_dev_put(drm); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int arcpgu_remove(struct platform_device *pdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct drm_device *drm = platform_get_drvdata(pdev); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci drm_dev_unregister(drm); 1988c2ecf20Sopenharmony_ci arcpgu_unload(drm); 1998c2ecf20Sopenharmony_ci drm_dev_put(drm); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct of_device_id arcpgu_of_table[] = { 2058c2ecf20Sopenharmony_ci {.compatible = "snps,arcpgu"}, 2068c2ecf20Sopenharmony_ci {} 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arcpgu_of_table); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct platform_driver arcpgu_platform_driver = { 2128c2ecf20Sopenharmony_ci .probe = arcpgu_probe, 2138c2ecf20Sopenharmony_ci .remove = arcpgu_remove, 2148c2ecf20Sopenharmony_ci .driver = { 2158c2ecf20Sopenharmony_ci .name = "arcpgu", 2168c2ecf20Sopenharmony_ci .of_match_table = arcpgu_of_table, 2178c2ecf20Sopenharmony_ci }, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cimodule_platform_driver(arcpgu_platform_driver); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>"); 2238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARC PGU DRM driver"); 2248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 225