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