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/interrupt.h>
8
9 #include <drm/drm_atomic.h>
10 #include <drm/drm_atomic_helper.h>
11 #include <drm/drm_drv.h>
12 #include <drm/drm_fbdev_dma.h>
13 #include <drm/drm_gem_dma_helper.h>
14 #include <drm/drm_gem_framebuffer_helper.h>
15 #include <drm/drm_managed.h>
16 #include <drm/drm_probe_helper.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_DMA_FOPS(komeda_cma_fops);
24
komeda_gem_dma_dumb_create(struct drm_file * file,struct drm_device * dev,struct drm_mode_create_dumb * args)25 static int komeda_gem_dma_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_dma_dumb_create_internal(file, dev, args);
35 }
36
komeda_kms_irq_handler(int irq,void * data)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 komeda_print_events(&evts, drm);
51
52 /* Notify the crtc to handle the events */
53 for (i = 0; i < kms->n_crtcs; i++)
54 komeda_crtc_handle_event(&kms->crtcs[i], &evts);
55
56 return status;
57 }
58
59 static const struct drm_driver komeda_kms_driver = {
60 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
61 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(komeda_gem_dma_dumb_create),
62 DRM_FBDEV_DMA_DRIVER_OPS,
63 .fops = &komeda_cma_fops,
64 .name = "komeda",
65 .desc = "Arm Komeda Display Processor driver",
66 .date = "20181101",
67 .major = 0,
68 .minor = 1,
69 };
70
komeda_kms_atomic_commit_hw_done(struct drm_atomic_state * state)71 static void komeda_kms_atomic_commit_hw_done(struct drm_atomic_state *state)
72 {
73 struct drm_device *dev = state->dev;
74 struct komeda_kms_dev *kms = to_kdev(dev);
75 int i;
76
77 for (i = 0; i < kms->n_crtcs; i++) {
78 struct komeda_crtc *kcrtc = &kms->crtcs[i];
79
80 if (kcrtc->base.state->active) {
81 struct completion *flip_done = NULL;
82 if (kcrtc->base.state->event)
83 flip_done = kcrtc->base.state->event->base.completion;
84 komeda_crtc_flush_and_wait_for_flip_done(kcrtc, flip_done);
85 }
86 }
87 drm_atomic_helper_commit_hw_done(state);
88 }
89
komeda_kms_commit_tail(struct drm_atomic_state * old_state)90 static void komeda_kms_commit_tail(struct drm_atomic_state *old_state)
91 {
92 struct drm_device *dev = old_state->dev;
93 bool fence_cookie = dma_fence_begin_signalling();
94
95 drm_atomic_helper_commit_modeset_disables(dev, old_state);
96
97 drm_atomic_helper_commit_planes(dev, old_state,
98 DRM_PLANE_COMMIT_ACTIVE_ONLY);
99
100 drm_atomic_helper_commit_modeset_enables(dev, old_state);
101
102 komeda_kms_atomic_commit_hw_done(old_state);
103
104 drm_atomic_helper_wait_for_flip_done(dev, old_state);
105
106 dma_fence_end_signalling(fence_cookie);
107
108 drm_atomic_helper_cleanup_planes(dev, old_state);
109 }
110
111 static const struct drm_mode_config_helper_funcs komeda_mode_config_helpers = {
112 .atomic_commit_tail = komeda_kms_commit_tail,
113 };
114
komeda_plane_state_list_add(struct drm_plane_state * plane_st,struct list_head * zorder_list)115 static int komeda_plane_state_list_add(struct drm_plane_state *plane_st,
116 struct list_head *zorder_list)
117 {
118 struct komeda_plane_state *new = to_kplane_st(plane_st);
119 struct komeda_plane_state *node, *last;
120
121 last = list_empty(zorder_list) ?
122 NULL : list_last_entry(zorder_list, typeof(*last), zlist_node);
123
124 /* Considering the list sequence is zpos increasing, so if list is empty
125 * or the zpos of new node bigger than the last node in list, no need
126 * loop and just insert the new one to the tail of the list.
127 */
128 if (!last || (new->base.zpos > last->base.zpos)) {
129 list_add_tail(&new->zlist_node, zorder_list);
130 return 0;
131 }
132
133 /* Build the list by zpos increasing */
134 list_for_each_entry(node, zorder_list, zlist_node) {
135 if (new->base.zpos < node->base.zpos) {
136 list_add_tail(&new->zlist_node, &node->zlist_node);
137 break;
138 } else if (node->base.zpos == new->base.zpos) {
139 struct drm_plane *a = node->base.plane;
140 struct drm_plane *b = new->base.plane;
141
142 /* Komeda doesn't support setting a same zpos for
143 * different planes.
144 */
145 DRM_DEBUG_ATOMIC("PLANE: %s and PLANE: %s are configured same zpos: %d.\n",
146 a->name, b->name, node->base.zpos);
147 return -EINVAL;
148 }
149 }
150
151 return 0;
152 }
153
komeda_crtc_normalize_zpos(struct drm_crtc * crtc,struct drm_crtc_state * crtc_st)154 static int komeda_crtc_normalize_zpos(struct drm_crtc *crtc,
155 struct drm_crtc_state *crtc_st)
156 {
157 struct drm_atomic_state *state = crtc_st->state;
158 struct komeda_crtc *kcrtc = to_kcrtc(crtc);
159 struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st);
160 struct komeda_plane_state *kplane_st;
161 struct drm_plane_state *plane_st;
162 struct drm_plane *plane;
163 struct list_head zorder_list;
164 int order = 0, err;
165 u32 slave_zpos = 0;
166
167 DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n",
168 crtc->base.id, crtc->name);
169
170 INIT_LIST_HEAD(&zorder_list);
171
172 /* This loop also added all effected planes into the new state */
173 drm_for_each_plane_mask(plane, crtc->dev, crtc_st->plane_mask) {
174 plane_st = drm_atomic_get_plane_state(state, plane);
175 if (IS_ERR(plane_st))
176 return PTR_ERR(plane_st);
177
178 /* Build a list by zpos increasing */
179 err = komeda_plane_state_list_add(plane_st, &zorder_list);
180 if (err)
181 return err;
182 }
183
184 kcrtc_st->max_slave_zorder = 0;
185
186 list_for_each_entry(kplane_st, &zorder_list, zlist_node) {
187 plane_st = &kplane_st->base;
188 plane = plane_st->plane;
189
190 plane_st->normalized_zpos = order++;
191 /* When layer_split has been enabled, one plane will be handled
192 * by two separated komeda layers (left/right), which may needs
193 * two zorders.
194 * - zorder: for left_layer for left display part.
195 * - zorder + 1: will be reserved for right layer.
196 */
197 if (to_kplane_st(plane_st)->layer_split)
198 order++;
199
200 DRM_DEBUG_ATOMIC("[PLANE:%d:%s] zpos:%d, normalized zpos: %d\n",
201 plane->base.id, plane->name,
202 plane_st->zpos, plane_st->normalized_zpos);
203
204 /* calculate max slave zorder */
205 if (has_bit(drm_plane_index(plane), kcrtc->slave_planes)) {
206 slave_zpos = plane_st->normalized_zpos;
207 if (to_kplane_st(plane_st)->layer_split)
208 slave_zpos++;
209 kcrtc_st->max_slave_zorder =
210 max(slave_zpos, kcrtc_st->max_slave_zorder);
211 }
212 }
213
214 crtc_st->zpos_changed = true;
215
216 return 0;
217 }
218
komeda_kms_check(struct drm_device * dev,struct drm_atomic_state * state)219 static int komeda_kms_check(struct drm_device *dev,
220 struct drm_atomic_state *state)
221 {
222 struct drm_crtc *crtc;
223 struct drm_crtc_state *new_crtc_st;
224 int i, err;
225
226 err = drm_atomic_helper_check_modeset(dev, state);
227 if (err)
228 return err;
229
230 /* Komeda need to re-calculate resource assumption in every commit
231 * so need to add all affected_planes (even unchanged) to
232 * drm_atomic_state.
233 */
234 for_each_new_crtc_in_state(state, crtc, new_crtc_st, i) {
235 err = drm_atomic_add_affected_planes(state, crtc);
236 if (err)
237 return err;
238
239 err = komeda_crtc_normalize_zpos(crtc, new_crtc_st);
240 if (err)
241 return err;
242 }
243
244 err = drm_atomic_helper_check_planes(dev, state);
245 if (err)
246 return err;
247
248 return 0;
249 }
250
251 static const struct drm_mode_config_funcs komeda_mode_config_funcs = {
252 .fb_create = komeda_fb_create,
253 .atomic_check = komeda_kms_check,
254 .atomic_commit = drm_atomic_helper_commit,
255 };
256
komeda_kms_mode_config_init(struct komeda_kms_dev * kms,struct komeda_dev * mdev)257 static void komeda_kms_mode_config_init(struct komeda_kms_dev *kms,
258 struct komeda_dev *mdev)
259 {
260 struct drm_mode_config *config = &kms->base.mode_config;
261
262 drm_mode_config_init(&kms->base);
263
264 komeda_kms_setup_crtcs(kms, mdev);
265
266 /* Get value from dev */
267 config->min_width = 0;
268 config->min_height = 0;
269 config->max_width = 4096;
270 config->max_height = 4096;
271
272 config->funcs = &komeda_mode_config_funcs;
273 config->helper_private = &komeda_mode_config_helpers;
274 }
275
komeda_kms_attach(struct komeda_dev * mdev)276 struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev)
277 {
278 struct komeda_kms_dev *kms;
279 struct drm_device *drm;
280 int err;
281
282 kms = devm_drm_dev_alloc(mdev->dev, &komeda_kms_driver,
283 struct komeda_kms_dev, base);
284 if (IS_ERR(kms))
285 return kms;
286
287 drm = &kms->base;
288
289 drm->dev_private = mdev;
290
291 komeda_kms_mode_config_init(kms, mdev);
292
293 err = komeda_kms_add_private_objs(kms, mdev);
294 if (err)
295 goto cleanup_mode_config;
296
297 err = komeda_kms_add_planes(kms, mdev);
298 if (err)
299 goto cleanup_mode_config;
300
301 err = drm_vblank_init(drm, kms->n_crtcs);
302 if (err)
303 goto cleanup_mode_config;
304
305 err = komeda_kms_add_crtcs(kms, mdev);
306 if (err)
307 goto cleanup_mode_config;
308
309 err = komeda_kms_add_wb_connectors(kms, mdev);
310 if (err)
311 goto cleanup_mode_config;
312
313 drm_mode_config_reset(drm);
314
315 err = devm_request_irq(drm->dev, mdev->irq,
316 komeda_kms_irq_handler, IRQF_SHARED,
317 drm->driver->name, drm);
318 if (err)
319 goto cleanup_mode_config;
320
321 drm_kms_helper_poll_init(drm);
322
323 err = drm_dev_register(drm, 0);
324 if (err)
325 goto free_interrupts;
326
327 return kms;
328
329 free_interrupts:
330 drm_kms_helper_poll_fini(drm);
331 cleanup_mode_config:
332 drm_mode_config_cleanup(drm);
333 komeda_kms_cleanup_private_objs(kms);
334 drm->dev_private = NULL;
335 return ERR_PTR(err);
336 }
337
komeda_kms_detach(struct komeda_kms_dev * kms)338 void komeda_kms_detach(struct komeda_kms_dev *kms)
339 {
340 struct drm_device *drm = &kms->base;
341
342 drm_dev_unregister(drm);
343 drm_kms_helper_poll_fini(drm);
344 drm_atomic_helper_shutdown(drm);
345 drm_mode_config_cleanup(drm);
346 komeda_kms_cleanup_private_objs(kms);
347 drm->dev_private = NULL;
348 }
349
komeda_kms_shutdown(struct komeda_kms_dev * kms)350 void komeda_kms_shutdown(struct komeda_kms_dev *kms)
351 {
352 struct drm_device *drm = &kms->base;
353
354 drm_atomic_helper_shutdown(drm);
355 }
356