Skip to content

Commit

Permalink
drm/imx: Add initial support for DCSS on iMX8MQ
Browse files Browse the repository at this point in the history
This adds initial support for iMX8MQ's Display Controller Subsystem (DCSS).
Some of its capabilities include:
 * 4K@60fps;
 * HDR10;
 * one graphics and 2 video pipelines;
 * on-the-fly decompression of compressed video and graphics;

The reference manual can be found here:
https://www.nxp.com/webapp/Download?colCode=IMX8MDQLQRM

The current patch adds only basic functionality: one primary plane for
graphics, linear, tiled and super-tiled buffers support (no graphics
decompression yet), no HDR10 and no video planes.

Video planes support and HDR10 will be added in subsequent patches once
per-plane de-gamma/CSC/gamma support is in.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
Acked-by: Guido Günther <agx@sigxcpu.org>
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20200731081836.3048-3-laurentiu.palcu@oss.nxp.com
  • Loading branch information
Laurentiu Palcu authored and Lucas Stach committed Sep 9, 2020
1 parent ce625f4 commit 9021c31
Show file tree
Hide file tree
Showing 17 changed files with 3,962 additions and 0 deletions.
2 changes: 2 additions & 0 deletions drivers/gpu/drm/imx/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ config DRM_IMX_HDMI
depends on DRM_IMX
help
Choose this if you want to use HDMI on i.MX6.

source "drivers/gpu/drm/imx/dcss/Kconfig"
1 change: 1 addition & 0 deletions drivers/gpu/drm/imx/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o

obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
9 changes: 9 additions & 0 deletions drivers/gpu/drm/imx/dcss/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
config DRM_IMX_DCSS
tristate "i.MX8MQ DCSS"
select IMX_IRQSTEER
select DRM_KMS_CMA_HELPER
select VIDEOMODE_HELPERS
depends on DRM && ARCH_MXC
help
Choose this if you have a NXP i.MX8MQ based system and want to use the
Display Controller Subsystem. This option enables DCSS support.
6 changes: 6 additions & 0 deletions drivers/gpu/drm/imx/dcss/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
imx-dcss-objs := dcss-drv.o dcss-dev.o dcss-blkctl.o dcss-ctxld.o dcss-dtg.o \
dcss-ss.o dcss-dpr.o dcss-scaler.o dcss-kms.o dcss-crtc.o \
dcss-plane.o

obj-$(CONFIG_DRM_IMX_DCSS) += imx-dcss.o

70 changes: 70 additions & 0 deletions drivers/gpu/drm/imx/dcss/dcss-blkctl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP.
*/

#include <linux/device.h>
#include <linux/of.h>
#include <linux/slab.h>

#include "dcss-dev.h"

#define DCSS_BLKCTL_RESET_CTRL 0x00
#define B_CLK_RESETN BIT(0)
#define APB_CLK_RESETN BIT(1)
#define P_CLK_RESETN BIT(2)
#define RTR_CLK_RESETN BIT(4)
#define DCSS_BLKCTL_CONTROL0 0x10
#define HDMI_MIPI_CLK_SEL BIT(0)
#define DISPMIX_REFCLK_SEL_POS 4
#define DISPMIX_REFCLK_SEL_MASK GENMASK(5, 4)
#define DISPMIX_PIXCLK_SEL BIT(8)
#define HDMI_SRC_SECURE_EN BIT(16)

struct dcss_blkctl {
struct dcss_dev *dcss;
void __iomem *base_reg;
};

void dcss_blkctl_cfg(struct dcss_blkctl *blkctl)
{
if (blkctl->dcss->hdmi_output)
dcss_writel(0, blkctl->base_reg + DCSS_BLKCTL_CONTROL0);
else
dcss_writel(DISPMIX_PIXCLK_SEL,
blkctl->base_reg + DCSS_BLKCTL_CONTROL0);

dcss_set(B_CLK_RESETN | APB_CLK_RESETN | P_CLK_RESETN | RTR_CLK_RESETN,
blkctl->base_reg + DCSS_BLKCTL_RESET_CTRL);
}

int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_base)
{
struct dcss_blkctl *blkctl;

blkctl = kzalloc(sizeof(*blkctl), GFP_KERNEL);
if (!blkctl)
return -ENOMEM;

blkctl->base_reg = ioremap(blkctl_base, SZ_4K);
if (!blkctl->base_reg) {
dev_err(dcss->dev, "unable to remap BLK CTRL base\n");
kfree(blkctl);
return -ENOMEM;
}

dcss->blkctl = blkctl;
blkctl->dcss = dcss;

dcss_blkctl_cfg(blkctl);

return 0;
}

void dcss_blkctl_exit(struct dcss_blkctl *blkctl)
{
if (blkctl->base_reg)
iounmap(blkctl->base_reg);

kfree(blkctl);
}
219 changes: 219 additions & 0 deletions drivers/gpu/drm/imx/dcss/dcss-crtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP.
*/

