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_irq.h> 17 #include <drm/drm_vblank.h> 18 19 #include "komeda_dev.h" 20 #include "komeda_framebuffer.h" 21 #include "komeda_kms.h" 22 23 DEFINE_DRM_GEM_CMA_FOPS(komeda_cma_fops); 24 25 static int komeda_gem_cma_dumb_create(struct drm_file *file, 26 struct drm_device *dev, 27 struct drm_mode_create_dumb *args) 28 { 29 u32 alignment = 16; /* TODO get alignment from dev */ 30 31 args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 32 alignment); 33 34 return drm_gem_cma_dumb_create_internal(file, dev, args); 35 } 36 37 static irqreturn_t komeda_kms_irq_handler(int irq, void *data) 38 { 39 struct drm_device *drm = data; 40 struct komeda_dev *mdev = drm->dev_private; 41 struct komeda_kms_dev *kms = to_kdev(drm); 42 struct komeda_events evts; 43 irqreturn_t status; 44 u32 i; 45 46 /* Call into the CHIP to recognize events */ 47 memset(&evts, 0, sizeof(evts)); 48 status = mdev->funcs->irq_handler(mdev, &evts); 49 50 /* Notify the crtc to handle the events */ 51 for (i = 0; i < kms->n_crtcs; i++) 52 komeda_crtc_handle_event(&kms->crtcs[i], &evts); 53 54 return status; 55 } 56 57 static struct drm_driver komeda_kms_driver = { 58 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | 59 DRIVER_PRIME | DRIVER_HAVE_IRQ, 60 .lastclose = drm_fb_helper_lastclose, 61 .irq_handler = komeda_kms_irq_handler, 62 .gem_free_object_unlocked = drm_gem_cma_free_object, 63 .gem_vm_ops = &drm_gem_cma_vm_ops, 64 .dumb_create = komeda_gem_cma_dumb_create, 65 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 66 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 67 .gem_prime_export = drm_gem_prime_export, 68 .gem_prime_import = drm_gem_prime_import, 69 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 70 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 71 .gem_prime_vmap = drm_gem_cma_prime_vmap, 72 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 73 .gem_prime_mmap = drm_gem_cma_prime_mmap, 74 .fops = &komeda_cma_fops, 75 .name = "komeda", 76 .desc = "Arm Komeda Display Processor driver", 77 .date = "20181101", 78 .major = 0, 79 .minor = 1, 80 }; 81 82 static void komeda_kms_commit_tail(struct drm_atomic_state *old_state) 83 { 84 struct drm_device *dev = old_state->dev; 85 86 drm_atomic_helper_commit_modeset_disables(dev, old_state); 87 88 drm_atomic_helper_commit_planes(dev, old_state, 0); 89 90 drm_atomic_helper_commit_modeset_enables(dev, old_state); 91 92 drm_atomic_helper_wait_for_flip_done(dev, old_state); 93 94 drm_atomic_helper_commit_hw_done(old_state); 95 96 drm_atomic_helper_cleanup_planes(dev, old_state); 97 } 98 99 static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = { 100 .atomic_commit_tail = komeda_kms_commit_tail, 101 }; 102 103 static const struct drm_mode_config_funcs komeda_mode_config_funcs = { 104 .fb_create = komeda_fb_create, 105 .atomic_check = drm_atomic_helper_check, 106 .atomic_commit = drm_atomic_helper_commit, 107 }; 108 109 static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms, 110 struct komeda_dev *mdev) 111 { 112 struct drm_mode_config *config = &kms->base.mode_config; 113 114 drm_mode_config_init(&kms->base); 115 116 komeda_kms_setup_crtcs(kms, mdev); 117 118 /* Get value from dev */ 119 config->min_width = 0; 120 config->min_height = 0; 121 config->max_width = 4096; 122 config->max_height = 4096; 123 config->allow_fb_modifiers = false; 124 125 config->funcs = &komeda_mode_config_funcs; 126 config->helper_private = &komeda_mode_config_helpers; 127 } 128 129 struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) 130 { 131 struct komeda_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL); 132 struct drm_device *drm; 133 int err; 134 135 if (!kms) 136 return ERR_PTR(-ENOMEM); 137 138 drm = &kms->base; 139 err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev); 140 if (err) 141 goto free_kms; 142 143 drm->dev_private = mdev; 144 145 komeda_kms_mode_config_init(kms, mdev); 146 147 err = komeda_kms_add_private_objs(kms, mdev); 148 if (err) 149 goto cleanup_mode_config; 150 151 err = komeda_kms_add_planes(kms, mdev); 152 if (err) 153 goto cleanup_mode_config; 154 155 err = drm_vblank_init(drm, kms->n_crtcs); 156 if (err) 157 goto cleanup_mode_config; 158 159 err = komeda_kms_add_crtcs(kms, mdev); 160 if (err) 161 goto cleanup_mode_config; 162 163 err = component_bind_all(mdev->dev, kms); 164 if (err) 165 goto cleanup_mode_config; 166 167 drm_mode_config_reset(drm); 168 169 err = drm_irq_install(drm, mdev->irq); 170 if (err) 171 goto cleanup_mode_config; 172 173 err = mdev->funcs->enable_irq(mdev); 174 if (err) 175 goto uninstall_irq; 176 177 err = drm_dev_register(drm, 0); 178 if (err) 179 goto uninstall_irq; 180 181 return kms; 182 183 uninstall_irq: 184 drm_irq_uninstall(drm); 185 cleanup_mode_config: 186 drm_mode_config_cleanup(drm); 187 free_kms: 188 kfree(kms); 189 return ERR_PTR(err); 190 } 191 192 void komeda_kms_detach(struct komeda_kms_dev *kms) 193 { 194 struct drm_device *drm = &kms->base; 195 struct komeda_dev *mdev = drm->dev_private; 196 197 mdev->funcs->disable_irq(mdev); 198 drm_dev_unregister(drm); 199 drm_irq_uninstall(drm); 200 component_unbind_all(mdev->dev, drm); 201 komeda_kms_cleanup_private_objs(mdev); 202 drm_mode_config_cleanup(drm); 203 drm->dev_private = NULL; 204 drm_dev_put(drm); 205 } 206