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 	struct komeda_dev *mdev = dev->dev_private;
30 	u32 pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
31 
32 	args->pitch = ALIGN(pitch, mdev->chip.bus_width);
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