#include <drm/drm_atomic_helper.h>
#include <drm/drm_vblank.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

#include "dcss-dev.h"
#include "dcss-kms.h"

static int dcss_enable_vblank(struct drm_crtc *crtc)
{
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
base);
struct dcss_dev *dcss = crtc->dev->dev_private;

dcss_dtg_vblank_irq_enable(dcss->dtg, true);

dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);

enable_irq(dcss_crtc->irq);

return 0;
}

static void dcss_disable_vblank(struct drm_crtc *crtc)
{
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
base);
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;

disable_irq_nosync(dcss_crtc->irq);

dcss_dtg_vblank_irq_enable(dcss->dtg, false);

if (dcss_crtc->disable_ctxld_kick_irq)
dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false);
}

static const struct drm_crtc_funcs dcss_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = dcss_enable_vblank,
.disable_vblank = dcss_disable_vblank,
};

static void dcss_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
drm_crtc_vblank_on(crtc);
}

static void dcss_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
base);
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;

spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
WARN_ON(drm_crtc_vblank_get(crtc));
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);

if (dcss_dtg_is_enabled(dcss->dtg))
dcss_ctxld_enable(dcss->ctxld);
}

static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
base);
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
struct videomode vm;

drm_display_mode_to_videomode(mode, &vm);

pm_runtime_get_sync(dcss->dev);

vm.pixelclock = mode->crtc_clock * 1000;

dcss_ss_subsam_set(dcss->ss);
dcss_dtg_css_set(dcss->dtg);

if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) {
dcss_dtg_sync_set(dcss->dtg, &vm);
dcss_ss_sync_set(dcss->ss, &vm,
mode->flags & DRM_MODE_FLAG_PHSYNC,
mode->flags & DRM_MODE_FLAG_PVSYNC);
}

dcss_enable_dtg_and_ss(dcss);

dcss_ctxld_enable(dcss->ctxld);

/* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */
dcss_crtc->disable_ctxld_kick_irq = true;
}

static void dcss_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
base);
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;

drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);

spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
drm_crtc_send_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
}
spin_unlock_irq(&crtc->dev->event_lock);

dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);

reinit_completion(&dcss->disable_completion);

dcss_disable_dtg_and_ss(dcss);

dcss_ctxld_enable(dcss->ctxld);

if (!drm_mode_equal(mode, old_mode) || !crtc->state->active)
if (!wait_for_completion_timeout(&dcss->disable_completion,
msecs_to_jiffies(100)))
dev_err(dcss->dev, "Shutting off DTG timed out.\n");

/*
* Do not shut off CTXLD kick interrupt when shutting VBLANK off. It
* will be needed to commit the last changes, before going to suspend.
*/
dcss_crtc->disable_ctxld_kick_irq = false;

drm_crtc_vblank_off(crtc);

pm_runtime_mark_last_busy(dcss->dev);
pm_runtime_put_autosuspend(dcss->dev);
}

static const struct drm_crtc_helper_funcs dcss_helper_funcs = {
.atomic_begin = dcss_crtc_atomic_begin,
.atomic_flush = dcss_crtc_atomic_flush,
.atomic_enable = dcss_crtc_atomic_enable,
.atomic_disable = dcss_crtc_atomic_disable,
};

static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id)
{
struct dcss_crtc *dcss_crtc = dev_id;
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;

if (!dcss_dtg_vblank_irq_valid(dcss->dtg))
return IRQ_NONE;

if (dcss_ctxld_is_flushed(dcss->ctxld))
drm_crtc_handle_vblank(&dcss_crtc->base);

dcss_dtg_vblank_irq_clear(dcss->dtg);

return IRQ_HANDLED;
}

int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm)
{
struct dcss_dev *dcss = drm->dev_private;
struct platform_device *pdev = to_platform_device(dcss->dev);
int ret;

crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base),
DRM_PLANE_TYPE_PRIMARY, 0);
if (IS_ERR(crtc->plane[0]))
return PTR_ERR(crtc->plane[0]);

crtc->base.port = dcss->of_port;

drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs);
ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base,
NULL, &dcss_crtc_funcs, NULL);
if (ret) {
dev_err(dcss->dev, "failed to init crtc\n");
return ret;
}

crtc->irq = platform_get_irq_byname(pdev, "vblank");
if (crtc->irq < 0)
return crtc->irq;

ret = request_irq(crtc->irq, dcss_crtc_irq_handler,
0, "dcss_drm", crtc);
if (ret) {
dev_err(dcss->dev, "irq request failed with %d.\n", ret);
return ret;
}

disable_irq(crtc->irq);

return 0;
}

void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm)
{
free_irq(crtc->irq, crtc);
}
Loading

0 comments on commit 9021c31

Please sign in to comment.