add support for Yhgc BMC soc chipset Signed-off-by: chuguangqing <chuguangq...@inspur.com> --- MAINTAINERS | 5 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/yhgch/Kconfig | 5 + drivers/gpu/drm/yhgch/Makefile | 4 + drivers/gpu/drm/yhgch/yhgch-drm/Kconfig | 12 + drivers/gpu/drm/yhgch/yhgch-drm/Makefile | 20 + .../gpu/drm/yhgch/yhgch-drm/yhgch_drm_de.c | 458 ++++++++++++++++++ .../gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.c | 374 ++++++++++++++ .../gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.h | 53 ++ .../gpu/drm/yhgch/yhgch-drm/yhgch_drm_i2c.c | 89 ++++ .../gpu/drm/yhgch/yhgch-drm/yhgch_drm_regs.h | 209 ++++++++ .../gpu/drm/yhgch/yhgch-drm/yhgch_drm_vdac.c | 116 +++++ 13 files changed, 1348 insertions(+) create mode 100644 drivers/gpu/drm/yhgch/Kconfig create mode 100644 drivers/gpu/drm/yhgch/Makefile create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/Kconfig create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/Makefile create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_de.c create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.c create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.h create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_i2c.c create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_regs.h create mode 100644 drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_vdac.c
diff --git a/MAINTAINERS b/MAINTAINERS index 10614ca41ed0..c79d9361fa81 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27744,6 +27744,11 @@ S: Maintained F: Documentation/input/devices/yealink.rst F: drivers/input/misc/yealink.* +YHGC DRM DRIVER +M: chuguangqing <chuguangq...@inspur.com> +S: Maintained +F: drivers/gpu/drm/yhgch + Z8530 DRIVER FOR AX.25 M: Joerg Reuter <jreu...@yaina.de> L: linux-h...@vger.kernel.org diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f7ea8e895c0c..8e0b1d12c81f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -396,6 +396,8 @@ source "drivers/gpu/drm/sprd/Kconfig" source "drivers/gpu/drm/imagination/Kconfig" +source "drivers/gpu/drm/yhgch/Kconfig" + config DRM_HYPERV tristate "DRM Support for Hyper-V synthetic video device" depends on DRM && PCI && HYPERV diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4dafbdc8f86a..f344e0173b29 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -231,6 +231,7 @@ obj-y += solomon/ obj-$(CONFIG_DRM_SPRD) += sprd/ obj-$(CONFIG_DRM_LOONGSON) += loongson/ obj-$(CONFIG_DRM_POWERVR) += imagination/ +obj-$(CONFIG_DRM_YHGCH) += yhgch/ # Ensure drm headers are self-contained and pass kernel-doc hdrtest-files := \ diff --git a/drivers/gpu/drm/yhgch/Kconfig b/drivers/gpu/drm/yhgch/Kconfig new file mode 100644 index 000000000000..ae9bb3974d04 --- /dev/null +++ b/drivers/gpu/drm/yhgch/Kconfig @@ -0,0 +1,5 @@ +# License: GPL-2.0 +# +# yhgch drm device configuration. + +source "drivers/gpu/drm/yhgch/yhgch-drm/Kconfig" diff --git a/drivers/gpu/drm/yhgch/Makefile b/drivers/gpu/drm/yhgch/Makefile new file mode 100644 index 000000000000..7ed11b275d55 --- /dev/null +++ b/drivers/gpu/drm/yhgch/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for yhgch drm drivers. + +obj-$(CONFIG_DRM_YHGCH) += yhgch-drm/ diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/Kconfig b/drivers/gpu/drm/yhgch/yhgch-drm/Kconfig new file mode 100644 index 000000000000..10d586bbe897 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/Kconfig @@ -0,0 +1,12 @@ +config DRM_YHGCH + tristate "DRM Support for Yhgch BMC" + depends on DRM && PCI && MMU + select DRM_CLIENT_SELECTION + select DRM_KMS_HELPER + select DRM_VRAM_HELPER + select DRM_TTM_HELPER + help + Choose this option if you have a Yhgch soc chipset. + If M is selected the module will be called yhgch - drm. + IF Y is selected the module will be built into the kernel. + IF N is selected the module will be excluded from the kernel. diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/Makefile b/drivers/gpu/drm/yhgch/yhgch-drm/Makefile new file mode 100644 index 000000000000..4e2332b1843b --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/Makefile @@ -0,0 +1,20 @@ +ifneq ($(KERNELRELEASE),) + +yhgch-drm-y := yhgch_drm_drv.o yhgch_drm_de.o yhgch_drm_vdac.o yhgch_drm_i2c.o + +#obj-$(CONFIG_DRM_YHGCH) += yhgch-drm.o +obj-m += yhgch-drm.o + + +else +knv := $(shell uname -r) +KERNELDIR := /lib/modules/$(knv)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules +install: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean +endif diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_de.c b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_de.c new file mode 100644 index 000000000000..aa81afd91b02 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_de.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/delay.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fourcc.h> + +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_vblank.h> + +#include "yhgch_drm_drv.h" +#include "yhgch_drm_regs.h" + +struct yhgch_dislay_pll_config { + u64 hdisplay; + u64 vdisplay; + u32 pll1_config_value; + u32 pll2_config_value; +}; + +static const struct yhgch_dislay_pll_config yhgch_pll_table[] = { + { 640, 480, CRT_PLL1_NS_25MHZ, CRT_PLL2_NS_25MHZ }, + { 800, 600, CRT_PLL1_NS_40MHZ, CRT_PLL2_NS_40MHZ }, + { 1024, 768, CRT_PLL1_NS_65MHZ, CRT_PLL2_NS_65MHZ }, + { 1280, 1024, CRT_PLL1_NS_108MHZ, CRT_PLL2_NS_108MHZ }, + { 1920, 1080, CRT_PLL1_NS_148MHZ, CRT_PLL2_NS_148MHZ }, +}; + +static inline unsigned long PADDING(unsigned long align, unsigned long data) +{ + return (((data) + (align) - 1) & (~((align) - 1))); +} + +static int yhgch_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] % 16 != 0) { + drm_dbg_atomic(plane->dev, "wrong stride with 16-byte aligned\n"); + return -EINVAL; + } + return 0; +} + +static void yhgch_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 yhgch_drm_private *priv = to_yhgch_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 (gpu_addr < 0) + return; + + writel(gpu_addr, priv->mmio + YHGCH_CRT_FB_ADDRESS); + + reg = new_state->fb->width * (new_state->fb->format->cpp[0]); + + line_l = new_state->fb->pitches[0]; + writel(YHGCH_FIELD(YHGCH_CRT_FB_WIDTH_WIDTH, reg) | + YHGCH_FIELD(YHGCH_CRT_FB_WIDTH_OFFS, line_l), + priv->mmio + YHGCH_CRT_FB_WIDTH); + + /* SET PIXEL FORMAT */ + reg = readl(priv->mmio + YHGCH_CRT_DISP_CTL); + reg &= ~YHGCH_CRT_DISP_CTL_FORMAT_MASK; + reg |= YHGCH_FIELD(YHGCH_CRT_DISP_CTL_FORMAT, + new_state->fb->format->cpp[0] * 8 / 16); + writel(reg, priv->mmio + YHGCH_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 struct drm_plane_funcs yhgch_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 yhgch_plane_helper_funcs = { + DRM_GEM_VRAM_PLANE_HELPER_FUNCS, + .atomic_check = yhgch_plane_atomic_check, + .atomic_update = yhgch_plane_atomic_update, +}; + +static void yhgch_crtc_dpms(struct drm_crtc *crtc, u32 dpms) +{ + struct yhgch_drm_private *priv = to_yhgch_drm_private(crtc->dev); + u32 reg; + + reg = readl(priv->mmio + YHGCH_CRT_DISP_CTL); + reg &= ~YHGCH_CRT_DISP_CTL_DPMS_MASK; + reg |= YHGCH_FIELD(YHGCH_CRT_DISP_CTL_DPMS, dpms); + reg &= ~YHGCH_CRT_DISP_CTL_TIMING_MASK; + if (dpms == YHGCH_CRT_DPMS_ON) + reg |= YHGCH_CRT_DISP_CTL_TIMING(1); + writel(reg, priv->mmio + YHGCH_CRT_DISP_CTL); +} + +static void yhgch_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *old_state) +{ + u32 reg; + struct yhgch_drm_private *priv = to_yhgch_drm_private(crtc->dev); + + yhgch_set_power_mode(priv, YHGCH_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate */ + reg = readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &= ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg &= ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg |= YHGCH_CURR_GATE_LOCALMEM(1); + reg |= YHGCH_CURR_GATE_DISPLAY(1); + yhgch_set_current_gate(priv, reg); + yhgch_crtc_dpms(crtc, YHGCH_CRT_DPMS_ON); +} + +static void yhgch_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *old_state) +{ + u32 reg; + struct yhgch_drm_private *priv = to_yhgch_drm_private(crtc->dev); + + yhgch_crtc_dpms(crtc, YHGCH_CRT_DPMS_OFF); + + yhgch_set_power_mode(priv, YHGCH_PW_MODE_CTL_MODE_SLEEP); + + /* Enable display power gate & LOCALMEM power gate */ + reg = readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &= ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg &= ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg |= YHGCH_CURR_GATE_LOCALMEM(0); + reg |= YHGCH_CURR_GATE_DISPLAY(0); + yhgch_set_current_gate(priv, reg); +} + +static enum drm_mode_status +yhgch_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(yhgch_pll_table); i++) { + if (yhgch_pll_table[i].hdisplay == mode->hdisplay && + yhgch_pll_table[i].vdisplay == mode->vdisplay) + return MODE_OK; + } + + return MODE_BAD; +} + +static void set_vclock_yhgch(struct drm_device *dev, u64 pll) +{ + u32 val; + struct yhgch_drm_private *priv = to_yhgch_drm_private(dev); + + val = readl(priv->mmio + CRT_PLL1_NS); + val &= ~(CRT_PLL1_NS_OUTER_BYPASS(1)); + writel(val, priv->mmio + CRT_PLL1_NS); + + val = CRT_PLL1_NS_INTER_BYPASS(1) | CRT_PLL1_NS_POWERON(1); + writel(val, priv->mmio + CRT_PLL1_NS); + + writel(pll, priv->mmio + CRT_PLL1_NS); + + usleep_range(1000, 2000); + + val = pll & ~(CRT_PLL1_NS_POWERON(1)); + writel(val, priv->mmio + CRT_PLL1_NS); + + usleep_range(1000, 2000); + + val &= ~(CRT_PLL1_NS_INTER_BYPASS(1)); + writel(val, priv->mmio + CRT_PLL1_NS); + + usleep_range(1000, 2000); + + val |= CRT_PLL1_NS_OUTER_BYPASS(1); + writel(val, priv->mmio + CRT_PLL1_NS); +} + +static void get_pll_config(u64 x, u64 y, u32 *pll1, u32 *pll2) +{ + size_t i; + size_t count = ARRAY_SIZE(yhgch_pll_table); + + for (i = 0; i < count; i++) { + if (yhgch_pll_table[i].hdisplay == x && + yhgch_pll_table[i].vdisplay == y) { + *pll1 = yhgch_pll_table[i].pll1_config_value; + *pll2 = yhgch_pll_table[i].pll2_config_value; + return; + } + } + + /* if found none, we use default value */ + *pll1 = CRT_PLL1_NS_25MHZ; + *pll2 = CRT_PLL2_NS_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 yhgch_drm_private *priv = to_yhgch_drm_private(dev); + + x = mode->hdisplay; + y = mode->vdisplay; + + get_pll_config(x, y, &pll1, &pll2); + writel(pll2, priv->mmio + CRT_PLL2_NS); + set_vclock_yhgch(dev, pll1); + + /* + * yhgch 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(YHGCH_FIELD(YHGCH_CRT_AUTO_CENTERING_TL_TOP, 0) | + YHGCH_FIELD(YHGCH_CRT_AUTO_CENTERING_TL_LEFT, 0), + priv->mmio + YHGCH_CRT_AUTO_CENTERING_TL); + + writel(YHGCH_FIELD(YHGCH_CRT_AUTO_CENTERING_BR_BOTTOM, y - 1) | + YHGCH_FIELD(YHGCH_CRT_AUTO_CENTERING_BR_RIGHT, x - 1), + priv->mmio + YHGCH_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 &= ~YHGCH_CRT_DISP_CTL_CRTSELECT_MASK; + ctrl &= ~YHGCH_CRT_DISP_CTL_CLOCK_PHASE_MASK; + + ctrl |= YHGCH_CRT_DISP_CTL_CRTSELECT(YHGCH_CRTSELECT_CRT); + + /* clock_phase_polarity is 0 */ + ctrl |= YHGCH_CRT_DISP_CTL_CLOCK_PHASE(0); + ctrl |= YHGCH_FIELD(YHGCH_CRT_DISP_CTL_FORMAT, 2); + + writel(ctrl, priv->mmio + YHGCH_CRT_DISP_CTL); + + return ctrl; +} + +static void yhgch_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 yhgch_drm_private *priv = to_yhgch_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 + YHGCH_CRT_PLL_CTRL); + writel(YHGCH_FIELD(YHGCH_CRT_HORZ_TOTAL_TOTAL, mode->htotal - 1) | + YHGCH_FIELD(YHGCH_CRT_HORZ_TOTAL_DISP_END, mode->hdisplay - 1), + priv->mmio + YHGCH_CRT_HORZ_TOTAL); + + writel(YHGCH_FIELD(YHGCH_CRT_HORZ_SYNC_WIDTH, width) | + YHGCH_FIELD(YHGCH_CRT_HORZ_SYNC_START, mode->hsync_start - 1), + priv->mmio + YHGCH_CRT_HORZ_SYNC); + + writel(YHGCH_FIELD(YHGCH_CRT_VERT_TOTAL_TOTAL, mode->vtotal - 1) | + YHGCH_FIELD(YHGCH_CRT_VERT_TOTAL_DISP_END, mode->vdisplay - 1), + priv->mmio + YHGCH_CRT_VERT_TOTAL); + + writel(YHGCH_FIELD(YHGCH_CRT_VERT_SYNC_HEIGHT, height) | + YHGCH_FIELD(YHGCH_CRT_VERT_SYNC_START, mode->vsync_start - 1), + priv->mmio + YHGCH_CRT_VERT_SYNC); + + val = YHGCH_FIELD(YHGCH_CRT_DISP_CTL_VSYNC_PHASE, 0); + val |= YHGCH_FIELD(YHGCH_CRT_DISP_CTL_HSYNC_PHASE, 0); + val |= YHGCH_CRT_DISP_CTL_TIMING(1); + val |= YHGCH_CRT_DISP_CTL_PLANE(1); + + display_ctrl_adjust(dev, mode, val); +} + +static void yhgch_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *old_state) +{ + u32 reg; + struct drm_device *dev = crtc->dev; + struct yhgch_drm_private *priv = to_yhgch_drm_private(dev); + + yhgch_set_power_mode(priv, YHGCH_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate */ + reg = readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &= ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg &= ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg |= YHGCH_CURR_GATE_DISPLAY(1); + reg |= YHGCH_CURR_GATE_LOCALMEM(1); + yhgch_set_current_gate(priv, reg); + + /* We can add more initialization as needed. */ +} + +static void yhgch_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 yhgch_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct yhgch_drm_private *priv = to_yhgch_drm_private(crtc->dev); + + writel(YHGCH_RAW_INTERRUPT_EN_VBLANK(1), + priv->mmio + YHGCH_RAW_INTERRUPT_EN); + + return 0; +} + +static void yhgch_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct yhgch_drm_private *priv = to_yhgch_drm_private(crtc->dev); + + writel(YHGCH_RAW_INTERRUPT_EN_VBLANK(0), + priv->mmio + YHGCH_RAW_INTERRUPT_EN); +} + +static const struct drm_crtc_funcs yhgch_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 = yhgch_crtc_enable_vblank, + .disable_vblank = yhgch_crtc_disable_vblank, + +}; + +static const struct drm_crtc_helper_funcs yhgch_crtc_helper_funcs = { + .mode_set_nofb = yhgch_crtc_mode_set_nofb, + .atomic_begin = yhgch_crtc_atomic_begin, + .atomic_flush = yhgch_crtc_atomic_flush, + .atomic_enable = yhgch_crtc_atomic_enable, + .atomic_disable = yhgch_crtc_atomic_disable, + .mode_valid = yhgch_crtc_mode_valid, +}; + +int yhgch_de_init(struct yhgch_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, &yhgch_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, &yhgch_plane_helper_funcs); + + ret = drm_crtc_init_with_planes(dev, crtc, plane, + NULL, &yhgch_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, &yhgch_crtc_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.c b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.c new file mode 100644 index 000000000000..382609bbf08e --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "yhgch_drm_drv.h" + +#include <linux/module.h> +#include <linux/pci.h> + +#include <linux/aperture.h> +#include <drm/clients/drm_client_setup.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_ttm.h> + +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_module.h> +#include <drm/drm_vblank.h> + +#include "yhgch_drm_drv.h" +#include "yhgch_drm_regs.h" + +#define MEM_SIZE_RESERVE4KVM 0x200000 + +DEFINE_DRM_GEM_FOPS(yhgch_fops); +static irqreturn_t yhgch_drm_interrupt(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct yhgch_drm_private *priv = to_yhgch_drm_private(dev); + u32 status; + + status = readl(priv->mmio + YHGCH_RAW_INTERRUPT); + + if (status & YHGCH_RAW_INTERRUPT_VBLANK(1)) { + writel(YHGCH_RAW_INTERRUPT_VBLANK(1), + priv->mmio + YHGCH_RAW_INTERRUPT); + drm_handle_vblank(dev, 0); + } + + return IRQ_HANDLED; +} + +int yhgch_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, 16, args); +} + +static struct drm_driver yhgch_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &yhgch_fops, + .name = "yhgch", + .desc = "yhgch drm driver", + .major = 3, + .minor = 1, + .debugfs_init = drm_vram_mm_debugfs_init, + .dumb_create = yhgch_dumb_create, + .dumb_map_offset = drm_gem_ttm_dumb_map_offset, + DRM_FBDEV_TTM_DRIVER_OPS, +}; + +static int __maybe_unused yhgch_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 yhgch_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 yhgch_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(yhgch_pm_suspend, + yhgch_pm_resume) +}; + +static const struct drm_mode_config_funcs yhgch_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 yhgch_kms_init(struct yhgch_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + int ret; + + ret = drmm_mode_config_init(dev); + if (ret) + return ret; + + priv->dev.mode_config.min_width = 0; + priv->dev.mode_config.min_height = 0; + priv->dev.mode_config.max_width = 1920; + priv->dev.mode_config.max_height = 1200; + dev->mode_config.preferred_depth = 24; + priv->dev.mode_config.prefer_shadow = 1; + priv->dev.mode_config.funcs = (void *)&yhgch_mode_funcs; + + ret = yhgch_de_init(priv); + if (ret) { + drm_err(dev, "failed to init de: %d\n", ret); + return ret; + } + + ret = yhgch_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 yhgch_set_power_mode(struct yhgch_drm_private *priv, u32 power_mode) +{ + unsigned int control_value = 0; + void __iomem *mmio = priv->mmio; + u32 input = 1; + + if (power_mode > YHGCH_PW_MODE_CTL_MODE_SLEEP) + return; + + if (power_mode == YHGCH_PW_MODE_CTL_MODE_SLEEP) + input = 0; + + control_value = readl(mmio + YHGCH_POWER_MODE_CTRL); + control_value &= ~(YHGCH_PW_MODE_CTL_MODE_MASK | + YHGCH_PW_MODE_CTL_OSC_INPUT_MASK); + control_value |= YHGCH_FIELD(YHGCH_PW_MODE_CTL_MODE, power_mode); + control_value |= YHGCH_FIELD(YHGCH_PW_MODE_CTL_OSC_INPUT, input); + writel(control_value, mmio + YHGCH_POWER_MODE_CTRL); +} + +void yhgch_set_current_gate(struct yhgch_drm_private *priv, unsigned int gate) +{ + u32 gate_reg; + u32 mode; + void __iomem *mmio = priv->mmio; + + /* Get current power mode. */ + mode = (readl(mmio + YHGCH_POWER_MODE_CTRL) & + YHGCH_PW_MODE_CTL_MODE_MASK) >> YHGCH_PW_MODE_CTL_MODE_SHIFT; + + switch (mode) { + case YHGCH_PW_MODE_CTL_MODE_MODE0: + gate_reg = YHGCH_MODE0_GATE; + break; + + case YHGCH_PW_MODE_CTL_MODE_MODE1: + gate_reg = YHGCH_MODE1_GATE; + break; + + default: + gate_reg = YHGCH_MODE0_GATE; + break; + } + writel(gate, mmio + gate_reg); +} + +static void yhgch_hw_config(struct yhgch_drm_private *priv) +{ + u32 reg; + + /* On hardware reset, power mode 0 is default. */ + yhgch_set_power_mode(priv, YHGCH_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate */ + reg = readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &= ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg &= ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg |= YHGCH_CURR_GATE_DISPLAY(1); + reg |= YHGCH_CURR_GATE_LOCALMEM(1); + + yhgch_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 + YHGCH_MISC_CTRL); + reg &= ~YHGCH_MSCCTL_LOCALMEM_RESET_MASK; + reg |= YHGCH_MSCCTL_LOCALMEM_RESET(0); + writel(reg, priv->mmio + YHGCH_MISC_CTRL); + + reg &= ~YHGCH_MSCCTL_LOCALMEM_RESET_MASK; + reg |= YHGCH_MSCCTL_LOCALMEM_RESET(1); + + writel(reg, priv->mmio + YHGCH_MISC_CTRL); +} + +static int yhgch_hw_map(struct yhgch_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 yhgch_hw_init(struct yhgch_drm_private *priv) +{ + int ret; + + ret = yhgch_hw_map(priv); + if (ret) + return ret; + yhgch_hw_config(priv); + return 0; +} + +static int yhgch_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 yhgch_load(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + int ret; + + struct yhgch_drm_private *priv = to_yhgch_drm_private(dev); + + ret = yhgch_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 = yhgch_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, yhgch_drm_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: + yhgch_unload(dev); + drm_err(dev, "failed to initialize drm driver: %d\n", ret); + return ret; +} + +static int yhgch_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct yhgch_drm_private *priv; + struct drm_device *dev; + int ret; + + ret = aperture_remove_conflicting_pci_devices(pdev, yhgch_driver.name); + + if (ret) + return ret; + + priv = devm_drm_dev_alloc(&pdev->dev, &yhgch_driver, + struct yhgch_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 = yhgch_load(dev); + if (ret) { + drm_err(dev, "failed to load yhgch: %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_client_setup(dev, NULL); + + return 0; + +err_unload: + yhgch_unload(dev); +err_return: + return ret; +} + +static void yhgch_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_dev_unregister(dev); + yhgch_unload(dev); +} + +static void yhgch_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + +static struct pci_device_id yhgch_pci_table[] = { + { 0x1bd4, 0x0750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, } +}; + +static struct pci_driver yhgch_pci_driver = { + .name = "yhgch-drm", + .id_table = yhgch_pci_table, + .probe = yhgch_pci_probe, + .remove = yhgch_pci_remove, + .shutdown = yhgch_pci_shutdown, + .driver.pm = &yhgch_pm_ops, +}; + +drm_module_pci_driver(yhgch_pci_driver); + +MODULE_DEVICE_TABLE(pci, yhgch_pci_table); +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("DRM Driver for YhgchBMC"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("3.1"); diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.h b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.h new file mode 100644 index 000000000000..a2d0b148d2c2 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_drv.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef YHGCH_DRM_DRV_H +#define YHGCH_DRM_DRV_H + +#include <linux/gpio/consumer.h> +#include <linux/i2c-algo-bit.h> +#include <linux/i2c.h> +#include <linux/version.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_encoder.h> + +struct yhgch_connector { + struct drm_connector base; + struct i2c_adapter adapter; + struct i2c_algo_bit_data bit_data; +}; + +struct yhgch_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 yhgch_connector connector; +}; + +static inline struct yhgch_connector *to_yhgch_connector(struct drm_connector *connector) +{ + return container_of(connector, struct yhgch_connector, base); +} + +static inline struct yhgch_drm_private *to_yhgch_drm_private(struct drm_device *dev) +{ + return container_of(dev, struct yhgch_drm_private, dev); +} + +void yhgch_set_power_mode(struct yhgch_drm_private *priv, u32 power_mode); +void yhgch_set_current_gate(struct yhgch_drm_private *priv, + u32 gate); + +int yhgch_de_init(struct yhgch_drm_private *priv); +int yhgch_vdac_init(struct yhgch_drm_private *priv); +int yhgch_mm_init(struct yhgch_drm_private *yhgch); +int yhgch_ddc_create(struct drm_device *drm_dev, struct yhgch_connector *connector); + +int yhgch_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + +#endif diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_i2c.c b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_i2c.c new file mode 100644 index 000000000000..5a826cb2ab46 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_i2c.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_probe_helper.h> + +#include "yhgch_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 yhgch_set_i2c_signal(void *data, u32 mask, int value) +{ + struct yhgch_connector *yhgch_connector = data; + struct yhgch_drm_private *priv = to_yhgch_drm_private(yhgch_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 yhgch_get_i2c_signal(void *data, u32 mask) +{ + struct yhgch_connector *yhgch_connector = data; + struct yhgch_drm_private *priv = to_yhgch_drm_private(yhgch_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 yhgch_ddc_setsda(void *data, int state) +{ + yhgch_set_i2c_signal(data, I2C_SDA_MASK, state); +} + +static void yhgch_ddc_setscl(void *data, int state) +{ + yhgch_set_i2c_signal(data, I2C_SCL_MASK, state); +} + +static int yhgch_ddc_getsda(void *data) +{ + return yhgch_get_i2c_signal(data, I2C_SDA_MASK); +} + +static int yhgch_ddc_getscl(void *data) +{ + return yhgch_get_i2c_signal(data, I2C_SCL_MASK); +} + +int yhgch_ddc_create(struct drm_device *drm_dev, + struct yhgch_connector *connector) +{ + connector->adapter.owner = THIS_MODULE; + snprintf(connector->adapter.name, I2C_NAME_SIZE, "INS 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 = yhgch_ddc_setsda; + connector->bit_data.setscl = yhgch_ddc_setscl; + connector->bit_data.getsda = yhgch_ddc_getsda; + connector->bit_data.getscl = yhgch_ddc_getscl; + + return i2c_bit_add_bus(&connector->adapter); +} diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_regs.h b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_regs.h new file mode 100644 index 000000000000..d707f5186ab4 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_regs.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef YHGCH_DRM_HW_H +#define YHGCH_DRM_HW_H + +/* register definition */ +#define YHGCH_MISC_CTRL 0x4 + +#define YHGCH_MSCCTL_LOCALMEM_RESET(x) ((x) << 6) +#define YHGCH_MSCCTL_LOCALMEM_RESET_MASK 0x40 + +#define YHGCH_CURRENT_GATE 0x000040 +#define YHGCH_CURR_GATE_DISPLAY(x) ((x) << 2) +#define YHGCH_CURR_GATE_DISPLAY_MASK 0x4 + +#define YHGCH_CURR_GATE_LOCALMEM(x) ((x) << 1) +#define YHGCH_CURR_GATE_LOCALMEM_MASK 0x2 + +#define YHGCH_MODE0_GATE 0x000044 +#define YHGCH_MODE1_GATE 0x000048 +#define YHGCH_POWER_MODE_CTRL 0x00004C + +#define YHGCH_PW_MODE_CTL_OSC_INPUT(x) ((x) << 3) +#define YHGCH_PW_MODE_CTL_OSC_INPUT_MASK 0x8 + +#define YHGCH_PW_MODE_CTL_MODE(x) ((x) << 0) +#define YHGCH_PW_MODE_CTL_MODE_MASK 0x03 +#define YHGCH_PW_MODE_CTL_MODE_SHIFT 0 + +#define YHGCH_PW_MODE_CTL_MODE_MODE0 0 +#define YHGCH_PW_MODE_CTL_MODE_MODE1 1 +#define YHGCH_PW_MODE_CTL_MODE_SLEEP 2 + +//#define YHGCH_CRT_PLL_CTRL 0x000060 + +#define YHGCH_PLL_CTRL_BYPASS(x) ((x) << 18) +#define YHGCH_PLL_CTRL_BYPASS_MASK 0x40000 + +#define YHGCH_PLL_CTRL_POWER(x) ((x) << 17) +#define YHGCH_PLL_CTRL_POWER_MASK 0x20000 + +#define YHGCH_PLL_CTRL_INPUT(x) ((x) << 16) +#define YHGCH_PLL_CTRL_INPUT_MASK 0x10000 + +#define YHGCH_PLL_CTRL_POD(x) ((x) << 14) +#define YHGCH_PLL_CTRL_POD_MASK 0xC000 + +#define YHGCH_PLL_CTRL_OD(x) ((x) << 12) +#define YHGCH_PLL_CTRL_OD_MASK 0x3000 + +#define YHGCH_PLL_CTRL_N(x) ((x) << 8) +#define YHGCH_PLL_CTRL_N_MASK 0xF00 + +#define YHGCH_PLL_CTRL_M(x) ((x) << 0) +#define YHGCH_PLL_CTRL_M_MASK 0xFF + +#define YHGCH_CRT_DISP_CTL 0x80200 + +#define YHGCH_CRT_DISP_CTL_DPMS(x) ((x) << 30) +#define YHGCH_CRT_DISP_CTL_DPMS_MASK 0xc0000000 + +#define YHGCH_CRT_DPMS_ON 0 +#define YHGCH_CRT_DPMS_OFF 3 + +#define YHGCH_CRT_DISP_CTL_CRTSELECT(x) ((x) << 25) +#define YHGCH_CRT_DISP_CTL_CRTSELECT_MASK 0x2000000 + +#define YHGCH_CRTSELECT_CRT 1 + +#define YHGCH_CRT_DISP_CTL_CLOCK_PHASE(x) ((x) << 14) +#define YHGCH_CRT_DISP_CTL_CLOCK_PHASE_MASK 0x4000 + +#define YHGCH_CRT_DISP_CTL_VSYNC_PHASE(x) ((x) << 13) +#define YHGCH_CRT_DISP_CTL_VSYNC_PHASE_MASK 0x2000 + +#define YHGCH_CRT_DISP_CTL_HSYNC_PHASE(x) ((x) << 12) +#define YHGCH_CRT_DISP_CTL_HSYNC_PHASE_MASK 0x1000 + +#define YHGCH_CRT_DISP_CTL_TIMING(x) ((x) << 8) +#define YHGCH_CRT_DISP_CTL_TIMING_MASK 0x100 + +#define YHGCH_CRT_DISP_CTL_PLANE(x) ((x) << 2) +#define YHGCH_CRT_DISP_CTL_PLANE_MASK 4 + +#define YHGCH_CRT_DISP_CTL_FORMAT(x) ((x) << 0) +#define YHGCH_CRT_DISP_CTL_FORMAT_MASK 0x03 + +#define YHGCH_CRT_FB_ADDRESS 0x080204 + +#define YHGCH_CRT_FB_WIDTH 0x080208 +#define YHGCH_CRT_FB_WIDTH_WIDTH(x) ((x) << 16) +#define YHGCH_CRT_FB_WIDTH_WIDTH_MASK 0x3FFF0000 +#define YHGCH_CRT_FB_WIDTH_OFFS(x) ((x) << 0) +#define YHGCH_CRT_FB_WIDTH_OFFS_MASK 0x3FFF + +#define YHGCH_CRT_HORZ_TOTAL 0x08020C +#define YHGCH_CRT_HORZ_TOTAL_TOTAL(x) ((x) << 16) +#define YHGCH_CRT_HORZ_TOTAL_TOTAL_MASK 0xFFF0000 + +#define YHGCH_CRT_HORZ_TOTAL_DISP_END(x) ((x) << 0) +#define YHGCH_CRT_HORZ_TOTAL_DISP_END_MASK 0xFFF + +#define YHGCH_CRT_HORZ_SYNC 0x080210 +#define YHGCH_CRT_HORZ_SYNC_WIDTH(x) ((x) << 16) +#define YHGCH_CRT_HORZ_SYNC_WIDTH_MASK 0xFF0000 + +#define YHGCH_CRT_HORZ_SYNC_START(x) ((x) << 0) +#define YHGCH_CRT_HORZ_SYNC_START_MASK 0xFFF + +#define YHGCH_CRT_VERT_TOTAL 0x080214 +#define YHGCH_CRT_VERT_TOTAL_TOTAL(x) ((x) << 16) +#define YHGCH_CRT_VERT_TOTAL_TOTAL_MASK 0x7FFF0000 + +#define YHGCH_CRT_VERT_TOTAL_DISP_END(x) ((x) << 0) +#define YHGCH_CRT_VERT_TOTAL_DISP_END_MASK 0x7FF + +#define YHGCH_CRT_VERT_SYNC 0x080218 +#define YHGCH_CRT_VERT_SYNC_HEIGHT(x) ((x) << 16) +#define YHGCH_CRT_VERT_SYNC_HEIGHT_MASK 0x3F0000 + +#define YHGCH_CRT_VERT_SYNC_START(x) ((x) << 0) +#define YHGCH_CRT_VERT_SYNC_START_MASK 0x7FF + +/* Hardware Cursor */ +#define YHGCH_HWC_ADDRESS 0x080230 +#define YHGCH_HWC_ADDRESS_ENABLE(x) ((x) << 31) +#define YHGCH_HWC_ADDRESS_ENABLE_MASK 0x80000000 +#define YHGCH_HWC_ADDRESS_ADDRESS(x) ((x) << 0) +#define YHGCH_HWC_ADDRESS_ADDRESS_MASK 0xFFFFFFF + +#define YHGCH_HWC_LOCATION 0x080234 +#define YHGCH_HWC_LOCATION_TOP(x) ((x) << 27) +#define YHGCH_HWC_LOCATION_TOP_MASK 0x8000000 +#define YHGCH_HWC_LOCATION_Y(x) ((x) << 16) +#define YHGCH_HWC_LOCATION_Y_MASK 0x7FF0000 +#define YHGCH_HWC_LOCATION_LEFT(x) ((x) << 11) +#define YHGCH_HWC_LOCATION_LEFT_MASK 0x800 +#define YHGCH_HWC_LOCATION_X(x) ((x) << 0) +#define YHGCH_HWC_LOCATION_X_MASK 0x7FF + +#define YHGCH_HWC_COLOR_12 0x080238 +#define YHGCH_HWC_COLOR_12_2_RGB(x) ((x) << 16) +#define YHGCH_HWC_COLOR_12_2_RGB_MASK 0xFFFF0000 +#define YHGCH_HWC_COLOR_12_1_RGB(x) ((x) << 0) +#define YHGCH_HWC_COLOR_12_1_RGB_MASK 0xFFFF + +#define YHGCH_HWC_COLOR_3 0x08023C +#define YHGCH_HWC_COLOR_3_RGB(x) ((x) << 0) +#define YHGCH_HWC_COLOR_3_RGB_MASK 0xFFFF + +/* Auto Centering */ +#define YHGCH_CRT_AUTO_CENTERING_TL 0x080280 +#define YHGCH_CRT_AUTO_CENTERING_TL_TOP(x) ((x) << 16) +#define YHGCH_CRT_AUTO_CENTERING_TL_TOP_MASK 0x7FF0000 + +#define YHGCH_CRT_AUTO_CENTERING_TL_LEFT(x) ((x) << 0) +#define YHGCH_CRT_AUTO_CENTERING_TL_LEFT_MASK 0x7FF + +#define YHGCH_CRT_AUTO_CENTERING_BR 0x080284 +#define YHGCH_CRT_AUTO_CENTERING_BR_BOTTOM(x) ((x) << 16) +#define YHGCH_CRT_AUTO_CENTERING_BR_BOTTOM_MASK 0x7FF0000 + +#define YHGCH_CRT_AUTO_CENTERING_BR_RIGHT(x) ((x) << 0) +#define YHGCH_CRT_AUTO_CENTERING_BR_RIGHT_MASK 0x7FF + +/* register to control panel output */ +#define YHGCH_DISPLAY_CONTROL_HISILE 0x80288 +#define YHGCH_DISPLAY_CONTROL_FPVDDEN(x) ((x) << 0) +#define YHGCH_DISPLAY_CONTROL_PANELDATE(x) ((x) << 1) +#define YHGCH_DISPLAY_CONTROL_FPEN(x) ((x) << 2) +#define YHGCH_DISPLAY_CONTROL_VBIASEN(x) ((x) << 3) + +#define YHGCH_RAW_INTERRUPT 0x80290 +#define YHGCH_RAW_INTERRUPT_VBLANK(x) ((x) << 2) +#define YHGCH_RAW_INTERRUPT_VBLANK_MASK 0x4 + +#define YHGCH_RAW_INTERRUPT_EN 0x80298 +#define YHGCH_RAW_INTERRUPT_EN_VBLANK(x) ((x) << 2) +#define YHGCH_RAW_INTERRUPT_EN_VBLANK_MASK 0x4 + +/* register and values for PLL control */ +#define CRT_PLL1_NS 0x802a8 +#define CRT_PLL1_NS_OUTER_BYPASS(x) ((x) << 30) +#define CRT_PLL1_NS_INTER_BYPASS(x) ((x) << 29) +#define CRT_PLL1_NS_POWERON(x) ((x) << 24) + +#define CRT_PLL1_NS_25MHZ 0x00006691 //640x480 +#define CRT_PLL1_NS_40MHZ 0x00004580 //800x600 +#define CRT_PLL1_NS_65MHZ 0x00002568 //1024x768 +#define CRT_PLL1_NS_83MHZ 0x000027bb //1280x800 +#define CRT_PLL1_NS_106MHZ 0x000027ef //1440x900 +#define CRT_PLL1_NS_108MHZ 0x000027f2 //1280x1024 +#define CRT_PLL1_NS_146MHZ 0x00001575 //1680x1050 +#define CRT_PLL1_NS_148MHZ 0x0000145f //1920x1080 +#define CRT_PLL1_NS_193MHZ 0x000018f7 //1920x1200 + +#define CRT_PLL2_NS 0x802ac +#define CRT_PLL2_NS_25MHZ 0x0 +#define CRT_PLL2_NS_40MHZ 0x0 +#define CRT_PLL2_NS_65MHZ 0x0 +#define CRT_PLL2_NS_83MHZ 0x0 +#define CRT_PLL2_NS_106MHZ 0x0 +#define CRT_PLL2_NS_108MHZ 0x0 +#define CRT_PLL2_NS_146MHZ 0x0 +#define CRT_PLL2_NS_148MHZ 0x0 +#define CRT_PLL2_NS_193MHZ 0x0 + +#define YHGCH_FIELD(field, value) (field(value) & field##_MASK) +#endif diff --git a/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_vdac.c b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_vdac.c new file mode 100644 index 000000000000..5a22b8ed007f --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch-drm/yhgch_drm_vdac.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/io.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_print.h> +#include <drm/drm_simple_kms_helper.h> + +#include "yhgch_drm_drv.h" +#include "yhgch_drm_regs.h" + +static int yhgch_connector_get_modes(struct drm_connector *connector) +{ + int count; + void *edid; + struct yhgch_connector *yhgch_connector = to_yhgch_connector(connector); + + edid = drm_get_edid(connector, &yhgch_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 yhgch_connector_destroy(struct drm_connector *connector) +{ + struct yhgch_connector *yhgch_connector = to_yhgch_connector(connector); + + i2c_del_adapter(&yhgch_connector->adapter); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_helper_funcs + yhgch_connector_helper_funcs = { + .get_modes = yhgch_connector_get_modes, +}; + +static const struct drm_connector_funcs yhgch_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = yhgch_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 yhgch_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 yhgch_drm_private *priv = to_yhgch_drm_private(dev); + + reg = readl(priv->mmio + YHGCH_DISPLAY_CONTROL_HISILE); + reg |= YHGCH_DISPLAY_CONTROL_FPVDDEN(1); + reg |= YHGCH_DISPLAY_CONTROL_PANELDATE(1); + reg |= YHGCH_DISPLAY_CONTROL_FPEN(1); + reg |= YHGCH_DISPLAY_CONTROL_VBIASEN(1); + writel(reg, priv->mmio + YHGCH_DISPLAY_CONTROL_HISILE); +} + +static const struct drm_encoder_helper_funcs yhgch_encoder_helper_funcs = { + .mode_set = yhgch_encoder_mode_set, +}; + +int yhgch_vdac_init(struct yhgch_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + struct yhgch_connector *yhgch_connector = &priv->connector; + struct drm_encoder *encoder = &priv->encoder; + struct drm_crtc *crtc = &priv->crtc; + struct drm_connector *connector = &yhgch_connector->base; + int ret; + + ret = yhgch_ddc_create(dev, yhgch_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, &yhgch_encoder_helper_funcs); + + ret = drm_connector_init_with_ddc(dev, connector, + &yhgch_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + &yhgch_connector->adapter); + if (ret) { + drm_err(dev, "failed to init connector: %d\n", ret); + return ret; + } + drm_connector_helper_add(connector, &yhgch_connector_helper_funcs); + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} -- 2.43.5