-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drm/imx: Add initial support for DCSS on iMX8MQ
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
Showing
17 changed files
with
3,962 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.