d09becc815621a6c47bed238aab9cd2eb81b5c6c
[openwrt/staging/robimarko.git] /
1 From dbe5aadb7ededc77902bad876421eb730bd1daeb 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 @@ -97,6 +97,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 @@ -764,8 +767,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 @@ -773,8 +783,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 @@ -345,6 +345,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 @@ -71,6 +71,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 @@ -790,6 +793,10 @@ extern struct platform_driver vc4_dsi_dr
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 +};