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 int komeda_kms_check(struct drm_device *dev, 104 struct drm_atomic_state *state) 105 { 106 struct drm_crtc *crtc; 107 struct drm_crtc_state *old_crtc_st, *new_crtc_st; 108 int i, err; 109 110 err = drm_atomic_helper_check_modeset(dev, state); 111 if (err) 112 return err; 113 114 /* komeda need to re-calculate resource assumption in every commit 115 * so need to add all affected_planes (even unchanged) to 116 * drm_atomic_state. 117 */ 118 for_each_oldnew_crtc_in_state(state, crtc, old_crtc_st, new_crtc_st, i) { 119 err = drm_atomic_add_affected_planes(state, crtc); 120 if (err) 121 return err; 122 } 123 124 err = drm_atomic_helper_check_planes(dev, state); 125 if (err) 126 return err; 127 128 return 0; 129 } 130 131 static const struct drm_mode_config_funcs komeda_mode_config_funcs = { 132 .fb_create = komeda_fb_create, 133 .atomic_check = komeda_kms_check, 134 .atomic_commit = drm_atomic_helper_commit, 135 }; 136 137 static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms, 138 struct komeda_dev *mdev) 139 { 140 struct drm_mode_config *config = &kms->base.mode_config; 141 142 drm_mode_config_init(&kms->base); 143 144 komeda_kms_setup_crtcs(kms, mdev); 145 146 /* Get value from dev */ 147 config->min_width = 0; 148 config->min_height = 0; 149 config->max_width = 4096; 150 config->max_height = 4096; 151 config->allow_fb_modifiers = false; 152 153 config->funcs = &komeda_mode_config_funcs; 154 config->helper_private = &komeda_mode_config_helpers; 155 } 156 157 struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) 158 { 159 struct komeda_kms_dev *kms = kzalloc(sizeof(*kms), GFP_KERNEL); 160 struct drm_device *drm; 161 int err; 162 163 if (!kms) 164 return ERR_PTR(-ENOMEM); 165 166 drm = &kms->base; 167 err = drm_dev_init(drm, &komeda_kms_driver, mdev->dev); 168 if (err) 169 goto free_kms; 170 171 drm->dev_private = mdev; 172 173 komeda_kms_mode_config_init(kms, mdev); 174 175 err = komeda_kms_add_private_objs(kms, mdev); 176 if (err) 177 goto cleanup_mode_config; 178 179 err = komeda_kms_add_planes(kms, mdev); 180 if (err) 181 goto cleanup_mode_config; 182 183 err = drm_vblank_init(drm, kms->n_crtcs); 184 if (err) 185 goto cleanup_mode_config; 186 187 err = komeda_kms_add_crtcs(kms, mdev); 188 if (err) 189 goto cleanup_mode_config; 190 191 err = component_bind_all(mdev->dev, kms); 192 if (err) 193 goto cleanup_mode_config; 194 195 drm_mode_config_reset(drm); 196 197 err = drm_irq_install(drm, mdev->irq); 198 if (err) 199 goto cleanup_mode_config; 200 201 err = mdev->funcs->enable_irq(mdev); 202 if (err) 203 goto uninstall_irq; 204 205 err = drm_dev_register(drm, 0); 206 if (err) 207 goto uninstall_irq; 208 209 return kms; 210 211 uninstall_irq: 212 drm_irq_uninstall(drm); 213 cleanup_mode_config: 214 drm_mode_config_cleanup(drm); 215 komeda_kms_cleanup_private_objs(kms); 216 free_kms: 217 kfree(kms); 218 return ERR_PTR(err); 219 } 220 221 void komeda_kms_detach(struct komeda_kms_dev *kms) 222 { 223 struct drm_device *drm = &kms->base; 224 struct komeda_dev *mdev = drm->dev_private; 225 226 mdev->funcs->disable_irq(mdev); 227 drm_dev_unregister(drm); 228 drm_irq_uninstall(drm); 229 component_unbind_all(mdev->dev, drm); 230 komeda_kms_cleanup_private_objs(kms); 231 drm_mode_config_cleanup(drm); 232 drm->dev_private = NULL; 233 drm_dev_put(drm); 234 } 235