b4db1d7c6055191c23a7a32eb2160801a5887c1d
[oweals/openwrt.git] /
1 From 3ddc9f2dc424c085ca646e92b40d156bb5318295 Mon Sep 17 00:00:00 2001
2 From: Eric Anholt <eric@anholt.net>
3 Date: Wed, 14 Sep 2016 08:39:33 +0100
4 Subject: [PATCH] drm/vc4: Add a mode for using the closed firmware for
5  display.
6
7 Signed-off-by: Eric Anholt <eric@anholt.net>
8 ---
9  drivers/gpu/drm/vc4/Makefile           |   1 +
10  drivers/gpu/drm/vc4/vc4_crtc.c         |  17 +
11  drivers/gpu/drm/vc4/vc4_drv.c          |   1 +
12  drivers/gpu/drm/vc4/vc4_drv.h          |   7 +
13  drivers/gpu/drm/vc4/vc4_firmware_kms.c | 656 +++++++++++++++++++++++++
14  5 files changed, 682 insertions(+)
15  create mode 100644 drivers/gpu/drm/vc4/vc4_firmware_kms.c
16
17 --- a/drivers/gpu/drm/vc4/Makefile
18 +++ b/drivers/gpu/drm/vc4/Makefile
19 @@ -9,6 +9,7 @@ vc4-y := \
20         vc4_dpi.o \
21         vc4_dsi.o \
22         vc4_fence.o \
23 +       vc4_firmware_kms.o \
24         vc4_kms.o \
25         vc4_gem.o \
26         vc4_hdmi.o \
27 --- a/drivers/gpu/drm/vc4/vc4_crtc.c
28 +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
29 @@ -133,6 +133,9 @@ bool vc4_crtc_get_scanoutpos(struct drm_
30         int vblank_lines;
31         bool ret = false;
32  
33 +       if (vc4->firmware_kms)
34 +               return 0;
35 +
36         /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
37  
38         /* Get optional system timestamp before query. */
39 @@ -761,8 +764,15 @@ static void vc4_crtc_atomic_flush(struct
40  
41  static int vc4_enable_vblank(struct drm_crtc *crtc)
42  {
43 +       struct drm_device *dev = crtc->dev;
44 +       struct vc4_dev *vc4 = to_vc4_dev(dev);
45         struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
46  
47 +       if (vc4->firmware_kms) {
48 +               /* XXX: Can we mask the SMI interrupt? */
49 +               return 0;
50 +       }
51 +
52         CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
53  
54         return 0;
55 @@ -770,8 +780,15 @@ static int vc4_enable_vblank(struct drm_
56  
57  static void vc4_disable_vblank(struct drm_crtc *crtc)
58  {
59 +       struct drm_device *dev = crtc->dev;
60 +       struct vc4_dev *vc4 = to_vc4_dev(dev);
61         struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
62  
63 +       if (vc4->firmware_kms) {
64 +               /* XXX: Can we mask the SMI interrupt? */
65 +               return;
66 +       }
67 +
68         CRTC_WRITE(PV_INTEN, 0);
69  }
70  
71 --- a/drivers/gpu/drm/vc4/vc4_drv.c
72 +++ b/drivers/gpu/drm/vc4/vc4_drv.c
73 @@ -347,6 +347,7 @@ static struct platform_driver *const com
74         &vc4_txp_driver,
75         &vc4_hvs_driver,
76         &vc4_crtc_driver,
77 +       &vc4_firmware_kms_driver,
78         &vc4_v3d_driver,
79  };
80  
81 --- a/drivers/gpu/drm/vc4/vc4_drv.h
82 +++ b/drivers/gpu/drm/vc4/vc4_drv.h
83 @@ -67,6 +67,9 @@ struct vc4_perfmon {
84  struct vc4_dev {
85         struct drm_device *dev;
86  
87 +       bool firmware_kms;
88 +       struct rpi_firmware *firmware;
89 +
90         struct vc4_hdmi *hdmi;
91         struct vc4_hvs *hvs;
92         struct vc4_v3d *v3d;
93 @@ -719,6 +722,10 @@ int vc4_dsi_debugfs_regs(struct seq_file
94  /* vc4_fence.c */
95  extern const struct dma_fence_ops vc4_fence_ops;
96  
97 +/* vc4_firmware_kms.c */
98 +extern struct platform_driver vc4_firmware_kms_driver;
99 +void vc4_fkms_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
100 +
101  /* vc4_gem.c */
102  void vc4_gem_init(struct drm_device *dev);
103  void vc4_gem_destroy(struct drm_device *dev);
104 --- /dev/null
105 +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
106 @@ -0,0 +1,656 @@
107 +/*
108 + * Copyright (C) 2016 Broadcom
109 + *
110 + * This program is free software; you can redistribute it and/or modify
111 + * it under the terms of the GNU General Public License version 2 as
112 + * published by the Free Software Foundation.
113 + */
114 +
115 +/**
116 + * DOC: VC4 firmware KMS module.
117 + *
118 + * As a hack to get us from the current closed source driver world
119 + * toward a totally open stack, implement KMS on top of the Raspberry
120 + * Pi's firmware display stack.
121 + */
122 +
123 +#include "drm/drm_atomic_helper.h"
124 +#include "drm/drm_plane_helper.h"
125 +#include "drm/drm_crtc_helper.h"
126 +#include "linux/clk.h"
127 +#include "linux/debugfs.h"
128 +#include "drm/drm_fb_cma_helper.h"
129 +#include "linux/component.h"
130 +#include "linux/of_device.h"
131 +#include "vc4_drv.h"
132 +#include "vc4_regs.h"
133 +#include <soc/bcm2835/raspberrypi-firmware.h>
134 +
135 +/* The firmware delivers a vblank interrupt to us through the SMI
136 + * hardware, which has only this one register.
137 + */
138 +#define SMICS 0x0
139 +#define SMICS_INTERRUPTS (BIT(9) | BIT(10) | BIT(11))
140 +
141 +struct vc4_crtc {
142 +       struct drm_crtc base;
143 +       struct drm_encoder *encoder;
144 +       struct drm_connector *connector;
145 +       void __iomem *regs;
146 +
147 +       struct drm_pending_vblank_event *event;
148 +};
149 +
150 +static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
151 +{
152 +       return container_of(crtc, struct vc4_crtc, base);
153 +}
154 +
155 +struct vc4_fkms_encoder {
156 +       struct drm_encoder base;
157 +};
158 +
159 +static inline struct vc4_fkms_encoder *
160 +to_vc4_fkms_encoder(struct drm_encoder *encoder)
161 +{
162 +       return container_of(encoder, struct vc4_fkms_encoder, base);
163 +}
164 +
165 +/* VC4 FKMS connector KMS struct */
166 +struct vc4_fkms_connector {
167 +       struct drm_connector base;
168 +
169 +       /* Since the connector is attached to just the one encoder,
170 +        * this is the reference to it so we can do the best_encoder()
171 +        * hook.
172 +        */
173 +       struct drm_encoder *encoder;
174 +};
175 +
176 +static inline struct vc4_fkms_connector *
177 +to_vc4_fkms_connector(struct drm_connector *connector)
178 +{
179 +       return container_of(connector, struct vc4_fkms_connector, base);
180 +}
181 +
182 +/* Firmware's structure for making an FB mbox call. */
183 +struct fbinfo_s {
184 +       u32 xres, yres, xres_virtual, yres_virtual;
185 +       u32 pitch, bpp;
186 +       u32 xoffset, yoffset;
187 +       u32 base;
188 +       u32 screen_size;
189 +       u16 cmap[256];
190 +};
191 +
192 +struct vc4_fkms_plane {
193 +       struct drm_plane base;
194 +       struct fbinfo_s *fbinfo;
195 +       dma_addr_t fbinfo_bus_addr;
196 +       u32 pitch;
197 +};
198 +
199 +static inline struct vc4_fkms_plane *to_vc4_fkms_plane(struct drm_plane *plane)
200 +{
201 +       return (struct vc4_fkms_plane *)plane;
202 +}
203 +
204 +/* Turns the display on/off. */
205 +static int vc4_plane_set_primary_blank(struct drm_plane *plane, bool blank)
206 +{
207 +       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
208 +
209 +       u32 packet = blank;
210 +       return rpi_firmware_property(vc4->firmware,
211 +                                    RPI_FIRMWARE_FRAMEBUFFER_BLANK,
212 +                                    &packet, sizeof(packet));
213 +}
214 +
215 +static void vc4_primary_plane_atomic_update(struct drm_plane *plane,
216 +                                           struct drm_plane_state *old_state)
217 +{
218 +       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
219 +       struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
220 +       struct drm_plane_state *state = plane->state;
221 +       struct drm_framebuffer *fb = state->fb;
222 +       struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
223 +       volatile struct fbinfo_s *fbinfo = vc4_plane->fbinfo;
224 +       u32 bpp = 32;
225 +       int ret;
226 +
227 +       vc4_plane_set_primary_blank(plane, false);
228 +
229 +       fbinfo->xres = state->crtc_w;
230 +       fbinfo->yres = state->crtc_h;
231 +       fbinfo->xres_virtual = state->crtc_w;
232 +       fbinfo->yres_virtual = state->crtc_h;
233 +       fbinfo->bpp = bpp;
234 +       fbinfo->xoffset = state->crtc_x;
235 +       fbinfo->yoffset = state->crtc_y;
236 +       fbinfo->base = bo->paddr + fb->offsets[0];
237 +       fbinfo->pitch = fb->pitches[0];
238 +       /* A bug in the firmware makes it so that if the fb->base is
239 +        * set to nonzero, the configured pitch gets overwritten with
240 +        * the previous pitch.  So, to get the configured pitch
241 +        * recomputed, we have to make it allocate itself a new buffer
242 +        * in VC memory, first.
243 +        */
244 +       if (vc4_plane->pitch != fb->pitches[0]) {
245 +               u32 saved_base = fbinfo->base;
246 +               fbinfo->base = 0;
247 +
248 +               ret = rpi_firmware_transaction(vc4->firmware,
249 +                                              RPI_FIRMWARE_CHAN_FB,
250 +                                              vc4_plane->fbinfo_bus_addr);
251 +               fbinfo->base = saved_base;
252 +
253 +               vc4_plane->pitch = fbinfo->pitch;
254 +               WARN_ON_ONCE(vc4_plane->pitch != fb->pitches[0]);
255 +       }
256 +
257 +       ret = rpi_firmware_transaction(vc4->firmware,
258 +                                      RPI_FIRMWARE_CHAN_FB,
259 +                                      vc4_plane->fbinfo_bus_addr);
260 +       WARN_ON_ONCE(fbinfo->pitch != fb->pitches[0]);
261 +       WARN_ON_ONCE(fbinfo->base != bo->paddr + fb->offsets[0]);
262 +}
263 +
264 +static void vc4_primary_plane_atomic_disable(struct drm_plane *plane,
265 +                                            struct drm_plane_state *old_state)
266 +{
267 +       vc4_plane_set_primary_blank(plane, true);
268 +}
269 +
270 +static void vc4_cursor_plane_atomic_update(struct drm_plane *plane,
271 +                                          struct drm_plane_state *old_state)
272 +{
273 +       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
274 +       struct drm_plane_state *state = plane->state;
275 +       struct drm_framebuffer *fb = state->fb;
276 +       struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
277 +       int ret;
278 +       u32 packet_state[] = { true, state->crtc_x, state->crtc_y, 0 };
279 +       u32 packet_info[] = { state->crtc_w, state->crtc_h,
280 +                             0, /* unused */
281 +                             bo->paddr + fb->offsets[0],
282 +                             0, 0, /* hotx, hoty */};
283 +       WARN_ON_ONCE(fb->pitches[0] != state->crtc_w * 4);
284 +
285 +       ret = rpi_firmware_property(vc4->firmware,
286 +                                   RPI_FIRMWARE_SET_CURSOR_STATE,
287 +                                   &packet_state,
288 +                                   sizeof(packet_state));
289 +       if (ret || packet_state[0] != 0)
290 +               DRM_ERROR("Failed to set cursor state: 0x%08x\n", packet_state[0]);
291 +
292 +       ret = rpi_firmware_property(vc4->firmware,
293 +                                   RPI_FIRMWARE_SET_CURSOR_INFO,
294 +                                   &packet_info,
295 +                                   sizeof(packet_info));
296 +       if (ret || packet_info[0] != 0)
297 +               DRM_ERROR("Failed to set cursor info: 0x%08x\n", packet_info[0]);
298 +}
299 +
300 +static void vc4_cursor_plane_atomic_disable(struct drm_plane *plane,
301 +                                           struct drm_plane_state *old_state)
302 +{
303 +       struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
304 +       u32 packet_state[] = { false, 0, 0, 0 };
305 +       int ret;
306 +
307 +       ret = rpi_firmware_property(vc4->firmware,
308 +                                   RPI_FIRMWARE_SET_CURSOR_STATE,
309 +                                   &packet_state,
310 +                                   sizeof(packet_state));
311 +       if (ret || packet_state[0] != 0)
312 +               DRM_ERROR("Failed to set cursor state: 0x%08x\n", packet_state[0]);
313 +}
314 +
315 +static int vc4_plane_atomic_check(struct drm_plane *plane,
316 +                                 struct drm_plane_state *state)
317 +{
318 +       return 0;
319 +}
320 +
321 +static void vc4_plane_destroy(struct drm_plane *plane)
322 +{
323 +       drm_plane_helper_disable(plane);
324 +       drm_plane_cleanup(plane);
325 +}
326 +
327 +static const struct drm_plane_funcs vc4_plane_funcs = {
328 +       .update_plane = drm_atomic_helper_update_plane,
329 +       .disable_plane = drm_atomic_helper_disable_plane,
330 +       .destroy = vc4_plane_destroy,
331 +       .set_property = NULL,
332 +       .reset = drm_atomic_helper_plane_reset,
333 +       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
334 +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
335 +};
336 +
337 +static const struct drm_plane_helper_funcs vc4_primary_plane_helper_funcs = {
338 +       .prepare_fb = NULL,
339 +       .cleanup_fb = NULL,
340 +       .atomic_check = vc4_plane_atomic_check,
341 +       .atomic_update = vc4_primary_plane_atomic_update,
342 +       .atomic_disable = vc4_primary_plane_atomic_disable,
343 +};
344 +
345 +static const struct drm_plane_helper_funcs vc4_cursor_plane_helper_funcs = {
346 +       .prepare_fb = NULL,
347 +       .cleanup_fb = NULL,
348 +       .atomic_check = vc4_plane_atomic_check,
349 +       .atomic_update = vc4_cursor_plane_atomic_update,
350 +       .atomic_disable = vc4_cursor_plane_atomic_disable,
351 +};
352 +
353 +static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
354 +                                            enum drm_plane_type type)
355 +{
356 +       struct drm_plane *plane = NULL;
357 +       struct vc4_fkms_plane *vc4_plane;
358 +       u32 xrgb8888 = DRM_FORMAT_XRGB8888;
359 +       u32 argb8888 = DRM_FORMAT_ARGB8888;
360 +       int ret = 0;
361 +       bool primary = (type == DRM_PLANE_TYPE_PRIMARY);
362 +
363 +       vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
364 +                                GFP_KERNEL);
365 +       if (!vc4_plane) {
366 +               ret = -ENOMEM;
367 +               goto fail;
368 +       }
369 +
370 +       plane = &vc4_plane->base;
371 +       ret = drm_universal_plane_init(dev, plane, 0xff,
372 +                                      &vc4_plane_funcs,
373 +                                      primary ? &xrgb8888 : &argb8888, 1, NULL,
374 +                                      type, NULL);
375 +
376 +       if (type == DRM_PLANE_TYPE_PRIMARY) {
377 +               vc4_plane->fbinfo =
378 +                       dma_alloc_coherent(dev->dev,
379 +                                          sizeof(*vc4_plane->fbinfo),
380 +                                          &vc4_plane->fbinfo_bus_addr,
381 +                                          GFP_KERNEL);
382 +               memset(vc4_plane->fbinfo, 0, sizeof(*vc4_plane->fbinfo));
383 +
384 +               drm_plane_helper_add(plane, &vc4_primary_plane_helper_funcs);
385 +       } else {
386 +               drm_plane_helper_add(plane, &vc4_cursor_plane_helper_funcs);
387 +       }
388 +
389 +       return plane;
390 +fail:
391 +       if (plane)
392 +               vc4_plane_destroy(plane);
393 +
394 +       return ERR_PTR(ret);
395 +}
396 +
397 +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
398 +{
399 +       /* Everyting is handled in the planes. */
400 +}
401 +
402 +static void vc4_crtc_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
403 +{
404 +}
405 +
406 +static void vc4_crtc_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
407 +{
408 +}
409 +
410 +static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
411 +                                struct drm_crtc_state *state)
412 +{
413 +       return 0;
414 +}
415 +
416 +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
417 +                                 struct drm_crtc_state *old_state)
418 +{
419 +}
420 +
421 +static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
422 +{
423 +       struct drm_crtc *crtc = &vc4_crtc->base;
424 +       struct drm_device *dev = crtc->dev;
425 +       unsigned long flags;
426 +
427 +       spin_lock_irqsave(&dev->event_lock, flags);
428 +       if (vc4_crtc->event) {
429 +               drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
430 +               vc4_crtc->event = NULL;
431 +               drm_crtc_vblank_put(crtc);
432 +       }
433 +       spin_unlock_irqrestore(&dev->event_lock, flags);
434 +}
435 +
436 +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
437 +{
438 +       struct vc4_crtc *vc4_crtc = data;
439 +       u32 stat = readl(vc4_crtc->regs + SMICS);
440 +       irqreturn_t ret = IRQ_NONE;
441 +
442 +       if (stat & SMICS_INTERRUPTS) {
443 +               writel(0, vc4_crtc->regs + SMICS);
444 +               drm_crtc_handle_vblank(&vc4_crtc->base);
445 +               vc4_crtc_handle_page_flip(vc4_crtc);
446 +               ret = IRQ_HANDLED;
447 +       }
448 +
449 +       return ret;
450 +}
451 +
452 +static int vc4_page_flip(struct drm_crtc *crtc,
453 +                        struct drm_framebuffer *fb,
454 +                        struct drm_pending_vblank_event *event,
455 +                        uint32_t flags, struct drm_modeset_acquire_ctx *ctx)
456 +{
457 +       if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
458 +               DRM_ERROR("Async flips aren't allowed\n");
459 +               return -EINVAL;
460 +       }
461 +
462 +       return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx);
463 +}
464 +
465 +static const struct drm_crtc_funcs vc4_crtc_funcs = {
466 +       .set_config = drm_atomic_helper_set_config,
467 +       .destroy = drm_crtc_cleanup,
468 +       .page_flip = vc4_page_flip,
469 +       .set_property = NULL,
470 +       .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
471 +       .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
472 +       .reset = drm_atomic_helper_crtc_reset,
473 +       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
474 +       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
475 +};
476 +
477 +static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
478 +       .mode_set_nofb = vc4_crtc_mode_set_nofb,
479 +       .atomic_disable = vc4_crtc_disable,
480 +       .atomic_enable = vc4_crtc_enable,
481 +       .atomic_check = vc4_crtc_atomic_check,
482 +       .atomic_flush = vc4_crtc_atomic_flush,
483 +};
484 +
485 +/* Frees the page flip event when the DRM device is closed with the
486 + * event still outstanding.
487 + */
488 +void vc4_fkms_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
489 +{
490 +       struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
491 +       struct drm_device *dev = crtc->dev;
492 +       unsigned long flags;
493 +
494 +       spin_lock_irqsave(&dev->event_lock, flags);
495 +
496 +       if (vc4_crtc->event && vc4_crtc->event->base.file_priv == file) {
497 +               kfree(&vc4_crtc->event->base);
498 +               drm_crtc_vblank_put(crtc);
499 +               vc4_crtc->event = NULL;
500 +       }
501 +
502 +       spin_unlock_irqrestore(&dev->event_lock, flags);
503 +}
504 +
505 +static const struct of_device_id vc4_firmware_kms_dt_match[] = {
506 +       { .compatible = "raspberrypi,rpi-firmware-kms" },
507 +       {}
508 +};
509 +
510 +static enum drm_connector_status
511 +vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
512 +{
513 +       return connector_status_connected;
514 +}
515 +
516 +static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
517 +{
518 +       struct drm_device *dev = connector->dev;
519 +       struct vc4_dev *vc4 = to_vc4_dev(dev);
520 +       u32 wh[2] = {0, 0};
521 +       int ret;
522 +       struct drm_display_mode *mode;
523 +
524 +       ret = rpi_firmware_property(vc4->firmware,
525 +                                   RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT,
526 +                                   &wh, sizeof(wh));
527 +       if (ret) {
528 +               DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
529 +                         ret, wh[0], wh[1]);
530 +               return 0;
531 +       }
532 +
533 +       mode = drm_cvt_mode(dev, wh[0], wh[1], 60 /* vrefresh */,
534 +                           0, 0, false);
535 +       drm_mode_probed_add(connector, mode);
536 +
537 +       return 1;
538 +}
539 +
540 +static struct drm_encoder *
541 +vc4_fkms_connector_best_encoder(struct drm_connector *connector)
542 +{
543 +       struct vc4_fkms_connector *fkms_connector =
544 +               to_vc4_fkms_connector(connector);
545 +       return fkms_connector->encoder;
546 +}
547 +
548 +static void vc4_fkms_connector_destroy(struct drm_connector *connector)
549 +{
550 +       drm_connector_unregister(connector);
551 +       drm_connector_cleanup(connector);
552 +}
553 +
554 +static const struct drm_connector_funcs vc4_fkms_connector_funcs = {
555 +       .detect = vc4_fkms_connector_detect,
556 +       .fill_modes = drm_helper_probe_single_connector_modes,
557 +       .destroy = vc4_fkms_connector_destroy,
558 +       .reset = drm_atomic_helper_connector_reset,
559 +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
560 +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
561 +};
562 +
563 +static const struct drm_connector_helper_funcs vc4_fkms_connector_helper_funcs = {
564 +       .get_modes = vc4_fkms_connector_get_modes,
565 +       .best_encoder = vc4_fkms_connector_best_encoder,
566 +};
567 +
568 +static struct drm_connector *vc4_fkms_connector_init(struct drm_device *dev,
569 +                                                    struct drm_encoder *encoder)
570 +{
571 +       struct drm_connector *connector = NULL;
572 +       struct vc4_fkms_connector *fkms_connector;
573 +       int ret = 0;
574 +
575 +       fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
576 +                                     GFP_KERNEL);
577 +       if (!fkms_connector) {
578 +               ret = -ENOMEM;
579 +               goto fail;
580 +       }
581 +       connector = &fkms_connector->base;
582 +
583 +       fkms_connector->encoder = encoder;
584 +
585 +       drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
586 +                          DRM_MODE_CONNECTOR_HDMIA);
587 +       drm_connector_helper_add(connector, &vc4_fkms_connector_helper_funcs);
588 +
589 +       connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
590 +                            DRM_CONNECTOR_POLL_DISCONNECT);
591 +
592 +       connector->interlace_allowed = 0;
593 +       connector->doublescan_allowed = 0;
594 +
595 +       drm_mode_connector_attach_encoder(connector, encoder);
596 +
597 +       return connector;
598 +
599 + fail:
600 +       if (connector)
601 +               vc4_fkms_connector_destroy(connector);
602 +
603 +       return ERR_PTR(ret);
604 +}
605 +
606 +static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
607 +{
608 +       drm_encoder_cleanup(encoder);
609 +}
610 +
611 +static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
612 +       .destroy = vc4_fkms_encoder_destroy,
613 +};
614 +
615 +static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
616 +{
617 +}
618 +
619 +static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
620 +{
621 +}
622 +
623 +static const struct drm_encoder_helper_funcs vc4_fkms_encoder_helper_funcs = {
624 +       .enable = vc4_fkms_encoder_enable,
625 +       .disable = vc4_fkms_encoder_disable,
626 +};
627 +
628 +static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
629 +{
630 +       struct platform_device *pdev = to_platform_device(dev);
631 +       struct drm_device *drm = dev_get_drvdata(master);
632 +       struct vc4_dev *vc4 = to_vc4_dev(drm);
633 +       struct vc4_crtc *vc4_crtc;
634 +       struct vc4_fkms_encoder *vc4_encoder;
635 +       struct drm_crtc *crtc;
636 +       struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp;
637 +       struct device_node *firmware_node;
638 +       int ret;
639 +
640 +       vc4->firmware_kms = true;
641 +
642 +       vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
643 +       if (!vc4_crtc)
644 +               return -ENOMEM;
645 +       crtc = &vc4_crtc->base;
646 +
647 +       firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
648 +       vc4->firmware = rpi_firmware_get(firmware_node);
649 +       if (!vc4->firmware) {
650 +               DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n");
651 +               return -EPROBE_DEFER;
652 +       }
653 +       of_node_put(firmware_node);
654 +
655 +       /* Map the SMI interrupt reg */
656 +       vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
657 +       if (IS_ERR(vc4_crtc->regs))
658 +               return PTR_ERR(vc4_crtc->regs);
659 +
660 +       /* For now, we create just the primary and the legacy cursor
661 +        * planes.  We should be able to stack more planes on easily,
662 +        * but to do that we would need to compute the bandwidth
663 +        * requirement of the plane configuration, and reject ones
664 +        * that will take too much.
665 +        */
666 +       primary_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
667 +       if (IS_ERR(primary_plane)) {
668 +               dev_err(dev, "failed to construct primary plane\n");
669 +               ret = PTR_ERR(primary_plane);
670 +               goto err;
671 +       }
672 +
673 +       cursor_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
674 +       if (IS_ERR(cursor_plane)) {
675 +               dev_err(dev, "failed to construct cursor plane\n");
676 +               ret = PTR_ERR(cursor_plane);
677 +               goto err;
678 +       }
679 +
680 +       drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
681 +                                 &vc4_crtc_funcs, NULL);
682 +       drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
683 +       primary_plane->crtc = crtc;
684 +       cursor_plane->crtc = crtc;
685 +
686 +       vc4_encoder = devm_kzalloc(dev, sizeof(*vc4_encoder), GFP_KERNEL);
687 +       if (!vc4_encoder)
688 +               return -ENOMEM;
689 +       vc4_crtc->encoder = &vc4_encoder->base;
690 +       vc4_encoder->base.possible_crtcs |= drm_crtc_mask(crtc) ;
691 +       drm_encoder_init(drm, &vc4_encoder->base, &vc4_fkms_encoder_funcs,
692 +                        DRM_MODE_ENCODER_TMDS, NULL);
693 +       drm_encoder_helper_add(&vc4_encoder->base,
694 +                              &vc4_fkms_encoder_helper_funcs);
695 +
696 +       vc4_crtc->connector = vc4_fkms_connector_init(drm, &vc4_encoder->base);
697 +       if (IS_ERR(vc4_crtc->connector)) {
698 +               ret = PTR_ERR(vc4_crtc->connector);
699 +               goto err_destroy_encoder;
700 +       }
701 +
702 +       writel(0, vc4_crtc->regs + SMICS);
703 +       ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
704 +                              vc4_crtc_irq_handler, 0, "vc4 firmware kms",
705 +                              vc4_crtc);
706 +       if (ret)
707 +               goto err_destroy_connector;
708 +
709 +       platform_set_drvdata(pdev, vc4_crtc);
710 +
711 +       return 0;
712 +
713 +err_destroy_connector:
714 +       vc4_fkms_connector_destroy(vc4_crtc->connector);
715 +err_destroy_encoder:
716 +       vc4_fkms_encoder_destroy(vc4_crtc->encoder);
717 +       list_for_each_entry_safe(destroy_plane, temp,
718 +                                &drm->mode_config.plane_list, head) {
719 +               if (destroy_plane->possible_crtcs == 1 << drm_crtc_index(crtc))
720 +                   destroy_plane->funcs->destroy(destroy_plane);
721 +       }
722 +err:
723 +       return ret;
724 +}
725 +
726 +static void vc4_fkms_unbind(struct device *dev, struct device *master,
727 +                           void *data)
728 +{
729 +       struct platform_device *pdev = to_platform_device(dev);
730 +       struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
731 +
732 +       vc4_fkms_connector_destroy(vc4_crtc->connector);
733 +       vc4_fkms_encoder_destroy(vc4_crtc->encoder);
734 +       drm_crtc_cleanup(&vc4_crtc->base);
735 +
736 +       platform_set_drvdata(pdev, NULL);
737 +}
738 +
739 +static const struct component_ops vc4_fkms_ops = {
740 +       .bind   = vc4_fkms_bind,
741 +       .unbind = vc4_fkms_unbind,
742 +};
743 +
744 +static int vc4_fkms_probe(struct platform_device *pdev)
745 +{
746 +       return component_add(&pdev->dev, &vc4_fkms_ops);
747 +}
748 +
749 +static int vc4_fkms_remove(struct platform_device *pdev)
750 +{
751 +       component_del(&pdev->dev, &vc4_fkms_ops);
752 +       return 0;
753 +}
754 +
755 +struct platform_driver vc4_firmware_kms_driver = {
756 +       .probe = vc4_fkms_probe,
757 +       .remove = vc4_fkms_remove,
758 +       .driver = {
759 +               .name = "vc4_firmware_kms",
760 +               .of_match_table = vc4_firmware_kms_dt_match,
761 +       },
762 +};