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