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
7 Signed-off-by: Eric Anholt <eric@anholt.net>
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
17 --- a/drivers/gpu/drm/vc4/Makefile
18 +++ b/drivers/gpu/drm/vc4/Makefile
19 @@ -8,6 +8,7 @@ vc4-y := \
23 + vc4_firmware_kms.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
33 + if (vc4->firmware_kms)
36 /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
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];
43 + if (vc4->firmware_kms) {
44 + /* XXX: Can we mask the SMI interrupt? */
48 CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
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];
55 + if (vc4->firmware_kms) {
56 + /* XXX: Can we mask the SMI interrupt? */
60 CRTC_WRITE(PV_INTEN, 0);
63 --- a/drivers/gpu/drm/vc4/vc4_drv.c
64 +++ b/drivers/gpu/drm/vc4/vc4_drv.c
65 @@ -295,6 +295,7 @@ static struct platform_driver *const com
69 + &vc4_firmware_kms_driver,
73 --- a/drivers/gpu/drm/vc4/vc4_drv.h
74 +++ b/drivers/gpu/drm/vc4/vc4_drv.h
77 struct drm_device *dev;
80 + struct rpi_firmware *firmware;
82 struct vc4_hdmi *hdmi;
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);
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);
94 void vc4_gem_init(struct drm_device *dev);
95 void vc4_gem_destroy(struct drm_device *dev);
97 +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
100 + * Copyright (C) 2016 Broadcom
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.
108 + * DOC: VC4 firmware KMS module.
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.
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>
128 +/* The firmware delivers a vblank interrupt to us through the SMI
129 + * hardware, which has only this one register.
132 +#define SMICS_INTERRUPTS (BIT(9) | BIT(10) | BIT(11))
135 + struct drm_crtc base;
136 + struct drm_encoder *encoder;
137 + struct drm_connector *connector;
138 + void __iomem *regs;
140 + struct drm_pending_vblank_event *event;
143 +static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
145 + return container_of(crtc, struct vc4_crtc, base);
148 +struct vc4_fkms_encoder {
149 + struct drm_encoder base;
152 +static inline struct vc4_fkms_encoder *
153 +to_vc4_fkms_encoder(struct drm_encoder *encoder)
155 + return container_of(encoder, struct vc4_fkms_encoder, base);
158 +/* VC4 FKMS connector KMS struct */
159 +struct vc4_fkms_connector {
160 + struct drm_connector base;
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()
166 + struct drm_encoder *encoder;
169 +static inline struct vc4_fkms_connector *
170 +to_vc4_fkms_connector(struct drm_connector *connector)
172 + return container_of(connector, struct vc4_fkms_connector, base);
175 +/* Firmware's structure for making an FB mbox call. */
177 + u32 xres, yres, xres_virtual, yres_virtual;
179 + u32 xoffset, yoffset;
185 +struct vc4_fkms_plane {
186 + struct drm_plane base;
187 + struct fbinfo_s *fbinfo;
188 + dma_addr_t fbinfo_bus_addr;
192 +static inline struct vc4_fkms_plane *to_vc4_fkms_plane(struct drm_plane *plane)
194 + return (struct vc4_fkms_plane *)plane;
197 +/* Turns the display on/off. */
198 +static int vc4_plane_set_primary_blank(struct drm_plane *plane, bool blank)
200 + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
202 + u32 packet = blank;
203 + return rpi_firmware_property(vc4->firmware,
204 + RPI_FIRMWARE_FRAMEBUFFER_BLANK,
205 + &packet, sizeof(packet));
208 +static void vc4_primary_plane_atomic_update(struct drm_plane *plane,
209 + struct drm_plane_state *old_state)
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;
220 + vc4_plane_set_primary_blank(plane, false);
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;
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.
237 + if (vc4_plane->pitch != fb->pitches[0]) {
238 + u32 saved_base = fbinfo->base;
241 + ret = rpi_firmware_transaction(vc4->firmware,
242 + RPI_FIRMWARE_CHAN_FB,
243 + vc4_plane->fbinfo_bus_addr);
244 + fbinfo->base = saved_base;
246 + vc4_plane->pitch = fbinfo->pitch;
247 + WARN_ON_ONCE(vc4_plane->pitch != fb->pitches[0]);
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]);
257 +static void vc4_primary_plane_atomic_disable(struct drm_plane *plane,
258 + struct drm_plane_state *old_state)
260 + vc4_plane_set_primary_blank(plane, true);
263 +static void vc4_cursor_plane_atomic_update(struct drm_plane *plane,
264 + struct drm_plane_state *old_state)
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);
271 + u32 packet_state[] = { true, state->crtc_x, state->crtc_y, 0 };
272 + u32 packet_info[] = { state->crtc_w, state->crtc_h,
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);
279 + ret = rpi_firmware_property(vc4->firmware,
280 + RPI_FIRMWARE_SET_CURSOR_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]);
286 + ret = rpi_firmware_property(vc4->firmware,
287 + RPI_FIRMWARE_SET_CURSOR_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]);
294 +static void vc4_cursor_plane_atomic_disable(struct drm_plane *plane,
295 + struct drm_plane_state *old_state)
297 + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
298 + u32 packet_state[] = { false, 0, 0, 0 };
301 + ret = rpi_firmware_property(vc4->firmware,
302 + RPI_FIRMWARE_SET_CURSOR_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]);
309 +static int vc4_plane_atomic_check(struct drm_plane *plane,
310 + struct drm_plane_state *state)
315 +static void vc4_plane_destroy(struct drm_plane *plane)
317 + drm_plane_helper_disable(plane);
318 + drm_plane_cleanup(plane);
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,
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,
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,
347 +static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
348 + enum drm_plane_type type)
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;
355 + bool primary = (type == DRM_PLANE_TYPE_PRIMARY);
357 + vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
364 + plane = &vc4_plane->base;
365 + ret = drm_universal_plane_init(dev, plane, 0xff,
367 + primary ? &xrgb8888 : &argb8888, 1,
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,
376 + memset(vc4_plane->fbinfo, 0, sizeof(*vc4_plane->fbinfo));
378 + drm_plane_helper_add(plane, &vc4_primary_plane_helper_funcs);
380 + drm_plane_helper_add(plane, &vc4_cursor_plane_helper_funcs);
386 + vc4_plane_destroy(plane);
388 + return ERR_PTR(ret);
391 +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
393 + /* Everyting is handled in the planes. */
396 +static void vc4_crtc_disable(struct drm_crtc *crtc)
400 +static void vc4_crtc_enable(struct drm_crtc *crtc)
404 +static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
405 + struct drm_crtc_state *state)
410 +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
411 + struct drm_crtc_state *old_state)
415 +static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
417 + struct drm_crtc *crtc = &vc4_crtc->base;
418 + struct drm_device *dev = crtc->dev;
419 + unsigned long flags;
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);
427 + spin_unlock_irqrestore(&dev->event_lock, flags);
430 +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
432 + struct vc4_crtc *vc4_crtc = data;
433 + u32 stat = readl(vc4_crtc->regs + SMICS);
434 + irqreturn_t ret = IRQ_NONE;
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);
446 +static int vc4_page_flip(struct drm_crtc *crtc,
447 + struct drm_framebuffer *fb,
448 + struct drm_pending_vblank_event *event,
451 + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
452 + DRM_ERROR("Async flips aren't allowed\n");
456 + return drm_atomic_helper_page_flip(crtc, fb, event, flags);
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,
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,
479 +/* Frees the page flip event when the DRM device is closed with the
480 + * event still outstanding.
482 +void vc4_fkms_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
484 + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
485 + struct drm_device *dev = crtc->dev;
486 + unsigned long flags;
488 + spin_lock_irqsave(&dev->event_lock, flags);
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;
496 + spin_unlock_irqrestore(&dev->event_lock, flags);
499 +static const struct of_device_id vc4_firmware_kms_dt_match[] = {
500 + { .compatible = "raspberrypi,rpi-firmware-kms" },
504 +static enum drm_connector_status
505 +vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
507 + return connector_status_connected;
510 +static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
512 + struct drm_device *dev = connector->dev;
513 + struct vc4_dev *vc4 = to_vc4_dev(dev);
514 + u32 wh[2] = {0, 0};
516 + struct drm_display_mode *mode;
518 + ret = rpi_firmware_property(vc4->firmware,
519 + RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT,
522 + DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
523 + ret, wh[0], wh[1]);
527 + mode = drm_cvt_mode(dev, wh[0], wh[1], 60 /* vrefresh */,
529 + drm_mode_probed_add(connector, mode);
534 +static struct drm_encoder *
535 +vc4_fkms_connector_best_encoder(struct drm_connector *connector)
537 + struct vc4_fkms_connector *fkms_connector =
538 + to_vc4_fkms_connector(connector);
539 + return fkms_connector->encoder;
542 +static void vc4_fkms_connector_destroy(struct drm_connector *connector)
544 + drm_connector_unregister(connector);
545 + drm_connector_cleanup(connector);
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,
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,
563 +static struct drm_connector *vc4_fkms_connector_init(struct drm_device *dev,
564 + struct drm_encoder *encoder)
566 + struct drm_connector *connector = NULL;
567 + struct vc4_fkms_connector *fkms_connector;
570 + fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
572 + if (!fkms_connector) {
576 + connector = &fkms_connector->base;
578 + fkms_connector->encoder = encoder;
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);
584 + connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
585 + DRM_CONNECTOR_POLL_DISCONNECT);
587 + connector->interlace_allowed = 0;
588 + connector->doublescan_allowed = 0;
590 + drm_mode_connector_attach_encoder(connector, encoder);
596 + vc4_fkms_connector_destroy(connector);
598 + return ERR_PTR(ret);
601 +static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
603 + drm_encoder_cleanup(encoder);
606 +static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
607 + .destroy = vc4_fkms_encoder_destroy,
610 +static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
614 +static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
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,
623 +static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
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;
635 + vc4->firmware_kms = true;
637 + vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
640 + crtc = &vc4_crtc->base;
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;
648 + of_node_put(firmware_node);
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);
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.
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);
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);
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;
682 + vc4_encoder = devm_kzalloc(dev, sizeof(*vc4_encoder), GFP_KERNEL);
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);
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;
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",
703 + goto err_destroy_connector;
705 + platform_set_drvdata(pdev, vc4_crtc);
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);
722 +static void vc4_fkms_unbind(struct device *dev, struct device *master,
725 + struct platform_device *pdev = to_platform_device(dev);
726 + struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
728 + vc4_fkms_connector_destroy(vc4_crtc->connector);
729 + vc4_fkms_encoder_destroy(vc4_crtc->encoder);
730 + drm_crtc_cleanup(&vc4_crtc->base);
732 + platform_set_drvdata(pdev, NULL);
735 +static const struct component_ops vc4_fkms_ops = {
736 + .bind = vc4_fkms_bind,
737 + .unbind = vc4_fkms_unbind,
740 +static int vc4_fkms_probe(struct platform_device *pdev)
742 + return component_add(&pdev->dev, &vc4_fkms_ops);
745 +static int vc4_fkms_remove(struct platform_device *pdev)
747 + component_del(&pdev->dev, &vc4_fkms_ops);
751 +struct platform_driver vc4_firmware_kms_driver = {
752 + .probe = vc4_fkms_probe,
753 + .remove = vc4_fkms_remove,
755 + .name = "vc4_firmware_kms",
756 + .of_match_table = vc4_firmware_kms_dt_match,