From ace9429bb58fd418f0c81d4c2835699bddf6bde6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:27:49 +0200 Subject: Adding upstream version 6.6.15. Signed-off-by: Daniel Baumann --- drivers/gpu/drm/hisilicon/Kconfig | 7 + drivers/gpu/drm/hisilicon/Makefile | 7 + drivers/gpu/drm/hisilicon/hibmc/Kconfig | 14 + drivers/gpu/drm/hisilicon/hibmc/Makefile | 4 + drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 538 +++++++++++++ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 378 +++++++++ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 64 ++ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c | 99 +++ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h | 204 +++++ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 127 +++ drivers/gpu/drm/hisilicon/kirin/Kconfig | 11 + drivers/gpu/drm/hisilicon/kirin/Makefile | 6 + drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 906 +++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h | 99 +++ drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h | 226 ++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 951 +++++++++++++++++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 310 ++++++++ drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h | 57 ++ 18 files changed, 4008 insertions(+) create mode 100644 drivers/gpu/drm/hisilicon/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/Makefile create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/hibmc/Makefile create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/Kconfig create mode 100644 drivers/gpu/drm/hisilicon/kirin/Makefile create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c create mode 100644 drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.h (limited to 'drivers/gpu/drm/hisilicon') diff --git a/drivers/gpu/drm/hisilicon/Kconfig b/drivers/gpu/drm/hisilicon/Kconfig new file mode 100644 index 0000000000..cc5a244db2 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# hisilicon drm device configuration. +# Please keep this list sorted alphabetically + +source "drivers/gpu/drm/hisilicon/hibmc/Kconfig" +source "drivers/gpu/drm/hisilicon/kirin/Kconfig" diff --git a/drivers/gpu/drm/hisilicon/Makefile b/drivers/gpu/drm/hisilicon/Makefile new file mode 100644 index 0000000000..69dec6084b --- /dev/null +++ b/drivers/gpu/drm/hisilicon/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for hisilicon drm drivers. +# Please keep this list sorted alphabetically + +obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc/ +obj-$(CONFIG_DRM_HISI_KIRIN) += kirin/ diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig new file mode 100644 index 0000000000..126504318a --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_HISI_HIBMC + tristate "DRM Support for Hisilicon Hibmc" + depends on DRM && PCI && (ARM64 || COMPILE_TEST) + depends on MMU + select DRM_KMS_HELPER + select DRM_VRAM_HELPER + select DRM_TTM + select DRM_TTM_HELPER + select I2C + select I2C_ALGOBIT + help + Choose this option if you have a Hisilicon Hibmc soc chipset. + If M is selected the module will be called hibmc-drm. diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile new file mode 100644 index 0000000000..d25c75e60d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o + +obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c new file mode 100644 index 0000000000..89bed78f14 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Hisilicon Hibmc SoC drm driver + * + * Based on the bochs drm driver. + * + * Copyright (c) 2016 Huawei Limited. + * + * Author: + * Rongrong Zou + * Rongrong Zou + * Jianhua Li + */ + +#include + +#include +#include +#include +#include +#include + +#include "hibmc_drm_drv.h" +#include "hibmc_drm_regs.h" + +struct hibmc_display_panel_pll { + u64 M; + u64 N; + u64 OD; + u64 POD; +}; + +struct hibmc_dislay_pll_config { + u64 hdisplay; + u64 vdisplay; + u32 pll1_config_value; + u32 pll2_config_value; +}; + +static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { + {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, + {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, + {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, + {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, + {1280, 768, CRT_PLL1_HS_80MHZ, CRT_PLL2_HS_80MHZ}, + {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, + {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, + {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, + {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, + {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, +}; + +static int hibmc_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_framebuffer *fb = new_plane_state->fb; + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_crtc_state *crtc_state; + u32 src_w = new_plane_state->src_w >> 16; + u32 src_h = new_plane_state->src_h >> 16; + + if (!crtc || !fb) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) { + drm_dbg_atomic(plane->dev, "scale not support\n"); + return -EINVAL; + } + + if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) { + drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n"); + return -EINVAL; + } + + if (!crtc_state->enable) + return 0; + + if (new_plane_state->crtc_x + new_plane_state->crtc_w > + crtc_state->adjusted_mode.hdisplay || + new_plane_state->crtc_y + new_plane_state->crtc_h > + crtc_state->adjusted_mode.vdisplay) { + drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n"); + return -EINVAL; + } + + if (new_plane_state->fb->pitches[0] % 128 != 0) { + drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n"); + return -EINVAL; + } + return 0; +} + +static void hibmc_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + u32 reg; + s64 gpu_addr = 0; + u32 line_l; + struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev); + struct drm_gem_vram_object *gbo; + + if (!new_state->fb) + return; + + gbo = drm_gem_vram_of_gem(new_state->fb->obj[0]); + + gpu_addr = drm_gem_vram_offset(gbo); + if (WARN_ON_ONCE(gpu_addr < 0)) + return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */ + + writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS); + + reg = new_state->fb->width * (new_state->fb->format->cpp[0]); + + line_l = new_state->fb->pitches[0]; + writel(HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_WIDTH, reg) | + HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_OFFS, line_l), + priv->mmio + HIBMC_CRT_FB_WIDTH); + + /* SET PIXEL FORMAT */ + reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); + reg &= ~HIBMC_CRT_DISP_CTL_FORMAT_MASK; + reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, + new_state->fb->format->cpp[0] * 8 / 16); + writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); +} + +static const u32 channel_formats1[] = { + DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888 +}; + +static const struct drm_plane_funcs hibmc_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, +}; + +static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = { + DRM_GEM_VRAM_PLANE_HELPER_FUNCS, + .atomic_check = hibmc_plane_atomic_check, + .atomic_update = hibmc_plane_atomic_update, +}; + +static void hibmc_crtc_dpms(struct drm_crtc *crtc, u32 dpms) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(crtc->dev); + u32 reg; + + reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); + reg &= ~HIBMC_CRT_DISP_CTL_DPMS_MASK; + reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_DPMS, dpms); + reg &= ~HIBMC_CRT_DISP_CTL_TIMING_MASK; + if (dpms == HIBMC_CRT_DPMS_ON) + reg |= HIBMC_CRT_DISP_CTL_TIMING(1); + writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); +} + +static void hibmc_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + u32 reg; + struct hibmc_drm_private *priv = to_hibmc_drm_private(crtc->dev); + + hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate*/ + reg = readl(priv->mmio + HIBMC_CURRENT_GATE); + reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK; + reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK; + reg |= HIBMC_CURR_GATE_LOCALMEM(1); + reg |= HIBMC_CURR_GATE_DISPLAY(1); + hibmc_set_current_gate(priv, reg); + drm_crtc_vblank_on(crtc); + hibmc_crtc_dpms(crtc, HIBMC_CRT_DPMS_ON); +} + +static void hibmc_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + u32 reg; + struct hibmc_drm_private *priv = to_hibmc_drm_private(crtc->dev); + + hibmc_crtc_dpms(crtc, HIBMC_CRT_DPMS_OFF); + drm_crtc_vblank_off(crtc); + + hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_SLEEP); + + /* Enable display power gate & LOCALMEM power gate*/ + reg = readl(priv->mmio + HIBMC_CURRENT_GATE); + reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK; + reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK; + reg |= HIBMC_CURR_GATE_LOCALMEM(0); + reg |= HIBMC_CURR_GATE_DISPLAY(0); + hibmc_set_current_gate(priv, reg); +} + +static enum drm_mode_status +hibmc_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + size_t i = 0; + int vrefresh = drm_mode_vrefresh(mode); + + if (vrefresh < 59 || vrefresh > 61) + return MODE_NOCLOCK; + + for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { + if (hibmc_pll_table[i].hdisplay == mode->hdisplay && + hibmc_pll_table[i].vdisplay == mode->vdisplay) + return MODE_OK; + } + + return MODE_BAD; +} + +static u32 format_pll_reg(void) +{ + u32 pllreg = 0; + struct hibmc_display_panel_pll pll = {0}; + + /* + * Note that all PLL's have the same format. Here, + * we just use Panel PLL parameter to work out the bit + * fields in the register.On returning a 32 bit number, the value can + * be applied to any PLL in the calling function. + */ + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_BYPASS, 0); + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_POWER, 1); + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_INPUT, 0); + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_POD, pll.POD); + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_OD, pll.OD); + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_N, pll.N); + pllreg |= HIBMC_FIELD(HIBMC_PLL_CTRL_M, pll.M); + + return pllreg; +} + +static void set_vclock_hisilicon(struct drm_device *dev, u64 pll) +{ + u32 val; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + + val = readl(priv->mmio + CRT_PLL1_HS); + val &= ~(CRT_PLL1_HS_OUTER_BYPASS(1)); + writel(val, priv->mmio + CRT_PLL1_HS); + + val = CRT_PLL1_HS_INTER_BYPASS(1) | CRT_PLL1_HS_POWERON(1); + writel(val, priv->mmio + CRT_PLL1_HS); + + writel(pll, priv->mmio + CRT_PLL1_HS); + + usleep_range(1000, 2000); + + val = pll & ~(CRT_PLL1_HS_POWERON(1)); + writel(val, priv->mmio + CRT_PLL1_HS); + + usleep_range(1000, 2000); + + val &= ~(CRT_PLL1_HS_INTER_BYPASS(1)); + writel(val, priv->mmio + CRT_PLL1_HS); + + usleep_range(1000, 2000); + + val |= CRT_PLL1_HS_OUTER_BYPASS(1); + writel(val, priv->mmio + CRT_PLL1_HS); +} + +static void get_pll_config(u64 x, u64 y, u32 *pll1, u32 *pll2) +{ + size_t i; + size_t count = ARRAY_SIZE(hibmc_pll_table); + + for (i = 0; i < count; i++) { + if (hibmc_pll_table[i].hdisplay == x && + hibmc_pll_table[i].vdisplay == y) { + *pll1 = hibmc_pll_table[i].pll1_config_value; + *pll2 = hibmc_pll_table[i].pll2_config_value; + return; + } + } + + /* if found none, we use default value */ + *pll1 = CRT_PLL1_HS_25MHZ; + *pll2 = CRT_PLL2_HS_25MHZ; +} + +/* + * This function takes care the extra registers and bit fields required to + * setup a mode in board. + * Explanation about Display Control register: + * FPGA only supports 7 predefined pixel clocks, and clock select is + * in bit 4:0 of new register 0x802a8. + */ +static u32 display_ctrl_adjust(struct drm_device *dev, + struct drm_display_mode *mode, + u32 ctrl) +{ + u64 x, y; + u32 pll1; /* bit[31:0] of PLL */ + u32 pll2; /* bit[63:32] of PLL */ + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + + x = mode->hdisplay; + y = mode->vdisplay; + + get_pll_config(x, y, &pll1, &pll2); + writel(pll2, priv->mmio + CRT_PLL2_HS); + set_vclock_hisilicon(dev, pll1); + + /* + * Hisilicon has to set up the top-left and bottom-right + * registers as well. + * Note that normal chip only use those two register for + * auto-centering mode. + */ + writel(HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_TL_TOP, 0) | + HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_TL_LEFT, 0), + priv->mmio + HIBMC_CRT_AUTO_CENTERING_TL); + + writel(HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM, y - 1) | + HIBMC_FIELD(HIBMC_CRT_AUTO_CENTERING_BR_RIGHT, x - 1), + priv->mmio + HIBMC_CRT_AUTO_CENTERING_BR); + + /* + * Assume common fields in ctrl have been properly set before + * calling this function. + * This function only sets the extra fields in ctrl. + */ + + /* Set bit 25 of display controller: Select CRT or VGA clock */ + ctrl &= ~HIBMC_CRT_DISP_CTL_CRTSELECT_MASK; + ctrl &= ~HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK; + + ctrl |= HIBMC_CRT_DISP_CTL_CRTSELECT(HIBMC_CRTSELECT_CRT); + + /* clock_phase_polarity is 0 */ + ctrl |= HIBMC_CRT_DISP_CTL_CLOCK_PHASE(0); + + writel(ctrl, priv->mmio + HIBMC_CRT_DISP_CTL); + + return ctrl; +} + +static void hibmc_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + u32 val; + struct drm_display_mode *mode = &crtc->state->mode; + struct drm_device *dev = crtc->dev; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + u32 width = mode->hsync_end - mode->hsync_start; + u32 height = mode->vsync_end - mode->vsync_start; + + writel(format_pll_reg(), priv->mmio + HIBMC_CRT_PLL_CTRL); + writel(HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_TOTAL, mode->htotal - 1) | + HIBMC_FIELD(HIBMC_CRT_HORZ_TOTAL_DISP_END, mode->hdisplay - 1), + priv->mmio + HIBMC_CRT_HORZ_TOTAL); + + writel(HIBMC_FIELD(HIBMC_CRT_HORZ_SYNC_WIDTH, width) | + HIBMC_FIELD(HIBMC_CRT_HORZ_SYNC_START, mode->hsync_start - 1), + priv->mmio + HIBMC_CRT_HORZ_SYNC); + + writel(HIBMC_FIELD(HIBMC_CRT_VERT_TOTAL_TOTAL, mode->vtotal - 1) | + HIBMC_FIELD(HIBMC_CRT_VERT_TOTAL_DISP_END, mode->vdisplay - 1), + priv->mmio + HIBMC_CRT_VERT_TOTAL); + + writel(HIBMC_FIELD(HIBMC_CRT_VERT_SYNC_HEIGHT, height) | + HIBMC_FIELD(HIBMC_CRT_VERT_SYNC_START, mode->vsync_start - 1), + priv->mmio + HIBMC_CRT_VERT_SYNC); + + val = HIBMC_FIELD(HIBMC_CRT_DISP_CTL_VSYNC_PHASE, 0); + val |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_HSYNC_PHASE, 0); + val |= HIBMC_CRT_DISP_CTL_TIMING(1); + val |= HIBMC_CRT_DISP_CTL_PLANE(1); + + display_ctrl_adjust(dev, mode, val); +} + +static void hibmc_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + u32 reg; + struct drm_device *dev = crtc->dev; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + + hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate*/ + reg = readl(priv->mmio + HIBMC_CURRENT_GATE); + reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK; + reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK; + reg |= HIBMC_CURR_GATE_DISPLAY(1); + reg |= HIBMC_CURR_GATE_LOCALMEM(1); + hibmc_set_current_gate(priv, reg); + + /* We can add more initialization as needed. */ +} + +static void hibmc_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) + +{ + unsigned long flags; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (crtc->state->event) + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); +} + +static int hibmc_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(crtc->dev); + + writel(HIBMC_RAW_INTERRUPT_EN_VBLANK(1), + priv->mmio + HIBMC_RAW_INTERRUPT_EN); + + return 0; +} + +static void hibmc_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(crtc->dev); + + writel(HIBMC_RAW_INTERRUPT_EN_VBLANK(0), + priv->mmio + HIBMC_RAW_INTERRUPT_EN); +} + +static void hibmc_crtc_load_lut(struct drm_crtc *crtc) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(crtc->dev); + void __iomem *mmio = priv->mmio; + u16 *r, *g, *b; + u32 reg; + u32 i; + + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + for (i = 0; i < crtc->gamma_size; i++) { + u32 offset = i << 2; + u8 red = *r++ >> 8; + u8 green = *g++ >> 8; + u8 blue = *b++ >> 8; + u32 rgb = (red << 16) | (green << 8) | blue; + + writel(rgb, mmio + HIBMC_CRT_PALETTE + offset); + } + + reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); + reg |= HIBMC_FIELD(HIBMC_CTL_DISP_CTL_GAMMA, 1); + writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); +} + +static int hibmc_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + hibmc_crtc_load_lut(crtc); + + return 0; +} + +static const struct drm_crtc_funcs hibmc_crtc_funcs = { + .page_flip = drm_atomic_helper_page_flip, + .set_config = drm_atomic_helper_set_config, + .destroy = drm_crtc_cleanup, + .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 = hibmc_crtc_enable_vblank, + .disable_vblank = hibmc_crtc_disable_vblank, + .gamma_set = hibmc_crtc_gamma_set, +}; + +static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = { + .mode_set_nofb = hibmc_crtc_mode_set_nofb, + .atomic_begin = hibmc_crtc_atomic_begin, + .atomic_flush = hibmc_crtc_atomic_flush, + .atomic_enable = hibmc_crtc_atomic_enable, + .atomic_disable = hibmc_crtc_atomic_disable, + .mode_valid = hibmc_crtc_mode_valid, +}; + +int hibmc_de_init(struct hibmc_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + struct drm_crtc *crtc = &priv->crtc; + struct drm_plane *plane = &priv->primary_plane; + int ret; + + ret = drm_universal_plane_init(dev, plane, 1, &hibmc_plane_funcs, + channel_formats1, + ARRAY_SIZE(channel_formats1), + NULL, + DRM_PLANE_TYPE_PRIMARY, + NULL); + + if (ret) { + drm_err(dev, "failed to init plane: %d\n", ret); + return ret; + } + + drm_plane_helper_add(plane, &hibmc_plane_helper_funcs); + + ret = drm_crtc_init_with_planes(dev, crtc, plane, + NULL, &hibmc_crtc_funcs, NULL); + if (ret) { + drm_err(dev, "failed to init crtc: %d\n", ret); + return ret; + } + + ret = drm_mode_crtc_set_gamma_size(crtc, 256); + if (ret) { + drm_err(dev, "failed to set gamma size: %d\n", ret); + return ret; + } + drm_crtc_helper_add(crtc, &hibmc_crtc_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c new file mode 100644 index 0000000000..8a98fa276e --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Hisilicon Hibmc SoC drm driver + * + * Based on the bochs drm driver. + * + * Copyright (c) 2016 Huawei Limited. + * + * Author: + * Rongrong Zou + * Rongrong Zou + * Jianhua Li + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hibmc_drm_drv.h" +#include "hibmc_drm_regs.h" + +DEFINE_DRM_GEM_FOPS(hibmc_fops); + +static irqreturn_t hibmc_interrupt(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + u32 status; + + status = readl(priv->mmio + HIBMC_RAW_INTERRUPT); + + if (status & HIBMC_RAW_INTERRUPT_VBLANK(1)) { + writel(HIBMC_RAW_INTERRUPT_VBLANK(1), + priv->mmio + HIBMC_RAW_INTERRUPT); + drm_handle_vblank(dev, 0); + } + + return IRQ_HANDLED; +} + +static int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + return drm_gem_vram_fill_create_dumb(file, dev, 0, 128, args); +} + +static const struct drm_driver hibmc_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &hibmc_fops, + .name = "hibmc", + .date = "20160828", + .desc = "hibmc drm driver", + .major = 1, + .minor = 0, + .debugfs_init = drm_vram_mm_debugfs_init, + .dumb_create = hibmc_dumb_create, + .dumb_map_offset = drm_gem_ttm_dumb_map_offset, +}; + +static int __maybe_unused hibmc_pm_suspend(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(drm_dev); +} + +static int __maybe_unused hibmc_pm_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(drm_dev); +} + +static const struct dev_pm_ops hibmc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(hibmc_pm_suspend, + hibmc_pm_resume) +}; + +static const struct drm_mode_config_funcs hibmc_mode_funcs = { + .mode_valid = drm_vram_helper_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_gem_fb_create, +}; + +static int hibmc_kms_init(struct hibmc_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + int ret; + + ret = drmm_mode_config_init(dev); + if (ret) + return ret; + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 1920; + dev->mode_config.max_height = 1200; + + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + + dev->mode_config.funcs = (void *)&hibmc_mode_funcs; + + ret = hibmc_de_init(priv); + if (ret) { + drm_err(dev, "failed to init de: %d\n", ret); + return ret; + } + + ret = hibmc_vdac_init(priv); + if (ret) { + drm_err(dev, "failed to init vdac: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * It can operate in one of three modes: 0, 1 or Sleep. + */ +void hibmc_set_power_mode(struct hibmc_drm_private *priv, u32 power_mode) +{ + u32 control_value = 0; + void __iomem *mmio = priv->mmio; + u32 input = 1; + + if (power_mode > HIBMC_PW_MODE_CTL_MODE_SLEEP) + return; + + if (power_mode == HIBMC_PW_MODE_CTL_MODE_SLEEP) + input = 0; + + control_value = readl(mmio + HIBMC_POWER_MODE_CTRL); + control_value &= ~(HIBMC_PW_MODE_CTL_MODE_MASK | + HIBMC_PW_MODE_CTL_OSC_INPUT_MASK); + control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_MODE, power_mode); + control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_OSC_INPUT, input); + writel(control_value, mmio + HIBMC_POWER_MODE_CTRL); +} + +void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) +{ + u32 gate_reg; + u32 mode; + void __iomem *mmio = priv->mmio; + + /* Get current power mode. */ + mode = (readl(mmio + HIBMC_POWER_MODE_CTRL) & + HIBMC_PW_MODE_CTL_MODE_MASK) >> HIBMC_PW_MODE_CTL_MODE_SHIFT; + + switch (mode) { + case HIBMC_PW_MODE_CTL_MODE_MODE0: + gate_reg = HIBMC_MODE0_GATE; + break; + + case HIBMC_PW_MODE_CTL_MODE_MODE1: + gate_reg = HIBMC_MODE1_GATE; + break; + + default: + gate_reg = HIBMC_MODE0_GATE; + break; + } + writel(gate, mmio + gate_reg); +} + +static void hibmc_hw_config(struct hibmc_drm_private *priv) +{ + u32 reg; + + /* On hardware reset, power mode 0 is default. */ + hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate*/ + reg = readl(priv->mmio + HIBMC_CURRENT_GATE); + reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK; + reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK; + reg |= HIBMC_CURR_GATE_DISPLAY(1); + reg |= HIBMC_CURR_GATE_LOCALMEM(1); + + hibmc_set_current_gate(priv, reg); + + /* + * Reset the memory controller. If the memory controller + * is not reset in chip,the system might hang when sw accesses + * the memory.The memory should be resetted after + * changing the MXCLK. + */ + reg = readl(priv->mmio + HIBMC_MISC_CTRL); + reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK; + reg |= HIBMC_MSCCTL_LOCALMEM_RESET(0); + writel(reg, priv->mmio + HIBMC_MISC_CTRL); + + reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK; + reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1); + + writel(reg, priv->mmio + HIBMC_MISC_CTRL); +} + +static int hibmc_hw_map(struct hibmc_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + resource_size_t ioaddr, iosize; + + ioaddr = pci_resource_start(pdev, 1); + iosize = pci_resource_len(pdev, 1); + priv->mmio = devm_ioremap(dev->dev, ioaddr, iosize); + if (!priv->mmio) { + drm_err(dev, "Cannot map mmio region\n"); + return -ENOMEM; + } + + return 0; +} + +static int hibmc_hw_init(struct hibmc_drm_private *priv) +{ + int ret; + + ret = hibmc_hw_map(priv); + if (ret) + return ret; + + hibmc_hw_config(priv); + + return 0; +} + +static int hibmc_unload(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + + drm_atomic_helper_shutdown(dev); + + free_irq(pdev->irq, dev); + + pci_disable_msi(to_pci_dev(dev->dev)); + + return 0; +} + +static int hibmc_load(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + int ret; + + ret = hibmc_hw_init(priv); + if (ret) + goto err; + + ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (ret) { + drm_err(dev, "Error initializing VRAM MM; %d\n", ret); + goto err; + } + + ret = hibmc_kms_init(priv); + if (ret) + goto err; + + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret) { + drm_err(dev, "failed to initialize vblank: %d\n", ret); + goto err; + } + + ret = pci_enable_msi(pdev); + if (ret) { + drm_warn(dev, "enabling MSI failed: %d\n", ret); + } else { + /* PCI devices require shared interrupts. */ + ret = request_irq(pdev->irq, hibmc_interrupt, IRQF_SHARED, + dev->driver->name, dev); + if (ret) + drm_warn(dev, "install irq failed: %d\n", ret); + } + + /* reset all the states of crtc/plane/encoder/connector */ + drm_mode_config_reset(dev); + + return 0; + +err: + hibmc_unload(dev); + drm_err(dev, "failed to initialize drm driver: %d\n", ret); + return ret; +} + +static int hibmc_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hibmc_drm_private *priv; + struct drm_device *dev; + int ret; + + ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &hibmc_driver); + if (ret) + return ret; + + priv = devm_drm_dev_alloc(&pdev->dev, &hibmc_driver, + struct hibmc_drm_private, dev); + if (IS_ERR(priv)) { + DRM_ERROR("failed to allocate drm_device\n"); + return PTR_ERR(priv); + } + + dev = &priv->dev; + pci_set_drvdata(pdev, dev); + + ret = pcim_enable_device(pdev); + if (ret) { + drm_err(dev, "failed to enable pci device: %d\n", ret); + goto err_return; + } + + ret = hibmc_load(dev); + if (ret) { + drm_err(dev, "failed to load hibmc: %d\n", ret); + goto err_return; + } + + ret = drm_dev_register(dev, 0); + if (ret) { + drm_err(dev, "failed to register drv for userspace access: %d\n", + ret); + goto err_unload; + } + + drm_fbdev_generic_setup(dev, 32); + + return 0; + +err_unload: + hibmc_unload(dev); +err_return: + return ret; +} + +static void hibmc_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_dev_unregister(dev); + hibmc_unload(dev); +} + +static const struct pci_device_id hibmc_pci_table[] = { + { PCI_VDEVICE(HUAWEI, 0x1711) }, + {0,} +}; + +static struct pci_driver hibmc_pci_driver = { + .name = "hibmc-drm", + .id_table = hibmc_pci_table, + .probe = hibmc_pci_probe, + .remove = hibmc_pci_remove, + .driver.pm = &hibmc_pm_ops, +}; + +drm_module_pci_driver(hibmc_pci_driver); + +MODULE_DEVICE_TABLE(pci, hibmc_pci_table); +MODULE_AUTHOR("RongrongZou "); +MODULE_DESCRIPTION("DRM Driver for Hisilicon Hibmc"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h new file mode 100644 index 0000000000..f957552c6c --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Hisilicon Hibmc SoC drm driver + * + * Based on the bochs drm driver. + * + * Copyright (c) 2016 Huawei Limited. + * + * Author: + * Rongrong Zou + * Rongrong Zou + * Jianhua Li + */ + +#ifndef HIBMC_DRM_DRV_H +#define HIBMC_DRM_DRV_H + +#include +#include +#include + +#include +#include + +struct hibmc_connector { + struct drm_connector base; + + struct i2c_adapter adapter; + struct i2c_algo_bit_data bit_data; +}; + +struct hibmc_drm_private { + /* hw */ + void __iomem *mmio; + + /* drm */ + struct drm_device dev; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct hibmc_connector connector; +}; + +static inline struct hibmc_connector *to_hibmc_connector(struct drm_connector *connector) +{ + return container_of(connector, struct hibmc_connector, base); +} + +static inline struct hibmc_drm_private *to_hibmc_drm_private(struct drm_device *dev) +{ + return container_of(dev, struct hibmc_drm_private, dev); +} + +void hibmc_set_power_mode(struct hibmc_drm_private *priv, + u32 power_mode); +void hibmc_set_current_gate(struct hibmc_drm_private *priv, + u32 gate); + +int hibmc_de_init(struct hibmc_drm_private *priv); +int hibmc_vdac_init(struct hibmc_drm_private *priv); + +int hibmc_mm_init(struct hibmc_drm_private *hibmc); +int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_connector *connector); + +#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c new file mode 100644 index 0000000000..410bd019bb --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Hisilicon Hibmc SoC drm driver + * + * Based on the bochs drm driver. + * + * Copyright (c) 2016 Huawei Limited. + * + * Author: + * Tian Tao + */ + +#include +#include + +#include +#include + +#include "hibmc_drm_drv.h" + +#define GPIO_DATA 0x0802A0 +#define GPIO_DATA_DIRECTION 0x0802A4 + +#define I2C_SCL_MASK BIT(0) +#define I2C_SDA_MASK BIT(1) + +static void hibmc_set_i2c_signal(void *data, u32 mask, int value) +{ + struct hibmc_connector *hibmc_connector = data; + struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev); + u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION); + + if (value) { + tmp_dir &= ~mask; + writel(tmp_dir, priv->mmio + GPIO_DATA_DIRECTION); + } else { + u32 tmp_data = readl(priv->mmio + GPIO_DATA); + + tmp_data &= ~mask; + writel(tmp_data, priv->mmio + GPIO_DATA); + + tmp_dir |= mask; + writel(tmp_dir, priv->mmio + GPIO_DATA_DIRECTION); + } +} + +static int hibmc_get_i2c_signal(void *data, u32 mask) +{ + struct hibmc_connector *hibmc_connector = data; + struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev); + u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION); + + if ((tmp_dir & mask) != mask) { + tmp_dir &= ~mask; + writel(tmp_dir, priv->mmio + GPIO_DATA_DIRECTION); + } + + return (readl(priv->mmio + GPIO_DATA) & mask) ? 1 : 0; +} + +static void hibmc_ddc_setsda(void *data, int state) +{ + hibmc_set_i2c_signal(data, I2C_SDA_MASK, state); +} + +static void hibmc_ddc_setscl(void *data, int state) +{ + hibmc_set_i2c_signal(data, I2C_SCL_MASK, state); +} + +static int hibmc_ddc_getsda(void *data) +{ + return hibmc_get_i2c_signal(data, I2C_SDA_MASK); +} + +static int hibmc_ddc_getscl(void *data) +{ + return hibmc_get_i2c_signal(data, I2C_SCL_MASK); +} + +int hibmc_ddc_create(struct drm_device *drm_dev, + struct hibmc_connector *connector) +{ + connector->adapter.owner = THIS_MODULE; + connector->adapter.class = I2C_CLASS_DDC; + snprintf(connector->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus"); + connector->adapter.dev.parent = drm_dev->dev; + i2c_set_adapdata(&connector->adapter, connector); + connector->adapter.algo_data = &connector->bit_data; + + connector->bit_data.udelay = 20; + connector->bit_data.timeout = usecs_to_jiffies(2000); + connector->bit_data.data = connector; + connector->bit_data.setsda = hibmc_ddc_setsda; + connector->bit_data.setscl = hibmc_ddc_setscl; + connector->bit_data.getsda = hibmc_ddc_getsda; + connector->bit_data.getscl = hibmc_ddc_getscl; + + return i2c_bit_add_bus(&connector->adapter); +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h new file mode 100644 index 0000000000..17b30c393b --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Hisilicon Hibmc SoC drm driver + * + * Based on the bochs drm driver. + * + * Copyright (c) 2016 Huawei Limited. + * + * Author: + * Rongrong Zou + * Rongrong Zou + * Jianhua Li + */ + +#ifndef HIBMC_DRM_HW_H +#define HIBMC_DRM_HW_H + +/* register definition */ +#define HIBMC_MISC_CTRL 0x4 + +#define HIBMC_MSCCTL_LOCALMEM_RESET(x) ((x) << 6) +#define HIBMC_MSCCTL_LOCALMEM_RESET_MASK 0x40 + +#define HIBMC_CURRENT_GATE 0x000040 +#define HIBMC_CURR_GATE_DISPLAY(x) ((x) << 2) +#define HIBMC_CURR_GATE_DISPLAY_MASK 0x4 + +#define HIBMC_CURR_GATE_LOCALMEM(x) ((x) << 1) +#define HIBMC_CURR_GATE_LOCALMEM_MASK 0x2 + +#define HIBMC_MODE0_GATE 0x000044 +#define HIBMC_MODE1_GATE 0x000048 +#define HIBMC_POWER_MODE_CTRL 0x00004C + +#define HIBMC_PW_MODE_CTL_OSC_INPUT(x) ((x) << 3) +#define HIBMC_PW_MODE_CTL_OSC_INPUT_MASK 0x8 + +#define HIBMC_PW_MODE_CTL_MODE(x) ((x) << 0) +#define HIBMC_PW_MODE_CTL_MODE_MASK 0x03 +#define HIBMC_PW_MODE_CTL_MODE_SHIFT 0 + +#define HIBMC_PW_MODE_CTL_MODE_MODE0 0 +#define HIBMC_PW_MODE_CTL_MODE_MODE1 1 +#define HIBMC_PW_MODE_CTL_MODE_SLEEP 2 + +#define HIBMC_PANEL_PLL_CTRL 0x00005C +#define HIBMC_CRT_PLL_CTRL 0x000060 + +#define HIBMC_PLL_CTRL_BYPASS(x) ((x) << 18) +#define HIBMC_PLL_CTRL_BYPASS_MASK 0x40000 + +#define HIBMC_PLL_CTRL_POWER(x) ((x) << 17) +#define HIBMC_PLL_CTRL_POWER_MASK 0x20000 + +#define HIBMC_PLL_CTRL_INPUT(x) ((x) << 16) +#define HIBMC_PLL_CTRL_INPUT_MASK 0x10000 + +#define HIBMC_PLL_CTRL_POD(x) ((x) << 14) +#define HIBMC_PLL_CTRL_POD_MASK 0xC000 + +#define HIBMC_PLL_CTRL_OD(x) ((x) << 12) +#define HIBMC_PLL_CTRL_OD_MASK 0x3000 + +#define HIBMC_PLL_CTRL_N(x) ((x) << 8) +#define HIBMC_PLL_CTRL_N_MASK 0xF00 + +#define HIBMC_PLL_CTRL_M(x) ((x) << 0) +#define HIBMC_PLL_CTRL_M_MASK 0xFF + +#define HIBMC_CRT_DISP_CTL 0x80200 + +#define HIBMC_CRT_DISP_CTL_DPMS(x) ((x) << 30) +#define HIBMC_CRT_DISP_CTL_DPMS_MASK 0xc0000000 + +#define HIBMC_CRT_DPMS_ON 0 +#define HIBMC_CRT_DPMS_OFF 3 + +#define HIBMC_CRT_DISP_CTL_CRTSELECT(x) ((x) << 25) +#define HIBMC_CRT_DISP_CTL_CRTSELECT_MASK 0x2000000 + +#define HIBMC_CRTSELECT_CRT 1 + +#define HIBMC_CRT_DISP_CTL_CLOCK_PHASE(x) ((x) << 14) +#define HIBMC_CRT_DISP_CTL_CLOCK_PHASE_MASK 0x4000 + +#define HIBMC_CRT_DISP_CTL_VSYNC_PHASE(x) ((x) << 13) +#define HIBMC_CRT_DISP_CTL_VSYNC_PHASE_MASK 0x2000 + +#define HIBMC_CRT_DISP_CTL_HSYNC_PHASE(x) ((x) << 12) +#define HIBMC_CRT_DISP_CTL_HSYNC_PHASE_MASK 0x1000 + +#define HIBMC_CRT_DISP_CTL_TIMING(x) ((x) << 8) +#define HIBMC_CRT_DISP_CTL_TIMING_MASK 0x100 + +#define HIBMC_CTL_DISP_CTL_GAMMA(x) ((x) << 3) +#define HIBMC_CTL_DISP_CTL_GAMMA_MASK 0x08 + +#define HIBMC_CRT_DISP_CTL_PLANE(x) ((x) << 2) +#define HIBMC_CRT_DISP_CTL_PLANE_MASK 4 + +#define HIBMC_CRT_DISP_CTL_FORMAT(x) ((x) << 0) +#define HIBMC_CRT_DISP_CTL_FORMAT_MASK 0x03 + +#define HIBMC_CRT_FB_ADDRESS 0x080204 + +#define HIBMC_CRT_FB_WIDTH 0x080208 +#define HIBMC_CRT_FB_WIDTH_WIDTH(x) ((x) << 16) +#define HIBMC_CRT_FB_WIDTH_WIDTH_MASK 0x3FFF0000 +#define HIBMC_CRT_FB_WIDTH_OFFS(x) ((x) << 0) +#define HIBMC_CRT_FB_WIDTH_OFFS_MASK 0x3FFF + +#define HIBMC_CRT_HORZ_TOTAL 0x08020C +#define HIBMC_CRT_HORZ_TOTAL_TOTAL(x) ((x) << 16) +#define HIBMC_CRT_HORZ_TOTAL_TOTAL_MASK 0xFFF0000 + +#define HIBMC_CRT_HORZ_TOTAL_DISP_END(x) ((x) << 0) +#define HIBMC_CRT_HORZ_TOTAL_DISP_END_MASK 0xFFF + +#define HIBMC_CRT_HORZ_SYNC 0x080210 +#define HIBMC_CRT_HORZ_SYNC_WIDTH(x) ((x) << 16) +#define HIBMC_CRT_HORZ_SYNC_WIDTH_MASK 0xFF0000 + +#define HIBMC_CRT_HORZ_SYNC_START(x) ((x) << 0) +#define HIBMC_CRT_HORZ_SYNC_START_MASK 0xFFF + +#define HIBMC_CRT_VERT_TOTAL 0x080214 +#define HIBMC_CRT_VERT_TOTAL_TOTAL(x) ((x) << 16) +#define HIBMC_CRT_VERT_TOTAL_TOTAL_MASK 0x7FFF0000 + +#define HIBMC_CRT_VERT_TOTAL_DISP_END(x) ((x) << 0) +#define HIBMC_CRT_VERT_TOTAL_DISP_END_MASK 0x7FF + +#define HIBMC_CRT_VERT_SYNC 0x080218 +#define HIBMC_CRT_VERT_SYNC_HEIGHT(x) ((x) << 16) +#define HIBMC_CRT_VERT_SYNC_HEIGHT_MASK 0x3F0000 + +#define HIBMC_CRT_VERT_SYNC_START(x) ((x) << 0) +#define HIBMC_CRT_VERT_SYNC_START_MASK 0x7FF + +/* Auto Centering */ +#define HIBMC_CRT_AUTO_CENTERING_TL 0x080280 +#define HIBMC_CRT_AUTO_CENTERING_TL_TOP(x) ((x) << 16) +#define HIBMC_CRT_AUTO_CENTERING_TL_TOP_MASK 0x7FF0000 + +#define HIBMC_CRT_AUTO_CENTERING_TL_LEFT(x) ((x) << 0) +#define HIBMC_CRT_AUTO_CENTERING_TL_LEFT_MASK 0x7FF + +#define HIBMC_CRT_AUTO_CENTERING_BR 0x080284 +#define HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM(x) ((x) << 16) +#define HIBMC_CRT_AUTO_CENTERING_BR_BOTTOM_MASK 0x7FF0000 + +#define HIBMC_CRT_AUTO_CENTERING_BR_RIGHT(x) ((x) << 0) +#define HIBMC_CRT_AUTO_CENTERING_BR_RIGHT_MASK 0x7FF + +/* register to control panel output */ +#define HIBMC_DISPLAY_CONTROL_HISILE 0x80288 +#define HIBMC_DISPLAY_CONTROL_FPVDDEN(x) ((x) << 0) +#define HIBMC_DISPLAY_CONTROL_PANELDATE(x) ((x) << 1) +#define HIBMC_DISPLAY_CONTROL_FPEN(x) ((x) << 2) +#define HIBMC_DISPLAY_CONTROL_VBIASEN(x) ((x) << 3) + +#define HIBMC_RAW_INTERRUPT 0x80290 +#define HIBMC_RAW_INTERRUPT_VBLANK(x) ((x) << 2) +#define HIBMC_RAW_INTERRUPT_VBLANK_MASK 0x4 + +#define HIBMC_RAW_INTERRUPT_EN 0x80298 +#define HIBMC_RAW_INTERRUPT_EN_VBLANK(x) ((x) << 2) +#define HIBMC_RAW_INTERRUPT_EN_VBLANK_MASK 0x4 + +/* register and values for PLL control */ +#define CRT_PLL1_HS 0x802a8 +#define CRT_PLL1_HS_OUTER_BYPASS(x) ((x) << 30) +#define CRT_PLL1_HS_INTER_BYPASS(x) ((x) << 29) +#define CRT_PLL1_HS_POWERON(x) ((x) << 24) + +#define CRT_PLL1_HS_25MHZ 0x23d40f02 +#define CRT_PLL1_HS_40MHZ 0x23940801 +#define CRT_PLL1_HS_65MHZ 0x23940d01 +#define CRT_PLL1_HS_78MHZ 0x23540F82 +#define CRT_PLL1_HS_74MHZ 0x23941dc2 +#define CRT_PLL1_HS_80MHZ 0x23941001 +#define CRT_PLL1_HS_80MHZ_1152 0x23540fc2 +#define CRT_PLL1_HS_106MHZ 0x237C1641 +#define CRT_PLL1_HS_108MHZ 0x23b41b01 +#define CRT_PLL1_HS_162MHZ 0x23480681 +#define CRT_PLL1_HS_148MHZ 0x23541dc2 +#define CRT_PLL1_HS_193MHZ 0x234807c1 + +#define CRT_PLL2_HS 0x802ac +#define CRT_PLL2_HS_25MHZ 0x206B851E +#define CRT_PLL2_HS_40MHZ 0x30000000 +#define CRT_PLL2_HS_65MHZ 0x40000000 +#define CRT_PLL2_HS_78MHZ 0x50E147AE +#define CRT_PLL2_HS_74MHZ 0x602B6AE7 +#define CRT_PLL2_HS_80MHZ 0x70000000 +#define CRT_PLL2_HS_106MHZ 0x0075c28f +#define CRT_PLL2_HS_108MHZ 0x80000000 +#define CRT_PLL2_HS_162MHZ 0xA0000000 +#define CRT_PLL2_HS_148MHZ 0xB0CCCCCD +#define CRT_PLL2_HS_193MHZ 0xC0872B02 + +#define HIBMC_CRT_PALETTE 0x80C00 + +#define HIBMC_FIELD(field, value) (field(value) & field##_MASK) +#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c new file mode 100644 index 0000000000..8c6d2ea2a4 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Hisilicon Hibmc SoC drm driver + * + * Based on the bochs drm driver. + * + * Copyright (c) 2016 Huawei Limited. + * + * Author: + * Rongrong Zou + * Rongrong Zou + * Jianhua Li + */ + +#include + +#include +#include +#include +#include + +#include "hibmc_drm_drv.h" +#include "hibmc_drm_regs.h" + +static int hibmc_connector_get_modes(struct drm_connector *connector) +{ + int count; + void *edid; + struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector); + + edid = drm_get_edid(connector, &hibmc_connector->adapter); + if (edid) { + drm_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + if (count) + goto out; + } + + count = drm_add_modes_noedid(connector, + connector->dev->mode_config.max_width, + connector->dev->mode_config.max_height); + drm_set_preferred_mode(connector, 1024, 768); + +out: + kfree(edid); + return count; +} + +static void hibmc_connector_destroy(struct drm_connector *connector) +{ + struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector); + + i2c_del_adapter(&hibmc_connector->adapter); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_helper_funcs + hibmc_connector_helper_funcs = { + .get_modes = hibmc_connector_get_modes, +}; + +static const struct drm_connector_funcs hibmc_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = hibmc_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 void hibmc_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + u32 reg; + struct drm_device *dev = encoder->dev; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + + reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); + reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1); + reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); + reg |= HIBMC_DISPLAY_CONTROL_FPEN(1); + reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1); + writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); +} + +static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = { + .mode_set = hibmc_encoder_mode_set, +}; + +int hibmc_vdac_init(struct hibmc_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + struct hibmc_connector *hibmc_connector = &priv->connector; + struct drm_encoder *encoder = &priv->encoder; + struct drm_crtc *crtc = &priv->crtc; + struct drm_connector *connector = &hibmc_connector->base; + int ret; + + ret = hibmc_ddc_create(dev, hibmc_connector); + if (ret) { + drm_err(dev, "failed to create ddc: %d\n", ret); + return ret; + } + + encoder->possible_crtcs = drm_crtc_mask(crtc); + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC); + if (ret) { + drm_err(dev, "failed to init encoder: %d\n", ret); + return ret; + } + + drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs); + + ret = drm_connector_init_with_ddc(dev, connector, + &hibmc_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + &hibmc_connector->adapter); + if (ret) { + drm_err(dev, "failed to init connector: %d\n", ret); + return ret; + } + + drm_connector_helper_add(connector, &hibmc_connector_helper_funcs); + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} diff --git a/drivers/gpu/drm/hisilicon/kirin/Kconfig b/drivers/gpu/drm/hisilicon/kirin/Kconfig new file mode 100644 index 0000000000..c5265675bf --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_HISI_KIRIN + tristate "DRM Support for Hisilicon Kirin series SoCs Platform" + depends on DRM && OF && ARM64 + select DRM_KMS_HELPER + select DRM_GEM_DMA_HELPER + select DRM_MIPI_DSI + help + Choose this option if you have a hisilicon Kirin chipsets(hi6220). + If M is selected the module will be called kirin-drm. + diff --git a/drivers/gpu/drm/hisilicon/kirin/Makefile b/drivers/gpu/drm/hisilicon/kirin/Makefile new file mode 100644 index 0000000000..d9323f66a7 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +kirin-drm-y := kirin_drm_drv.o \ + kirin_drm_ade.o + +obj-$(CONFIG_DRM_HISI_KIRIN) += kirin-drm.o dw_drm_dsi.o + diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c new file mode 100644 index 0000000000..566de46587 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -0,0 +1,906 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DesignWare MIPI DSI Host Controller v1.02 driver + * + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 HiSilicon Limited. + * + * Author: + * Xinliang Liu + * Xinliang Liu + * Xinwei Kong + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dw_dsi_reg.h" + +#define MAX_TX_ESC_CLK 10 +#define ROUND(x, y) ((x) / (y) + \ + ((x) % (y) * 10 / (y) >= 5 ? 1 : 0)) +#define PHY_REF_CLK_RATE 19200000 +#define PHY_REF_CLK_PERIOD_PS (1000000000 / (PHY_REF_CLK_RATE / 1000)) + +#define encoder_to_dsi(encoder) \ + container_of(encoder, struct dw_dsi, encoder) +#define host_to_dsi(host) \ + container_of(host, struct dw_dsi, host) + +struct mipi_phy_params { + u32 clk_t_lpx; + u32 clk_t_hs_prepare; + u32 clk_t_hs_zero; + u32 clk_t_hs_trial; + u32 clk_t_wakeup; + u32 data_t_lpx; + u32 data_t_hs_prepare; + u32 data_t_hs_zero; + u32 data_t_hs_trial; + u32 data_t_ta_go; + u32 data_t_ta_get; + u32 data_t_wakeup; + u32 hstx_ckg_sel; + u32 pll_fbd_div5f; + u32 pll_fbd_div1f; + u32 pll_fbd_2p; + u32 pll_enbwt; + u32 pll_fbd_p; + u32 pll_fbd_s; + u32 pll_pre_div1p; + u32 pll_pre_p; + u32 pll_vco_750M; + u32 pll_lpf_rs; + u32 pll_lpf_cs; + u32 clklp2hs_time; + u32 clkhs2lp_time; + u32 lp2hs_time; + u32 hs2lp_time; + u32 clk_to_data_delay; + u32 data_to_clk_delay; + u32 lane_byte_clk_kHz; + u32 clk_division; +}; + +struct dsi_hw_ctx { + void __iomem *base; + struct clk *pclk; +}; + +struct dw_dsi { + struct drm_encoder encoder; + struct device *dev; + struct mipi_dsi_host host; + struct drm_display_mode cur_mode; + struct dsi_hw_ctx *ctx; + struct mipi_phy_params phy; + + u32 lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + bool enable; +}; + +struct dsi_data { + struct dw_dsi dsi; + struct dsi_hw_ctx ctx; +}; + +struct dsi_phy_range { + u32 min_range_kHz; + u32 max_range_kHz; + u32 pll_vco_750M; + u32 hstx_ckg_sel; +}; + +static const struct dsi_phy_range dphy_range_info[] = { + { 46875, 62500, 1, 7 }, + { 62500, 93750, 0, 7 }, + { 93750, 125000, 1, 6 }, + { 125000, 187500, 0, 6 }, + { 187500, 250000, 1, 5 }, + { 250000, 375000, 0, 5 }, + { 375000, 500000, 1, 4 }, + { 500000, 750000, 0, 4 }, + { 750000, 1000000, 1, 0 }, + { 1000000, 1500000, 0, 0 } +}; + +static u32 dsi_calc_phy_rate(u32 req_kHz, struct mipi_phy_params *phy) +{ + u32 ref_clk_ps = PHY_REF_CLK_PERIOD_PS; + u32 tmp_kHz = req_kHz; + u32 i = 0; + u32 q_pll = 1; + u32 m_pll = 0; + u32 n_pll = 0; + u32 r_pll = 1; + u32 m_n = 0; + u32 m_n_int = 0; + u32 f_kHz = 0; + u64 temp; + + /* + * Find a rate >= req_kHz. + */ + do { + f_kHz = tmp_kHz; + + for (i = 0; i < ARRAY_SIZE(dphy_range_info); i++) + if (f_kHz >= dphy_range_info[i].min_range_kHz && + f_kHz <= dphy_range_info[i].max_range_kHz) + break; + + if (i == ARRAY_SIZE(dphy_range_info)) { + DRM_ERROR("%dkHz out of range\n", f_kHz); + return 0; + } + + phy->pll_vco_750M = dphy_range_info[i].pll_vco_750M; + phy->hstx_ckg_sel = dphy_range_info[i].hstx_ckg_sel; + + if (phy->hstx_ckg_sel <= 7 && + phy->hstx_ckg_sel >= 4) + q_pll = 0x10 >> (7 - phy->hstx_ckg_sel); + + temp = f_kHz * (u64)q_pll * (u64)ref_clk_ps; + m_n_int = temp / (u64)1000000000; + m_n = (temp % (u64)1000000000) / (u64)100000000; + + if (m_n_int % 2 == 0) { + if (m_n * 6 >= 50) { + n_pll = 2; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 30) { + n_pll = 3; + m_pll = m_n_int * n_pll + 2; + } else { + n_pll = 1; + m_pll = m_n_int * n_pll; + } + } else { + if (m_n * 6 >= 50) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 30) { + n_pll = 1; + m_pll = (m_n_int + 1) * n_pll; + } else if (m_n * 6 >= 10) { + n_pll = 3; + m_pll = m_n_int * n_pll + 1; + } else { + n_pll = 2; + m_pll = m_n_int * n_pll; + } + } + + if (n_pll == 1) { + phy->pll_fbd_p = 0; + phy->pll_pre_div1p = 1; + } else { + phy->pll_fbd_p = n_pll; + phy->pll_pre_div1p = 0; + } + + if (phy->pll_fbd_2p <= 7 && phy->pll_fbd_2p >= 4) + r_pll = 0x10 >> (7 - phy->pll_fbd_2p); + + if (m_pll == 2) { + phy->pll_pre_p = 0; + phy->pll_fbd_s = 0; + phy->pll_fbd_div1f = 0; + phy->pll_fbd_div5f = 1; + } else if (m_pll >= 2 * 2 * r_pll && m_pll <= 2 * 4 * r_pll) { + phy->pll_pre_p = m_pll / (2 * r_pll); + phy->pll_fbd_s = 0; + phy->pll_fbd_div1f = 1; + phy->pll_fbd_div5f = 0; + } else if (m_pll >= 2 * 5 * r_pll && m_pll <= 2 * 150 * r_pll) { + if (((m_pll / (2 * r_pll)) % 2) == 0) { + phy->pll_pre_p = + (m_pll / (2 * r_pll)) / 2 - 1; + phy->pll_fbd_s = + (m_pll / (2 * r_pll)) % 2 + 2; + } else { + phy->pll_pre_p = + (m_pll / (2 * r_pll)) / 2; + phy->pll_fbd_s = + (m_pll / (2 * r_pll)) % 2; + } + phy->pll_fbd_div1f = 0; + phy->pll_fbd_div5f = 0; + } else { + phy->pll_pre_p = 0; + phy->pll_fbd_s = 0; + phy->pll_fbd_div1f = 0; + phy->pll_fbd_div5f = 1; + } + + f_kHz = (u64)1000000000 * (u64)m_pll / + ((u64)ref_clk_ps * (u64)n_pll * (u64)q_pll); + + if (f_kHz >= req_kHz) + break; + + tmp_kHz += 10; + + } while (true); + + return f_kHz; +} + +static void dsi_get_phy_params(u32 phy_req_kHz, + struct mipi_phy_params *phy) +{ + u32 ref_clk_ps = PHY_REF_CLK_PERIOD_PS; + u32 phy_rate_kHz; + u32 ui; + + memset(phy, 0, sizeof(*phy)); + + phy_rate_kHz = dsi_calc_phy_rate(phy_req_kHz, phy); + if (!phy_rate_kHz) + return; + + ui = 1000000 / phy_rate_kHz; + + phy->clk_t_lpx = ROUND(50, 8 * ui); + phy->clk_t_hs_prepare = ROUND(133, 16 * ui) - 1; + + phy->clk_t_hs_zero = ROUND(262, 8 * ui); + phy->clk_t_hs_trial = 2 * (ROUND(60, 8 * ui) - 1); + phy->clk_t_wakeup = ROUND(1000000, (ref_clk_ps / 1000) - 1); + if (phy->clk_t_wakeup > 0xff) + phy->clk_t_wakeup = 0xff; + phy->data_t_wakeup = phy->clk_t_wakeup; + phy->data_t_lpx = phy->clk_t_lpx; + phy->data_t_hs_prepare = ROUND(125 + 10 * ui, 16 * ui) - 1; + phy->data_t_hs_zero = ROUND(105 + 6 * ui, 8 * ui); + phy->data_t_hs_trial = 2 * (ROUND(60 + 4 * ui, 8 * ui) - 1); + phy->data_t_ta_go = 3; + phy->data_t_ta_get = 4; + + phy->pll_enbwt = 1; + phy->clklp2hs_time = ROUND(407, 8 * ui) + 12; + phy->clkhs2lp_time = ROUND(105 + 12 * ui, 8 * ui); + phy->lp2hs_time = ROUND(240 + 12 * ui, 8 * ui) + 1; + phy->hs2lp_time = phy->clkhs2lp_time; + phy->clk_to_data_delay = 1 + phy->clklp2hs_time; + phy->data_to_clk_delay = ROUND(60 + 52 * ui, 8 * ui) + + phy->clkhs2lp_time; + + phy->lane_byte_clk_kHz = phy_rate_kHz / 8; + phy->clk_division = + DIV_ROUND_UP(phy->lane_byte_clk_kHz, MAX_TX_ESC_CLK); +} + +static u32 dsi_get_dpi_color_coding(enum mipi_dsi_pixel_format format) +{ + u32 val; + + /* + * TODO: only support RGB888 now, to support more + */ + switch (format) { + case MIPI_DSI_FMT_RGB888: + val = DSI_24BITS_1; + break; + default: + val = DSI_24BITS_1; + break; + } + + return val; +} + +/* + * dsi phy reg write function + */ +static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val) +{ + u32 reg_write = 0x10000 + reg; + + /* + * latch reg first + */ + writel(reg_write, base + PHY_TST_CTRL1); + writel(0x02, base + PHY_TST_CTRL0); + writel(0x00, base + PHY_TST_CTRL0); + + /* + * then latch value + */ + writel(val, base + PHY_TST_CTRL1); + writel(0x02, base + PHY_TST_CTRL0); + writel(0x00, base + PHY_TST_CTRL0); +} + +static void dsi_set_phy_timer(void __iomem *base, + struct mipi_phy_params *phy, + u32 lanes) +{ + u32 val; + + /* + * Set lane value and phy stop wait time. + */ + val = (lanes - 1) | (PHY_STOP_WAIT_TIME << 8); + writel(val, base + PHY_IF_CFG); + + /* + * Set phy clk division. + */ + val = readl(base + CLKMGR_CFG) | phy->clk_division; + writel(val, base + CLKMGR_CFG); + + /* + * Set lp and hs switching params. + */ + dw_update_bits(base + PHY_TMR_CFG, 24, MASK(8), phy->hs2lp_time); + dw_update_bits(base + PHY_TMR_CFG, 16, MASK(8), phy->lp2hs_time); + dw_update_bits(base + PHY_TMR_LPCLK_CFG, 16, MASK(10), + phy->clkhs2lp_time); + dw_update_bits(base + PHY_TMR_LPCLK_CFG, 0, MASK(10), + phy->clklp2hs_time); + dw_update_bits(base + CLK_DATA_TMR_CFG, 8, MASK(8), + phy->data_to_clk_delay); + dw_update_bits(base + CLK_DATA_TMR_CFG, 0, MASK(8), + phy->clk_to_data_delay); +} + +static void dsi_set_mipi_phy(void __iomem *base, + struct mipi_phy_params *phy, + u32 lanes) +{ + u32 delay_count; + u32 val; + u32 i; + + /* phy timer setting */ + dsi_set_phy_timer(base, phy, lanes); + + /* + * Reset to clean up phy tst params. + */ + writel(0, base + PHY_RSTZ); + writel(0, base + PHY_TST_CTRL0); + writel(1, base + PHY_TST_CTRL0); + writel(0, base + PHY_TST_CTRL0); + + /* + * Clock lane timing control setting: TLPX, THS-PREPARE, + * THS-ZERO, THS-TRAIL, TWAKEUP. + */ + dsi_phy_tst_set(base, CLK_TLPX, phy->clk_t_lpx); + dsi_phy_tst_set(base, CLK_THS_PREPARE, phy->clk_t_hs_prepare); + dsi_phy_tst_set(base, CLK_THS_ZERO, phy->clk_t_hs_zero); + dsi_phy_tst_set(base, CLK_THS_TRAIL, phy->clk_t_hs_trial); + dsi_phy_tst_set(base, CLK_TWAKEUP, phy->clk_t_wakeup); + + /* + * Data lane timing control setting: TLPX, THS-PREPARE, + * THS-ZERO, THS-TRAIL, TTA-GO, TTA-GET, TWAKEUP. + */ + for (i = 0; i < lanes; i++) { + dsi_phy_tst_set(base, DATA_TLPX(i), phy->data_t_lpx); + dsi_phy_tst_set(base, DATA_THS_PREPARE(i), + phy->data_t_hs_prepare); + dsi_phy_tst_set(base, DATA_THS_ZERO(i), phy->data_t_hs_zero); + dsi_phy_tst_set(base, DATA_THS_TRAIL(i), phy->data_t_hs_trial); + dsi_phy_tst_set(base, DATA_TTA_GO(i), phy->data_t_ta_go); + dsi_phy_tst_set(base, DATA_TTA_GET(i), phy->data_t_ta_get); + dsi_phy_tst_set(base, DATA_TWAKEUP(i), phy->data_t_wakeup); + } + + /* + * physical configuration: I, pll I, pll II, pll III, + * pll IV, pll V. + */ + dsi_phy_tst_set(base, PHY_CFG_I, phy->hstx_ckg_sel); + val = (phy->pll_fbd_div5f << 5) + (phy->pll_fbd_div1f << 4) + + (phy->pll_fbd_2p << 1) + phy->pll_enbwt; + dsi_phy_tst_set(base, PHY_CFG_PLL_I, val); + dsi_phy_tst_set(base, PHY_CFG_PLL_II, phy->pll_fbd_p); + dsi_phy_tst_set(base, PHY_CFG_PLL_III, phy->pll_fbd_s); + val = (phy->pll_pre_div1p << 7) + phy->pll_pre_p; + dsi_phy_tst_set(base, PHY_CFG_PLL_IV, val); + val = (5 << 5) + (phy->pll_vco_750M << 4) + (phy->pll_lpf_rs << 2) + + phy->pll_lpf_cs; + dsi_phy_tst_set(base, PHY_CFG_PLL_V, val); + + writel(PHY_ENABLECLK, base + PHY_RSTZ); + udelay(1); + writel(PHY_ENABLECLK | PHY_UNSHUTDOWNZ, base + PHY_RSTZ); + udelay(1); + writel(PHY_ENABLECLK | PHY_UNRSTZ | PHY_UNSHUTDOWNZ, base + PHY_RSTZ); + usleep_range(1000, 1500); + + /* + * wait for phy's clock ready + */ + delay_count = 100; + while (delay_count) { + val = readl(base + PHY_STATUS); + if ((BIT(0) | BIT(2)) & val) + break; + + udelay(1); + delay_count--; + } + + if (!delay_count) + DRM_INFO("phylock and phystopstateclklane is not ready.\n"); +} + +static void dsi_set_mode_timing(void __iomem *base, + u32 lane_byte_clk_kHz, + struct drm_display_mode *mode, + enum mipi_dsi_pixel_format format) +{ + u32 hfp, hbp, hsw, vfp, vbp, vsw; + u32 hline_time; + u32 hsa_time; + u32 hbp_time; + u32 pixel_clk_kHz; + int htot, vtot; + u32 val; + u64 tmp; + + val = dsi_get_dpi_color_coding(format); + writel(val, base + DPI_COLOR_CODING); + + val = (mode->flags & DRM_MODE_FLAG_NHSYNC ? 1 : 0) << 2; + val |= (mode->flags & DRM_MODE_FLAG_NVSYNC ? 1 : 0) << 1; + writel(val, base + DPI_CFG_POL); + + /* + * The DSI IP accepts vertical timing using lines as normal, + * but horizontal timing is a mixture of pixel-clocks for the + * active region and byte-lane clocks for the blanking-related + * timings. hfp is specified as the total hline_time in byte- + * lane clocks minus hsa, hbp and active. + */ + pixel_clk_kHz = mode->clock; + htot = mode->htotal; + vtot = mode->vtotal; + hfp = mode->hsync_start - mode->hdisplay; + hbp = mode->htotal - mode->hsync_end; + hsw = mode->hsync_end - mode->hsync_start; + vfp = mode->vsync_start - mode->vdisplay; + vbp = mode->vtotal - mode->vsync_end; + vsw = mode->vsync_end - mode->vsync_start; + if (vsw > 15) { + DRM_DEBUG_DRIVER("vsw exceeded 15\n"); + vsw = 15; + } + + hsa_time = (hsw * lane_byte_clk_kHz) / pixel_clk_kHz; + hbp_time = (hbp * lane_byte_clk_kHz) / pixel_clk_kHz; + tmp = (u64)htot * (u64)lane_byte_clk_kHz; + hline_time = DIV_ROUND_UP(tmp, pixel_clk_kHz); + + /* all specified in byte-lane clocks */ + writel(hsa_time, base + VID_HSA_TIME); + writel(hbp_time, base + VID_HBP_TIME); + writel(hline_time, base + VID_HLINE_TIME); + + writel(vsw, base + VID_VSA_LINES); + writel(vbp, base + VID_VBP_LINES); + writel(vfp, base + VID_VFP_LINES); + writel(mode->vdisplay, base + VID_VACTIVE_LINES); + writel(mode->hdisplay, base + VID_PKT_SIZE); + + DRM_DEBUG_DRIVER("htot=%d, hfp=%d, hbp=%d, hsw=%d\n", + htot, hfp, hbp, hsw); + DRM_DEBUG_DRIVER("vtol=%d, vfp=%d, vbp=%d, vsw=%d\n", + vtot, vfp, vbp, vsw); + DRM_DEBUG_DRIVER("hsa_time=%d, hbp_time=%d, hline_time=%d\n", + hsa_time, hbp_time, hline_time); +} + +static void dsi_set_video_mode(void __iomem *base, unsigned long flags) +{ + u32 val; + u32 mode_mask = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + u32 non_burst_sync_pulse = MIPI_DSI_MODE_VIDEO | + MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + u32 non_burst_sync_event = MIPI_DSI_MODE_VIDEO; + + /* + * choose video mode type + */ + if ((flags & mode_mask) == non_burst_sync_pulse) + val = DSI_NON_BURST_SYNC_PULSES; + else if ((flags & mode_mask) == non_burst_sync_event) + val = DSI_NON_BURST_SYNC_EVENTS; + else + val = DSI_BURST_SYNC_PULSES_1; + writel(val, base + VID_MODE_CFG); + + writel(PHY_TXREQUESTCLKHS, base + LPCLK_CTRL); + writel(DSI_VIDEO_MODE, base + MODE_CFG); +} + +static void dsi_mipi_init(struct dw_dsi *dsi) +{ + struct dsi_hw_ctx *ctx = dsi->ctx; + struct mipi_phy_params *phy = &dsi->phy; + struct drm_display_mode *mode = &dsi->cur_mode; + u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + void __iomem *base = ctx->base; + u32 dphy_req_kHz; + + /* + * count phy params + */ + dphy_req_kHz = mode->clock * bpp / dsi->lanes; + dsi_get_phy_params(dphy_req_kHz, phy); + + /* reset Core */ + writel(RESET, base + PWR_UP); + + /* set dsi phy params */ + dsi_set_mipi_phy(base, phy, dsi->lanes); + + /* set dsi mode timing */ + dsi_set_mode_timing(base, phy->lane_byte_clk_kHz, mode, dsi->format); + + /* set dsi video mode */ + dsi_set_video_mode(base, dsi->mode_flags); + + /* dsi wake up */ + writel(POWERUP, base + PWR_UP); + + DRM_DEBUG_DRIVER("lanes=%d, pixel_clk=%d kHz, bytes_freq=%d kHz\n", + dsi->lanes, mode->clock, phy->lane_byte_clk_kHz); +} + +static void dsi_encoder_disable(struct drm_encoder *encoder) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + struct dsi_hw_ctx *ctx = dsi->ctx; + void __iomem *base = ctx->base; + + if (!dsi->enable) + return; + + writel(0, base + PWR_UP); + writel(0, base + LPCLK_CTRL); + writel(0, base + PHY_RSTZ); + clk_disable_unprepare(ctx->pclk); + + dsi->enable = false; +} + +static void dsi_encoder_enable(struct drm_encoder *encoder) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + struct dsi_hw_ctx *ctx = dsi->ctx; + int ret; + + if (dsi->enable) + return; + + ret = clk_prepare_enable(ctx->pclk); + if (ret) { + DRM_ERROR("fail to enable pclk: %d\n", ret); + return; + } + + dsi_mipi_init(dsi); + + dsi->enable = true; +} + +static enum drm_mode_status dsi_encoder_phy_mode_valid( + struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + struct mipi_phy_params phy; + u32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + u32 req_kHz, act_kHz, lane_byte_clk_kHz; + + /* Calculate the lane byte clk using the adjusted mode clk */ + memset(&phy, 0, sizeof(phy)); + req_kHz = mode->clock * bpp / dsi->lanes; + act_kHz = dsi_calc_phy_rate(req_kHz, &phy); + lane_byte_clk_kHz = act_kHz / 8; + + DRM_DEBUG_DRIVER("Checking mode %ix%i-%i@%i clock: %i...", + mode->hdisplay, mode->vdisplay, bpp, + drm_mode_vrefresh(mode), mode->clock); + + /* + * Make sure the adjusted mode clock and the lane byte clk + * have a common denominator base frequency + */ + if (mode->clock/dsi->lanes == lane_byte_clk_kHz/3) { + DRM_DEBUG_DRIVER("OK!\n"); + return MODE_OK; + } + + DRM_DEBUG_DRIVER("BAD!\n"); + return MODE_BAD; +} + +static enum drm_mode_status dsi_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) + +{ + const struct drm_crtc_helper_funcs *crtc_funcs = NULL; + struct drm_crtc *crtc = NULL; + struct drm_display_mode adj_mode; + enum drm_mode_status ret; + + /* + * The crtc might adjust the mode, so go through the + * possible crtcs (technically just one) and call + * mode_fixup to figure out the adjusted mode before we + * validate it. + */ + drm_for_each_crtc(crtc, encoder->dev) { + /* + * reset adj_mode to the mode value each time, + * so we don't adjust the mode twice + */ + drm_mode_init(&adj_mode, mode); + + crtc_funcs = crtc->helper_private; + if (crtc_funcs && crtc_funcs->mode_fixup) + if (!crtc_funcs->mode_fixup(crtc, mode, &adj_mode)) + return MODE_BAD; + + ret = dsi_encoder_phy_mode_valid(encoder, &adj_mode); + if (ret != MODE_OK) + return ret; + } + return MODE_OK; +} + +static void dsi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + struct dw_dsi *dsi = encoder_to_dsi(encoder); + + drm_mode_copy(&dsi->cur_mode, adj_mode); +} + +static int dsi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + /* do nothing */ + return 0; +} + +static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = { + .atomic_check = dsi_encoder_atomic_check, + .mode_valid = dsi_encoder_mode_valid, + .mode_set = dsi_encoder_mode_set, + .enable = dsi_encoder_enable, + .disable = dsi_encoder_disable +}; + +static int dw_drm_encoder_init(struct device *dev, + struct drm_device *drm_dev, + struct drm_encoder *encoder) +{ + int ret; + u32 crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node); + + if (!crtc_mask) { + DRM_ERROR("failed to find crtc mask\n"); + return -EINVAL; + } + + encoder->possible_crtcs = crtc_mask; + ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI); + if (ret) { + DRM_ERROR("failed to init dsi encoder\n"); + return ret; + } + + drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs); + + return 0; +} + +static const struct component_ops dsi_ops; +static int dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *mdsi) +{ + struct dw_dsi *dsi = host_to_dsi(host); + struct device *dev = host->dev; + int ret; + + if (mdsi->lanes < 1 || mdsi->lanes > 4) { + DRM_ERROR("dsi device params invalid\n"); + return -EINVAL; + } + + dsi->lanes = mdsi->lanes; + dsi->format = mdsi->format; + dsi->mode_flags = mdsi->mode_flags; + + ret = component_add(dev, &dsi_ops); + if (ret) + return ret; + + return 0; +} + +static int dsi_host_detach(struct mipi_dsi_host *host, + struct mipi_dsi_device *mdsi) +{ + struct device *dev = host->dev; + + component_del(dev, &dsi_ops); + + return 0; +} + +static const struct mipi_dsi_host_ops dsi_host_ops = { + .attach = dsi_host_attach, + .detach = dsi_host_detach, +}; + +static int dsi_host_init(struct device *dev, struct dw_dsi *dsi) +{ + struct mipi_dsi_host *host = &dsi->host; + int ret; + + host->dev = dev; + host->ops = &dsi_host_ops; + ret = mipi_dsi_host_register(host); + if (ret) { + DRM_ERROR("failed to register dsi host\n"); + return ret; + } + + return 0; +} + +static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) +{ + struct drm_encoder *encoder = &dsi->encoder; + struct drm_bridge *bridge; + struct device_node *np = dsi->dev->of_node; + int ret; + + /* + * Get the endpoint node. In our case, dsi has one output port1 + * to which the external HDMI bridge is connected. + */ + ret = drm_of_find_panel_or_bridge(np, 1, 0, NULL, &bridge); + if (ret) + return ret; + + /* associate the bridge to dsi encoder */ + return drm_bridge_attach(encoder, bridge, NULL, 0); +} + +static int dsi_bind(struct device *dev, struct device *master, void *data) +{ + struct dsi_data *ddata = dev_get_drvdata(dev); + struct dw_dsi *dsi = &ddata->dsi; + struct drm_device *drm_dev = data; + int ret; + + ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder); + if (ret) + return ret; + + ret = dsi_bridge_init(drm_dev, dsi); + if (ret) + return ret; + + return 0; +} + +static void dsi_unbind(struct device *dev, struct device *master, void *data) +{ + /* do nothing */ +} + +static const struct component_ops dsi_ops = { + .bind = dsi_bind, + .unbind = dsi_unbind, +}; + +static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) +{ + struct dsi_hw_ctx *ctx = dsi->ctx; + struct resource *res; + + ctx->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(ctx->pclk)) { + DRM_ERROR("failed to get pclk clock\n"); + return PTR_ERR(ctx->pclk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctx->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctx->base)) { + DRM_ERROR("failed to remap dsi io region\n"); + return PTR_ERR(ctx->base); + } + + return 0; +} + +static int dsi_probe(struct platform_device *pdev) +{ + struct dsi_data *data; + struct dw_dsi *dsi; + struct dsi_hw_ctx *ctx; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + DRM_ERROR("failed to allocate dsi data.\n"); + return -ENOMEM; + } + dsi = &data->dsi; + ctx = &data->ctx; + dsi->ctx = ctx; + dsi->dev = &pdev->dev; + + ret = dsi_parse_dt(pdev, dsi); + if (ret) + return ret; + + platform_set_drvdata(pdev, data); + + ret = dsi_host_init(&pdev->dev, dsi); + if (ret) + return ret; + + return 0; +} + +static void dsi_remove(struct platform_device *pdev) +{ + struct dsi_data *data = platform_get_drvdata(pdev); + struct dw_dsi *dsi = &data->dsi; + + mipi_dsi_host_unregister(&dsi->host); +} + +static const struct of_device_id dsi_of_match[] = { + {.compatible = "hisilicon,hi6220-dsi"}, + { } +}; +MODULE_DEVICE_TABLE(of, dsi_of_match); + +static struct platform_driver dsi_driver = { + .probe = dsi_probe, + .remove_new = dsi_remove, + .driver = { + .name = "dw-dsi", + .of_match_table = dsi_of_match, + }, +}; + +module_platform_driver(dsi_driver); + +MODULE_AUTHOR("Xinliang Liu "); +MODULE_AUTHOR("Xinliang Liu "); +MODULE_AUTHOR("Xinwei Kong "); +MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h new file mode 100644 index 0000000000..d79fc031e5 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/dw_dsi_reg.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 HiSilicon Limited. + */ + +#ifndef __DW_DSI_REG_H__ +#define __DW_DSI_REG_H__ + +#define MASK(x) (BIT(x) - 1) + +/* + * regs + */ +#define PWR_UP 0x04 /* Core power-up */ +#define RESET 0 +#define POWERUP BIT(0) +#define PHY_IF_CFG 0xA4 /* D-PHY interface configuration */ +#define CLKMGR_CFG 0x08 /* the internal clock dividers */ +#define PHY_RSTZ 0xA0 /* D-PHY reset control */ +#define PHY_ENABLECLK BIT(2) +#define PHY_UNRSTZ BIT(1) +#define PHY_UNSHUTDOWNZ BIT(0) +#define PHY_TST_CTRL0 0xB4 /* D-PHY test interface control 0 */ +#define PHY_TST_CTRL1 0xB8 /* D-PHY test interface control 1 */ +#define CLK_TLPX 0x10 +#define CLK_THS_PREPARE 0x11 +#define CLK_THS_ZERO 0x12 +#define CLK_THS_TRAIL 0x13 +#define CLK_TWAKEUP 0x14 +#define DATA_TLPX(x) (0x20 + ((x) << 4)) +#define DATA_THS_PREPARE(x) (0x21 + ((x) << 4)) +#define DATA_THS_ZERO(x) (0x22 + ((x) << 4)) +#define DATA_THS_TRAIL(x) (0x23 + ((x) << 4)) +#define DATA_TTA_GO(x) (0x24 + ((x) << 4)) +#define DATA_TTA_GET(x) (0x25 + ((x) << 4)) +#define DATA_TWAKEUP(x) (0x26 + ((x) << 4)) +#define PHY_CFG_I 0x60 +#define PHY_CFG_PLL_I 0x63 +#define PHY_CFG_PLL_II 0x64 +#define PHY_CFG_PLL_III 0x65 +#define PHY_CFG_PLL_IV 0x66 +#define PHY_CFG_PLL_V 0x67 +#define DPI_COLOR_CODING 0x10 /* DPI color coding */ +#define DPI_CFG_POL 0x14 /* DPI polarity configuration */ +#define VID_HSA_TIME 0x48 /* Horizontal Sync Active time */ +#define VID_HBP_TIME 0x4C /* Horizontal Back Porch time */ +#define VID_HLINE_TIME 0x50 /* Line time */ +#define VID_VSA_LINES 0x54 /* Vertical Sync Active period */ +#define VID_VBP_LINES 0x58 /* Vertical Back Porch period */ +#define VID_VFP_LINES 0x5C /* Vertical Front Porch period */ +#define VID_VACTIVE_LINES 0x60 /* Vertical resolution */ +#define VID_PKT_SIZE 0x3C /* Video packet size */ +#define VID_MODE_CFG 0x38 /* Video mode configuration */ +#define PHY_TMR_CFG 0x9C /* Data lanes timing configuration */ +#define BTA_TO_CNT 0x8C /* Response timeout definition */ +#define PHY_TMR_LPCLK_CFG 0x98 /* clock lane timing configuration */ +#define CLK_DATA_TMR_CFG 0xCC +#define LPCLK_CTRL 0x94 /* Low-power in clock lane */ +#define PHY_TXREQUESTCLKHS BIT(0) +#define MODE_CFG 0x34 /* Video or Command mode selection */ +#define PHY_STATUS 0xB0 /* D-PHY PPI status interface */ + +#define PHY_STOP_WAIT_TIME 0x30 + +/* + * regs relevant enum + */ +enum dpi_color_coding { + DSI_24BITS_1 = 5, +}; + +enum dsi_video_mode_type { + DSI_NON_BURST_SYNC_PULSES = 0, + DSI_NON_BURST_SYNC_EVENTS, + DSI_BURST_SYNC_PULSES_1, + DSI_BURST_SYNC_PULSES_2 +}; + +enum dsi_work_mode { + DSI_VIDEO_MODE = 0, + DSI_COMMAND_MODE +}; + +/* + * Register Write/Read Helper functions + */ +static inline void dw_update_bits(void __iomem *addr, u32 bit_start, + u32 mask, u32 val) +{ + u32 tmp, orig; + + orig = readl(addr); + tmp = orig & ~(mask << bit_start); + tmp |= (val & mask) << bit_start; + writel(tmp, addr); +} + +#endif /* __DW_DRM_DSI_H__ */ diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h new file mode 100644 index 0000000000..be9e789c2d --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_ade_reg.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 HiSilicon Limited. + */ + +#ifndef __KIRIN_ADE_REG_H__ +#define __KIRIN_ADE_REG_H__ + +/* + * ADE Registers + */ +#define MASK(x) (BIT(x) - 1) + +#define ADE_CTRL 0x0004 +#define FRM_END_START_OFST 0 +#define FRM_END_START_MASK MASK(2) +#define AUTO_CLK_GATE_EN_OFST 0 +#define AUTO_CLK_GATE_EN BIT(0) +#define ADE_DISP_SRC_CFG 0x0018 +#define ADE_CTRL1 0x008C +#define ADE_EN 0x0100 +#define ADE_DISABLE 0 +#define ADE_ENABLE 1 +/* reset and reload regs */ +#define ADE_SOFT_RST_SEL(x) (0x0078 + (x) * 0x4) +#define ADE_RELOAD_DIS(x) (0x00AC + (x) * 0x4) +#define RDMA_OFST 0 +#define CLIP_OFST 15 +#define SCL_OFST 21 +#define CTRAN_OFST 24 +#define OVLY_OFST 37 /* 32+5 */ +/* channel regs */ +#define RD_CH_CTRL(x) (0x1004 + (x) * 0x80) +#define RD_CH_ADDR(x) (0x1008 + (x) * 0x80) +#define RD_CH_SIZE(x) (0x100C + (x) * 0x80) +#define RD_CH_STRIDE(x) (0x1010 + (x) * 0x80) +#define RD_CH_SPACE(x) (0x1014 + (x) * 0x80) +#define RD_CH_EN(x) (0x1020 + (x) * 0x80) +/* overlay regs */ +#define ADE_OVLY1_TRANS_CFG 0x002C +#define ADE_OVLY_CTL 0x0098 +#define ADE_OVLY_CH_XY0(x) (0x2004 + (x) * 4) +#define ADE_OVLY_CH_XY1(x) (0x2024 + (x) * 4) +#define ADE_OVLY_CH_CTL(x) (0x204C + (x) * 4) +#define ADE_OVLY_OUTPUT_SIZE(x) (0x2070 + (x) * 8) +#define OUTPUT_XSIZE_OFST 16 +#define ADE_OVLYX_CTL(x) (0x209C + (x) * 4) +#define CH_OVLY_SEL_OFST(x) ((x) * 4) +#define CH_OVLY_SEL_MASK MASK(2) +#define CH_OVLY_SEL_VAL(x) ((x) + 1) +#define CH_ALP_MODE_OFST 0 +#define CH_ALP_SEL_OFST 2 +#define CH_UNDER_ALP_SEL_OFST 4 +#define CH_EN_OFST 6 +#define CH_ALP_GBL_OFST 15 +#define CH_SEL_OFST 28 +/* ctran regs */ +#define ADE_CTRAN_DIS(x) (0x5004 + (x) * 0x100) +#define CTRAN_BYPASS_ON 1 +#define CTRAN_BYPASS_OFF 0 +#define ADE_CTRAN_IMAGE_SIZE(x) (0x503C + (x) * 0x100) +/* clip regs */ +#define ADE_CLIP_DISABLE(x) (0x6800 + (x) * 0x100) +#define ADE_CLIP_SIZE0(x) (0x6804 + (x) * 0x100) +#define ADE_CLIP_SIZE1(x) (0x6808 + (x) * 0x100) + +/* + * LDI Registers + */ +#define LDI_HRZ_CTRL0 0x7400 +#define HBP_OFST 20 +#define LDI_HRZ_CTRL1 0x7404 +#define LDI_VRT_CTRL0 0x7408 +#define VBP_OFST 20 +#define LDI_VRT_CTRL1 0x740C +#define LDI_PLR_CTRL 0x7410 +#define FLAG_NVSYNC BIT(0) +#define FLAG_NHSYNC BIT(1) +#define FLAG_NPIXCLK BIT(2) +#define FLAG_NDE BIT(3) +#define LDI_DSP_SIZE 0x7414 +#define VSIZE_OFST 20 +#define LDI_INT_EN 0x741C +#define FRAME_END_INT_EN_OFST 1 +#define LDI_CTRL 0x7420 +#define BPP_OFST 3 +#define DATA_GATE_EN BIT(2) +#define LDI_EN BIT(0) +#define LDI_MSK_INT 0x7428 +#define LDI_INT_CLR 0x742C +#define LDI_WORK_MODE 0x7430 +#define LDI_HDMI_DSI_GT 0x7434 + +/* + * ADE media bus service regs + */ +#define ADE0_QOSGENERATOR_MODE 0x010C +#define QOSGENERATOR_MODE_MASK MASK(2) +#define ADE0_QOSGENERATOR_EXTCONTROL 0x0118 +#define SOCKET_QOS_EN BIT(0) +#define ADE1_QOSGENERATOR_MODE 0x020C +#define ADE1_QOSGENERATOR_EXTCONTROL 0x0218 + +/* + * ADE regs relevant enums + */ +enum frame_end_start { + /* regs take effect in every vsync */ + REG_EFFECTIVE_IN_VSYNC = 0, + /* regs take effect in fist ade en and every frame end */ + REG_EFFECTIVE_IN_ADEEN_FRMEND, + /* regs take effect in ade en immediately */ + REG_EFFECTIVE_IN_ADEEN, + /* regs take effect in first vsync and every frame end */ + REG_EFFECTIVE_IN_VSYNC_FRMEND +}; + +enum ade_fb_format { + ADE_RGB_565 = 0, + ADE_BGR_565, + ADE_XRGB_8888, + ADE_XBGR_8888, + ADE_ARGB_8888, + ADE_ABGR_8888, + ADE_RGBA_8888, + ADE_BGRA_8888, + ADE_RGB_888, + ADE_BGR_888 = 9, + ADE_FORMAT_UNSUPPORT = 800 +}; + +enum ade_channel { + ADE_CH1 = 0, /* channel 1 for primary plane */ + ADE_CH_NUM +}; + +enum ade_scale { + ADE_SCL1 = 0, + ADE_SCL2, + ADE_SCL3, + ADE_SCL_NUM +}; + +enum ade_ctran { + ADE_CTRAN1 = 0, + ADE_CTRAN2, + ADE_CTRAN3, + ADE_CTRAN4, + ADE_CTRAN5, + ADE_CTRAN6, + ADE_CTRAN_NUM +}; + +enum ade_overlay { + ADE_OVLY1 = 0, + ADE_OVLY2, + ADE_OVLY3, + ADE_OVLY_NUM +}; + +enum ade_alpha_mode { + ADE_ALP_GLOBAL = 0, + ADE_ALP_PIXEL, + ADE_ALP_PIXEL_AND_GLB +}; + +enum ade_alpha_blending_mode { + ADE_ALP_MUL_COEFF_0 = 0, /* alpha */ + ADE_ALP_MUL_COEFF_1, /* 1-alpha */ + ADE_ALP_MUL_COEFF_2, /* 0 */ + ADE_ALP_MUL_COEFF_3 /* 1 */ +}; + +/* + * LDI regs relevant enums + */ +enum dsi_pclk_en { + DSI_PCLK_ON = 0, + DSI_PCLK_OFF +}; + +enum ldi_output_format { + LDI_OUT_RGB_565 = 0, + LDI_OUT_RGB_666, + LDI_OUT_RGB_888 +}; + +enum ldi_work_mode { + TEST_MODE = 0, + NORMAL_MODE +}; + +enum ldi_input_source { + DISP_SRC_NONE = 0, + DISP_SRC_OVLY2, + DISP_SRC_DISP, + DISP_SRC_ROT, + DISP_SRC_SCL2 +}; + +/* + * ADE media bus service relevant enums + */ +enum qos_generator_mode { + FIXED_MODE = 0, + LIMITER_MODE, + BYPASS_MODE, + REGULATOR_MODE +}; + +/* + * Register Write/Read Helper functions + */ +static inline void ade_update_bits(void __iomem *addr, u32 bit_start, + u32 mask, u32 val) +{ + u32 tmp, orig; + + orig = readl(addr); + tmp = orig & ~(mask << bit_start); + tmp |= (val & mask) << bit_start; + writel(tmp, addr); +} + +#endif diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c new file mode 100644 index 0000000000..871f79a6b1 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -0,0 +1,951 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver + * + * Copyright (c) 2016 Linaro Limited. + * Copyright (c) 2014-2016 HiSilicon Limited. + * + * Author: + * Xinliang Liu + * Xinliang Liu + * Xinwei Kong + */ + +#include +#include +#include +#include +#include +#include + +#include