789f0013a5ba5587b2dea55c48beef7ebb962dd0
[openwrt/staging/xback.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 @@ -162,6 +162,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 @@ -655,6 +658,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 @@ -665,6 +673,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 @@ -462,6 +465,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 +};