1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2019 NXP. 4 */ 5 6 #include <drm/drm_atomic.h> 7 #include <drm/drm_atomic_helper.h> 8 #include <drm/drm_bridge_connector.h> 9 #include <drm/drm_drv.h> 10 #include <drm/drm_fb_helper.h> 11 #include <drm/drm_gem_cma_helper.h> 12 #include <drm/drm_gem_framebuffer_helper.h> 13 #include <drm/drm_of.h> 14 #include <drm/drm_probe_helper.h> 15 #include <drm/drm_vblank.h> 16 17 #include "dcss-dev.h" 18 #include "dcss-kms.h" 19 20 DEFINE_DRM_GEM_CMA_FOPS(dcss_cma_fops); 21 22 static const struct drm_mode_config_funcs dcss_drm_mode_config_funcs = { 23 .fb_create = drm_gem_fb_create, 24 .output_poll_changed = drm_fb_helper_output_poll_changed, 25 .atomic_check = drm_atomic_helper_check, 26 .atomic_commit = drm_atomic_helper_commit, 27 }; 28 29 static struct drm_driver dcss_kms_driver = { 30 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 31 .gem_free_object_unlocked = drm_gem_cma_free_object, 32 .gem_vm_ops = &drm_gem_cma_vm_ops, 33 .dumb_create = drm_gem_cma_dumb_create, 34 35 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 36 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 37 .gem_prime_import = drm_gem_prime_import, 38 .gem_prime_export = drm_gem_prime_export, 39 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 40 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 41 .gem_prime_vmap = drm_gem_cma_prime_vmap, 42 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 43 .gem_prime_mmap = drm_gem_cma_prime_mmap, 44 .fops = &dcss_cma_fops, 45 .name = "imx-dcss", 46 .desc = "i.MX8MQ Display Subsystem", 47 .date = "20190917", 48 .major = 1, 49 .minor = 0, 50 .patchlevel = 0, 51 }; 52 53 static const struct drm_mode_config_helper_funcs dcss_mode_config_helpers = { 54 .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 55 }; 56 57 static void dcss_kms_mode_config_init(struct dcss_kms_dev *kms) 58 { 59 struct drm_mode_config *config = &kms->base.mode_config; 60 61 drm_mode_config_init(&kms->base); 62 63 config->min_width = 1; 64 config->min_height = 1; 65 config->max_width = 4096; 66 config->max_height = 4096; 67 config->allow_fb_modifiers = true; 68 config->normalize_zpos = true; 69 70 config->funcs = &dcss_drm_mode_config_funcs; 71 config->helper_private = &dcss_mode_config_helpers; 72 } 73 74 static const struct drm_encoder_funcs dcss_kms_simple_encoder_funcs = { 75 .destroy = drm_encoder_cleanup, 76 }; 77 78 static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms) 79 { 80 struct drm_device *ddev = &kms->base; 81 struct drm_encoder *encoder = &kms->encoder; 82 struct drm_crtc *crtc = (struct drm_crtc *)&kms->crtc; 83 struct drm_panel *panel; 84 struct drm_bridge *bridge; 85 int ret; 86 87 ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0, 88 &panel, &bridge); 89 if (ret) 90 return ret; 91 92 if (!bridge) { 93 dev_err(ddev->dev, "No bridge found %d.\n", ret); 94 return -ENODEV; 95 } 96 97 encoder->possible_crtcs = drm_crtc_mask(crtc); 98 99 ret = drm_encoder_init(&kms->base, encoder, 100 &dcss_kms_simple_encoder_funcs, 101 DRM_MODE_ENCODER_NONE, NULL); 102 if (ret) { 103 dev_err(ddev->dev, "Failed initializing encoder %d.\n", ret); 104 return ret; 105 } 106 107 ret = drm_bridge_attach(encoder, bridge, NULL, 108 DRM_BRIDGE_ATTACH_NO_CONNECTOR); 109 if (ret < 0) { 110 dev_err(ddev->dev, "Unable to attach bridge %pOF\n", 111 bridge->of_node); 112 return ret; 113 } 114 115 kms->connector = drm_bridge_connector_init(ddev, encoder); 116 if (IS_ERR(kms->connector)) { 117 dev_err(ddev->dev, "Unable to create bridge connector.\n"); 118 return PTR_ERR(kms->connector); 119 } 120 121 drm_connector_attach_encoder(kms->connector, encoder); 122 123 return 0; 124 } 125 126 struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) 127 { 128 struct dcss_kms_dev *kms; 129 struct drm_device *drm; 130 struct dcss_crtc *crtc; 131 int ret; 132 133 kms = devm_drm_dev_alloc(dcss->dev, &dcss_kms_driver, 134 struct dcss_kms_dev, base); 135 if (IS_ERR(kms)) 136 return kms; 137 138 drm = &kms->base; 139 crtc = &kms->crtc; 140 141 drm->dev_private = dcss; 142 143 dcss_kms_mode_config_init(kms); 144 145 ret = drm_vblank_init(drm, 1); 146 if (ret) 147 goto cleanup_mode_config; 148 149 drm->irq_enabled = true; 150 151 ret = dcss_kms_bridge_connector_init(kms); 152 if (ret) 153 goto cleanup_mode_config; 154 155 ret = dcss_crtc_init(crtc, drm); 156 if (ret) 157 goto cleanup_mode_config; 158 159 drm_mode_config_reset(drm); 160 161 drm_kms_helper_poll_init(drm); 162 163 drm_bridge_connector_enable_hpd(kms->connector); 164 165 ret = drm_dev_register(drm, 0); 166 if (ret) 167 goto cleanup_crtc; 168 169 drm_fbdev_generic_setup(drm, 32); 170 171 return kms; 172 173 cleanup_crtc: 174 drm_bridge_connector_disable_hpd(kms->connector); 175 drm_kms_helper_poll_fini(drm); 176 dcss_crtc_deinit(crtc, drm); 177 178 cleanup_mode_config: 179 drm_mode_config_cleanup(drm); 180 drm->dev_private = NULL; 181 182 return ERR_PTR(ret); 183 } 184 185 void dcss_kms_detach(struct dcss_kms_dev *kms) 186 { 187 struct drm_device *drm = &kms->base; 188 189 drm_dev_unregister(drm); 190 drm_bridge_connector_disable_hpd(kms->connector); 191 drm_kms_helper_poll_fini(drm); 192 drm_atomic_helper_shutdown(drm); 193 drm_crtc_vblank_off(&kms->crtc.base); 194 drm->irq_enabled = false; 195 drm_mode_config_cleanup(drm); 196 dcss_crtc_deinit(&kms->crtc, drm); 197 drm->dev_private = NULL; 198 } 199