18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2019 NXP. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 78c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 88c2ecf20Sopenharmony_ci#include <drm/drm_bridge_connector.h> 98c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 108c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "dcss-dev.h" 188c2ecf20Sopenharmony_ci#include "dcss-kms.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(dcss_cma_fops); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs dcss_drm_mode_config_funcs = { 238c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create, 248c2ecf20Sopenharmony_ci .output_poll_changed = drm_fb_helper_output_poll_changed, 258c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 268c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic struct drm_driver dcss_kms_driver = { 308c2ecf20Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 318c2ecf20Sopenharmony_ci .gem_free_object_unlocked = drm_gem_cma_free_object, 328c2ecf20Sopenharmony_ci .gem_vm_ops = &drm_gem_cma_vm_ops, 338c2ecf20Sopenharmony_ci .dumb_create = drm_gem_cma_dumb_create, 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 368c2ecf20Sopenharmony_ci .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 378c2ecf20Sopenharmony_ci .gem_prime_import = drm_gem_prime_import, 388c2ecf20Sopenharmony_ci .gem_prime_export = drm_gem_prime_export, 398c2ecf20Sopenharmony_ci .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 408c2ecf20Sopenharmony_ci .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 418c2ecf20Sopenharmony_ci .gem_prime_vmap = drm_gem_cma_prime_vmap, 428c2ecf20Sopenharmony_ci .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 438c2ecf20Sopenharmony_ci .gem_prime_mmap = drm_gem_cma_prime_mmap, 448c2ecf20Sopenharmony_ci .fops = &dcss_cma_fops, 458c2ecf20Sopenharmony_ci .name = "imx-dcss", 468c2ecf20Sopenharmony_ci .desc = "i.MX8MQ Display Subsystem", 478c2ecf20Sopenharmony_ci .date = "20190917", 488c2ecf20Sopenharmony_ci .major = 1, 498c2ecf20Sopenharmony_ci .minor = 0, 508c2ecf20Sopenharmony_ci .patchlevel = 0, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const struct drm_mode_config_helper_funcs dcss_mode_config_helpers = { 548c2ecf20Sopenharmony_ci .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void dcss_kms_mode_config_init(struct dcss_kms_dev *kms) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct drm_mode_config *config = &kms->base.mode_config; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci drm_mode_config_init(&kms->base); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci config->min_width = 1; 648c2ecf20Sopenharmony_ci config->min_height = 1; 658c2ecf20Sopenharmony_ci config->max_width = 4096; 668c2ecf20Sopenharmony_ci config->max_height = 4096; 678c2ecf20Sopenharmony_ci config->allow_fb_modifiers = true; 688c2ecf20Sopenharmony_ci config->normalize_zpos = true; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci config->funcs = &dcss_drm_mode_config_funcs; 718c2ecf20Sopenharmony_ci config->helper_private = &dcss_mode_config_helpers; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs dcss_kms_simple_encoder_funcs = { 758c2ecf20Sopenharmony_ci .destroy = drm_encoder_cleanup, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct drm_device *ddev = &kms->base; 818c2ecf20Sopenharmony_ci struct drm_encoder *encoder = &kms->encoder; 828c2ecf20Sopenharmony_ci struct drm_crtc *crtc = (struct drm_crtc *)&kms->crtc; 838c2ecf20Sopenharmony_ci struct drm_panel *panel; 848c2ecf20Sopenharmony_ci struct drm_bridge *bridge; 858c2ecf20Sopenharmony_ci int ret; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0, 888c2ecf20Sopenharmony_ci &panel, &bridge); 898c2ecf20Sopenharmony_ci if (ret) 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (!bridge) { 938c2ecf20Sopenharmony_ci dev_err(ddev->dev, "No bridge found %d.\n", ret); 948c2ecf20Sopenharmony_ci return -ENODEV; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci encoder->possible_crtcs = drm_crtc_mask(crtc); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = drm_encoder_init(&kms->base, encoder, 1008c2ecf20Sopenharmony_ci &dcss_kms_simple_encoder_funcs, 1018c2ecf20Sopenharmony_ci DRM_MODE_ENCODER_NONE, NULL); 1028c2ecf20Sopenharmony_ci if (ret) { 1038c2ecf20Sopenharmony_ci dev_err(ddev->dev, "Failed initializing encoder %d.\n", ret); 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = drm_bridge_attach(encoder, bridge, NULL, 1088c2ecf20Sopenharmony_ci DRM_BRIDGE_ATTACH_NO_CONNECTOR); 1098c2ecf20Sopenharmony_ci if (ret < 0) { 1108c2ecf20Sopenharmony_ci dev_err(ddev->dev, "Unable to attach bridge %pOF\n", 1118c2ecf20Sopenharmony_ci bridge->of_node); 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci kms->connector = drm_bridge_connector_init(ddev, encoder); 1168c2ecf20Sopenharmony_ci if (IS_ERR(kms->connector)) { 1178c2ecf20Sopenharmony_ci dev_err(ddev->dev, "Unable to create bridge connector.\n"); 1188c2ecf20Sopenharmony_ci return PTR_ERR(kms->connector); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci drm_connector_attach_encoder(kms->connector, encoder); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct dcss_kms_dev *kms; 1298c2ecf20Sopenharmony_ci struct drm_device *drm; 1308c2ecf20Sopenharmony_ci struct dcss_crtc *crtc; 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci kms = devm_drm_dev_alloc(dcss->dev, &dcss_kms_driver, 1348c2ecf20Sopenharmony_ci struct dcss_kms_dev, base); 1358c2ecf20Sopenharmony_ci if (IS_ERR(kms)) 1368c2ecf20Sopenharmony_ci return kms; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci drm = &kms->base; 1398c2ecf20Sopenharmony_ci crtc = &kms->crtc; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci drm->dev_private = dcss; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci dcss_kms_mode_config_init(kms); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ret = drm_vblank_init(drm, 1); 1468c2ecf20Sopenharmony_ci if (ret) 1478c2ecf20Sopenharmony_ci goto cleanup_mode_config; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci drm->irq_enabled = true; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = dcss_kms_bridge_connector_init(kms); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci goto cleanup_mode_config; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ret = dcss_crtc_init(crtc, drm); 1568c2ecf20Sopenharmony_ci if (ret) 1578c2ecf20Sopenharmony_ci goto cleanup_mode_config; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci drm_kms_helper_poll_init(drm); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci drm_bridge_connector_enable_hpd(kms->connector); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci goto cleanup_crtc; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 32); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return kms; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cicleanup_crtc: 1748c2ecf20Sopenharmony_ci drm_bridge_connector_disable_hpd(kms->connector); 1758c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 1768c2ecf20Sopenharmony_ci dcss_crtc_deinit(crtc, drm); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cicleanup_mode_config: 1798c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 1808c2ecf20Sopenharmony_ci drm->dev_private = NULL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_civoid dcss_kms_detach(struct dcss_kms_dev *kms) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct drm_device *drm = &kms->base; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci drm_dev_unregister(drm); 1908c2ecf20Sopenharmony_ci drm_bridge_connector_disable_hpd(kms->connector); 1918c2ecf20Sopenharmony_ci drm_kms_helper_poll_fini(drm); 1928c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 1938c2ecf20Sopenharmony_ci drm_crtc_vblank_off(&kms->crtc.base); 1948c2ecf20Sopenharmony_ci drm->irq_enabled = false; 1958c2ecf20Sopenharmony_ci drm_mode_config_cleanup(drm); 1968c2ecf20Sopenharmony_ci dcss_crtc_deinit(&kms->crtc, drm); 1978c2ecf20Sopenharmony_ci drm->dev_private = NULL; 1988c2ecf20Sopenharmony_ci} 199