diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index 2a141ad4145e451ddcf2cf602e1e5d9bb01ad40b..99ba230d89f3c32132e985f65f100b4197482ed4 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -90,6 +90,7 @@ config DRM_IMX_SEC_DSIM Choose this to enable the internal SEC MIPI DSIM controller found on i.MX platform. +source "drivers/gpu/drm/imx/dcnano/Kconfig" source "drivers/gpu/drm/imx/dpu/Kconfig" source "drivers/gpu/drm/imx/dcss/Kconfig" source "drivers/gpu/drm/imx/mhdp/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 82aaa99b036c64b9d4514a26cf77ff3d9575c5c4..5aad8b074155a8291d5abf026d9776c65f9d6dfb 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o obj-$(CONFIG_DRM_IMX8QXP_LDB) += imx8qxp-ldb.o obj-$(CONFIG_DRM_IMX8MP_LDB) += imx8mp-ldb.o +obj-$(CONFIG_DRM_IMX_DCNANO) += dcnano/ obj-$(CONFIG_DRM_IMX_DPU) += dpu/ obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3/ obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o diff --git a/drivers/gpu/drm/imx/dcnano/Kconfig b/drivers/gpu/drm/imx/dcnano/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..ec3ba3ad7b00c8737a57ec009e29dddd942a2e24 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/Kconfig @@ -0,0 +1,10 @@ +config DRM_IMX_DCNANO + tristate "DRM support for NXP i.MX DCNANO Graphics" + select DRM_KMS_HELPER + select VIDEOMODE_HELPERS + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + depends on DRM && OF && ARCH_MXC + depends on COMMON_CLK + help + enable NXP i.MX DCNANO graphics support diff --git a/drivers/gpu/drm/imx/dcnano/Makefile b/drivers/gpu/drm/imx/dcnano/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b08d6712861fdd4d7b4abf7399e17581d1c5f157 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +imx-dcnano-drm-objs := dcnano-crtc.o dcnano-drv.o dcnano-kms.o dcnano-plane.o + +obj-$(CONFIG_DRM_IMX_DCNANO) += imx-dcnano-drm.o diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-crtc.c b/drivers/gpu/drm/imx/dcnano/dcnano-crtc.c new file mode 100644 index 0000000000000000000000000000000000000000..f6018b62a9f84fbf73e14c138743d7c5f418e79e --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/dcnano-crtc.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2020,2021 NXP + */ + +#include <linux/clk.h> +#include <linux/irq.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_plane.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> + +#include "dcnano-drv.h" +#include "dcnano-reg.h" + +#define DCNANO_CRTC_PLL_MIN_RATE 271500000 +#define DCNANO_CRTC_PLL_MAX_RATE 792000000 + +#define DCNANO_CRTC_PLL_MIN_DIV 1 +#define DCNANO_CRTC_PLL_MAX_DIV 64 + +#define dcnano_crtc_dbg(crtc, fmt, ...) \ + drm_dbg_kms((crtc)->dev, "[CRTC:%d:%s] " fmt, \ + (crtc)->base.id, (crtc)->name, ##__VA_ARGS__) + +#define dcnano_crtc_err(crtc, fmt, ...) \ + drm_err((crtc)->dev, "[CRTC:%d:%s] " fmt, \ + (crtc)->base.id, (crtc)->name, ##__VA_ARGS__) + +static inline struct dcnano_dev *crtc_to_dcnano_dev(struct drm_crtc *crtc) +{ + return to_dcnano_dev(crtc->dev); +} + +static void dcnano_crtc_mode_set_nofb_dpi(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + struct drm_display_mode *adj = &crtc->state->adjusted_mode; + u32 val; + + /* select output bus */ + dcnano_write(dcnano, DCNANO_DBICONFIG, DBICFG_BUS_OUTPUT_SEL_DPI); + + /* set bus format */ + dcnano_write(dcnano, DCNANO_DPICONFIG, DPICFG_DATA_FORMAT_D24); + + /* horizontal timing */ + val = HDISPLAY_END(adj->crtc_hdisplay) | + HDISPLAY_TOTAL(adj->crtc_htotal); + dcnano_write(dcnano, DCNANO_HDISPLAY, val); + + val = HSYNC_START(adj->crtc_hsync_start) | + HSYNC_END(adj->crtc_hsync_end) | HSYNC_PULSE_ENABLE; + if (adj->flags & DRM_MODE_FLAG_PHSYNC) + val |= HSYNC_POL_POSITIVE; + else + val |= HSYNC_POL_NEGATIVE; + dcnano_write(dcnano, DCNANO_HSYNC, val); + + /* vertical timing */ + val = VDISPLAY_END(adj->crtc_vdisplay) | + VDISPLAY_TOTAL(adj->crtc_vtotal); + dcnano_write(dcnano, DCNANO_VDISPLAY, val); + + val = VSYNC_START(adj->crtc_vsync_start) | + VSYNC_END(adj->crtc_vsync_end) | VSYNC_PULSE_ENABLE; + if (adj->flags & DRM_MODE_FLAG_PVSYNC) + val |= VSYNC_POL_POSITIVE; + else + val |= VSYNC_POL_NEGATIVE; + dcnano_write(dcnano, DCNANO_VSYNC, val); + + /* panel configuration */ + val = PANELCFG_DE_ENABLE | PANELCFG_DE_POL_POSITIVE | + PANELCFG_DATA_ENABLE | PANELCFG_DATA_POL_POSITIVE | + PANELCFG_CLOCK_ENABLE | PANELCFG_CLOCK_POL_POSITIVE | + PANELCFG_SEQUENCING_SOFTWARE; + dcnano_write(dcnano, DCNANO_PANELCONFIG, val); +} + +static bool dcnano_crtc_pll_clock_rate_is_valid(unsigned long pll_clk_rate) +{ + return pll_clk_rate >= DCNANO_CRTC_PLL_MIN_RATE && + pll_clk_rate <= DCNANO_CRTC_PLL_MAX_RATE; +} + +static unsigned long +dcnano_crtc_find_pll_clock_rate(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + unsigned long pll_clk_rate, rounded_pll_clk_rate; + int i; + + for (i = DCNANO_CRTC_PLL_MIN_DIV; i <= DCNANO_CRTC_PLL_MAX_DIV; i++) { + pll_clk_rate = mode->clock * 1000 * i; + + if (!dcnano_crtc_pll_clock_rate_is_valid(pll_clk_rate)) + continue; + + rounded_pll_clk_rate = clk_round_rate(dcnano->pll_clk, + pll_clk_rate); + if (rounded_pll_clk_rate != pll_clk_rate) { + dcnano_crtc_dbg(crtc, + "rounded pll clock rate %lu, expected %lu\n", + rounded_pll_clk_rate, pll_clk_rate); + continue; + } + + dcnano_crtc_dbg(crtc, "find pll clock rate %lu with div %d\n", + pll_clk_rate, i); + + return pll_clk_rate; + } + + dcnano_crtc_dbg(crtc, "failed to find pll clock rate\n"); + + return 0; +} + +static void dcnano_crtc_set_pixel_clock(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + struct drm_display_mode *adj = &crtc->state->adjusted_mode; + struct clk *parent; + unsigned long pixel_clk_rate = adj->crtc_clock * 1000; + unsigned long pll_clk_rate; + int ret; + + parent = clk_get_parent(dcnano->pixel_clk); + if (!parent) { + dcnano_crtc_err(crtc, "%s: no pixel clock's parent\n", __func__); + } else if (IS_ERR(parent)) { + ret = PTR_ERR(parent); + dcnano_crtc_err(crtc, + "%s: failed to get pixel clock's parent: %d\n", + __func__, ret); + return; + } + + pll_clk_rate = dcnano_crtc_find_pll_clock_rate(crtc, adj); + if (pll_clk_rate == 0) + dcnano_crtc_err(crtc, "%s: failed to find pll clock rate\n", + __func__); + + ret = clk_set_rate(dcnano->pll_clk, pll_clk_rate); + if (ret) + dcnano_crtc_err(crtc, "%s: failed to set pll clock rate: %d\n", + __func__, ret); + + /* FIXME: The rate of pixel clock's parent is pixel clock rate. */ + ret = clk_set_rate(parent, pixel_clk_rate); + if (ret) + dcnano_crtc_err(crtc, + "%s: failed to set pixel clock's parent rate: %d\n", + __func__, ret); + + ret = clk_set_rate(dcnano->pixel_clk, pixel_clk_rate); + if (ret) + dcnano_crtc_err(crtc, "%s: failed to set pixel clock rate: %d\n", + __func__, ret); + + ret = clk_prepare_enable(dcnano->pixel_clk); + if (ret) + dcnano_crtc_err(crtc, "%s: failed to enable pixel clock: %d\n", + __func__, ret); + + dcnano_crtc_dbg(crtc, "%s: get pll clock rate: %lu\n", + __func__, clk_get_rate(dcnano->pll_clk)); + + dcnano_crtc_dbg(crtc, "%s: get rate of pixel clock's parent: %lu\n", + __func__, clk_get_rate(parent)); + + dcnano_crtc_dbg(crtc, "%s: get pixel clock rate %lu\n", + __func__, clk_get_rate(dcnano->pixel_clk)); +} + +static enum drm_mode_status +dcnano_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + dcnano_crtc_dbg(crtc, "validating mode " DRM_MODE_FMT "\n", + DRM_MODE_ARG(mode)); + + if (dcnano_crtc_find_pll_clock_rate(crtc, mode) == 0) + return MODE_NOCLOCK; + + return MODE_OK; +} + +static void dcnano_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + struct drm_display_mode *adj = &crtc->state->adjusted_mode; + + dcnano_crtc_dbg(crtc, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj)); + + clk_prepare_enable(dcnano->axi_clk); + clk_prepare_enable(dcnano->ahb_clk); + + dcnano_crtc_set_pixel_clock(crtc); + + if (dcnano->port == DCNANO_DPI_PORT) + dcnano_crtc_mode_set_nofb_dpi(crtc); +} + +static void dcnano_crtc_queue_state_event(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc)); + WARN_ON(dcnano->event); + dcnano->event = crtc->state->event; + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static int dcnano_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + bool has_primary = state->plane_mask & drm_plane_mask(crtc->primary); + + if (state->active && !has_primary) + return -EINVAL; + + if (state->active_changed && state->active) + state->mode_changed = true; + + return 0; +} + +static void dcnano_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + /* + * Enable clocks by ourselves in case the plane callbacks + * need to access registers. + */ + clk_prepare_enable(dcnano->ahb_clk); + clk_prepare_enable(dcnano->pixel_clk); +} + +static void dcnano_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); + + if (!crtc->state->active && !old_crtc_state->active) + return; + + if (!drm_atomic_crtc_needs_modeset(crtc->state)) + dcnano_crtc_queue_state_event(crtc); +} + +static void dcnano_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + struct drm_plane *plane; + struct drm_plane_state *new_plane_state; + int i; + u32 primary_fb_fmt = 0; + u32 val; + + drm_crtc_vblank_on(crtc); + + for_each_new_plane_in_state(old_crtc_state->state, + plane, new_plane_state, i) { + if (!new_plane_state->fb) + continue; + + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + continue; + + switch (new_plane_state->fb->format->format) { + case DRM_FORMAT_RGB565: + primary_fb_fmt = FBCFG_FORMAT_R5G6B5; + break; + case DRM_FORMAT_XRGB8888: + primary_fb_fmt = FBCFG_FORMAT_R8G8B8; + break; + } + } + + val = FBCFG_OUTPUT_ENABLE | primary_fb_fmt; + + /* enable DPI timing and start a DPI transfer, if needed */ + if (dcnano->port == DCNANO_DPI_PORT) + val |= FBCFG_RESET_ENABLE; + + dcnano_write(dcnano, DCNANO_FRAMEBUFFERCONFIG, val); + + dcnano_crtc_queue_state_event(crtc); +} + +static void dcnano_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + /* simply write '0' to the framebuffer and timing control register */ + dcnano_write(dcnano, DCNANO_FRAMEBUFFERCONFIG, 0); + + drm_crtc_vblank_off(crtc); + + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); + clk_disable_unprepare(dcnano->axi_clk); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event && !crtc->state->active) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static bool +dcnano_crtc_get_scanout_position(struct drm_crtc *crtc, + bool in_vblank_irq, + int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + int hdisplay = mode->crtc_hdisplay; + int htotal = mode->crtc_htotal; + int vdisplay = mode->crtc_vdisplay; + int vtotal = mode->crtc_vtotal; + int x, y; + u32 val; + bool reliable; + + if (stime) + *stime = ktime_get(); + + val = dcnano_read(dcnano, DCNANO_DISPLAYCURRENTLOCATION); + + x = CURRENTLOCATION_X_GET(val); + y = CURRENTLOCATION_Y_GET(val); + + if (x < hdisplay) + *hpos = x + 1; /* active scanout area - positive */ + else + *hpos = x - (htotal - 1); /* inside vblank - negative */ + + if (y < vdisplay) + *vpos = y + 1; /* active scanout area - positive */ + else + *vpos = y - (vtotal - 1); /* inside vblank - negative */ + + reliable = true; + + if (etime) + *etime = ktime_get(); + + return reliable; +} + +static const struct drm_crtc_helper_funcs dcnano_crtc_helper_funcs = { + .mode_valid = dcnano_crtc_mode_valid, + .mode_set_nofb = dcnano_crtc_mode_set_nofb, + .atomic_check = dcnano_crtc_atomic_check, + .atomic_begin = dcnano_crtc_atomic_begin, + .atomic_flush = dcnano_crtc_atomic_flush, + .atomic_enable = dcnano_crtc_atomic_enable, + .atomic_disable = dcnano_crtc_atomic_disable, + .get_scanout_position = dcnano_crtc_get_scanout_position, +}; + +static u32 dcnano_crtc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + dcnano_write(dcnano, DCNANO_DEBUGCOUNTERSELECT, TOTAL_FRAME_CNT); + return dcnano_read(dcnano, DCNANO_DEBUGCOUNTERVALUE); +} + +static int dcnano_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + dcnano_write(dcnano, DCNANO_DISPLAYINTRENABLE, DISPLAYINTR_DISP0); + + return 0; +} + +static void dcnano_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc); + + dcnano_write(dcnano, DCNANO_DISPLAYINTRENABLE, 0); +} + +static const struct drm_crtc_funcs dcnano_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .get_vblank_counter = dcnano_crtc_get_vblank_counter, + .enable_vblank = dcnano_crtc_enable_vblank, + .disable_vblank = dcnano_crtc_disable_vblank, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, +}; + +irqreturn_t dcnano_irq_handler(int irq, void *data) +{ + struct drm_device *drm = data; + struct dcnano_dev *dcnano = to_dcnano_dev(drm); + unsigned long flags; + + /* DCNANO_DISPLAYINTR will automatically clear after a read. */ + dcnano_read(dcnano, DCNANO_DISPLAYINTR); + + drm_crtc_handle_vblank(&dcnano->crtc); + + spin_lock_irqsave(&drm->event_lock, flags); + if (dcnano->event) { + drm_crtc_send_vblank_event(&dcnano->crtc, dcnano->event); + dcnano->event = NULL; + drm_crtc_vblank_put(&dcnano->crtc); + } + spin_unlock_irqrestore(&drm->event_lock, flags); + + return IRQ_HANDLED; +} + +static int dcnano_get_pll_clock(struct dcnano_dev *dcnano) +{ + int ret, i; + + /* + * There are one divider clock and gate clock between the pixel + * clock and the pll clock, so walk through the clock tree 3 levels + * up to get the pll clock. + */ + dcnano->pll_clk = dcnano->pixel_clk; + for (i = 0; i < 3; i++) { + dcnano->pll_clk = clk_get_parent(dcnano->pll_clk); + if (IS_ERR(dcnano->pll_clk)) { + ret = PTR_ERR(dcnano->pll_clk); + drm_err(&dcnano->base, + "failed to get pll clock: %d\n", ret); + return ret; + } else if (!dcnano->pll_clk) { + drm_err(&dcnano->base, "no pll clock\n"); + return -ENODEV; + } + } + + return 0; +} + +static void dcnano_reset_all_debug_counters(struct dcnano_dev *dcnano) +{ + clk_prepare_enable(dcnano->ahb_clk); + clk_prepare_enable(dcnano->pixel_clk); + dcnano_write(dcnano, DCNANO_DEBUGCOUNTERSELECT, RESET_ALL_CNTS); + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); +} + +int dcnano_crtc_init(struct dcnano_dev *dcnano) +{ + int ret; + + ret = dcnano_plane_init(dcnano); + if (ret) + return ret; + + drm_crtc_helper_add(&dcnano->crtc, &dcnano_crtc_helper_funcs); + ret = drm_crtc_init_with_planes(&dcnano->base, &dcnano->crtc, + &dcnano->primary, NULL, + &dcnano_crtc_funcs, NULL); + if (ret) { + drm_err(&dcnano->base, "failed to initialize CRTC: %d\n", ret); + return ret; + } + + ret = dcnano_get_pll_clock(dcnano); + if (ret) + return ret; + + dcnano_reset_all_debug_counters(dcnano); + + return 0; +} diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-drv.c b/drivers/gpu/drm/imx/dcnano/dcnano-drv.c new file mode 100644 index 0000000000000000000000000000000000000000..d8524e574a8665e9993a66ac8470a906b612cc90 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/dcnano-drv.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2020,2021 NXP + */ + +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_irq.h> +#include <drm/drm_modeset_helper.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include "dcnano-drv.h" +#include "dcnano-reg.h" + +#define DRIVER_NAME "imx-dcnano-drm" + +static int legacyfb_depth = 32; +module_param(legacyfb_depth, uint, 0444); + +DEFINE_DRM_GEM_CMA_FOPS(dcnano_driver_fops); + +static struct drm_driver dcnano_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .irq_handler = dcnano_irq_handler, + DRM_GEM_CMA_DRIVER_OPS, + .fops = &dcnano_driver_fops, + .name = "imx-dcnano", + .desc = "i.MX DCNANO DRM graphics", + .date = "20201221", + .major = 1, + .minor = 0, + .patchlevel = 0, +}; + +static int dcnano_check_chip_info(struct dcnano_dev *dcnano) +{ + struct drm_device *drm = &dcnano->base; + u32 val; + int ret = 0; + + clk_prepare_enable(dcnano->ahb_clk); + clk_prepare_enable(dcnano->pixel_clk); + + val = dcnano_read(dcnano, DCNANO_DCCHIPREV); + if (val != DCCHIPREV) { + DRM_DEV_ERROR(drm->dev, "invalid chip revision(0x%08x)\n", val); + ret = -ENODEV; + goto err; + } + DRM_DEV_DEBUG(drm->dev, "chip revision is 0x%08x\n", val); + + val = dcnano_read(dcnano, DCNANO_DCCHIPDATE); + if (val != DCCHIPDATE) { + DRM_DEV_ERROR(drm->dev, "invalid chip date(0x%08x)\n", val); + ret = -ENODEV; + goto err; + } + DRM_DEV_DEBUG(drm->dev, "chip date is 0x%08x\n", val); + + val = dcnano_read(dcnano, DCNANO_DCCHIPPATCHREV); + if (val != DCCHIPPATCHREV) { + DRM_DEV_ERROR(drm->dev, + "invalid chip patch revision(0x%08x)\n", val); + ret = -ENODEV; + goto err; + } + DRM_DEV_DEBUG(drm->dev, "chip patch revision is 0x%08x\n", val); +err: + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); + return ret; +} + +static int dcnano_probe(struct platform_device *pdev) +{ + struct dcnano_dev *dcnano; + struct drm_device *drm; + int irq, ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + dcnano = devm_drm_dev_alloc(&pdev->dev, &dcnano_driver, + struct dcnano_dev, base); + if (IS_ERR(dcnano)) + return PTR_ERR(dcnano); + + drm = &dcnano->base; + dev_set_drvdata(&pdev->dev, dcnano); + + dcnano->mmio_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dcnano->mmio_base)) + return PTR_ERR(dcnano->mmio_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dcnano->axi_clk = devm_clk_get(drm->dev, "axi"); + if (IS_ERR(dcnano->axi_clk)) { + ret = PTR_ERR(dcnano->axi_clk); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(drm->dev, + "failed to get axi clk: %d\n", ret); + return ret; + } + + dcnano->ahb_clk = devm_clk_get(drm->dev, "ahb"); + if (IS_ERR(dcnano->ahb_clk)) { + ret = PTR_ERR(dcnano->ahb_clk); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(drm->dev, + "failed to get ahb clk: %d\n", ret); + return ret; + } + + dcnano->pixel_clk = devm_clk_get(drm->dev, "pixel"); + if (IS_ERR(dcnano->pixel_clk)) { + ret = PTR_ERR(dcnano->pixel_clk); + if (ret != -EPROBE_DEFER) + DRM_DEV_ERROR(drm->dev, + "failed to get pixel clk: %d\n", ret); + return ret; + } + + ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); + if (ret) { + DRM_DEV_ERROR(drm->dev, + "failed to set dma mask and coherent: %d\n", ret); + return ret; + } + + clk_prepare_enable(dcnano->ahb_clk); + clk_prepare_enable(dcnano->pixel_clk); + ret = drm_irq_install(drm, irq); + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); + + if (ret < 0) { + DRM_DEV_ERROR(drm->dev, + "failed to install IRQ handler: %d\n", ret); + return ret; + } + + ret = dcnano_check_chip_info(dcnano); + if (ret) + goto err_check_chip_info; + + ret = dcnano_kms_prepare(dcnano); + if (ret) + goto err_kms_prepare; + + ret = drm_dev_register(drm, 0); + if (ret) { + DRM_DEV_ERROR(drm->dev, + "failed to register drm device: %d\n", ret); + goto err_register; + } + + if (legacyfb_depth != 16 && legacyfb_depth != 32) { + DRM_DEV_INFO(drm->dev, + "Invalid legacyfb_depth. Defaulting to 32bpp\n"); + legacyfb_depth = 32; + } + + drm_fbdev_generic_setup(drm, legacyfb_depth); + + return 0; + +err_register: + drm_kms_helper_poll_fini(drm); +err_kms_prepare: +err_check_chip_info: + clk_prepare_enable(dcnano->ahb_clk); + clk_prepare_enable(dcnano->pixel_clk); + drm_irq_uninstall(drm); + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); + return ret; +} + +static int dcnano_remove(struct platform_device *pdev) +{ + struct dcnano_dev *dcnano = dev_get_drvdata(&pdev->dev); + struct drm_device *drm = &dcnano->base; + + drm_dev_unregister(drm); + + drm_kms_helper_poll_fini(drm); + + drm_atomic_helper_shutdown(drm); + + clk_prepare_enable(dcnano->ahb_clk); + clk_prepare_enable(dcnano->pixel_clk); + drm_irq_uninstall(drm); + clk_disable_unprepare(dcnano->pixel_clk); + clk_disable_unprepare(dcnano->ahb_clk); + + return 0; +} + +static int __maybe_unused dcnano_suspend(struct device *dev) +{ + struct dcnano_dev *dcnano = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(&dcnano->base); +} + +static int __maybe_unused dcnano_resume(struct device *dev) +{ + struct dcnano_dev *dcnano = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(&dcnano->base); +} + +static const struct dev_pm_ops dcnano_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dcnano_suspend, dcnano_resume) +}; + +static const struct of_device_id dcnano_dt_ids[] = { + { .compatible = "nxp,imx8ulp-dcnano", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dcnano_dt_ids); + +static struct platform_driver dcnano_platform_driver = { + .probe = dcnano_probe, + .remove = dcnano_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = dcnano_dt_ids, + .pm = &dcnano_pm_ops, + }, +}; +module_platform_driver(dcnano_platform_driver); + +MODULE_AUTHOR("NXP Semiconductor"); +MODULE_DESCRIPTION("i.MX DCNANO DRM driver"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-drv.h b/drivers/gpu/drm/imx/dcnano/dcnano-drv.h new file mode 100644 index 0000000000000000000000000000000000000000..287f8b3f0c60c050a6804ec605b1fcec2343f098 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/dcnano-drv.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +/* + * Copyright 2020,2021 NXP + */ + +#ifndef __DCNANO_DRV_H__ +#define __DCNANO_DRV_H__ + +#include <linux/clk.h> +#include <linux/irqreturn.h> +#include <linux/kernel.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_encoder.h> +#include <drm/drm_plane.h> +#include <drm/drm_vblank.h> + +enum dcnano_port { + DCNANO_DPI_PORT, + DCNANO_DBI_PORT, + DCNANO_PORT_NUM, +}; + +struct dcnano_dev { + struct drm_device base; + void __iomem *mmio_base; + + struct clk *axi_clk; + struct clk *ahb_clk; + struct clk *pixel_clk; + struct clk *pll_clk; + + struct drm_crtc crtc; + struct drm_plane primary; + struct drm_encoder encoder; + + struct drm_pending_vblank_event *event; + + enum dcnano_port port; +}; + +static inline struct dcnano_dev *to_dcnano_dev(struct drm_device *drm) +{ + return container_of(drm, struct dcnano_dev, base); +} + +irqreturn_t dcnano_irq_handler(int irq, void *data); + +int dcnano_crtc_init(struct dcnano_dev *dcnano); + +int dcnano_plane_init(struct dcnano_dev *dcnano); + +int dcnano_kms_prepare(struct dcnano_dev *dcnano); + +#endif diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-kms.c b/drivers/gpu/drm/imx/dcnano/dcnano-kms.c new file mode 100644 index 0000000000000000000000000000000000000000..223795fa0aa8f1aea3ee74a928e1d63736de8f49 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/dcnano-kms.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2020,2021 NXP + */ + +#include <linux/of.h> +#include <linux/of_graph.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_vblank.h> + +#include "dcnano-drv.h" +#include "dcnano-reg.h" + +static int dcnano_kms_init(struct dcnano_dev *dcnano) +{ + struct drm_device *drm = &dcnano->base; + struct drm_panel *panel; + struct drm_bridge *bridge; + struct drm_connector *connector; + struct device_node *np = drm->dev->of_node; + struct device_node *port, *ep, *remote; + struct of_endpoint endpoint; + u32 port_id; + bool found_ep = false; + int ret; + + ret = dcnano_crtc_init(dcnano); + if (ret) + return ret; + + for (port_id = 0; port_id < DCNANO_PORT_NUM; port_id++) { + port = of_graph_get_port_by_id(np, port_id); + if (!port) { + drm_err(drm, "failed to get output port%u\n", port_id); + return -EINVAL; + } + + for_each_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote) || + !of_device_is_available(remote->parent)) { + of_node_put(remote); + continue; + } + + of_node_put(remote); + + ret = of_graph_parse_endpoint(ep, &endpoint); + if (ret) { + drm_err(drm, + "failed to parse endpoint of port%u: %d\n", + port_id, ret); + of_node_put(ep); + of_node_put(port); + return ret; + } + + ret = drm_of_find_panel_or_bridge(np, + port_id, endpoint.id, + &panel, &bridge); + if (ret) { + if (ret == -ENODEV) { + drm_dbg(drm, + "no panel or bridge on port%u ep%d\n", + port_id, endpoint.id); + continue; + } else if (ret != -EPROBE_DEFER) { + drm_err(drm, + "failed to find panel or bridge on port%u ep%d: %d\n", + port_id, endpoint.id, ret); + } + of_node_put(ep); + of_node_put(port); + return ret; + } + + found_ep = true; + break; + } + + of_node_put(port); + dcnano->port = port_id; + + if (found_ep) { + drm_dbg(drm, "found valid endpoint%d @ port%u\n", + endpoint.id, port_id); + break; + } + } + + if (!found_ep) { + drm_info(drm, "no valid endpoint\n"); + return 0; + } + + if (panel) { + bridge = devm_drm_panel_bridge_add(drm->dev, panel); + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); + drm_err(drm, + "failed to add panel bridge on port%u ep%d: %d\n", + port_id, endpoint.id, ret); + goto err; + } + } + + dcnano->encoder.possible_crtcs = drm_crtc_mask(&dcnano->crtc); + ret = drm_simple_encoder_init(drm, &dcnano->encoder, + DRM_MODE_ENCODER_NONE); + if (ret) { + drm_err(drm, "failed to initialize encoder on port%u ep%d: %d\n", + port_id, endpoint.id, ret); + goto err; + } + + ret = drm_bridge_attach(&dcnano->encoder, bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + drm_err(drm, + "failed to attach bridge to encoder on port%u ep%d: %d\n", + port_id, endpoint.id, ret); + goto err; + } + + connector = drm_bridge_connector_init(drm, &dcnano->encoder); + if (IS_ERR(connector)) { + ret = PTR_ERR(connector); + drm_err(drm, + "failed to initialize bridge connector on port%u ep%d: %d\n", + port_id, endpoint.id, ret); + goto err; + } + + ret = drm_connector_attach_encoder(connector, &dcnano->encoder); + if (ret) + drm_err(drm, + "failed to attach encoder to connector on port%u ep%d: %d\n", + port_id, endpoint.id, ret); +err: + return ret; +} + +static const struct drm_mode_config_funcs dcnano_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int dcnano_kms_prepare(struct dcnano_dev *dcnano) +{ + struct drm_device *drm = &dcnano->base; + int ret; + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + ret = dcnano_kms_init(dcnano); + if (ret) + return ret; + + drm->mode_config.min_width = 32; + drm->mode_config.min_height = 32; + drm->mode_config.max_width = 1280; + drm->mode_config.max_height = 1280; + drm->mode_config.funcs = &dcnano_mode_config_funcs; + drm->max_vblank_count = DEBUGCOUNTERVALUE_MAX; + + ret = drm_vblank_init(drm, 1); + if (ret < 0) { + drm_err(drm, "failed to initialize vblank: %d\n", ret); + return ret; + } + + drm_mode_config_reset(drm); + + drm_kms_helper_poll_init(drm); + + return 0; +} diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-plane.c b/drivers/gpu/drm/imx/dcnano/dcnano-plane.c new file mode 100644 index 0000000000000000000000000000000000000000..bb332f2bb54c54cb3d2daac562606e3e17a69167 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/dcnano-plane.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2020,2021 NXP + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_print.h> +#include <drm/drm_rect.h> + +#include "dcnano-drv.h" +#include "dcnano-reg.h" + +#define DCNANO_FB_PITCH_ALIGN 128 /* in byte */ + +#define dcnano_plane_dbg(plane, fmt, ...) \ + drm_dbg_kms((plane)->dev, "[PLANE:%d:%s] " fmt, \ + (plane)->base.id, (plane)->name, ##__VA_ARGS__) + +/* primary plane formats */ +static const u32 dcnano_primary_plane_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, +}; + +static unsigned int +dcnano_primary_plane_format_count = ARRAY_SIZE(dcnano_primary_plane_formats); + +static inline struct dcnano_dev *plane_to_dcnano_dev(struct drm_plane *plane) +{ + return to_dcnano_dev(plane->dev); +} + +static inline dma_addr_t +drm_plane_state_to_baseaddr(struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *cma_obj; + unsigned int x = state->src.x1 >> 16; + unsigned int y = state->src.y1 >> 16; + + cma_obj = drm_fb_cma_get_gem_obj(fb, 0); + + return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y + + fb->format->cpp[0] * x; +} + +/***************************/ +/* primary plane functions */ +/***************************/ + +static int dcnano_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct dcnano_dev *dcnano = plane_to_dcnano_dev(plane); + struct drm_crtc_state *crtc_state; + struct drm_framebuffer *fb = state->fb; + struct drm_framebuffer *old_fb = plane->state->fb; + u32 src_w; + unsigned int pitch_no_padding; + int ret; + + /* ok to disable */ + if (!fb) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state->state, &dcnano->crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); + if (ret) + return ret; + + if (fb->pitches[0] > FBSTRIDE_MAX) { + dcnano_plane_dbg(plane, "fb pitches[0] 0x%08x is out of range\n", + fb->pitches[0]); + return -EINVAL; + } + + /* + * The primary plane's stride value in register has to be 128byte + * aligned, _but_ no dedicated padding is allowed. + */ + src_w = drm_rect_width(&state->src) >> 16; + pitch_no_padding = fb->format->cpp[0] * src_w; + if (fb->pitches[0] != pitch_no_padding) { + dcnano_plane_dbg(plane, + "fb pitches[0] 0x%08x should be no padding - 0x%08x\n", + fb->pitches[0], pitch_no_padding); + return -EINVAL; + } + + /* + * Force CRTC mode change if framebuffer stride or pixel format + * are changed. + */ + if (old_fb && + (fb->pitches[0] != old_fb->pitches[0] || + fb->format->format != old_fb->format->format)) + crtc_state->mode_changed = true; + + return 0; +} + +static void dcnano_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct dcnano_dev *dcnano = plane_to_dcnano_dev(plane); + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + dma_addr_t baseaddr; + + if (!fb) { + dcnano_plane_dbg(plane, "no fb\n"); + return; + } + + baseaddr = drm_plane_state_to_baseaddr(state); + + dcnano_plane_dbg(plane, "fb address %pad, pitch 0x%08x\n", + &baseaddr, fb->pitches[0]); + + dcnano_write(dcnano, DCNANO_FRAMEBUFFERADDRESS, baseaddr); + + dcnano_write(dcnano, DCNANO_FRAMEBUFFERSTRIDE, + ALIGN(fb->pitches[0], DCNANO_FB_PITCH_ALIGN)); +} + +static const struct drm_plane_helper_funcs dcnano_primary_plane_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, + .atomic_check = dcnano_primary_plane_atomic_check, + .atomic_update = dcnano_primary_plane_atomic_update, +}; + +static const struct drm_plane_funcs dcnano_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +int dcnano_plane_init(struct dcnano_dev *dcnano) +{ + int ret; + + /* primary plane */ + drm_plane_helper_add(&dcnano->primary, + &dcnano_primary_plane_helper_funcs); + ret = drm_universal_plane_init(&dcnano->base, &dcnano->primary, 0, + &dcnano_plane_funcs, + dcnano_primary_plane_formats, + dcnano_primary_plane_format_count, + NULL, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + drm_err(&dcnano->base, + "failed to initialize primary plane: %d\n", ret); + + return ret; +} diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-reg.h b/drivers/gpu/drm/imx/dcnano/dcnano-reg.h new file mode 100644 index 0000000000000000000000000000000000000000..aa31adcc2935f6025dd9a0fea4f4871f01b58ef4 --- /dev/null +++ b/drivers/gpu/drm/imx/dcnano/dcnano-reg.h @@ -0,0 +1,438 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +/* + * Copyright 2020,2021 NXP + */ + +#ifndef _DCNANO_REG_H_ +#define _DCNANO_REG_H_ + +#include <linux/bitfield.h> +#include <linux/io.h> +#include <linux/sizes.h> + +#define DCNANO_FRAMEBUFFERCONFIG 0x1240 +/* double buffered */ +#define FBCFG_FORMAT_MASK GENMASK(2, 0) +#define FBCFG_FORMAT_NONE FIELD_PREP(FBCFG_FORMAT_MASK, 0) +#define FBCFG_FORMAT_R4G4B4 FIELD_PREP(FBCFG_FORMAT_MASK, 1) +#define FBCFG_FORMAT_R5G5B5 FIELD_PREP(FBCFG_FORMAT_MASK, 2) +#define FBCFG_FORMAT_R5G6B5 FIELD_PREP(FBCFG_FORMAT_MASK, 3) +#define FBCFG_FORMAT_R8G8B8 FIELD_PREP(FBCFG_FORMAT_MASK, 4) +/* double buffered */ +#define FBCFG_MODE_LINEAR 0 +#define FBCFG_MODE_TILE4X4 BIT(4) +/* double buffered */ +#define FBCFG_OUTPUT_MASK BIT(8) +#define FBCFG_OUTPUT_DISABLE 0 +#define FBCFG_OUTPUT_ENABLE BIT(8) +/* double buffered */ +#define FBCFG_SWITCHPANEL_DISABLE 0 +#define FBCFG_SWITCHPANEL_ENABLE BIT(9) +/* double buffered */ +#define FBCFG_GAMMA_DISABLE 0 +#define FBCFG_GAMMA_ENABLE BIT(12) +#define FBCFG_VALID_WORKING 0 +#define FBCFG_VALID_PENDING BIT(16) +#define FBCFG_RESET_MASK BIT(20) +#define FBCFG_RESET_DISABLE 0 +#define FBCFG_RESET_ENABLE BIT(20) +#define FBCFG_UNDERFLOW_NO 0 +#define FBCFG_UNDERFLOW_YES BIT(24) +#define FBCFG_FLIP_INPROGRSS_NO 0 +#define FBCFG_FLIP_INPROGRSS_YES BIT(28) +#define FBCFG_BACK_PRES_DISABLE_NO 0 +#define FBCFG_BACK_PRES_DISABLE_YES BIT(29) + +/* double buffered */ +#define DCNANO_FRAMEBUFFERADDRESS 0x1260 +#define FBADDRESS_MASK GENMASK(31, 0) +#define FBADDRESS(x) FIELD_PREP(FBADDRESS_MASK, (x)) +#define FBADDRESS_TYPE_SYSTEM 0 +#define FBADDRESS_TYPE_VIRTUAL BIT(31) + +/* double buffered */ +#define DCNANO_FRAMEBUFFERSTRIDE 0x1280 +#define FBSTRIDE_MASK GENMASK(16, 0) +#define FBSTRIDE(x) FIELD_PREP(FBSTRIDE_MASK, (x)) +#define FBSTRIDE_MAX FIELD_MAX(FBSTRIDE_MASK) + +#define DCNANO_DISPLAYDITHERCONFIG 0x1360 +#define DITHERCFG_BLUESIZE_MASK GENMASK(3, 0) +#define DITHERCFG_BLUESIZE(x) FIELD_PREP(DITHERCFG_BLUESIZE_MASK, (x)) +#define DITHERCFG_GREENSIZE_MASK GENMASK(11, 8) +#define DITHERCFG_GREENSIZE(x) FIELD_PREP(DITHERCFG_GREENSIZE_MASK, (x)) +#define DITHERCFG_REDSIZE_MASK GENMASK(19, 16) +#define DITHERCFG_REDSIZE(x) FIELD_PREP(DITHERCFG_REDSIZE_MASK, (x)) +#define DITHERCFG_DISABLE 0 +/* double buffered */ +#define DITHERCFG_ENABLE BIT(31) + +#define DCNANO_DISPLAYDITHERTABLELOW 0x1380 +#define DITHERTLB_LOW_Y0X0_MASK GENMASK(3, 0) +#define DITHERTLB_LOW_Y0X0(x) FIELD_PREP(DITHERTLB_LOW_Y0X0_MASK, (x)) +#define DITHERTLB_LOW_Y0X1_MASK GENMASK(7, 4) +#define DITHERTLB_LOW_Y0X1(x) FIELD_PREP(DITHERTLB_LOW_Y0X1_MASK, (x)) +#define DITHERTLB_LOW_Y0X2_MASK GENMASK(11, 8) +#define DITHERTLB_LOW_Y0X2(x) FIELD_PREP(DITHERTLB_LOW_Y0X2_MASK, (x)) +#define DITHERTLB_LOW_Y0X3_MASK GENMASK(15, 12) +#define DITHERTLB_LOW_Y0X3(x) FIELD_PREP(DITHERTLB_LOW_Y0X3_MASK, (x)) +#define DITHERTLB_LOW_Y1X0_MASK GENMASK(19, 16) +#define DITHERTLB_LOW_Y1X0(x) FIELD_PREP(DITHERTLB_LOW_Y1X0_MASK, (x)) +#define DITHERTLB_LOW_Y1X1_MASK GENMASK(23, 20) +#define DITHERTLB_LOW_Y1X1(x) FIELD_PREP(DITHERTLB_LOW_Y1X1_MASK, (x)) +#define DITHERTLB_LOW_Y1X2_MASK GENMASK(27, 24) +#define DITHERTLB_LOW_Y1X2(x) FIELD_PREP(DITHERTLB_LOW_Y1X2_MASK, (x)) +#define DITHERTLB_LOW_Y1X3_MASK GENMASK(31, 28) +#define DITHERTLB_LOW_Y1X3(x) FIELD_PREP(DITHERTLB_LOW_Y1X3_MASK, (x)) + +#define DCNANO_DISPLAYDITHERTABLEHIGH 0x13a0 +#define DITHERTLB_HIGH_Y2X0_MASK GENMASK(3, 0) +#define DITHERTLB_HIGH_Y2X0(x) FIELD_PREP(DITHERTLB_HIGH_Y2X0_MASK, (x)) +#define DITHERTLB_HIGH_Y2X1_MASK GENMASK(7, 4) +#define DITHERTLB_HIGH_Y2X1(x) FIELD_PREP(DITHERTLB_HIGH_Y2X1_MASK, (x)) +#define DITHERTLB_HIGH_Y2X2_MASK GENMASK(11, 8) +#define DITHERTLB_HIGH_Y2X2(x) FIELD_PREP(DITHERTLB_HIGH_Y2X2_MASK, (x)) +#define DITHERTLB_HIGH_Y2X3_MASK GENMASK(15, 12) +#define DITHERTLB_HIGH_Y2X3(x) FIELD_PREP(DITHERTLB_HIGH_Y2X3_MASK, (x)) +#define DITHERTLB_HIGH_Y3X0_MASK GENMASK(19, 16) +#define DITHERTLB_HIGH_Y3X0(x) FIELD_PREP(DITHERTLB_HIGH_Y3X0_MASK, (x)) +#define DITHERTLB_HIGH_Y3X1_MASK GENMASK(23, 20) +#define DITHERTLB_HIGH_Y3X1(x) FIELD_PREP(DITHERTLB_HIGH_Y3X1_MASK, (x)) +#define DITHERTLB_HIGH_Y3X2_MASK GENMASK(27, 24) +#define DITHERTLB_HIGH_Y3X2(x) FIELD_PREP(DITHERTLB_HIGH_Y3X2_MASK, (x)) +#define DITHERTLB_HIGH_Y3X3_MASK GENMASK(31, 28) +#define DITHERTLB_HIGH_Y3X3(x) FIELD_PREP(DITHERTLB_HIGH_Y3X3_MASK, (x)) + +#define DCNANO_PANELCONFIG 0x13c0 +#define PANELCFG_DE_DISABLE 0 +#define PANELCFG_DE_ENABLE BIT(0) +#define PANELCFG_DE_POL_POSITIVE 0 +#define PANELCFG_DE_POL_NEGATIVE BIT(1) +/* double buffered? */ +#define PANELCFG_DATA_DISABLE 0 +#define PANELCFG_DATA_ENABLE BIT(4) +#define PANELCFG_DATA_POL_POSITIVE 0 +#define PANELCFG_DATA_POL_NEGATIVE BIT(5) +#define PANELCFG_CLOCK_DISABLE 0 +#define PANELCFG_CLOCK_ENABLE BIT(8) +#define PANELCFG_CLOCK_POL_POSITIVE 0 +#define PANELCFG_CLOCK_POL_NEGATIVE BIT(9) +#define PANELCFG_SEQUENCING_HARDWARE 0 +#define PANELCFG_SEQUENCING_SOFTWARE BIT(31) + +#define DCNANO_PANELTIMING 0x13e0 +#define PANELTIMING_POWER_ENABLE_MASK GENMASK(3, 0) +#define PANELTIMING_POWER_ENABLE(x) \ + FIELD_PREP(PANELTIMING_POWER_ENABLE_MASK, (x)) +#define PANELTIMING_BACKLIGHT_ENABLE_MASK GENMASK(7, 4) +#define PANELTIMING_BACKLIGHT_ENABLE(x) \ + FIELD_PREP(PANELTIMING_BACKLIGHT_ENABLE_MASK, (x)) +#define PANELTIMING_CLOCK_ENABLE_MASK GENMASK(11, 8) +#define PANELTIMING_CLOCK_ENABLE(x) \ + FIELD_PREP(PANELTIMING_CLOCK_ENABLE_MASK, (x)) +#define PANELTIMING_DATA_ENABLE_MASK GENMASK(15, 12) +#define PANELTIMING_DATA_ENABLE(x) \ + FIELD_PREP(PANELTIMING_DATA_ENABLE_MASK, (x)) +#define PANELTIMING_DATA_DISABLE_MASK GENMASK(19, 16) +#define PANELTIMING_DATA_DISABLE(x) \ + FIELD_PREP(PANELTIMING_DATA_DISABLE_MASK, (x)) +#define PANELTIMING_CLOCK_DISABLE_MASK GENMASK(23, 20) +#define PANELTIMING_CLOCK_DISABLE(x) \ + FIELD_PREP(PANELTIMING_CLOCK_DISABLE_MASK, (x)) +#define PANELTIMING_BACKLIGHT_DISABLE_MASK GENMASK(27, 24) +#define PANELTIMING_BACKLIGHT_DISABLE(x) \ + FIELD_PREP(PANELTIMING_BACKLIGHT_DISABLE_MASK, (x)) +#define PANELTIMING_POWER_DISABLE_MASK GENMASK(31, 28) +#define PANELTIMING_POWER_DISABLE(x) \ + FIELD_PREP(PANELTIMING_POWER_DISABLE_MASK, (x)) + +#define DCNANO_HDISPLAY 0x1400 +#define HDISPLAY_END_MASK GENMASK(12, 0) +#define HDISPLAY_END(x) FIELD_PREP(HDISPLAY_END_MASK, (x)) +#define HDISPLAY_TOTAL_MASK GENMASK(28, 16) +#define HDISPLAY_TOTAL(x) FIELD_PREP(HDISPLAY_TOTAL_MASK, (x)) + +#define DCNANO_HSYNC 0x1420 +#define HSYNC_START_MASK GENMASK(12, 0) +#define HSYNC_START(x) FIELD_PREP(HSYNC_START_MASK, (x)) +#define HSYNC_END_MASK GENMASK(28, 16) +#define HSYNC_END(x) FIELD_PREP(HSYNC_END_MASK, (x)) +/* double buffered? */ +#define HSYNC_PULSE_DISABLE 0 +#define HSYNC_PULSE_ENABLE BIT(30) +#define HSYNC_POL_MASK BIT(31) +#define HSYNC_POL_POSITIVE 0 +#define HSYNC_POL_NEGATIVE BIT(31) + +#define DCNANO_VDISPLAY 0x1480 +#define VDISPLAY_END_MASK GENMASK(11, 0) +#define VDISPLAY_END(x) FIELD_PREP(VDISPLAY_END_MASK, (x)) +#define VDISPLAY_TOTAL_MASK GENMASK(27, 16) +#define VDISPLAY_TOTAL(x) FIELD_PREP(VDISPLAY_TOTAL_MASK, (x)) + +#define DCNANO_VSYNC 0x14a0 +#define VSYNC_START_MASK GENMASK(11, 0) +#define VSYNC_START(x) FIELD_PREP(VSYNC_START_MASK, (x)) +#define VSYNC_END_MASK GENMASK(27, 16) +#define VSYNC_END(x) FIELD_PREP(VSYNC_END_MASK, (x)) +/* double buffered? */ +#define VSYNC_PULSE_DISABLE 0 +#define VSYNC_PULSE_ENABLE BIT(30) +#define VSYNC_POL_MASK BIT(31) +#define VSYNC_POL_POSITIVE 0 +#define VSYNC_POL_NEGATIVE BIT(31) + +#define DCNANO_DISPLAYCURRENTLOCATION 0x14c0 +#define CURRENTLOCATION_X_MASK GENMASK(15, 0) +#define CURRENTLOCATION_X(x) FIELD_PREP(CURRENTLOCATION_X_MASK, (x)) +#define CURRENTLOCATION_X_GET(x) FIELD_GET(CURRENTLOCATION_X_MASK, (x)) +#define CURRENTLOCATION_Y_MASK GENMASK(31, 16) +#define CURRENTLOCATION_Y(x) FIELD_PREP(CURRENTLOCATION_Y_MASK, (x)) +#define CURRENTLOCATION_Y_GET(x) FIELD_GET(CURRENTLOCATION_Y_MASK, (x)) + +#define DCNANO_GAMMAINDEX 0x14e0 +#define GAMMAINDEX_MASK GENMASK(7, 0) +#define GAMMAINDEX(x) FIELD_PREP(GAMMAINDEX_MASK, (x)) + +#define DCNANO_GAMMADATA 0x1500 +#define GAMMADATA_BLUE_MASK GENMASK(7, 0) +#define GAMMADATA_BLUE(x) FIELD_PREP(GAMMADATA_BLUE_MASK, (x)) +#define GAMMADATA_GREEN_MASK GENMASK(15, 8) +#define GAMMADATA_GREEN(x) FIELD_PREP(GAMMADATA_GREEN_MASK, (x)) +#define GAMMADATA_RED_MASK GENMASK(23, 16) +#define GAMMADATA_RED(x) FIELD_PREP(GAMMADATA_RED_MASK, (x)) + +#define DCNANO_CURSORCONFIG 0x1520 +/* double buffered */ +#define CURSORCFG_FORMAT_MASK GENMASK(1, 0) +#define CURSORCFG_FORMAT_NONE FIELD_PREP(CURSORCFG_FORMAT_MASK, 0) +#define CURSORCFG_FORMAT_MASKED FIELD_PREP(CURSORCFG_FORMAT_MASK, 1) +#define CURSORCFG_FORMAT_A8R8G8B8 FIELD_PREP(CURSORCFG_FORMAT_MASK, 2) +#define CURSORCFG_DISPLAY_MASK BIT(4) +#define CURSORCFG_DISPLAY0 0 +#define CURSORCFG_DISPLAY1 BIT(4) +/* double buffered */ +#define CURSORCFG_HOTSPOT_Y_MASK GENMASK(12, 8) +#define CURSORCFG_HOTSPOT_Y(x) FIELD_PREP(CURSORCFG_HOTSPOT_Y_MASK, 0) +/* double buffered */ +#define CURSORCFG_HOTSPOT_X_MASK GENMASK(20, 16) +#define CURSORCFG_HOTSPOT_X(x) FIELD_PREP(CURSORCFG_HOTSPOT_X_MASK, 0) +#define CURSORCFG_FLIP_INPROGRSS_NO 0 +#define CURSORCFG_FLIP_INPROGRSS_YES BIT(31) + +/* double buffered */ +#define DCNANO_CURSORADDRESS 0x1530 +#define CURSORADDRESS_MASK GENMASK(31, 0) +#define CURSORADDRESS(x) FIELD_PREP(CURSORADDRESS_MASK, (x)) +#define CURSORADDRESS_TYPE_SYSTEM 0 +#define CURSORADDRESS_TYPE_VIRTUAL BIT(31) + +/* double buffered */ +#define DCNANO_CURSORLOCATION 0x1540 +#define CURSORLOCATION_X_MASK GENMASK(12, 0) +#define CURSORLOCATION_X(x) FIELD_PREP(CURSORLOCATION_X_MASK, (x)) +#define CURSORLOCATION_X_MAX FIELD_MAX(CURSORLOCATION_X_MASK) +#define CURSORLOCATION_Y_MASK GENMASK(27, 16) +#define CURSORLOCATION_Y(x) FIELD_PREP(CURSORLOCATION_Y_MASK, (x)) +#define CURSORLOCATION_Y_MAX FIELD_MAX(CURSORLOCATION_Y_MASK) + +/* double buffered */ +#define DCNANO_CURSORBACKGROUND 0x1550 +/* double buffered */ +#define DCNANO_CURSORFOREGROUND 0x1560 +#define CURSOR_BLUE_MASK GENMASK(7, 0) +#define CURSOR_BLUE(x) FIELD_PREP(CURSOR_BLUE_MASK, (x)) +#define CURSOR_GREEN_MASK GENMASK(15, 8) +#define CURSOR_GREEN(x) FIELD_PREP(CURSOR_GREEN_MASK, (x)) +#define CURSOR_RED_MASK GENMASK(23, 16) +#define CURSOR_RED(x) FIELD_PREP(CURSOR_RED_MASK, (x)) + +#define DCNANO_DISPLAYINTR 0x1600 +#define DCNANO_DISPLAYINTRENABLE 0x1610 +#define DISPLAYINTR_DISP0 BIT(0) + +#define DCNANO_DBICONFIG 0x1620 +#define DBICFG_DBI_TYPE_MASK GENMASK(1, 0) +#define DBICFG_DBI_TYPE_A_FIXED_E FIELD_PREP(DBICFG_DBI_TYPE_MASK, 0) +#define DBICFG_DBI_TYPE_A_CLOCK_E FIELD_PREP(DBICFG_DBI_TYPE_MASK, 1) +#define DBICFG_DBI_TYPE_B FIELD_PREP(DBICFG_DBI_TYPE_MASK, 2) +#define DBICFG_DBI_TYPE_C FIELD_PREP(DBICFG_DBI_TYPE_MASK, 3) +#define DBICFG_DATA_FORMAT_MASK GENMASK(5, 2) + +/* 8bit data bus - D[7 : 0] */ +/* 8bpp */ +#define DBICFG_DATA_FORMAT_D8R3G3B2 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 0) +/* 12bpp */ +#define DBICFG_DATA_FORMAT_D8R4G4B4 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 1) +/* 16bpp */ +#define DBICFG_DATA_FORMAT_D8R5G6B5 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 2) +/* 18bpp */ +#define DBICFG_DATA_FORMAT_D8R6G6B6 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 3) +/* 24bpp */ +#define DBICFG_DATA_FORMAT_D8R8G8B8 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 4) + +/* 9bit data bus - D[8 : 0] */ +/* 18bpp */ +#define DBICFG_DATA_FORMAT_D9R6G6B6 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 5) + +/* 16bit data bus - D[15 : 0] */ +/* 8bpp */ +#define DBICFG_DATA_FORMAT_D16R3G3B2 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 6) +/* 12bpp */ +#define DBICFG_DATA_FORMAT_D16R4G4B4 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 7) +/* 16bpp */ +#define DBICFG_DATA_FORMAT_D16R5G6B5 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 8) +/* 18bpp */ +#define DBICFG_DATA_FORMAT_D16R6G6B6OP1 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 9) +#define DBICFG_DATA_FORMAT_D16R6G6B6OP2 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 10) +/* 24bpp */ +#define DBICFG_DATA_FORMAT_D16R8G8B8OP1 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 11) +#define DBICFG_DATA_FORMAT_D16R8G8B8OP2 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 12) + +#define DBICFG_BUS_OUTPUT_SEL_DPI 0 +#define DBICFG_BUS_OUTPUT_SEL_DBI BIT(6) +#define DBICFG_DBIX_POLARITY_DEFAULT 0 +#define DBICFG_DBIX_POLARITY_REVERSE BIT(7) +#define DBICFG_DBI_AC_TIME_UNIT_MASK GENMASK(11, 8) +#define DBICFG_DBI_AC_TIME_UNIT(x) \ + FIELD_PREP(DBICFG_DBI_AC_TIME_UNIT_MASK, (x)) +#define DBICFG_DBI_TYPEC_OPT_MASK GENMASK(13, 12) +#define DBICFG_DBI_TYPEC_OPT1 FIELD_PREP(DBICFG_DBI_TYPEC_OPT_MASK, 0) +#define DBICFG_DBI_TYPEC_OPT2 FIELD_PREP(DBICFG_DBI_TYPEC_OPT_MASK, 1) +#define DBICFG_DBI_TYPEC_OPT3 FIELD_PREP(DBICFG_DBI_TYPEC_OPT_MASK, 2) + +#define DCNANO_DBIIFRESET 0x1640 +#define DBIIF_LEVEL_NO_RESET 0 +#define DBIIF_LEVEL_RESET BIT(0) + +#define DCNANO_DBIWRCHAR1 0x1660 +#define DBIWR_PERIOD_MASK GENMASK(7, 0) +#define DBIWR_PERIOD(x) FIELD_PREP(DBIWR_PERIOD_MASK, (x)) +#define DBIWR_EOR_WR_ASSERT_MASK GENMASK(11, 8) +#define DBIWR_EOR_WR_ASSERT(x) FIELD_PREP(DBIWR_EOR_WR_ASSERT_MASK, (x)) +#define DBIWR_CS_ASSERT_MASK GENMASK(15, 12) +#define DBIWR_CS_ASSERT(x) FIELD_PREP(DBIWR_CS_ASSERT_MASK, (x)) + +#define DCNANO_DBIWRCHAR2 0x1680 +#define DBIWR_EOR_WR_DE_ASRT_MASK GENMASK(7, 0) +#define DBIWR_EOR_WR_DE_ASRT(x) \ + FIELD_PREP(DBIWR_EOR_WR_DE_ASRT_MASK, (x)) +#define DBIWR_CS_DE_ASRT_MASK GENMASK(15, 8) +#define DBIWR_CS_DE_ASRT(x) FIELD_PREP(DBIWR_CS_DE_ASRT_MASK, (x)) + +#define DCNANO_DBICMD 0x16a0 +#define DBICMD_WORD_MASK GENMASK(15, 0) +#define DBICMD_WORD(x) FIELD_PREP(DBICMD_WORD_MASK, (x)) +#define DBICMD_FLAG_MASK GENMASK(31, 30) +#define DBICMD_FLAG_ADDRESS FIELD_PREP(DBICMD_FLAG_MASK, 0) +#define DBICMD_FLAG_WRITE_MEM_START FIELD_PREP(DBICMD_FLAG_MASK, 1) +#define DBICMD_FLAG_PARAMETER_OR_DATA FIELD_PREP(DBICMD_FLAG_MASK, 2) +/* Read is unused. */ +#define DBICMD_FLAG_READ FIELD_PREP(DBICMD_FLAG_MASK, 3) + +#define DCNANO_DPICONFIG 0x16c0 +#define DPICFG_DATA_FORMAT_MASK GENMASK(2, 0) +#define DPICFG_DATA_FORMAT_D16CFG1 FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 0) +#define DPICFG_DATA_FORMAT_D16CFG2 FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 1) +#define DPICFG_DATA_FORMAT_D16CFG3 FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 2) +#define DPICFG_DATA_FORMAT_D18CFG1 FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 3) +#define DPICFG_DATA_FORMAT_D18CFG2 FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 4) +#define DPICFG_DATA_FORMAT_D24 FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 5) + +#define DCNANO_DCCHIPREV 0x16f0 +#define DCCHIPREV_MASK GENMASK(31, 0) +#define DCCHIPREV 0x00005543 + +#define DCNANO_DCCHIPDATE 0x1700 +#define DCCHIPDATE_MASK GENMASK(31, 0) +#define DCCHIPDATE 0x20180612 + +#define DCNANO_DCCHIPPATCHREV 0x1720 +#define DCCHIPPATCHREV_MASK GENMASK(31, 0) +#define DCCHIPPATCHREV 0x00000003 + +#define DCNANO_DCTILEINCFG 0x1740 +/* double buffered */ +#define DCTILEINCFG_TILE_FORMAT_MASK GENMASK(1, 0) +#define DCTILEINCFG_TILE_FORMAT_NONE \ + FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 0) +#define DCTILEINCFG_TILE_FORMAT_ARGB8888 \ + FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 1) +#define DCTILEINCFG_TILE_FORMAT_YUY2 \ + FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 2) +#define DCTILEINCFG_TILE_FORMAT_NV12 \ + FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 3) +#define DCTILEINCFG_YUV_STANDARD_MASK GENMASK(3, 2) +/* double buffered */ +#define DCTILEINCFG_YUV_BT601 \ + FIELD_PREP(DCTILEINCFG_YUV_STANDARD_MASK, 0) +#define DCTILEINCFG_YUV_BT709 \ + FIELD_PREP(DCTILEINCFG_YUV_STANDARD_MASK, 1) +/* double buffered */ +#define DCTILEINCFG_YUV2_RGB_EN_MASK BIT(4) +#define DCTILEINCFG_YUV2_RGB_ENABLE BIT(4) +#define DCTILEINCFG_YUV2_RGB_DISABLE 0 +#define DCTILEINCFG_CFG_MODE_EN BIT(5) +#define DCTILEINCFG_CFG_MODE_ENABLE BIT(5) +#define DCTILEINCFG_CFG_MODE_DISABLE 0 + +/* double buffered */ +#define DCNANO_DCTILEUVFRAMEBUFFERADR 0x1760 +#define DCTILEUVFB_ADDRESS_MASK GENMASK(31, 0) +#define DCTILEUVFB_ADDRESS(x) FIELD_PREP(DCTILEUVFB_ADDRESS_MASK, (x)) +#define DCTILEUVFB_ADDRESS_MAX FIELD_MAX(DCTILEUVFB_ADDRESS_MASK) + +/* double buffered */ +#define DCNANO_DCTILEUVFRAMEBUFFERSTR 0x1780 +#define DCTILEUVFB_STRIDE_MASK GENMASK(15, 0) +#define DCTILEUVFB_STRIDE(x) FIELD_PREP(DCTILEUVFB_STRIDE_MASK, (x)) +#define DCTILEUVFB_STRIDE_MAX FIELD_MAX(DCTILEUVFB_STRIDE_MASK) + +#define DCNANO_DCPRODUCTID 0x17b0 +#define DCPRODUCTID_MASK GENMASK(31, 0) +#define DCPRODUCTID 0x02000361 + +#define DCNANO_DCSTATUS 0x1800 +#define DCSTATUS_DBI_TYPEC_FIFO_FULL BIT(0) + +#define DCNANO_DEBUGCOUNTERSELECT 0x1820 +#define DEBUGCOUNTERSELECT_MASK GENMASK(7, 0) +#define TOTAL_AXI_RD_REQ_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 0) +#define TOTAL_AXI_RD_LAST_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 1) +#define TOTAL_AXI_REQ_BURST_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 2) +#define TOTAL_AXI_RD_BURST_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 3) +#define TOTAL_PIXEL_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 4) +#define TOTAL_FRAME_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 5) +#define TOTAL_INPUT_DBI_CMD_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 6) +#define TOTAL_OUTPUT_DBI_CMD_CNT FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 7) +#define DEBUG_SIGNALS0 FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 8) +#define RESET_ALL_CNTS FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 0xFF) + +#define DCNANO_DEBUGCOUNTERVALUE 0x1840 +#define DEBUGCOUNTERVALUE_MASK GENMASK(31, 0) +#define DEBUGCOUNTERVALUE(x) FIELD_PREP(DEBUGCOUNTERVALUE_MASK, (x)) +#define DEBUGCOUNTERVALUE_MAX FIELD_MAX(DEBUGCOUNTERVALUE_MASK) + +static inline u32 dcnano_read(struct dcnano_dev *dcnano, unsigned int reg) +{ + return readl(dcnano->mmio_base + reg); +} + +static inline void dcnano_write(struct dcnano_dev *dcnano, + unsigned int reg, u32 value) +{ + writel(value, dcnano->mmio_base + reg); +} + +static inline void dcnano_write_mask(struct dcnano_dev *dcnano, + unsigned int reg, u32 mask, u32 value) +{ + u32 tmp; + + tmp = dcnano_read(dcnano, reg); + tmp &= ~mask; + dcnano_write(dcnano, reg, tmp | value); +} + +#endif