1 From a59320bbcf8c137b180517324a4ba97debe0125e 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 111/773] 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 | 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
17 --- a/drivers/gpu/drm/vc4/Makefile
18 +++ b/drivers/gpu/drm/vc4/Makefile
19 @@ -9,6 +9,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 @@ -133,6 +133,9 @@ bool vc4_crtc_get_scanoutpos(struct drm_
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 @@ -761,8 +764,15 @@ static void vc4_crtc_atomic_flush(struct
41 static int vc4_enable_vblank(struct drm_crtc *crtc)
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);
47 + if (vc4->firmware_kms) {
48 + /* XXX: Can we mask the SMI interrupt? */
52 CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
55 @@ -770,8 +780,15 @@ static int vc4_enable_vblank(struct drm_
57 static void vc4_disable_vblank(struct drm_crtc *crtc)
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);
63 + if (vc4->firmware_kms) {
64 + /* XXX: Can we mask the SMI interrupt? */
68 CRTC_WRITE(PV_INTEN, 0);
71 --- a/drivers/gpu/drm/vc4/vc4_drv.c
72 +++ b/drivers/gpu/drm/vc4/vc4_drv.c
73 @@ -347,6 +347,7 @@ static struct platform_driver *const com
77 + &vc4_firmware_kms_driver,
81 --- a/drivers/gpu/drm/vc4/vc4_drv.h
82 +++ b/drivers/gpu/drm/vc4/vc4_drv.h
83 @@ -67,6 +67,9 @@ struct vc4_perfmon {
85 struct drm_device *dev;
88 + struct rpi_firmware *firmware;
90 struct vc4_hdmi *hdmi;
93 @@ -719,6 +722,10 @@ int vc4_dsi_debugfs_regs(struct seq_file
95 extern const struct dma_fence_ops vc4_fence_ops;
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);
102 void vc4_gem_init(struct drm_device *dev);
103 void vc4_gem_destroy(struct drm_device *dev);
105 +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
108 + * Copyright (C) 2016 Broadcom
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.
116 + * DOC: VC4 firmware KMS module.
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.
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>
135 +/* The firmware delivers a vblank interrupt to us through the SMI
136 + * hardware, which has only this one register.
139 +#define SMICS_INTERRUPTS (BIT(9) | BIT(10) | BIT(11))
142 + struct drm_crtc base;
143 + struct drm_encoder *encoder;
144 + struct drm_connector *connector;
145 + void __iomem *regs;
147 + struct drm_pending_vblank_event *event;
150 +static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
152 + return container_of(crtc, struct vc4_crtc, base);
155 +struct vc4_fkms_encoder {
156 + struct drm_encoder base;
159 +static inline struct vc4_fkms_encoder *
160 +to_vc4_fkms_encoder(struct drm_encoder *encoder)
162 + return container_of(encoder, struct vc4_fkms_encoder, base);
165 +/* VC4 FKMS connector KMS struct */
166 +struct vc4_fkms_connector {
167 + struct drm_connector base;
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()
173 + struct drm_encoder *encoder;
176 +static inline struct vc4_fkms_connector *
177 +to_vc4_fkms_connector(struct drm_connector *connector)
179 + return container_of(connector, struct vc4_fkms_connector, base);
182 +/* Firmware's structure for making an FB mbox call. */
184 + u32 xres, yres, xres_virtual, yres_virtual;
186 + u32 xoffset, yoffset;
192 +struct vc4_fkms_plane {
193 + struct drm_plane base;
194 + struct fbinfo_s *fbinfo;
195 + dma_addr_t fbinfo_bus_addr;
199 +static inline struct vc4_fkms_plane *to_vc4_fkms_plane(struct drm_plane *plane)
201 + return (struct vc4_fkms_plane *)plane;
204 +/* Turns the display on/off. */
205 +static int vc4_plane_set_primary_blank(struct drm_plane *plane, bool blank)
207 + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
209 + u32 packet = blank;
210 + return rpi_firmware_property(vc4->firmware,
211 + RPI_FIRMWARE_FRAMEBUFFER_BLANK,
212 + &packet, sizeof(packet));
215 +static void vc4_primary_plane_atomic_update(struct drm_plane *plane,
216 + struct drm_plane_state *old_state)
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;
227 + vc4_plane_set_primary_blank(plane, false);
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;
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.
244 + if (vc4_plane->pitch != fb->pitches[0]) {
245 + u32 saved_base = fbinfo->base;
248 + ret = rpi_firmware_transaction(vc4->firmware,
249 + RPI_FIRMWARE_CHAN_FB,
250 + vc4_plane->fbinfo_bus_addr);
251 + fbinfo->base = saved_base;
253 + vc4_plane->pitch = fbinfo->pitch;
254 + WARN_ON_ONCE(vc4_plane->pitch != fb->pitches[0]);
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]);
264 +static void vc4_primary_plane_atomic_disable(struct drm_plane *plane,
265 + struct drm_plane_state *old_state)
267 + vc4_plane_set_primary_blank(plane, true);
270 +static void vc4_cursor_plane_atomic_update(struct drm_plane *plane,
271 + struct drm_plane_state *old_state)
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);
278 + u32 packet_state[] = { true, state->crtc_x, state->crtc_y, 0 };
279 + u32 packet_info[] = { state->crtc_w, state->crtc_h,
281 + bo->paddr + fb->offsets[0],
282 + 0, 0, /* hotx, hoty */};
283 + WARN_ON_ONCE(fb->pitches[0] != state->crtc_w * 4);
285 + ret = rpi_firmware_property(vc4->firmware,
286 + RPI_FIRMWARE_SET_CURSOR_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]);
292 + ret = rpi_firmware_property(vc4->firmware,
293 + RPI_FIRMWARE_SET_CURSOR_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]);
300 +static void vc4_cursor_plane_atomic_disable(struct drm_plane *plane,
301 + struct drm_plane_state *old_state)
303 + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
304 + u32 packet_state[] = { false, 0, 0, 0 };
307 + ret = rpi_firmware_property(vc4->firmware,
308 + RPI_FIRMWARE_SET_CURSOR_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]);
315 +static int vc4_plane_atomic_check(struct drm_plane *plane,
316 + struct drm_plane_state *state)
321 +static void vc4_plane_destroy(struct drm_plane *plane)
323 + drm_plane_helper_disable(plane);
324 + drm_plane_cleanup(plane);
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,
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,
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,
353 +static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
354 + enum drm_plane_type type)
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;
361 + bool primary = (type == DRM_PLANE_TYPE_PRIMARY);
363 + vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
370 + plane = &vc4_plane->base;
371 + ret = drm_universal_plane_init(dev, plane, 0xff,
373 + primary ? &xrgb8888 : &argb8888, 1, NULL,
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,
382 + memset(vc4_plane->fbinfo, 0, sizeof(*vc4_plane->fbinfo));
384 + drm_plane_helper_add(plane, &vc4_primary_plane_helper_funcs);
386 + drm_plane_helper_add(plane, &vc4_cursor_plane_helper_funcs);
392 + vc4_plane_destroy(plane);
394 + return ERR_PTR(ret);
397 +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
399 + /* Everyting is handled in the planes. */
402 +static void vc4_crtc_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
406 +static void vc4_crtc_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
410 +static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
411 + struct drm_crtc_state *state)
416 +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
417 + struct drm_crtc_state *old_state)
421 +static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
423 + struct drm_crtc *crtc = &vc4_crtc->base;
424 + struct drm_device *dev = crtc->dev;
425 + unsigned long flags;
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);
433 + spin_unlock_irqrestore(&dev->event_lock, flags);
436 +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
438 + struct vc4_crtc *vc4_crtc = data;
439 + u32 stat = readl(vc4_crtc->regs + SMICS);
440 + irqreturn_t ret = IRQ_NONE;
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);
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)
457 + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
458 + DRM_ERROR("Async flips aren't allowed\n");
462 + return drm_atomic_helper_page_flip(crtc, fb, event, flags, ctx);
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,
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,
485 +/* Frees the page flip event when the DRM device is closed with the
486 + * event still outstanding.
488 +void vc4_fkms_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
490 + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
491 + struct drm_device *dev = crtc->dev;
492 + unsigned long flags;
494 + spin_lock_irqsave(&dev->event_lock, flags);
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;
502 + spin_unlock_irqrestore(&dev->event_lock, flags);
505 +static const struct of_device_id vc4_firmware_kms_dt_match[] = {
506 + { .compatible = "raspberrypi,rpi-firmware-kms" },
510 +static enum drm_connector_status
511 +vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
513 + return connector_status_connected;
516 +static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
518 + struct drm_device *dev = connector->dev;
519 + struct vc4_dev *vc4 = to_vc4_dev(dev);
520 + u32 wh[2] = {0, 0};
522 + struct drm_display_mode *mode;
524 + ret = rpi_firmware_property(vc4->firmware,
525 + RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT,
528 + DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
529 + ret, wh[0], wh[1]);
533 + mode = drm_cvt_mode(dev, wh[0], wh[1], 60 /* vrefresh */,
535 + drm_mode_probed_add(connector, mode);
540 +static struct drm_encoder *
541 +vc4_fkms_connector_best_encoder(struct drm_connector *connector)
543 + struct vc4_fkms_connector *fkms_connector =
544 + to_vc4_fkms_connector(connector);
545 + return fkms_connector->encoder;
548 +static void vc4_fkms_connector_destroy(struct drm_connector *connector)
550 + drm_connector_unregister(connector);
551 + drm_connector_cleanup(connector);
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,
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,
568 +static struct drm_connector *vc4_fkms_connector_init(struct drm_device *dev,
569 + struct drm_encoder *encoder)
571 + struct drm_connector *connector = NULL;
572 + struct vc4_fkms_connector *fkms_connector;
575 + fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
577 + if (!fkms_connector) {
581 + connector = &fkms_connector->base;
583 + fkms_connector->encoder = encoder;
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);
589 + connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
590 + DRM_CONNECTOR_POLL_DISCONNECT);
592 + connector->interlace_allowed = 0;
593 + connector->doublescan_allowed = 0;
595 + drm_mode_connector_attach_encoder(connector, encoder);
601 + vc4_fkms_connector_destroy(connector);
603 + return ERR_PTR(ret);
606 +static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
608 + drm_encoder_cleanup(encoder);
611 +static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
612 + .destroy = vc4_fkms_encoder_destroy,
615 +static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
619 +static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
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,
628 +static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
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;
640 + vc4->firmware_kms = true;
642 + vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
645 + crtc = &vc4_crtc->base;
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;
653 + of_node_put(firmware_node);
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);
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.
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);
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);
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;
686 + vc4_encoder = devm_kzalloc(dev, sizeof(*vc4_encoder), GFP_KERNEL);
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);
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;
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",
707 + goto err_destroy_connector;
709 + platform_set_drvdata(pdev, vc4_crtc);
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);
726 +static void vc4_fkms_unbind(struct device *dev, struct device *master,
729 + struct platform_device *pdev = to_platform_device(dev);
730 + struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
732 + vc4_fkms_connector_destroy(vc4_crtc->connector);
733 + vc4_fkms_encoder_destroy(vc4_crtc->encoder);
734 + drm_crtc_cleanup(&vc4_crtc->base);
736 + platform_set_drvdata(pdev, NULL);
739 +static const struct component_ops vc4_fkms_ops = {
740 + .bind = vc4_fkms_bind,
741 + .unbind = vc4_fkms_unbind,
744 +static int vc4_fkms_probe(struct platform_device *pdev)
746 + return component_add(&pdev->dev, &vc4_fkms_ops);
749 +static int vc4_fkms_remove(struct platform_device *pdev)
751 + component_del(&pdev->dev, &vc4_fkms_ops);
755 +struct platform_driver vc4_firmware_kms_driver = {
756 + .probe = vc4_fkms_probe,
757 + .remove = vc4_fkms_remove,
759 + .name = "vc4_firmware_kms",
760 + .of_match_table = vc4_firmware_kms_dt_match,