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