18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2016 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright 2016 ZTE Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 248c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "zx_drm_drv.h" 278c2ecf20Sopenharmony_ci#include "zx_vou.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs zx_drm_mode_config_funcs = { 308c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create, 318c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 328c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct drm_driver zx_drm_driver = { 388c2ecf20Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 398c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS, 408c2ecf20Sopenharmony_ci .fops = &zx_drm_fops, 418c2ecf20Sopenharmony_ci .name = "zx-vou", 428c2ecf20Sopenharmony_ci .desc = "ZTE VOU Controller DRM", 438c2ecf20Sopenharmony_ci .date = "20160811", 448c2ecf20Sopenharmony_ci .major = 1, 458c2ecf20Sopenharmony_ci .minor = 0, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int zx_drm_bind(struct device *dev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct drm_device *drm; 518c2ecf20Sopenharmony_ci int ret; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci drm = drm_dev_alloc(&zx_drm_driver, dev); 548c2ecf20Sopenharmony_ci if (IS_ERR(drm)) 558c2ecf20Sopenharmony_ci return PTR_ERR(drm); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci dev_set_drvdata(dev, drm); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci drm_mode_config_init(drm); 608c2ecf20Sopenharmony_ci drm->mode_config.min_width = 16; 618c2ecf20Sopenharmony_ci drm->mode_config.min_height = 16; 628c2ecf20Sopenharmony_ci drm->mode_config.max_width = 4096; 638c2ecf20Sopenharmony_ci drm->mode_config.max_height = 4096; 648c2ecf20Sopenharmony_ci drm->mode_config.funcs = &zx_drm_mode_config_funcs; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci ret = component_bind_all(dev, drm); 678c2ecf20Sopenharmony_ci if (ret) { 688c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to bind all components: %d\n", ret); 698c2ecf20Sopenharmony_ci goto out_unregister; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 738c2ecf20Sopenharmony_ci if (ret < 0) { 748c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init vblank: %d\n", ret); 758c2ecf20Sopenharmony_ci goto out_unbind; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * We will manage irq handler on our own. In this case, irq_enabled 808c2ecf20Sopenharmony_ci * need to be true for using vblank core support. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci drm->irq_enabled = true; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 858c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(drm); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 888c2ecf20Sopenharmony_ci if (ret) 898c2ecf20Sopenharmony_ci goto out_poll_fini; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 32); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciout_poll_fini: 968c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 978c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 988c2ecf20Sopenharmony_ciout_unbind: 998c2ecf20Sopenharmony_ci component_unbind_all(dev, drm); 1008c2ecf20Sopenharmony_ciout_unregister: 1018c2ecf20Sopenharmony_ci dev_set_drvdata(dev, NULL); 1028c2ecf20Sopenharmony_ci drm_dev_put(drm); 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void zx_drm_unbind(struct device *dev) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci drm_dev_unregister(drm); 1118c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 1128c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 1138c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 1148c2ecf20Sopenharmony_ci component_unbind_all(dev, drm); 1158c2ecf20Sopenharmony_ci dev_set_drvdata(dev, NULL); 1168c2ecf20Sopenharmony_ci drm_dev_put(drm); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const struct component_master_ops zx_drm_master_ops = { 1208c2ecf20Sopenharmony_ci .bind = zx_drm_bind, 1218c2ecf20Sopenharmony_ci .unbind = zx_drm_unbind, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int compare_of(struct device *dev, void *data) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci return dev->of_node == data; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int zx_drm_probe(struct platform_device *pdev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1328c2ecf20Sopenharmony_ci struct device_node *parent = dev->of_node; 1338c2ecf20Sopenharmony_ci struct device_node *child; 1348c2ecf20Sopenharmony_ci struct component_match *match = NULL; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = devm_of_platform_populate(dev); 1388c2ecf20Sopenharmony_ci if (ret) 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for_each_available_child_of_node(parent, child) 1428c2ecf20Sopenharmony_ci component_match_add(dev, &match, compare_of, child); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return component_master_add_with_match(dev, &zx_drm_master_ops, match); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int zx_drm_remove(struct platform_device *pdev) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci component_master_del(&pdev->dev, &zx_drm_master_ops); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct of_device_id zx_drm_of_match[] = { 1548c2ecf20Sopenharmony_ci { .compatible = "zte,zx296718-vou", }, 1558c2ecf20Sopenharmony_ci { /* end */ }, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zx_drm_of_match); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic struct platform_driver zx_drm_platform_driver = { 1608c2ecf20Sopenharmony_ci .probe = zx_drm_probe, 1618c2ecf20Sopenharmony_ci .remove = zx_drm_remove, 1628c2ecf20Sopenharmony_ci .driver = { 1638c2ecf20Sopenharmony_ci .name = "zx-drm", 1648c2ecf20Sopenharmony_ci .of_match_table = zx_drm_of_match, 1658c2ecf20Sopenharmony_ci }, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic struct platform_driver *drivers[] = { 1698c2ecf20Sopenharmony_ci &zx_crtc_driver, 1708c2ecf20Sopenharmony_ci &zx_hdmi_driver, 1718c2ecf20Sopenharmony_ci &zx_tvenc_driver, 1728c2ecf20Sopenharmony_ci &zx_vga_driver, 1738c2ecf20Sopenharmony_ci &zx_drm_platform_driver, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int zx_drm_init(void) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_cimodule_init(zx_drm_init); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void zx_drm_exit(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_cimodule_exit(zx_drm_exit); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); 1898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ZTE ZX VOU DRM driver"); 1908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 191