1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. 4 * Author: James.Qian.Wang <james.qian.wang@arm.com> 5 * 6 */ 7 #include <linux/component.h> 8 #include <linux/interrupt.h> 9 10 #include <drm/drm_atomic.h> 11 #include <drm/drm_atomic_helper.h> 12 #include <drm/drm_drv.h> 13 #include <drm/drm_fb_helper.h> 14 #include <drm/drm_gem_cma_helper.h> 15 #include <drm/drm_gem_framebuffer_helper.h> 16 #include <drm/drm_vblank.h> 17 18 #include "komeda_dev.h" 19 #include "komeda_framebuffer.h" 20 #include "komeda_kms.h" 21 22 DEFINE_DRM_GEM_CMA_FOPS(komeda_cma_fops); 23 24 static int komeda_gem_cma_dumb_create(struct drm_file *file, 25 struct drm_device *dev, 26 struct drm_mode_create_dumb *args) 27 { 28 u32 alignment = 16; /* TODO get alignment from dev */ 29 30 args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 31 alignment); 32 33 return drm_gem_cma_dumb_create_internal(file, dev, args); 34 } 35 36 static struct drm_driver komeda_kms_driver = { 37 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | 38 DRIVER_PRIME, 39 .lastclose = drm_fb_helper_lastclose, 40 .gem_free_object_unlocked = drm_gem_cma_free_object, 41 .gem_vm_ops = &drm_gem_cma_vm_ops, 42 .dumb_create = komeda_gem_cma_dumb_create, 43 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 44 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 45 .gem_prime_export = drm_gem_prime_export, 46 .gem_prime_import = drm_gem_prime_import, 47 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 48 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 49 .gem_prime_vmap = drm_gem_cma_prime_vmap, 50 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 51 .gem_prime_mmap = drm_gem_cma_prime_mmap, 52 .fops = &komeda_cma_fops, 53 .name = "komeda", 54 .desc = "Arm Komeda Display Processor driver", 55 .date = "20181101", 56 .major = 0, 57 .minor = 1, 58 }; 59 60 static void komeda_kms_commit_tail(struct drm_atomic_state *old_state) 61 { 62 struct drm_device *dev = old_state->dev; 63 64 drm_atomic_helper_commit_modeset_disables(dev, old_state); 65 66 drm_atomic_helper_commit_planes(dev, old_state, 0); 67 68 drm_atomic_helper_commit_modeset_enables(dev, old_state); 69 70 drm_atomic_helper_wait_for_flip_done(dev, old_state); 71 72 drm_atomic_helper_commit_hw_done(old_state); 73 74 drm_atomic_helper_cleanup_planes(dev, old_state); 75 } 76 77 static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = { 78 .atomic_commit_tail = komeda_kms_commit_tail, 79 }; 80 81 static const struct drm_mode_config_funcs komeda_mode_config_funcs = { 82 .fb_create = komeda_fb_create, 83 .atomic_check = drm_atomic_helper_check, 84 .atomic_commit = drm_atomic_helper_commit, 85 }; 86 87 static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms, 88 struct komeda_dev *mdev) 89 { 90 struct drm_mode_config *config = &kms->base.mode_config; 91 92 drm_mode_config_init(&kms->base); 93 94 komeda_kms_setup_crtcs(kms, mdev); 95 96 /* Get value from dev */ 97 config->min_width = 0; 98 config->min_height = 0; 99 config->max_width = 4096; 100 config->max_height = 4096; 101 config->allow_fb_modifiers = false; 102 103 config->funcs = &komeda_mode_config_funcs; 104 config->helper_private = &komeda_mode_config_helpers; 105 } 106 107 struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) 108 { 109 struct komeda_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL); 110 struct drm_device *drm; 111 int err; 112 113 if (!kms) 114 return ERR_PTR(-ENOMEM); 115 116 drm = &kms->base; 117 err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev); 118 if (err) 119 goto free_kms; 120 121 drm->dev_private = mdev; 122 123 komeda_kms_mode_config_init(kms, mdev); 124 125 err = komeda_kms_add_private_objs(kms, mdev); 126 if (err) 127 goto cleanup_mode_config; 128 129 err = komeda_kms_add_planes(kms, mdev); 130 if (err) 131 goto cleanup_mode_config; 132 133 err = drm_vblank_init(drm, kms->n_crtcs); 134 if (err) 135 goto cleanup_mode_config; 136 137 err = komeda_kms_add_crtcs(kms, mdev); 138 if (err) 139 goto cleanup_mode_config; 140 141 err = component_bind_all(mdev->dev, kms); 142 if (err) 143 goto cleanup_mode_config; 144 145 drm_mode_config_reset(drm); 146 147 err = drm_dev_register(drm, 0); 148 if (err) 149 goto cleanup_mode_config; 150 151 return kms; 152 153 cleanup_mode_config: 154 drm_mode_config_cleanup(drm); 155 free_kms: 156 kfree(kms); 157 return ERR_PTR(err); 158 } 159 160 void komeda_kms_detach(struct komeda_kms_dev *kms) 161 { 162 struct drm_device *drm = &kms->base; 163 struct komeda_dev *mdev = drm->dev_private; 164 165 drm_dev_unregister(drm); 166 component_unbind_all(mdev->dev, drm); 167 komeda_kms_cleanup_private_objs(mdev); 168 drm_mode_config_cleanup(drm); 169 drm->dev_private = NULL; 170 drm_dev_put(drm); 171 } 172