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