123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776 |
- From ceb205fa42f950c74172f9bbce65f1df3fdfdb34 Mon Sep 17 00:00:00 2001
- From: Eric Anholt <eric@anholt.net>
- Date: Wed, 14 Sep 2016 08:39:33 +0100
- Subject: [PATCH] drm/vc4: Add a mode for using the closed firmware for
- display.
- Signed-off-by: Eric Anholt <eric@anholt.net>
- ---
- drivers/gpu/drm/vc4/Makefile | 1 +
- drivers/gpu/drm/vc4/vc4_crtc.c | 13 +
- drivers/gpu/drm/vc4/vc4_drv.c | 10 +-
- drivers/gpu/drm/vc4/vc4_drv.h | 7 +
- drivers/gpu/drm/vc4/vc4_firmware_kms.c | 660 +++++++++++++++++++++++++++++++++
- 5 files changed, 689 insertions(+), 2 deletions(-)
- create mode 100644 drivers/gpu/drm/vc4/vc4_firmware_kms.c
- --- a/drivers/gpu/drm/vc4/Makefile
- +++ b/drivers/gpu/drm/vc4/Makefile
- @@ -9,6 +9,7 @@ vc4-y := \
- vc4_drv.o \
- vc4_dpi.o \
- vc4_dsi.o \
- + vc4_firmware_kms.o \
- vc4_kms.o \
- vc4_gem.o \
- vc4_hdmi.o \
- --- a/drivers/gpu/drm/vc4/vc4_crtc.c
- +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
- @@ -151,6 +151,9 @@ int vc4_crtc_get_scanoutpos(struct drm_d
- int vblank_lines;
- int ret = 0;
-
- + if (vc4->firmware_kms)
- + return 0;
- +
- /*
- * XXX Doesn't work well in interlaced mode yet, partially due
- * to problems in vc4 kms or drm core interlaced mode handling,
- @@ -639,6 +642,11 @@ int vc4_enable_vblank(struct drm_device
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
-
- + if (vc4->firmware_kms) {
- + /* XXX: Can we mask the SMI interrupt? */
- + return 0;
- + }
- +
- CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
-
- return 0;
- @@ -649,6 +657,11 @@ void vc4_disable_vblank(struct drm_devic
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
-
- + if (vc4->firmware_kms) {
- + /* XXX: Can we mask the SMI interrupt? */
- + return;
- + }
- +
- CRTC_WRITE(PV_INTEN, 0);
- }
-
- --- a/drivers/gpu/drm/vc4/vc4_drv.c
- +++ b/drivers/gpu/drm/vc4/vc4_drv.c
- @@ -47,10 +47,15 @@ void __iomem *vc4_ioremap_regs(struct pl
-
- static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
- {
- + struct vc4_dev *vc4 = to_vc4_dev(dev);
- struct drm_crtc *crtc;
-
- - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- - vc4_cancel_page_flip(crtc, file);
- + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- + if (vc4->firmware_kms)
- + vc4_fkms_cancel_page_flip(crtc, file);
- + else
- + vc4_cancel_page_flip(crtc, file);
- + }
- }
-
- void vc4_dump_regs32(const struct debugfs_reg32 *regs, unsigned int num_regs,
- @@ -331,6 +336,7 @@ static struct platform_driver *const com
- &vc4_dsi_driver,
- &vc4_hvs_driver,
- &vc4_crtc_driver,
- + &vc4_firmware_kms_driver,
- &vc4_v3d_driver,
- };
-
- --- a/drivers/gpu/drm/vc4/vc4_drv.h
- +++ b/drivers/gpu/drm/vc4/vc4_drv.h
- @@ -14,6 +14,9 @@ struct debugfs_reg32;
- struct vc4_dev {
- struct drm_device *dev;
-
- + bool firmware_kms;
- + struct rpi_firmware *firmware;
- +
- struct vc4_hdmi *hdmi;
- struct vc4_hvs *hvs;
- struct vc4_crtc *crtc[3];
- @@ -461,6 +464,10 @@ int vc4_dpi_debugfs_regs(struct seq_file
- extern struct platform_driver vc4_dsi_driver;
- int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
-
- +/* vc4_firmware_kms.c */
- +extern struct platform_driver vc4_firmware_kms_driver;
- +void vc4_fkms_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
- +
- /* vc4_gem.c */
- void vc4_gem_init(struct drm_device *dev);
- void vc4_gem_destroy(struct drm_device *dev);
- --- /dev/null
- +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
- @@ -0,0 +1,660 @@
- +/*
- + * Copyright (C) 2016 Broadcom
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License version 2 as
- + * published by the Free Software Foundation.
- + */
- +
- +/**
- + * DOC: VC4 firmware KMS module.
- + *
- + * As a hack to get us from the current closed source driver world
- + * toward a totally open stack, implement KMS on top of the Raspberry
- + * Pi's firmware display stack.
- + */
- +
- +#include "drm_atomic.h"
- +#include "drm_atomic_helper.h"
- +#include "drm_plane_helper.h"
- +#include "drm_crtc_helper.h"
- +#include "linux/clk.h"
- +#include "linux/debugfs.h"
- +#include "drm_fb_cma_helper.h"
- +#include "linux/component.h"
- +#include "linux/of_device.h"
- +#include "vc4_drv.h"
- +#include "vc4_regs.h"
- +#include <soc/bcm2835/raspberrypi-firmware.h>
- +
- +/* The firmware delivers a vblank interrupt to us through the SMI
- + * hardware, which has only this one register.
- + */
- +#define SMICS 0x0
- +#define SMICS_INTERRUPTS (BIT(9) | BIT(10) | BIT(11))
- +
- +struct vc4_crtc {
- + struct drm_crtc base;
- + struct drm_encoder *encoder;
- + struct drm_connector *connector;
- + void __iomem *regs;
- +
- + struct drm_pending_vblank_event *event;
- +};
- +
- +static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
- +{
- + return container_of(crtc, struct vc4_crtc, base);
- +}
- +
- +struct vc4_fkms_encoder {
- + struct drm_encoder base;
- +};
- +
- +static inline struct vc4_fkms_encoder *
- +to_vc4_fkms_encoder(struct drm_encoder *encoder)
- +{
- + return container_of(encoder, struct vc4_fkms_encoder, base);
- +}
- +
- +/* VC4 FKMS connector KMS struct */
- +struct vc4_fkms_connector {
- + struct drm_connector base;
- +
- + /* Since the connector is attached to just the one encoder,
- + * this is the reference to it so we can do the best_encoder()
- + * hook.
- + */
- + struct drm_encoder *encoder;
- +};
- +
- +static inline struct vc4_fkms_connector *
- +to_vc4_fkms_connector(struct drm_connector *connector)
- +{
- + return container_of(connector, struct vc4_fkms_connector, base);
- +}
- +
- +/* Firmware's structure for making an FB mbox call. */
- +struct fbinfo_s {
- + u32 xres, yres, xres_virtual, yres_virtual;
- + u32 pitch, bpp;
- + u32 xoffset, yoffset;
- + u32 base;
- + u32 screen_size;
- + u16 cmap[256];
- +};
- +
- +struct vc4_fkms_plane {
- + struct drm_plane base;
- + struct fbinfo_s *fbinfo;
- + dma_addr_t fbinfo_bus_addr;
- + u32 pitch;
- +};
- +
- +static inline struct vc4_fkms_plane *to_vc4_fkms_plane(struct drm_plane *plane)
- +{
- + return (struct vc4_fkms_plane *)plane;
- +}
- +
- +/* Turns the display on/off. */
- +static int vc4_plane_set_primary_blank(struct drm_plane *plane, bool blank)
- +{
- + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
- +
- + u32 packet = blank;
- + return rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_FRAMEBUFFER_BLANK,
- + &packet, sizeof(packet));
- +}
- +
- +static void vc4_primary_plane_atomic_update(struct drm_plane *plane,
- + struct drm_plane_state *old_state)
- +{
- + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
- + struct vc4_fkms_plane *vc4_plane = to_vc4_fkms_plane(plane);
- + struct drm_plane_state *state = plane->state;
- + struct drm_framebuffer *fb = state->fb;
- + struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
- + volatile struct fbinfo_s *fbinfo = vc4_plane->fbinfo;
- + u32 bpp = 32;
- + int ret;
- +
- + vc4_plane_set_primary_blank(plane, false);
- +
- + fbinfo->xres = state->crtc_w;
- + fbinfo->yres = state->crtc_h;
- + fbinfo->xres_virtual = state->crtc_w;
- + fbinfo->yres_virtual = state->crtc_h;
- + fbinfo->bpp = bpp;
- + fbinfo->xoffset = state->crtc_x;
- + fbinfo->yoffset = state->crtc_y;
- + fbinfo->base = bo->paddr + fb->offsets[0];
- + fbinfo->pitch = fb->pitches[0];
- + /* A bug in the firmware makes it so that if the fb->base is
- + * set to nonzero, the configured pitch gets overwritten with
- + * the previous pitch. So, to get the configured pitch
- + * recomputed, we have to make it allocate itself a new buffer
- + * in VC memory, first.
- + */
- + if (vc4_plane->pitch != fb->pitches[0]) {
- + u32 saved_base = fbinfo->base;
- + fbinfo->base = 0;
- +
- + ret = rpi_firmware_transaction(vc4->firmware,
- + RPI_FIRMWARE_CHAN_FB,
- + vc4_plane->fbinfo_bus_addr);
- + fbinfo->base = saved_base;
- +
- + vc4_plane->pitch = fbinfo->pitch;
- + WARN_ON_ONCE(vc4_plane->pitch != fb->pitches[0]);
- + }
- +
- + ret = rpi_firmware_transaction(vc4->firmware,
- + RPI_FIRMWARE_CHAN_FB,
- + vc4_plane->fbinfo_bus_addr);
- + WARN_ON_ONCE(fbinfo->pitch != fb->pitches[0]);
- + WARN_ON_ONCE(fbinfo->base != bo->paddr + fb->offsets[0]);
- +}
- +
- +static void vc4_primary_plane_atomic_disable(struct drm_plane *plane,
- + struct drm_plane_state *old_state)
- +{
- + vc4_plane_set_primary_blank(plane, true);
- +}
- +
- +static void vc4_cursor_plane_atomic_update(struct drm_plane *plane,
- + struct drm_plane_state *old_state)
- +{
- + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
- + struct drm_plane_state *state = plane->state;
- + struct drm_framebuffer *fb = state->fb;
- + struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
- + int ret;
- + u32 packet_state[] = { true, state->crtc_x, state->crtc_y, 0 };
- + u32 packet_info[] = { state->crtc_w, state->crtc_h,
- + 0, /* unused */
- + bo->paddr + fb->offsets[0],
- + 0, 0, /* hotx, hoty */};
- + WARN_ON_ONCE(fb->pitches[0] != state->crtc_w * 4);
- + WARN_ON_ONCE(fb->bits_per_pixel != 32);
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_SET_CURSOR_STATE,
- + &packet_state,
- + sizeof(packet_state));
- + if (ret || packet_state[0] != 0)
- + DRM_ERROR("Failed to set cursor state: 0x%08x\n", packet_state[0]);
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_SET_CURSOR_INFO,
- + &packet_info,
- + sizeof(packet_info));
- + if (ret || packet_info[0] != 0)
- + DRM_ERROR("Failed to set cursor info: 0x%08x\n", packet_info[0]);
- +}
- +
- +static void vc4_cursor_plane_atomic_disable(struct drm_plane *plane,
- + struct drm_plane_state *old_state)
- +{
- + struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
- + u32 packet_state[] = { false, 0, 0, 0 };
- + int ret;
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_SET_CURSOR_STATE,
- + &packet_state,
- + sizeof(packet_state));
- + if (ret || packet_state[0] != 0)
- + DRM_ERROR("Failed to set cursor state: 0x%08x\n", packet_state[0]);
- +}
- +
- +static int vc4_plane_atomic_check(struct drm_plane *plane,
- + struct drm_plane_state *state)
- +{
- + return 0;
- +}
- +
- +static void vc4_plane_destroy(struct drm_plane *plane)
- +{
- + drm_plane_helper_disable(plane);
- + drm_plane_cleanup(plane);
- +}
- +
- +static const struct drm_plane_funcs vc4_plane_funcs = {
- + .update_plane = drm_atomic_helper_update_plane,
- + .disable_plane = drm_atomic_helper_disable_plane,
- + .destroy = vc4_plane_destroy,
- + .set_property = NULL,
- + .reset = drm_atomic_helper_plane_reset,
- + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
- +};
- +
- +static const struct drm_plane_helper_funcs vc4_primary_plane_helper_funcs = {
- + .prepare_fb = NULL,
- + .cleanup_fb = NULL,
- + .atomic_check = vc4_plane_atomic_check,
- + .atomic_update = vc4_primary_plane_atomic_update,
- + .atomic_disable = vc4_primary_plane_atomic_disable,
- +};
- +
- +static const struct drm_plane_helper_funcs vc4_cursor_plane_helper_funcs = {
- + .prepare_fb = NULL,
- + .cleanup_fb = NULL,
- + .atomic_check = vc4_plane_atomic_check,
- + .atomic_update = vc4_cursor_plane_atomic_update,
- + .atomic_disable = vc4_cursor_plane_atomic_disable,
- +};
- +
- +static struct drm_plane *vc4_fkms_plane_init(struct drm_device *dev,
- + enum drm_plane_type type)
- +{
- + struct drm_plane *plane = NULL;
- + struct vc4_fkms_plane *vc4_plane;
- + u32 xrgb8888 = DRM_FORMAT_XRGB8888;
- + u32 argb8888 = DRM_FORMAT_ARGB8888;
- + int ret = 0;
- + bool primary = (type == DRM_PLANE_TYPE_PRIMARY);
- +
- + vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
- + GFP_KERNEL);
- + if (!vc4_plane) {
- + ret = -ENOMEM;
- + goto fail;
- + }
- +
- + plane = &vc4_plane->base;
- + ret = drm_universal_plane_init(dev, plane, 0xff,
- + &vc4_plane_funcs,
- + primary ? &xrgb8888 : &argb8888, 1,
- + type);
- +
- + if (type == DRM_PLANE_TYPE_PRIMARY) {
- + vc4_plane->fbinfo =
- + dma_alloc_coherent(dev->dev,
- + sizeof(*vc4_plane->fbinfo),
- + &vc4_plane->fbinfo_bus_addr,
- + GFP_KERNEL);
- + memset(vc4_plane->fbinfo, 0, sizeof(*vc4_plane->fbinfo));
- +
- + drm_plane_helper_add(plane, &vc4_primary_plane_helper_funcs);
- + } else {
- + drm_plane_helper_add(plane, &vc4_cursor_plane_helper_funcs);
- + }
- +
- + return plane;
- +fail:
- + if (plane)
- + vc4_plane_destroy(plane);
- +
- + return ERR_PTR(ret);
- +}
- +
- +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
- +{
- + /* Everyting is handled in the planes. */
- +}
- +
- +static void vc4_crtc_disable(struct drm_crtc *crtc)
- +{
- +}
- +
- +static void vc4_crtc_enable(struct drm_crtc *crtc)
- +{
- +}
- +
- +static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
- + struct drm_crtc_state *state)
- +{
- + return 0;
- +}
- +
- +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
- + struct drm_crtc_state *old_state)
- +{
- +}
- +
- +static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
- +{
- + struct drm_crtc *crtc = &vc4_crtc->base;
- + struct drm_device *dev = crtc->dev;
- + unsigned long flags;
- +
- + spin_lock_irqsave(&dev->event_lock, flags);
- + if (vc4_crtc->event) {
- + drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
- + vc4_crtc->event = NULL;
- + drm_crtc_vblank_put(crtc);
- + }
- + spin_unlock_irqrestore(&dev->event_lock, flags);
- +}
- +
- +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
- +{
- + struct vc4_crtc *vc4_crtc = data;
- + u32 stat = readl(vc4_crtc->regs + SMICS);
- + irqreturn_t ret = IRQ_NONE;
- +
- + if (stat & SMICS_INTERRUPTS) {
- + writel(0, vc4_crtc->regs + SMICS);
- + drm_crtc_handle_vblank(&vc4_crtc->base);
- + vc4_crtc_handle_page_flip(vc4_crtc);
- + ret = IRQ_HANDLED;
- + }
- +
- + return ret;
- +}
- +
- +static int vc4_page_flip(struct drm_crtc *crtc,
- + struct drm_framebuffer *fb,
- + struct drm_pending_vblank_event *event,
- + uint32_t flags)
- +{
- + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
- + DRM_ERROR("Async flips aren't allowed\n");
- + return -EINVAL;
- + }
- +
- + return drm_atomic_helper_page_flip(crtc, fb, event, flags);
- +}
- +
- +static const struct drm_crtc_funcs vc4_crtc_funcs = {
- + .set_config = drm_atomic_helper_set_config,
- + .destroy = drm_crtc_cleanup,
- + .page_flip = vc4_page_flip,
- + .set_property = NULL,
- + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
- + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
- + .reset = drm_atomic_helper_crtc_reset,
- + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
- +};
- +
- +static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
- + .mode_set_nofb = vc4_crtc_mode_set_nofb,
- + .disable = vc4_crtc_disable,
- + .enable = vc4_crtc_enable,
- + .atomic_check = vc4_crtc_atomic_check,
- + .atomic_flush = vc4_crtc_atomic_flush,
- +};
- +
- +/* Frees the page flip event when the DRM device is closed with the
- + * event still outstanding.
- + */
- +void vc4_fkms_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
- +{
- + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
- + struct drm_device *dev = crtc->dev;
- + unsigned long flags;
- +
- + spin_lock_irqsave(&dev->event_lock, flags);
- +
- + if (vc4_crtc->event && vc4_crtc->event->base.file_priv == file) {
- + vc4_crtc->event->base.destroy(&vc4_crtc->event->base);
- + drm_crtc_vblank_put(crtc);
- + vc4_crtc->event = NULL;
- + }
- +
- + spin_unlock_irqrestore(&dev->event_lock, flags);
- +}
- +
- +static const struct of_device_id vc4_firmware_kms_dt_match[] = {
- + { .compatible = "raspberrypi,rpi-firmware-kms" },
- + {}
- +};
- +
- +static enum drm_connector_status
- +vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
- +{
- + return connector_status_connected;
- +}
- +
- +static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
- +{
- + struct drm_device *dev = connector->dev;
- + struct vc4_dev *vc4 = to_vc4_dev(dev);
- + u32 wh[2] = {0, 0};
- + int ret;
- + struct drm_display_mode *mode;
- +
- + ret = rpi_firmware_property(vc4->firmware,
- + RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT,
- + &wh, sizeof(wh));
- + if (ret) {
- + DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
- + ret, wh[0], wh[1]);
- + return 0;
- + }
- +
- + mode = drm_cvt_mode(dev, wh[0], wh[1], 60 /* vrefresh */,
- + 0, 0, false);
- + drm_mode_probed_add(connector, mode);
- +
- + return 1;
- +}
- +
- +static struct drm_encoder *
- +vc4_fkms_connector_best_encoder(struct drm_connector *connector)
- +{
- + struct vc4_fkms_connector *fkms_connector =
- + to_vc4_fkms_connector(connector);
- + return fkms_connector->encoder;
- +}
- +
- +static void vc4_fkms_connector_destroy(struct drm_connector *connector)
- +{
- + drm_connector_unregister(connector);
- + drm_connector_cleanup(connector);
- +}
- +
- +static const struct drm_connector_funcs vc4_fkms_connector_funcs = {
- + .dpms = drm_atomic_helper_connector_dpms,
- + .detect = vc4_fkms_connector_detect,
- + .fill_modes = drm_helper_probe_single_connector_modes,
- + .destroy = vc4_fkms_connector_destroy,
- + .reset = drm_atomic_helper_connector_reset,
- + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- +};
- +
- +static const struct drm_connector_helper_funcs vc4_fkms_connector_helper_funcs = {
- + .get_modes = vc4_fkms_connector_get_modes,
- + .best_encoder = vc4_fkms_connector_best_encoder,
- +};
- +
- +static struct drm_connector *vc4_fkms_connector_init(struct drm_device *dev,
- + struct drm_encoder *encoder)
- +{
- + struct drm_connector *connector = NULL;
- + struct vc4_fkms_connector *fkms_connector;
- + int ret = 0;
- +
- + fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
- + GFP_KERNEL);
- + if (!fkms_connector) {
- + ret = -ENOMEM;
- + goto fail;
- + }
- + connector = &fkms_connector->base;
- +
- + fkms_connector->encoder = encoder;
- +
- + drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
- + DRM_MODE_CONNECTOR_HDMIA);
- + drm_connector_helper_add(connector, &vc4_fkms_connector_helper_funcs);
- +
- + connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
- + DRM_CONNECTOR_POLL_DISCONNECT);
- +
- + connector->interlace_allowed = 0;
- + connector->doublescan_allowed = 0;
- +
- + drm_mode_connector_attach_encoder(connector, encoder);
- +
- + return connector;
- +
- + fail:
- + if (connector)
- + vc4_fkms_connector_destroy(connector);
- +
- + return ERR_PTR(ret);
- +}
- +
- +static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
- +{
- + drm_encoder_cleanup(encoder);
- +}
- +
- +static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
- + .destroy = vc4_fkms_encoder_destroy,
- +};
- +
- +static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
- +{
- +}
- +
- +static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
- +{
- +}
- +
- +static const struct drm_encoder_helper_funcs vc4_fkms_encoder_helper_funcs = {
- + .enable = vc4_fkms_encoder_enable,
- + .disable = vc4_fkms_encoder_disable,
- +};
- +
- +static int vc4_fkms_bind(struct device *dev, struct device *master, void *data)
- +{
- + struct platform_device *pdev = to_platform_device(dev);
- + struct drm_device *drm = dev_get_drvdata(master);
- + struct vc4_dev *vc4 = to_vc4_dev(drm);
- + struct vc4_crtc *vc4_crtc;
- + struct vc4_fkms_encoder *vc4_encoder;
- + struct drm_crtc *crtc;
- + struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp;
- + struct device_node *firmware_node;
- + int ret;
- +
- + vc4->firmware_kms = true;
- +
- + vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
- + if (!vc4_crtc)
- + return -ENOMEM;
- + crtc = &vc4_crtc->base;
- +
- + firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
- + vc4->firmware = rpi_firmware_get(firmware_node);
- + if (!vc4->firmware) {
- + DRM_DEBUG("Failed to get Raspberry Pi firmware reference.\n");
- + return -EPROBE_DEFER;
- + }
- + of_node_put(firmware_node);
- +
- + /* Map the SMI interrupt reg */
- + vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
- + if (IS_ERR(vc4_crtc->regs))
- + return PTR_ERR(vc4_crtc->regs);
- +
- + /* For now, we create just the primary and the legacy cursor
- + * planes. We should be able to stack more planes on easily,
- + * but to do that we would need to compute the bandwidth
- + * requirement of the plane configuration, and reject ones
- + * that will take too much.
- + */
- + primary_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
- + if (IS_ERR(primary_plane)) {
- + dev_err(dev, "failed to construct primary plane\n");
- + ret = PTR_ERR(primary_plane);
- + goto err;
- + }
- +
- + cursor_plane = vc4_fkms_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
- + if (IS_ERR(cursor_plane)) {
- + dev_err(dev, "failed to construct cursor plane\n");
- + ret = PTR_ERR(cursor_plane);
- + goto err;
- + }
- +
- + drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
- + &vc4_crtc_funcs);
- + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
- + primary_plane->crtc = crtc;
- + cursor_plane->crtc = crtc;
- + vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
- +
- + vc4_encoder = devm_kzalloc(dev, sizeof(*vc4_encoder), GFP_KERNEL);
- + if (!vc4_encoder)
- + return -ENOMEM;
- + vc4_crtc->encoder = &vc4_encoder->base;
- + vc4_encoder->base.possible_crtcs |= drm_crtc_mask(crtc) ;
- + drm_encoder_init(drm, &vc4_encoder->base, &vc4_fkms_encoder_funcs,
- + DRM_MODE_ENCODER_TMDS);
- + drm_encoder_helper_add(&vc4_encoder->base,
- + &vc4_fkms_encoder_helper_funcs);
- +
- + vc4_crtc->connector = vc4_fkms_connector_init(drm, &vc4_encoder->base);
- + if (IS_ERR(vc4_crtc->connector)) {
- + ret = PTR_ERR(vc4_crtc->connector);
- + goto err_destroy_encoder;
- + }
- +
- + writel(0, vc4_crtc->regs + SMICS);
- + ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
- + vc4_crtc_irq_handler, 0, "vc4 firmware kms",
- + vc4_crtc);
- + if (ret)
- + goto err_destroy_connector;
- +
- + platform_set_drvdata(pdev, vc4_crtc);
- +
- + return 0;
- +
- +err_destroy_connector:
- + vc4_fkms_connector_destroy(vc4_crtc->connector);
- +err_destroy_encoder:
- + vc4_fkms_encoder_destroy(vc4_crtc->encoder);
- + list_for_each_entry_safe(destroy_plane, temp,
- + &drm->mode_config.plane_list, head) {
- + if (destroy_plane->possible_crtcs == 1 << drm_crtc_index(crtc))
- + destroy_plane->funcs->destroy(destroy_plane);
- + }
- +err:
- + return ret;
- +}
- +
- +static void vc4_fkms_unbind(struct device *dev, struct device *master,
- + void *data)
- +{
- + struct platform_device *pdev = to_platform_device(dev);
- + struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
- +
- + vc4_fkms_connector_destroy(vc4_crtc->connector);
- + vc4_fkms_encoder_destroy(vc4_crtc->encoder);
- + drm_crtc_cleanup(&vc4_crtc->base);
- +
- + platform_set_drvdata(pdev, NULL);
- +}
- +
- +static const struct component_ops vc4_fkms_ops = {
- + .bind = vc4_fkms_bind,
- + .unbind = vc4_fkms_unbind,
- +};
- +
- +static int vc4_fkms_probe(struct platform_device *pdev)
- +{
- + return component_add(&pdev->dev, &vc4_fkms_ops);
- +}
- +
- +static int vc4_fkms_remove(struct platform_device *pdev)
- +{
- + component_del(&pdev->dev, &vc4_fkms_ops);
- + return 0;
- +}
- +
- +struct platform_driver vc4_firmware_kms_driver = {
- + .probe = vc4_fkms_probe,
- + .remove = vc4_fkms_remove,
- + .driver = {
- + .name = "vc4_firmware_kms",
- + .of_match_table = vc4_firmware_kms_dt_match,
- + },
- +};
|