From nobody Wed Oct 1 21:24:54 2025 Received: from ssh248.corpemail.net (ssh248.corpemail.net [210.51.61.248]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C2AD922F77E for ; Mon, 29 Sep 2025 06:31:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.51.61.248 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759127484; cv=none; b=XPblA31n/QxhURRLUk1Db3f/Kbw62/Db2JLibMye/X2kcwY7XA8w6jpE9frKt9Xi7XE0aei08mjCYRjAkbGsLk/aNPgmHVxTsUg4TyvasSNihAj25fKKR8mNk8lEpHH3b4Z2L8N+RlxekG1eIRe+nWu0QV9FxUAAYLlthAX0xBQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1759127484; c=relaxed/simple; bh=WepRGD4memxLG2jE6CgLcAyP8cX6hT8IPPk9cpSfeEY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=fDtKwGdR39FM9xTugLxPk+/l76dqZLy8plIl/zwgS6sF21ON+8KdvdgDIMvUtyaZOtjiGchSH8ZugX73QqLj+PGoipVNZ16NM+QMvLkbe2udy3tr3QW10kOOUNJSvruvWzxLRUXg9F8FI0ZWIcmVaxxC/9NoMQC6LvaE2PIRBTs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=inspur.com; spf=pass smtp.mailfrom=inspur.com; arc=none smtp.client-ip=210.51.61.248 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=inspur.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=inspur.com Received: from jtjnmail201605.home.langchao.com by ssh248.corpemail.net ((D)) with ASMTP (SSL) id 202509291431073390; Mon, 29 Sep 2025 14:31:07 +0800 Received: from localhost.localdomain.com (10.94.18.252) by jtjnmail201605.home.langchao.com (10.100.2.5) with Microsoft SMTP Server id 15.1.2507.58; Mon, 29 Sep 2025 14:31:06 +0800 From: Chu Guangqing To: , , , , , CC: , , Chu Guangqing Subject: [PATCH v7 1/1] [DRIVER] gpu: drm: add support for Yhgc ZX1000 soc chipset Date: Mon, 29 Sep 2025 14:31:03 +0800 Message-ID: <20250929063103.7375-2-chuguangqing@inspur.com> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20250929063103.7375-1-chuguangqing@inspur.com> References: <20250928054123.32895-1-chuguangqing@inspur.com> <20250929063103.7375-1-chuguangqing@inspur.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable tUid: 202592914310887ade18c003dee1c1390393dc23a942e X-Abuse-Reports-To: service@corp-email.com Abuse-Reports-To: service@corp-email.com X-Complaints-To: service@corp-email.com X-Report-Abuse-To: service@corp-email.com Content-Type: text/plain; charset="utf-8" add support for Yhgc BMC soc chipset Signed-off-by: Chu Guangqing --- MAINTAINERS | 5 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/yhgch/Kconfig | 11 + drivers/gpu/drm/yhgch/Makefile | 4 + drivers/gpu/drm/yhgch/yhgch_drm_de.c | 398 +++++++++++++++++++++++++ drivers/gpu/drm/yhgch/yhgch_drm_drv.c | 308 +++++++++++++++++++ drivers/gpu/drm/yhgch/yhgch_drm_drv.h | 51 ++++ drivers/gpu/drm/yhgch/yhgch_drm_i2c.c | 114 +++++++ drivers/gpu/drm/yhgch/yhgch_drm_regs.h | 208 +++++++++++++ drivers/gpu/drm/yhgch/yhgch_drm_vdac.c | 134 +++++++++ 11 files changed, 1236 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_de.c create mode 100644 drivers/gpu/drm/yhgch/yhgch_drm_drv.c create mode 100644 drivers/gpu/drm/yhgch/yhgch_drm_drv.h create mode 100644 drivers/gpu/drm/yhgch/yhgch_drm_i2c.c create mode 100644 drivers/gpu/drm/yhgch/yhgch_drm_regs.h create mode 100644 drivers/gpu/drm/yhgch/yhgch_drm_vdac.c diff --git a/MAINTAINERS b/MAINTAINERS index 520fb4e379a3..24e122d3f3fc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27781,6 +27781,11 @@ S: Maintained F: Documentation/input/devices/yealink.rst F: drivers/input/misc/yealink.* =20 +YHGC DRM DRIVER +M: Chu Guangqing +S: Maintained +F: drivers/gpu/drm/yhgch + Z8530 DRIVER FOR AX.25 M: Joerg Reuter L: linux-hams@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" =20 source "drivers/gpu/drm/imagination/Kconfig" =20 +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 +=3D solomon/ obj-$(CONFIG_DRM_SPRD) +=3D sprd/ obj-$(CONFIG_DRM_LOONGSON) +=3D loongson/ obj-$(CONFIG_DRM_POWERVR) +=3D imagination/ +obj-$(CONFIG_DRM_YHGCH) +=3D yhgch/ =20 # Ensure drm headers are self-contained and pass kernel-doc hdrtest-files :=3D \ diff --git a/drivers/gpu/drm/yhgch/Kconfig b/drivers/gpu/drm/yhgch/Kconfig new file mode 100644 index 000000000000..1e67c4b5e809 --- /dev/null +++ b/drivers/gpu/drm/yhgch/Kconfig @@ -0,0 +1,11 @@ +config DRM_YHGCH + tristate "DRM Support for Yhgch BMC" + depends on DRM && PCI && MMU + select DRM_CLIENT_SELECTION + select DRM_KMS_HELPER + select DRM_GEM_SHMEM_HELPER + help + Choose this option if you have a Yhgch ZX1000 chipset 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/Makefile b/drivers/gpu/drm/yhgch/Makefile new file mode 100644 index 000000000000..30de2fd27f18 --- /dev/null +++ b/drivers/gpu/drm/yhgch/Makefile @@ -0,0 +1,4 @@ +yhgch-drm-y :=3D yhgch_drm_drv.o yhgch_drm_de.o yhgch_drm_vdac.o yhgch_drm= _i2c.o + +obj-$(CONFIG_DRM_YHGCH) +=3D yhgch-drm.o + diff --git a/drivers/gpu/drm/yhgch/yhgch_drm_de.c b/drivers/gpu/drm/yhgch/y= hgch_drm_de.c new file mode 100644 index 000000000000..1a72b5517618 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch_drm_de.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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[] =3D { + { 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 int yhgch_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state =3D drm_atomic_get_new_plane_stat= e(state, + plane); + struct drm_framebuffer *fb =3D new_plane_state->fb; + struct drm_crtc_state *new_crtc_state =3D NULL; + + if (!fb) + return 0; + + if (new_plane_state->crtc) + new_crtc_state =3D drm_atomic_get_new_crtc_state(state, new_plane_state-= >crtc); + + return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_stat= e, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); +} + +static void yhgch_handle_damage(void __iomem *addr_base, struct iosys_map = *src, + struct drm_framebuffer *fb, + struct drm_rect *clip) +{ + struct iosys_map dst; + + iosys_map_set_vaddr_iomem(&dst, addr_base); + iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip)= ); + drm_fb_memcpy(&dst, fb->pitches, src, fb, clip); +} + +static void yhgch_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state =3D drm_atomic_get_new_plane_state(st= ate, plane); + struct drm_framebuffer *fb =3D plane_state->fb; + struct drm_plane_state *old_plane_state =3D drm_atomic_get_old_plane_stat= e(state, plane); + struct drm_shadow_plane_state *shadow_plane_state =3D to_drm_shadow_plane= _state(plane_state); + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(plane->dev); + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + u32 reg; + s64 gpu_addr =3D 0; + u32 line_l; + + if (!plane_state->crtc || !plane_state->fb) + return; + + if (!plane_state->visible) + return; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + yhgch_handle_damage(priv->vram_base, shadow_plane_state->data, fb, &dama= ge); + } + + fb->pitches[0] =3D (fb->pitches[0] + 15) & ~15; + + writel(gpu_addr, priv->mmio + YHGCH_CRT_FB_ADDRESS); + + reg =3D fb->width * fb->format->cpp[0]; + + line_l =3D fb->pitches[0]; + writel(FIELD_PREP(YHGCH_CRT_FB_WIDTH_WIDTH_MASK, reg) | + FIELD_PREP(YHGCH_CRT_FB_WIDTH_OFFS_MASK, line_l), + priv->mmio + YHGCH_CRT_FB_WIDTH); + + /* SET PIXEL FORMAT */ + reg =3D readl(priv->mmio + YHGCH_CRT_DISP_CTL); + reg &=3D ~YHGCH_CRT_DISP_CTL_FORMAT_MASK; + reg |=3D FIELD_PREP(YHGCH_CRT_DISP_CTL_FORMAT_MASK, + fb->format->cpp[0] * 8 / 16); + writel(reg, priv->mmio + YHGCH_CRT_DISP_CTL); +} + +static const u32 channel_formats1[] =3D { + DRM_FORMAT_RGB565, DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, +}; + +static struct drm_plane_funcs yhgch_plane_funcs =3D { + .update_plane =3D drm_atomic_helper_update_plane, + .disable_plane =3D drm_atomic_helper_disable_plane, + .destroy =3D drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +static const struct drm_plane_helper_funcs yhgch_plane_helper_funcs =3D { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check =3D yhgch_plane_atomic_check, + .atomic_update =3D yhgch_plane_atomic_update, +}; + +static void yhgch_crtc_dpms(struct drm_crtc *crtc, u32 dpms) +{ + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(crtc->dev); + u32 reg; + + reg =3D readl(priv->mmio + YHGCH_CRT_DISP_CTL); + reg &=3D ~YHGCH_CRT_DISP_CTL_DPMS_MASK; + reg |=3D FIELD_PREP(YHGCH_CRT_DISP_CTL_DPMS_MASK, dpms); + reg &=3D ~YHGCH_CRT_DISP_CTL_TIMING_MASK; + if (dpms =3D=3D YHGCH_CRT_DPMS_ON) + reg |=3D 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 =3D 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 =3D readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &=3D ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg &=3D ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg |=3D YHGCH_CURR_GATE_LOCALMEM(1); + reg |=3D 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 =3D 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 =3D readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &=3D ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg &=3D ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg |=3D YHGCH_CURR_GATE_LOCALMEM(0); + reg |=3D 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 =3D 0; + int vrefresh =3D drm_mode_vrefresh(mode); + + if (vrefresh < 59 || vrefresh > 61) + return MODE_NOCLOCK; + + for (i =3D 0; i < ARRAY_SIZE(yhgch_pll_table); i++) { + if (yhgch_pll_table[i].hdisplay =3D=3D mode->hdisplay && + yhgch_pll_table[i].vdisplay =3D=3D 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 =3D to_yhgch_drm_private(dev); + + val =3D readl(priv->mmio + CRT_PLL1_NS); + val &=3D ~(CRT_PLL1_NS_OUTER_BYPASS(1)); + writel(val, priv->mmio + CRT_PLL1_NS); + + val =3D 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 =3D pll & ~(CRT_PLL1_NS_POWERON(1)); + writel(val, priv->mmio + CRT_PLL1_NS); + + usleep_range(1000, 2000); + + val &=3D ~(CRT_PLL1_NS_INTER_BYPASS(1)); + writel(val, priv->mmio + CRT_PLL1_NS); + + usleep_range(1000, 2000); + + val |=3D 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 =3D ARRAY_SIZE(yhgch_pll_table); + + for (i =3D 0; i < count; i++) { + if (yhgch_pll_table[i].hdisplay =3D=3D x && + yhgch_pll_table[i].vdisplay =3D=3D y) { + *pll1 =3D yhgch_pll_table[i].pll1_config_value; + *pll2 =3D yhgch_pll_table[i].pll2_config_value; + return; + } + } +} + +/* + * 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 w, h; + u32 pll1; /* bit[31:0] of PLL */ + u32 pll2; /* bit[63:32] of PLL */ + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(dev); + + w =3D mode->hdisplay; + h =3D mode->vdisplay; + + get_pll_config(w, h, &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(FIELD_PREP(YHGCH_CRT_AUTO_CENTERING_TL_TOP_MASK, 0) | + FIELD_PREP(YHGCH_CRT_AUTO_CENTERING_TL_LEFT_MASK, 0), + priv->mmio + YHGCH_CRT_AUTO_CENTERING_TL); + + writel(FIELD_PREP(YHGCH_CRT_AUTO_CENTERING_BR_BOTTOM_MASK, h - 1) | + FIELD_PREP(YHGCH_CRT_AUTO_CENTERING_BR_RIGHT_MASK, w - 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 &=3D ~YHGCH_CRT_DISP_CTL_CRTSELECT_MASK; + ctrl &=3D ~YHGCH_CRT_DISP_CTL_CLOCK_PHASE_MASK; + + ctrl |=3D YHGCH_CRT_DISP_CTL_CRTSELECT(YHGCH_CRTSELECT_CRT); + + /* clock_phase_polarity is 0 */ + ctrl |=3D YHGCH_CRT_DISP_CTL_CLOCK_PHASE(0); + ctrl |=3D FIELD_PREP(YHGCH_CRT_DISP_CTL_FORMAT_MASK, 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 =3D &crtc->state->mode; + struct drm_device *dev =3D crtc->dev; + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(dev); + u32 width =3D mode->hsync_end - mode->hsync_start; + u32 height =3D mode->vsync_end - mode->vsync_start; + + //writel(format_pll_reg(), priv->mmio + YHGCH_CRT_PLL_CTRL); + writel(FIELD_PREP(YHGCH_CRT_HORZ_TOTAL_TOTAL_MASK, mode->htotal - 1) | + FIELD_PREP(YHGCH_CRT_HORZ_TOTAL_DISP_END_MASK, mode->hdisplay - 1), + priv->mmio + YHGCH_CRT_HORZ_TOTAL); + + writel(FIELD_PREP(YHGCH_CRT_HORZ_SYNC_WIDTH_MASK, width) | + FIELD_PREP(YHGCH_CRT_HORZ_SYNC_START_MASK, mode->hsync_start - 1), + priv->mmio + YHGCH_CRT_HORZ_SYNC); + + writel(FIELD_PREP(YHGCH_CRT_VERT_TOTAL_TOTAL_MASK, mode->vtotal - 1) | + FIELD_PREP(YHGCH_CRT_VERT_TOTAL_DISP_END_MASK, mode->vdisplay - 1), + priv->mmio + YHGCH_CRT_VERT_TOTAL); + + writel(FIELD_PREP(YHGCH_CRT_VERT_SYNC_HEIGHT_MASK, height) | + FIELD_PREP(YHGCH_CRT_VERT_SYNC_START_MASK, mode->vsync_start - 1), + priv->mmio + YHGCH_CRT_VERT_SYNC); + + val =3D FIELD_PREP(YHGCH_CRT_DISP_CTL_VSYNC_PHASE_MASK, 0); + val |=3D FIELD_PREP(YHGCH_CRT_DISP_CTL_HSYNC_PHASE_MASK, 0); + val |=3D YHGCH_CRT_DISP_CTL_TIMING(1); + val |=3D 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 =3D crtc->dev; + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(dev); + + yhgch_set_power_mode(priv, YHGCH_PW_MODE_CTL_MODE_MODE0); + + /* Enable display power gate & LOCALMEM power gate */ + reg =3D readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &=3D ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg &=3D ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg |=3D YHGCH_CURR_GATE_DISPLAY(1); + reg |=3D YHGCH_CURR_GATE_LOCALMEM(1); + yhgch_set_current_gate(priv, reg); +} + +static const struct drm_crtc_funcs yhgch_crtc_funcs =3D { + .page_flip =3D drm_atomic_helper_page_flip, + .set_config =3D drm_atomic_helper_set_config, + .destroy =3D drm_crtc_cleanup, + .reset =3D drm_atomic_helper_crtc_reset, + .atomic_duplicate_state =3D drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state =3D drm_atomic_helper_crtc_destroy_state, +}; + +static const struct drm_crtc_helper_funcs yhgch_crtc_helper_funcs =3D { + .mode_set_nofb =3D yhgch_crtc_mode_set_nofb, + .atomic_begin =3D yhgch_crtc_atomic_begin, + .atomic_enable =3D yhgch_crtc_atomic_enable, + .atomic_disable =3D yhgch_crtc_atomic_disable, + .mode_valid =3D yhgch_crtc_mode_valid, +}; + +int yhgch_de_init(struct yhgch_drm_private *priv) +{ + struct drm_device *dev =3D &priv->dev; + struct drm_crtc *crtc =3D &priv->crtc; + struct drm_plane *plane =3D &priv->primary_plane; + int ret; + + ret =3D 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); + drm_plane_enable_fb_damage_clips(plane); + + ret =3D 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; + } + + drm_crtc_helper_add(crtc, &yhgch_crtc_helper_funcs); + + return 0; +} diff --git a/drivers/gpu/drm/yhgch/yhgch_drm_drv.c b/drivers/gpu/drm/yhgch/= yhgch_drm_drv.c new file mode 100644 index 000000000000..911ac3f8d335 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch_drm_drv.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "yhgch_drm_drv.h" +#include "yhgch_drm_regs.h" + +DEFINE_DRM_GEM_FOPS(yhgch_fops); + +int yhgch_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + args->width =3D ALIGN(args->width, 8); + return drm_gem_shmem_dumb_create(file, dev, args); +} + +static struct drm_driver yhgch_driver =3D { + .driver_features =3D DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops =3D &yhgch_fops, + .name =3D "yhgch", + .desc =3D "yhgch drm driver", + .major =3D 3, + .minor =3D 1, + .dumb_create =3D yhgch_dumb_create, + DRM_FBDEV_SHMEM_DRIVER_OPS, +}; + +static int __maybe_unused yhgch_pm_suspend(struct device *dev) +{ + struct drm_device *drm_dev =3D 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 =3D dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(drm_dev); +} + +static const struct dev_pm_ops yhgch_pm_ops =3D { + SET_SYSTEM_SLEEP_PM_OPS(yhgch_pm_suspend, + yhgch_pm_resume) +}; + +static const struct drm_mode_config_funcs yhgch_mode_funcs =3D { + .atomic_check =3D drm_atomic_helper_check, + .atomic_commit =3D drm_atomic_helper_commit, + .fb_create =3D drm_gem_fb_create_with_dirty, +}; + +static int yhgch_kms_init(struct yhgch_drm_private *priv) +{ + struct drm_device *dev =3D &priv->dev; + int ret; + + ret =3D drmm_mode_config_init(dev); + if (ret) + return ret; + + dev->mode_config.min_width =3D 0; + dev->mode_config.min_height =3D 0; + dev->mode_config.max_width =3D 1920; + dev->mode_config.max_height =3D 1200; + dev->mode_config.preferred_depth =3D 24; + dev->mode_config.prefer_shadow =3D 1; + dev->mode_config.funcs =3D &yhgch_mode_funcs; + + ret =3D yhgch_de_init(priv); + if (ret) { + drm_err(dev, "failed to init de: %d\n", ret); + return ret; + } + + ret =3D yhgch_vdac_init(priv); + if (ret) { + drm_err(dev, "failed to init vdac: %d\n", ret); + return ret; + } + drm_kms_helper_poll_init(dev); + + 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 =3D 0; + void __iomem *mmio =3D priv->mmio; + u32 input =3D 1; + + if (power_mode > YHGCH_PW_MODE_CTL_MODE_SLEEP) + return; + + if (power_mode =3D=3D YHGCH_PW_MODE_CTL_MODE_SLEEP) + input =3D 0; + + control_value =3D readl(mmio + YHGCH_POWER_MODE_CTRL); + control_value &=3D ~(YHGCH_PW_MODE_CTL_MODE_MASK | + YHGCH_PW_MODE_CTL_OSC_INPUT_MASK); + control_value |=3D FIELD_PREP(YHGCH_PW_MODE_CTL_MODE_MASK, power_mode); + control_value |=3D FIELD_PREP(YHGCH_PW_MODE_CTL_OSC_INPUT_MASK, input); + writel(control_value, mmio + YHGCH_POWER_MODE_CTRL); +} + +void yhgch_set_current_gate(struct yhgch_drm_private *priv, unsigned int g= ate) +{ + u32 gate_reg; + u32 mode; + void __iomem *mmio =3D priv->mmio; + + /* Get current power mode. */ + mode =3D (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 =3D YHGCH_MODE0_GATE; + break; + + case YHGCH_PW_MODE_CTL_MODE_MODE1: + gate_reg =3D YHGCH_MODE1_GATE; + break; + + default: + gate_reg =3D 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 =3D readl(priv->mmio + YHGCH_CURRENT_GATE); + reg &=3D ~YHGCH_CURR_GATE_DISPLAY_MASK; + reg &=3D ~YHGCH_CURR_GATE_LOCALMEM_MASK; + reg |=3D YHGCH_CURR_GATE_DISPLAY(1); + reg |=3D 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 =3D readl(priv->mmio + YHGCH_MISC_CTRL); + reg &=3D ~YHGCH_MSCCTL_LOCALMEM_RESET_MASK; + reg |=3D YHGCH_MSCCTL_LOCALMEM_RESET(0); + writel(reg, priv->mmio + YHGCH_MISC_CTRL); + + reg &=3D ~YHGCH_MSCCTL_LOCALMEM_RESET_MASK; + reg |=3D 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 =3D &priv->dev; + struct pci_dev *pdev =3D to_pci_dev(dev->dev); + resource_size_t ioaddr, iosize; + + ioaddr =3D pci_resource_start(pdev, 1); + iosize =3D pci_resource_len(pdev, 1); + priv->mmio =3D devm_ioremap(dev->dev, ioaddr, iosize); + if (!priv->mmio) { + drm_err(dev, "Cannot map mmio region\n"); + return -ENOMEM; + } + + ioaddr =3D pci_resource_start(pdev, 0); + iosize =3D pci_resource_len(pdev, 0); + priv->vram_base =3D devm_ioremap_wc(dev->dev, ioaddr, iosize); + if (!priv->vram_base) { + drm_err(dev, "Cannot map vram region\n"); + return -ENOMEM; + } + return 0; +} + +static int yhgch_hw_init(struct yhgch_drm_private *priv) +{ + int ret; + + ret =3D yhgch_hw_map(priv); + if (ret) + return ret; + yhgch_hw_config(priv); + return 0; +} + +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 =3D aperture_remove_conflicting_pci_devices(pdev, yhgch_driver.name); + + if (ret) + return ret; + + priv =3D devm_drm_dev_alloc(&pdev->dev, &yhgch_driver, + struct yhgch_drm_private, dev); + + if (IS_ERR(priv)) + return PTR_ERR(priv); + + dev =3D &priv->dev; + pci_set_drvdata(pdev, dev); + + ret =3D pcim_enable_device(pdev); + if (ret) { + drm_err(dev, "failed to enable pci device: %d\n", ret); + goto err_return; + } + + ret =3D yhgch_hw_init(priv); + if (ret) + goto err_return; + + ret =3D yhgch_kms_init(priv); + if (ret) + goto err_return; + + ret =3D pci_enable_msi(pdev); + if (ret) + drm_warn(dev, "enabling MSI failed: %d\n", ret); + /* reset all the states of crtc/plane/encoder/connector */ + drm_mode_config_reset(dev); + + ret =3D drm_dev_register(dev, 0); + if (ret) { + drm_err(dev, "failed to register drv for userspace access: %d\n", + ret); + goto err_return; + } + drm_client_setup(dev, NULL); + + return 0; + +err_return: + return ret; +} + +static void yhgch_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev =3D pci_get_drvdata(pdev); + + drm_dev_unregister(dev); + drm_dev_put(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[] =3D { + { 0x1bd4, 0x0750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, } +}; + +static struct pci_driver yhgch_pci_driver =3D { + .name =3D "yhgch-drm", + .id_table =3D yhgch_pci_table, + .probe =3D yhgch_pci_probe, + .remove =3D yhgch_pci_remove, + .shutdown =3D yhgch_pci_shutdown, + .driver.pm =3D &yhgch_pm_ops, +}; + +drm_module_pci_driver(yhgch_pci_driver); + +MODULE_DEVICE_TABLE(pci, yhgch_pci_table); +MODULE_AUTHOR("Chu Guangqing "); +MODULE_DESCRIPTION("DRM Driver for YhgchBMC"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("3.1"); diff --git a/drivers/gpu/drm/yhgch/yhgch_drm_drv.h b/drivers/gpu/drm/yhgch/= yhgch_drm_drv.h new file mode 100644 index 000000000000..1b8b1e5b0a43 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch_drm_drv.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef YHGCH_DRM_DRV_H +#define YHGCH_DRM_DRV_H + +#include +#include +#include +#include +#include +#include +#include + +struct yhgch_ddc { + struct yhgch_drm_private *priv; + struct i2c_adapter adapter; + struct i2c_algo_bit_data bit_data; +}; + +struct yhgch_drm_private { + /* hw */ + void __iomem *mmio; + void __iomem *vram_base; + + /* drm */ + struct drm_device dev; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +static inline struct yhgch_drm_private *to_yhgch_drm_private(struct drm_de= vice *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); +struct i2c_adapter *yhgch_ddc_create(struct yhgch_drm_private *priv); + +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_i2c.c b/drivers/gpu/drm/yhgch/= yhgch_drm_i2c.c new file mode 100644 index 000000000000..7bbe9a0540d4 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch_drm_i2c.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include +#include + +#include + +#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_ddc *ddc =3D data; + struct yhgch_drm_private *priv =3D ddc->priv; + u32 tmp_dir =3D readl(priv->mmio + GPIO_DATA_DIRECTION); + + if (value) { + tmp_dir &=3D ~mask; + writel(tmp_dir, priv->mmio + GPIO_DATA_DIRECTION); + } else { + u32 tmp_data =3D readl(priv->mmio + GPIO_DATA); + + tmp_data &=3D ~mask; + writel(tmp_data, priv->mmio + GPIO_DATA); + + tmp_dir |=3D mask; + writel(tmp_dir, priv->mmio + GPIO_DATA_DIRECTION); + } +} + +static int yhgch_get_i2c_signal(void *data, u32 mask) +{ + struct yhgch_ddc *ddc =3D data; + struct yhgch_drm_private *priv =3D ddc->priv; + u32 tmp_dir =3D readl(priv->mmio + GPIO_DATA_DIRECTION); + + if ((tmp_dir & mask) !=3D mask) { + tmp_dir &=3D ~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); +} + +static void yhgch_ddc_release(struct drm_device *dev, void *res) +{ + struct yhgch_ddc *ddc =3D res; + + i2c_del_adapter(&ddc->adapter); +} + +struct i2c_adapter *yhgch_ddc_create(struct yhgch_drm_private *priv) +{ + int ret =3D 0; + struct yhgch_ddc *ddc; + struct drm_device *dev =3D &priv->dev; + + ddc =3D drmm_kzalloc(dev, sizeof(struct yhgch_ddc), GFP_KERNEL); + if (!ddc) + return ERR_PTR(-ENOMEM); + + ddc->adapter.owner =3D THIS_MODULE; + ddc->priv =3D priv; + snprintf(ddc->adapter.name, I2C_NAME_SIZE, "INS i2c bit bus"); + ddc->adapter.dev.parent =3D priv->dev.dev; + i2c_set_adapdata(&ddc->adapter, ddc); + ddc->adapter.algo_data =3D &ddc->bit_data; + + ddc->bit_data.udelay =3D 20; + ddc->bit_data.timeout =3D usecs_to_jiffies(2000); + ddc->bit_data.data =3D ddc; + ddc->bit_data.setsda =3D yhgch_ddc_setsda; + ddc->bit_data.setscl =3D yhgch_ddc_setscl; + ddc->bit_data.getsda =3D yhgch_ddc_getsda; + ddc->bit_data.getscl =3D yhgch_ddc_getscl; + + ret =3D i2c_bit_add_bus(&ddc->adapter); + if (ret) + return ERR_PTR(ret); + + ret =3D drmm_add_action_or_reset(&priv->dev, yhgch_ddc_release, ddc); + if (ret) + return ERR_PTR(ret); + + return &ddc->adapter; +} diff --git a/drivers/gpu/drm/yhgch/yhgch_drm_regs.h b/drivers/gpu/drm/yhgch= /yhgch_drm_regs.h new file mode 100644 index 000000000000..cecb6173a445 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch_drm_regs.h @@ -0,0 +1,208 @@ +/* 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 + +#endif diff --git a/drivers/gpu/drm/yhgch/yhgch_drm_vdac.c b/drivers/gpu/drm/yhgch= /yhgch_drm_vdac.c new file mode 100644 index 000000000000..2e222af29f69 --- /dev/null +++ b/drivers/gpu/drm/yhgch/yhgch_drm_vdac.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include +#include +#include +#include +#include + +#include "yhgch_drm_drv.h" +#include "yhgch_drm_regs.h" + +static int yhgch_connector_get_modes(struct drm_connector *connector) +{ + int count; + const struct drm_edid *drm_edid; + + drm_edid =3D drm_edid_read(connector); + if (drm_edid) { + drm_edid_connector_update(connector, drm_edid); + count =3D drm_edid_connector_add_modes(connector); + if (count) + goto out; + } + + count =3D 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: + drm_edid_free(drm_edid); + return count; +} + +static int yhgch_connector_helper_detect_from_ddc(struct drm_connector *co= nnector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + if (drm_connector_helper_detect_from_ddc(connector, ctx, force) + !=3D connector_status_connected) { + drm_dbg_kms(connector->dev, "ddc detect failed, force connect\n"); + } + return connector_status_connected; +} + +static const struct drm_connector_helper_funcs + yhgch_connector_helper_funcs =3D { + .get_modes =3D yhgch_connector_get_modes, + .detect_ctx =3D yhgch_connector_helper_detect_from_ddc, +}; + +static const struct drm_connector_funcs yhgch_connector_funcs =3D { + .fill_modes =3D drm_helper_probe_single_connector_modes, + .destroy =3D drm_connector_cleanup, + .reset =3D drm_atomic_helper_connector_reset, + .atomic_duplicate_state =3D drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state =3D drm_atomic_helper_connector_destroy_state, +}; + +static void yhgch_encoder_enable(struct drm_encoder *encoder) +{ + u32 reg; + struct drm_device *dev =3D encoder->dev; + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(dev); + + reg =3D readl(priv->mmio + YHGCH_DISPLAY_CONTROL_HISILE); + reg |=3D YHGCH_DISPLAY_CONTROL_FPVDDEN(1); + reg |=3D YHGCH_DISPLAY_CONTROL_PANELDATE(1); + reg |=3D YHGCH_DISPLAY_CONTROL_FPEN(1); + reg |=3D YHGCH_DISPLAY_CONTROL_VBIASEN(1); + writel(reg, priv->mmio + YHGCH_DISPLAY_CONTROL_HISILE); +} + +static void yhgch_encoder_disable(struct drm_encoder *encoder) +{ + u32 reg =3D 0, regmask =3D 0; + struct drm_device *dev =3D encoder->dev; + struct yhgch_drm_private *priv =3D to_yhgch_drm_private(dev); + + reg =3D readl(priv->mmio + YHGCH_DISPLAY_CONTROL_HISILE); + regmask |=3D YHGCH_DISPLAY_CONTROL_FPVDDEN(1); + regmask |=3D YHGCH_DISPLAY_CONTROL_PANELDATE(1); + regmask |=3D YHGCH_DISPLAY_CONTROL_FPEN(1); + regmask |=3D YHGCH_DISPLAY_CONTROL_VBIASEN(1); + reg &=3D ~regmask; + writel(reg, priv->mmio + YHGCH_DISPLAY_CONTROL_HISILE); +} + +static const struct drm_encoder_helper_funcs yhgch_encoder_helper_funcs = =3D { + .enable =3D yhgch_encoder_enable, + .disable =3D yhgch_encoder_disable, +}; + +int yhgch_vdac_init(struct yhgch_drm_private *priv) +{ + struct drm_device *dev =3D &priv->dev; + struct drm_encoder *encoder =3D &priv->encoder; + struct drm_crtc *crtc =3D &priv->crtc; + struct drm_connector *connector =3D &priv->connector; + struct i2c_adapter *ddc; + int ret; + + ddc =3D yhgch_ddc_create(priv); + if (IS_ERR(ddc)) { + ret =3D PTR_ERR(ddc); + drm_err(dev, "failed to create ddc: %d\n", ret); + return ret; + } + + encoder->possible_crtcs =3D drm_crtc_mask(crtc); + ret =3D drmm_encoder_init(dev, encoder, NULL, DRM_MODE_ENCODER_DAC, NULL); + if (ret) { + drm_err(dev, "failed to init encoder: %d\n", ret); + return ret; + } + + drm_encoder_helper_add(encoder, &yhgch_encoder_helper_funcs); + + ret =3D drm_connector_init_with_ddc(dev, connector, + &yhgch_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + ddc); + 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; +} --=20 2.43.5