summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/ast
diff options
context:
space:
mode:
authorJinke Han <jinkehan@didiglobal.com>2026-09-08 15:37:42 +0800
committerIngo Molnar <mingo@kernel.org>2026-09-17 09:48:42 +0200
commita5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch)
tree73fef824f9a846cf935869d0dbc5070ef6e2f741 /drivers/gpu/drm/ast
downloadlinux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz
linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Jinke Han <jinkehan@didiglobal.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Acked-by: Yafang Shao <laoar.shao@gmail.com> Acked-by: Borislav Petkov <bp@alien8.de> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'drivers/gpu/drm/ast')
-rw-r--r--drivers/gpu/drm/ast/Kconfig16
-rw-r--r--drivers/gpu/drm/ast/Makefile26
-rw-r--r--drivers/gpu/drm/ast/ast_2000.c252
-rw-r--r--drivers/gpu/drm/ast/ast_2100.c482
-rw-r--r--drivers/gpu/drm/ast/ast_2200.c92
-rw-r--r--drivers/gpu/drm/ast/ast_2300.c1457
-rw-r--r--drivers/gpu/drm/ast/ast_2400.c100
-rw-r--r--drivers/gpu/drm/ast/ast_2500.c678
-rw-r--r--drivers/gpu/drm/ast/ast_2600.c116
-rw-r--r--drivers/gpu/drm/ast/ast_cursor.c363
-rw-r--r--drivers/gpu/drm/ast/ast_ddc.c187
-rw-r--r--drivers/gpu/drm/ast/ast_ddc.h11
-rw-r--r--drivers/gpu/drm/ast/ast_dp.c576
-rw-r--r--drivers/gpu/drm/ast/ast_dp501.c601
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c616
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h566
-rw-r--r--drivers/gpu/drm/ast/ast_mm.c97
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c1078
-rw-r--r--drivers/gpu/drm/ast/ast_post.c91
-rw-r--r--drivers/gpu/drm/ast/ast_post.h48
-rw-r--r--drivers/gpu/drm/ast/ast_reg.h265
-rw-r--r--drivers/gpu/drm/ast/ast_sil164.c126
-rw-r--r--drivers/gpu/drm/ast/ast_tables.h109
-rw-r--r--drivers/gpu/drm/ast/ast_vbios.c241
-rw-r--r--drivers/gpu/drm/ast/ast_vbios.h108
-rw-r--r--drivers/gpu/drm/ast/ast_vga.c126
26 files changed, 8428 insertions, 0 deletions
diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig
new file mode 100644
index 000000000..242fbccdf
--- /dev/null
+++ b/drivers/gpu/drm/ast/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DRM_AST
+ tristate "AST server chips"
+ depends on DRM && PCI
+ select DRM_CLIENT_SELECTION
+ select DRM_GEM_SHMEM_HELPER
+ select DRM_KMS_HELPER
+ select I2C
+ select I2C_ALGOBIT
+ help
+ Say yes for experimental AST GPU driver. Do not enable
+ this driver without having a working -modesetting,
+ and a version of AST that knows to fail if KMS
+ is bound to the driver. These GPUs are commonly found
+ in server chipsets.
+
diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile
new file mode 100644
index 000000000..cdbcba3b4
--- /dev/null
+++ b/drivers/gpu/drm/ast/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ast-y := \
+ ast_2000.o \
+ ast_2100.o \
+ ast_2200.o \
+ ast_2300.o \
+ ast_2400.o \
+ ast_2500.o \
+ ast_2600.o \
+ ast_cursor.o \
+ ast_ddc.o \
+ ast_dp501.o \
+ ast_dp.o \
+ ast_drv.o \
+ ast_mm.o \
+ ast_mode.o \
+ ast_post.o \
+ ast_sil164.o \
+ ast_vbios.o \
+ ast_vga.o
+
+obj-$(CONFIG_DRM_AST) := ast.o
diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c
new file mode 100644
index 000000000..4cf951b35
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2000.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "ast_drv.h"
+#include "ast_post.h"
+
+/*
+ * POST
+ */
+
+void ast_2000_set_def_ext_reg(struct ast_device *ast)
+{
+ static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
+ u8 i, index, reg;
+ const u8 *ext_reg_info;
+
+ /* reset scratch */
+ for (i = 0x81; i <= 0x9f; i++)
+ ast_set_index_reg(ast, AST_IO_VGACRI, i, 0x00);
+
+ ext_reg_info = extreginfo;
+ index = 0xa0;
+ while (*ext_reg_info != 0xff) {
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, index, 0x00, *ext_reg_info);
+ index++;
+ ext_reg_info++;
+ }
+
+ /* disable standard IO/MEM decode if secondary */
+ /* ast_set_index_reg-mask(ast, AST_IO_VGACRI, 0xa1, 0xff, 0x3); */
+
+ /* Set Ext. Default */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x8c, 0x00, 0x01);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x00, 0x00);
+
+ /* Enable RAMDAC for A1 */
+ reg = 0x04;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xff, reg);
+}
+
+static const struct ast_dramstruct ast2000_dram_table_data[] = {
+ AST_DRAMSTRUCT_REG(AST_REG_MCR108, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR120, 0x00004a21),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR00, 0xffffffff), /* FIXME: This locks the MCR registers. */
+ AST_DRAMSTRUCT_REG(AST_REG_MCR04, 0x00000089), /* DRAM type */
+ AST_DRAMSTRUCT_REG(AST_REG_MCR08, 0x22331353),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x0d07000b),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR10, 0x11113333),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR20, 0x00110350),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x1e0828f0),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR1C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x00000003),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x00000131),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x00000001),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x00000031),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x00000001),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x1e0828f1),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x1f0f28fb),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0xfffffe01),
+ AST_DRAMSTRUCT_INVALID,
+};
+
+static void ast_post_chip_2000(struct ast_device *ast)
+{
+ u8 j;
+ u32 i;
+
+ j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+
+ if ((j & 0x80) == 0) { /* VGA only */
+ const struct ast_dramstruct *dram_reg_info = ast2000_dram_table_data;
+ u32 mcr140;
+
+ ast_moutdwm_poll(ast, AST_REG_MCR100, 0xa8, 0xa8);
+
+ while (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) {
+ if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) {
+ for (i = 0; i < 15; i++)
+ udelay(dram_reg_info->data);
+ } else {
+ ast_moutdwm(ast, dram_reg_info->index, dram_reg_info->data);
+ }
+ dram_reg_info++;
+ }
+
+ mcr140 = ast_mindwm(ast, AST_REG_MCR140);
+ mcr140 |= 0x00000040;
+ ast_moutdwm(ast, AST_REG_MCR140, mcr140);
+ }
+
+ /* wait ready */
+ do {
+ j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ } while ((j & 0x40) == 0);
+}
+
+int ast_2000_post(struct ast_device *ast)
+{
+ ast_2000_set_def_ext_reg(ast);
+
+ if (ast->config_mode == ast_use_p2a) {
+ ast_post_chip_2000(ast);
+ } else {
+ if (ast->tx_chip == AST_TX_SIL164) {
+ /* Enable DVO */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Mode setting
+ */
+
+const struct ast_vbios_dclk_info ast_2000_dclk_table[] = {
+ {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */
+ {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
+ {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
+ {0x76, 0x63, 0x01}, /* 03: VCLK36 */
+ {0xee, 0x67, 0x01}, /* 04: VCLK40 */
+ {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
+ {0xc6, 0x64, 0x01}, /* 06: VCLK50 */
+ {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
+ {0x80, 0x64, 0x00}, /* 08: VCLK65 */
+ {0x7b, 0x63, 0x00}, /* 09: VCLK75 */
+ {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */
+ {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */
+ {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */
+ {0x85, 0x24, 0x00}, /* 0d: VCLK135 */
+ {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */
+ {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */
+ {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
+ {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */
+ {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
+ {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
+ {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
+ {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
+ {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
+ {0x77, 0x58, 0x80}, /* 17: VCLK119 */
+ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
+ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
+ {0x3b, 0x2c, 0x81}, /* 1a: VCLK118_25 */
+};
+
+/*
+ * Device initialization
+ */
+
+void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post)
+{
+ enum ast_tx_chip tx_chip = AST_TX_NONE;
+ u8 vgacra3;
+
+ /*
+ * VGACRA3 Enhanced Color Mode Register, check if DVO is already
+ * enabled, in that case, assume we have a SIL164 TMDS transmitter
+ *
+ * Don't make that assumption if we the chip wasn't enabled and
+ * is at power-on reset, otherwise we'll incorrectly "detect" a
+ * SIL164 when there is none.
+ */
+ if (!need_post) {
+ vgacra3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff);
+ if (vgacra3 & AST_IO_VGACRA3_DVO_ENABLED)
+ tx_chip = AST_TX_SIL164;
+ }
+
+ __ast_device_set_tx_chip(ast, tx_chip);
+}
+
+static const struct ast_device_quirks ast_2000_device_quirks = {
+ .crtc_mem_req_threshold_low = 31,
+ .crtc_mem_req_threshold_high = 47,
+};
+
+struct drm_device *ast_2000_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2000_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2000_detect_tx_chip(ast, need_post);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c
new file mode 100644
index 000000000..5cf8ad9c3
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2100.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "ast_drv.h"
+#include "ast_post.h"
+
+/*
+ * DRAM type
+ */
+
+static enum ast_dram_layout ast_2100_get_dram_layout_p2a(struct ast_device *ast)
+{
+ u32 mcr04;
+ enum ast_dram_layout dram_layout;
+
+ mcr04 = ast_mindwm(ast, AST_REG_MCR04);
+
+ switch (mcr04 & GENMASK(3, 2)) {
+ case 0:
+ case 4:
+ default:
+ dram_layout = AST_DRAM_512Mx16;
+ break;
+ case 8:
+ if (mcr04 & 0x40)
+ dram_layout = AST_DRAM_1Gx16;
+ else
+ dram_layout = AST_DRAM_512Mx32;
+ break;
+ case 0xc:
+ dram_layout = AST_DRAM_1Gx32;
+ break;
+ }
+
+ return dram_layout;
+}
+
+/*
+ * POST
+ */
+
+static const struct ast_dramstruct ast1100_dram_table_data[] = {
+ AST_DRAMSTRUCT_REG(AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_SCU020, 0x000041f0),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR6C, 0x00909090),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x00050000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR04, 0x00000585), // DRAM type
+ AST_DRAMSTRUCT_REG(AST_REG_MCR08, 0x0011030f),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR10, 0x22201724),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x1e29011a),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR20, 0x00c82222),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x01001523),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR1C, 0x1024010d),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00cb2522),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR38, 0xffffff82),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR3C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR40, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR44, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR48, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR4C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR50, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR54, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR58, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR5C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR60, 0x032aa02a),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x002d3000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR68, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR70, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR74, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR78, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR7C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00000001),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000732),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000005),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000007),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a08),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000632),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x000003c0),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a21),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00007c03),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR120, 0x00004c41),
+ AST_DRAMSTRUCT_INVALID,
+};
+
+static const struct ast_dramstruct ast2100_dram_table_data[] = {
+ AST_DRAMSTRUCT_REG(AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_SCU020, 0x00004120),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR6C, 0x00909090),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x00070000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR04, 0x00000489), // DRAM type
+ AST_DRAMSTRUCT_REG(AST_REG_MCR08, 0x0011030f),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR10, 0x32302926),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x274c0122),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR20, 0x00ce2222),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x01001523),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR1C, 0x1024010d),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00cb2522),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR38, 0xffffff82),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR3C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR40, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR44, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR48, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR4C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR50, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR54, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR58, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR5C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR60, 0x0f2aa02a),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x003f3005),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR68, 0x02020202),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR70, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR74, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR78, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR7C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00000001),
+ AST_DRAMSTRUCT_UDELAY(67u),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000942),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000005),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000007),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a08),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000842),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x000003c0),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a21),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00007c03),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR120, 0x00005061),
+ AST_DRAMSTRUCT_INVALID,
+};
+
+/*
+ * AST2100/2150 DLL CBR Setting
+ */
+#define CBR_SIZE_AST2150 ((16 << 10) - 1)
+#define CBR_PASSNUM_AST2150 5
+#define CBR_THRESHOLD_AST2150 10
+#define CBR_THRESHOLD2_AST2150 10
+#define TIMEOUT_AST2150 5000000
+
+#define CBR_PATNUM_AST2150 8
+
+static const u32 pattern_AST2150[14] = {
+ 0xFF00FF00,
+ 0xCC33CC33,
+ 0xAA55AA55,
+ 0xFFFE0001,
+ 0x683501FE,
+ 0x0F1929B0,
+ 0x2D0B4346,
+ 0x60767F02,
+ 0x6FBE36A6,
+ 0x3A253035,
+ 0x3019686D,
+ 0x41C6167E,
+ 0x620152BF,
+ 0x20F050E0
+};
+
+static u32 mmctestburst2_ast2150(struct ast_device *ast, u32 datagen)
+{
+ u32 data, timeout;
+
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000001 | (datagen << 3));
+ timeout = 0;
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x40;
+ if (++timeout > TIMEOUT_AST2150) {
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ return 0xffffffff;
+ }
+ } while (!data);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000003 | (datagen << 3));
+ timeout = 0;
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x40;
+ if (++timeout > TIMEOUT_AST2150) {
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ return 0xffffffff;
+ }
+ } while (!data);
+ data = (ast_mindwm(ast, AST_REG_MCR70) & 0x80) >> 7;
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ return data;
+}
+
+static int cbrtest_ast2150(struct ast_device *ast)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (mmctestburst2_ast2150(ast, i))
+ return 0;
+ return 1;
+}
+
+static int cbrscan_ast2150(struct ast_device *ast, int busw)
+{
+ u32 patcnt, loop;
+
+ for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern_AST2150[patcnt]);
+ for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
+ if (cbrtest_ast2150(ast))
+ break;
+ }
+ if (loop == CBR_PASSNUM_AST2150)
+ return 0;
+ }
+ return 1;
+}
+
+static void cbrdlli_ast2150(struct ast_device *ast, int busw)
+{
+ u32 dll_min[4], dll_max[4], dlli, data, passcnt;
+
+cbr_start:
+ dll_min[0] = 0xff;
+ dll_min[1] = 0xff;
+ dll_min[2] = 0xff;
+ dll_min[3] = 0xff;
+ dll_max[0] = 0x00;
+ dll_max[1] = 0x00;
+ dll_max[2] = 0x00;
+ dll_max[3] = 0x00;
+ passcnt = 0;
+
+ for (dlli = 0; dlli < 100; dlli++) {
+ ast_moutdwm(ast, AST_REG_MCR68, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+ data = cbrscan_ast2150(ast, busw);
+ if (data != 0) {
+ if (data & 0x1) {
+ if (dll_min[0] > dlli)
+ dll_min[0] = dlli;
+ if (dll_max[0] < dlli)
+ dll_max[0] = dlli;
+ }
+ passcnt++;
+ } else if (passcnt >= CBR_THRESHOLD_AST2150) {
+ goto cbr_start;
+ }
+ }
+ if (dll_max[0] == 0 || (dll_max[0] - dll_min[0]) < CBR_THRESHOLD_AST2150)
+ goto cbr_start;
+
+ dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
+ ast_moutdwm(ast, AST_REG_MCR68, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+}
+
+static void ast_post_chip_2100(struct ast_device *ast)
+{
+ u8 j;
+ u32 i;
+ enum ast_dram_layout dram_layout = ast_2100_get_dram_layout_p2a(ast);
+ u32 mcr120;
+ u32 scu00c, scu040;
+
+ j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+
+ if ((j & 0x80) == 0) { /* VGA only */
+ const struct ast_dramstruct *dram_reg_info;
+
+ if (ast->chip == AST2100 || ast->chip == AST2200)
+ dram_reg_info = ast2100_dram_table_data;
+ else
+ dram_reg_info = ast1100_dram_table_data;
+
+ ast_moutdwm_poll(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY, 0x01);
+ ast_moutdwm_poll(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY, 0x01);
+
+ while (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) {
+ if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) {
+ for (i = 0; i < 15; i++)
+ udelay(dram_reg_info->data);
+ } else if (AST_DRAMSTRUCT_IS_REG(dram_reg_info, AST_REG_MCR04)) {
+ u32 mcr04;
+ u32 scu070;
+
+ switch (dram_layout) {
+ case AST_DRAM_1Gx16:
+ mcr04 = 0x00000d89;
+ break;
+ case AST_DRAM_1Gx32:
+ mcr04 = 0x00000c8d;
+ break;
+ default:
+ mcr04 = dram_reg_info->data;
+ break;
+ }
+
+ /*
+ * FIXME: There might be bits already in MCR04[5:4]. Should
+ * we only do this in the default case?
+ */
+ scu070 = ast_mindwm(ast, AST_REG_SCU070);
+ mcr04 |= (scu070 & GENMASK(3, 2)) << 2;
+
+ ast_moutdwm(ast, dram_reg_info->index, mcr04);
+ } else {
+ ast_moutdwm(ast, dram_reg_info->index, dram_reg_info->data);
+ }
+ dram_reg_info++;
+ }
+
+ /* AST 2100/2150 DRAM calibration */
+ mcr120 = ast_mindwm(ast, AST_REG_MCR120);
+ if (mcr120 == 0x5061) { /* 266Mhz */
+ u32 mcr04 = ast_mindwm(ast, AST_REG_MCR04);
+
+ if (mcr04 & 0x40)
+ cbrdlli_ast2150(ast, 16); /* 16 bits */
+ else
+ cbrdlli_ast2150(ast, 32); /* 32 bits */
+ }
+
+ scu00c = ast_mindwm(ast, AST_REG_SCU00C);
+ scu00c &= 0xfffffffd;
+ ast_moutdwm(ast, AST_REG_SCU00C, scu00c);
+
+ scu040 = ast_mindwm(ast, AST_REG_SCU040);
+ scu040 |= 0x00000040;
+ ast_moutdwm(ast, AST_REG_SCU040, scu040);
+ }
+
+ /* wait ready */
+ do {
+ j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ } while ((j & 0x40) == 0);
+}
+
+int ast_2100_post(struct ast_device *ast)
+{
+ ast_2000_set_def_ext_reg(ast);
+
+ if (ast->config_mode == ast_use_p2a) {
+ ast_post_chip_2100(ast);
+ } else {
+ if (ast->tx_chip == AST_TX_SIL164) {
+ /* Enable DVO */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Widescreen detection
+ */
+
+/* Try to detect WSXGA+ on Gen2+ */
+bool __ast_2100_detect_wsxga_p(struct ast_device *ast)
+{
+ u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd0);
+
+ if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC))
+ return true;
+ if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN)
+ return true;
+
+ return false;
+}
+
+/* Try to detect WUXGA on Gen2+ */
+bool __ast_2100_detect_wuxga(struct ast_device *ast)
+{
+ u8 vgacrd1;
+
+ if (ast->support_fullhd) {
+ vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1);
+ if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA))
+ return true;
+ }
+
+ return false;
+}
+
+static void ast_2100_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast)) {
+ ast->support_wsxga_p = true;
+ if (ast->chip == AST2100)
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2100_device_quirks = {
+ .crtc_mem_req_threshold_low = 47,
+ .crtc_mem_req_threshold_high = 63,
+};
+
+struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2100_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2000_detect_tx_chip(ast, need_post);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ast_2100_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c
new file mode 100644
index 000000000..b64345d11
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2200.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "ast_drv.h"
+
+static void ast_2200_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast)) {
+ ast->support_wsxga_p = true;
+ if (ast->chip == AST2200)
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2200_device_quirks = {
+ .crtc_mem_req_threshold_low = 47,
+ .crtc_mem_req_threshold_high = 63,
+};
+
+struct drm_device *ast_2200_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2200_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2000_detect_tx_chip(ast, need_post);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ast_2200_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
+
diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c
new file mode 100644
index 000000000..cbeac6990
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2300.c
@@ -0,0 +1,1457 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+#include "ast_post.h"
+
+/*
+ * POST
+ */
+
+void ast_2300_set_def_ext_reg(struct ast_device *ast)
+{
+ static const u8 extreginfo[] = { 0x0f, 0x04, 0x1f, 0xff };
+ u8 i, index, reg;
+ const u8 *ext_reg_info;
+
+ /* reset scratch */
+ for (i = 0x81; i <= 0x9f; i++)
+ ast_set_index_reg(ast, AST_IO_VGACRI, i, 0x00);
+
+ ext_reg_info = extreginfo;
+ index = 0xa0;
+ while (*ext_reg_info != 0xff) {
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, index, 0x00, *ext_reg_info);
+ index++;
+ ext_reg_info++;
+ }
+
+ /* disable standard IO/MEM decode if secondary */
+ /* ast_set_index_reg-mask(ast, AST_IO_VGACRI, 0xa1, 0xff, 0x3); */
+
+ /* Set Ext. Default */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x8c, 0x00, 0x01);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x00, 0x00);
+
+ /* Enable RAMDAC for A1 */
+ reg = 0x04;
+ reg |= 0x20;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xff, reg);
+}
+
+/* AST 2300 DRAM settings */
+#define AST_DDR3 0
+#define AST_DDR2 1
+
+struct ast2300_dram_param {
+ u32 dram_type;
+ u32 dram_chipid;
+ u32 dram_freq;
+ u32 vram_size;
+ u32 odt;
+ u32 wodt;
+ u32 rodt;
+ u32 dram_config;
+ u32 reg_PERIOD;
+ u32 reg_MADJ;
+ u32 reg_SADJ;
+ u32 reg_MRS;
+ u32 reg_EMRS;
+ u32 reg_AC1;
+ u32 reg_AC2;
+ u32 reg_DQSIC;
+ u32 reg_DRV;
+ u32 reg_IOZ;
+ u32 reg_DQIDLY;
+ u32 reg_FREQ;
+ u32 madj_max;
+ u32 dll2_finetune_step;
+};
+
+/*
+ * DQSI DLL CBR Setting
+ */
+#define CBR_SIZE0 ((1 << 10) - 1)
+#define CBR_SIZE1 ((4 << 10) - 1)
+#define CBR_SIZE2 ((64 << 10) - 1)
+#define CBR_PASSNUM 5
+#define CBR_PASSNUM2 5
+#define CBR_THRESHOLD 10
+#define CBR_THRESHOLD2 10
+#define TIMEOUT 5000000
+#define CBR_PATNUM 8
+
+static const u32 pattern[8] = {
+ 0xFF00FF00,
+ 0xCC33CC33,
+ 0xAA55AA55,
+ 0x88778877,
+ 0x92CC4D6E,
+ 0x543D3CDE,
+ 0xF1E843C7,
+ 0x7C61D253
+};
+
+static u32 mmc_test2(struct ast_device *ast, u32 datagen, u8 test_ctl)
+{
+ u32 data, timeout;
+
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, (datagen << 3) | test_ctl);
+ timeout = 0;
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x1000;
+ if (++timeout > TIMEOUT) {
+ ast_moutdwm(ast, AST_REG_MCR70, 0x0);
+ return 0xffffffff;
+ }
+ } while (!data);
+ data = ast_mindwm(ast, AST_REG_MCR78);
+ data = (data | (data >> 16)) & 0xffff;
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ return data;
+}
+
+static u32 mmc_test_burst2(struct ast_device *ast, u32 datagen)
+{
+ return mmc_test2(ast, datagen, 0x41);
+}
+
+static bool mmc_test_single(struct ast_device *ast, u32 datagen)
+{
+ return mmc_test(ast, datagen, 0xc5);
+}
+
+static u32 mmc_test_single2(struct ast_device *ast, u32 datagen)
+{
+ return mmc_test2(ast, datagen, 0x05);
+}
+
+static int cbr_test(struct ast_device *ast)
+{
+ u32 data;
+ int i;
+
+ data = mmc_test_single2(ast, 0);
+ if ((data & 0xff) && (data & 0xff00))
+ return 0;
+ for (i = 0; i < 8; i++) {
+ data = mmc_test_burst2(ast, i);
+ if ((data & 0xff) && (data & 0xff00))
+ return 0;
+ }
+ if (!data)
+ return 3;
+ else if (data & 0xff)
+ return 2;
+ return 1;
+}
+
+static int cbr_scan(struct ast_device *ast)
+{
+ u32 data, data2, patcnt, loop;
+
+ data2 = 3;
+ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern[patcnt]);
+ for (loop = 0; loop < CBR_PASSNUM2; loop++) {
+ data = cbr_test(ast);
+ if (data != 0) {
+ data2 &= data;
+ if (!data2)
+ return 0;
+ break;
+ }
+ }
+ if (loop == CBR_PASSNUM2)
+ return 0;
+ }
+ return data2;
+}
+
+static u32 cbr_test2(struct ast_device *ast)
+{
+ u32 data;
+
+ data = mmc_test_burst2(ast, 0);
+ if (data == 0xffff)
+ return 0;
+ data |= mmc_test_single2(ast, 0);
+ if (data == 0xffff)
+ return 0;
+
+ return ~data & 0xffff;
+}
+
+static u32 cbr_scan2(struct ast_device *ast)
+{
+ u32 data, data2, patcnt, loop;
+
+ data2 = 0xffff;
+ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern[patcnt]);
+ for (loop = 0; loop < CBR_PASSNUM2; loop++) {
+ data = cbr_test2(ast);
+ if (data != 0) {
+ data2 &= data;
+ if (!data2)
+ return 0;
+ break;
+ }
+ }
+ if (loop == CBR_PASSNUM2)
+ return 0;
+ }
+ return data2;
+}
+
+static bool cbr_test3(struct ast_device *ast)
+{
+ if (!mmc_test_burst(ast, 0))
+ return false;
+ if (!mmc_test_single(ast, 0))
+ return false;
+ return true;
+}
+
+static bool cbr_scan3(struct ast_device *ast)
+{
+ u32 patcnt, loop;
+
+ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern[patcnt]);
+ for (loop = 0; loop < 2; loop++) {
+ if (cbr_test3(ast))
+ break;
+ }
+ if (loop == 2)
+ return false;
+ }
+ return true;
+}
+
+static bool finetuneDQI_L(struct ast_device *ast, struct ast2300_dram_param *param)
+{
+ u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, retry = 0;
+ bool status = false;
+FINETUNE_START:
+ for (cnt = 0; cnt < 16; cnt++) {
+ dllmin[cnt] = 0xff;
+ dllmax[cnt] = 0x0;
+ }
+ passcnt = 0;
+ for (dlli = 0; dlli < 76; dlli++) {
+ ast_moutdwm(ast, AST_REG_MCR68, 0x00001400 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR74, CBR_SIZE1);
+ data = cbr_scan2(ast);
+ if (data != 0) {
+ mask = 0x00010001;
+ for (cnt = 0; cnt < 16; cnt++) {
+ if (data & mask) {
+ if (dllmin[cnt] > dlli)
+ dllmin[cnt] = dlli;
+ if (dllmax[cnt] < dlli)
+ dllmax[cnt] = dlli;
+ }
+ mask <<= 1;
+ }
+ passcnt++;
+ } else if (passcnt >= CBR_THRESHOLD2) {
+ break;
+ }
+ }
+ gold_sadj[0] = 0x0;
+ passcnt = 0;
+ for (cnt = 0; cnt < 16; cnt++) {
+ if ((dllmax[cnt] > dllmin[cnt]) &&
+ ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
+ gold_sadj[0] += dllmin[cnt];
+ passcnt++;
+ }
+ }
+ if (retry++ > 10)
+ goto FINETUNE_DONE;
+ if (passcnt != 16)
+ goto FINETUNE_START;
+ status = true;
+FINETUNE_DONE:
+ gold_sadj[0] = gold_sadj[0] >> 4;
+ gold_sadj[1] = gold_sadj[0];
+
+ data = 0;
+ for (cnt = 0; cnt < 8; cnt++) {
+ data >>= 3;
+ if ((dllmax[cnt] > dllmin[cnt]) &&
+ ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
+ dlli = dllmin[cnt];
+ if (gold_sadj[0] >= dlli) {
+ dlli = ((gold_sadj[0] - dlli) * 19) >> 5;
+ if (dlli > 3)
+ dlli = 3;
+ } else {
+ dlli = ((dlli - gold_sadj[0]) * 19) >> 5;
+ if (dlli > 4)
+ dlli = 4;
+ dlli = (8 - dlli) & 0x7;
+ }
+ data |= dlli << 21;
+ }
+ }
+ ast_moutdwm(ast, AST_REG_MCR80, data);
+
+ data = 0;
+ for (cnt = 8; cnt < 16; cnt++) {
+ data >>= 3;
+ if ((dllmax[cnt] > dllmin[cnt]) &&
+ ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
+ dlli = dllmin[cnt];
+ if (gold_sadj[1] >= dlli) {
+ dlli = ((gold_sadj[1] - dlli) * 19) >> 5;
+ if (dlli > 3)
+ dlli = 3;
+ else
+ dlli = (dlli - 1) & 0x7;
+ } else {
+ dlli = ((dlli - gold_sadj[1]) * 19) >> 5;
+ dlli += 1;
+ if (dlli > 4)
+ dlli = 4;
+ dlli = (8 - dlli) & 0x7;
+ }
+ data |= dlli << 21;
+ }
+ }
+ ast_moutdwm(ast, AST_REG_MCR84, data);
+ return status;
+} /* finetuneDQI_L */
+
+static void finetuneDQSI(struct ast_device *ast)
+{
+ u32 dlli, dqsip, dqidly;
+ u32 reg_mcr18, reg_mcr0c, passcnt[2], diff;
+ u32 g_dqidly, g_dqsip, g_margin, g_side;
+ u16 pass[32][2][2];
+ char tag[2][76];
+
+ /* Disable DQI CBR */
+ reg_mcr0c = ast_mindwm(ast, AST_REG_MCR0C);
+ reg_mcr18 = ast_mindwm(ast, AST_REG_MCR18);
+ reg_mcr18 &= 0x0000ffff;
+ ast_moutdwm(ast, AST_REG_MCR18, reg_mcr18);
+
+ for (dlli = 0; dlli < 76; dlli++) {
+ tag[0][dlli] = 0x0;
+ tag[1][dlli] = 0x0;
+ }
+ for (dqidly = 0; dqidly < 32; dqidly++) {
+ pass[dqidly][0][0] = 0xff;
+ pass[dqidly][0][1] = 0x0;
+ pass[dqidly][1][0] = 0xff;
+ pass[dqidly][1][1] = 0x0;
+ }
+ for (dqidly = 0; dqidly < 32; dqidly++) {
+ passcnt[0] = 0;
+ passcnt[1] = 0;
+ for (dqsip = 0; dqsip < 2; dqsip++) {
+ ast_moutdwm(ast, AST_REG_MCR0C, 0);
+ ast_moutdwm(ast, AST_REG_MCR18, reg_mcr18 | (dqidly << 16) | (dqsip << 23));
+ ast_moutdwm(ast, AST_REG_MCR0C, reg_mcr0c);
+ for (dlli = 0; dlli < 76; dlli++) {
+ ast_moutdwm(ast, AST_REG_MCR68,
+ 0x00001300 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR70, 0);
+ ast_moutdwm(ast, AST_REG_MCR74, CBR_SIZE0);
+ if (cbr_scan3(ast)) {
+ if (dlli == 0)
+ break;
+ passcnt[dqsip]++;
+ tag[dqsip][dlli] = 'P';
+ if (dlli < pass[dqidly][dqsip][0])
+ pass[dqidly][dqsip][0] = (u16)dlli;
+ if (dlli > pass[dqidly][dqsip][1])
+ pass[dqidly][dqsip][1] = (u16)dlli;
+ } else if (passcnt[dqsip] >= 5) {
+ break;
+ } else {
+ pass[dqidly][dqsip][0] = 0xff;
+ pass[dqidly][dqsip][1] = 0x0;
+ }
+ }
+ }
+ if (passcnt[0] == 0 && passcnt[1] == 0)
+ dqidly++;
+ }
+ /* Search margin */
+ g_dqidly = 0;
+ g_dqsip = 0;
+ g_margin = 0;
+ g_side = 0;
+
+ for (dqidly = 0; dqidly < 32; dqidly++) {
+ for (dqsip = 0; dqsip < 2; dqsip++) {
+ if (pass[dqidly][dqsip][0] > pass[dqidly][dqsip][1])
+ continue;
+ diff = pass[dqidly][dqsip][1] - pass[dqidly][dqsip][0];
+ if ((diff + 2) < g_margin)
+ continue;
+ passcnt[0] = 0;
+ passcnt[1] = 0;
+ for (dlli = pass[dqidly][dqsip][0];
+ dlli > 0 && tag[dqsip][dlli] != 0;
+ dlli--, passcnt[0]++) {
+ }
+ for (dlli = pass[dqidly][dqsip][1];
+ dlli < 76 && tag[dqsip][dlli] != 0;
+ dlli++, passcnt[1]++) {
+ }
+ if (passcnt[0] > passcnt[1])
+ passcnt[0] = passcnt[1];
+ passcnt[1] = 0;
+ if (passcnt[0] > g_side)
+ passcnt[1] = passcnt[0] - g_side;
+ if (diff > (g_margin + 1) && (passcnt[1] > 0 || passcnt[0] > 8)) {
+ g_margin = diff;
+ g_dqidly = dqidly;
+ g_dqsip = dqsip;
+ g_side = passcnt[0];
+ } else if (passcnt[1] > 1 && g_side < 8) {
+ if (diff > g_margin)
+ g_margin = diff;
+ g_dqidly = dqidly;
+ g_dqsip = dqsip;
+ g_side = passcnt[0];
+ }
+ }
+ }
+ reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23);
+ ast_moutdwm(ast, AST_REG_MCR18, reg_mcr18);
+}
+
+static bool cbr_dll2(struct ast_device *ast, struct ast2300_dram_param *param)
+{
+ u32 dllmin[2], dllmax[2], dlli, data, passcnt, retry = 0;
+ bool status = false;
+
+ finetuneDQSI(ast);
+ if (finetuneDQI_L(ast, param) == false)
+ return status;
+
+CBR_START2:
+ dllmin[0] = 0xff;
+ dllmin[1] = 0xff;
+ dllmax[0] = 0x0;
+ dllmax[1] = 0x0;
+ passcnt = 0;
+ for (dlli = 0; dlli < 76; dlli++) {
+ ast_moutdwm(ast, AST_REG_MCR68, 0x00001300 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR74, CBR_SIZE2);
+ data = cbr_scan(ast);
+ if (data != 0) {
+ if (data & 0x1) {
+ if (dllmin[0] > dlli)
+ dllmin[0] = dlli;
+ if (dllmax[0] < dlli)
+ dllmax[0] = dlli;
+ }
+ if (data & 0x2) {
+ if (dllmin[1] > dlli)
+ dllmin[1] = dlli;
+ if (dllmax[1] < dlli)
+ dllmax[1] = dlli;
+ }
+ passcnt++;
+ } else if (passcnt >= CBR_THRESHOLD) {
+ break;
+ }
+ }
+ if (retry++ > 10)
+ goto CBR_DONE2;
+ if (dllmax[0] == 0 || (dllmax[0] - dllmin[0]) < CBR_THRESHOLD)
+ goto CBR_START2;
+ if (dllmax[1] == 0 || (dllmax[1] - dllmin[1]) < CBR_THRESHOLD)
+ goto CBR_START2;
+ status = true;
+CBR_DONE2:
+ dlli = (dllmin[1] + dllmax[1]) >> 1;
+ dlli <<= 8;
+ dlli += (dllmin[0] + dllmax[0]) >> 1;
+ ast_moutdwm(ast, AST_REG_MCR68, ast_mindwm(ast, AST_REG_A2P58) | (dlli << 16));
+ return status;
+} /* CBRDLL2 */
+
+static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *param)
+{
+ u32 trap, trap_AC2, trap_MRS;
+
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+
+ /* Ger trap info */
+ trap = (ast_mindwm(ast, AST_REG_SCU070) >> 25) & 0x3;
+ trap_AC2 = 0x00020000 + (trap << 16);
+ trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19);
+ trap_MRS = 0x00000010 + (trap << 4);
+ trap_MRS |= ((trap & 0x2) << 18);
+
+ param->reg_MADJ = 0x00034C4C;
+ param->reg_SADJ = 0x00001800;
+ param->reg_DRV = 0x000000F0;
+ param->reg_PERIOD = param->dram_freq;
+ param->rodt = 0;
+
+ switch (param->dram_freq) {
+ case 336:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0190);
+ param->wodt = 0;
+ param->reg_AC1 = 0x22202725;
+ param->reg_AC2 = 0xAA007613 | trap_AC2;
+ param->reg_DQSIC = 0x000000BA;
+ param->reg_MRS = 0x04001400 | trap_MRS;
+ param->reg_EMRS = 0x00000000;
+ param->reg_IOZ = 0x00000023;
+ param->reg_DQIDLY = 0x00000074;
+ param->reg_FREQ = 0x00004DC0;
+ param->madj_max = 96;
+ param->dll2_finetune_step = 3;
+ switch (param->dram_chipid) {
+ default:
+ case AST_DRAM_512Mx16:
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xAA007613 | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xAA00761C | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xAA007636 | trap_AC2;
+ break;
+ }
+ break;
+ default:
+ case 396:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x03F1);
+ param->wodt = 1;
+ param->reg_AC1 = 0x33302825;
+ param->reg_AC2 = 0xCC009617 | trap_AC2;
+ param->reg_DQSIC = 0x000000E2;
+ param->reg_MRS = 0x04001600 | trap_MRS;
+ param->reg_EMRS = 0x00000000;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DRV = 0x000000FA;
+ param->reg_DQIDLY = 0x00000089;
+ param->reg_FREQ = 0x00005040;
+ param->madj_max = 96;
+ param->dll2_finetune_step = 4;
+
+ switch (param->dram_chipid) {
+ default:
+ case AST_DRAM_512Mx16:
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xCC009617 | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xCC009622 | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xCC00963F | trap_AC2;
+ break;
+ }
+ break;
+
+ case 408:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x01F0);
+ param->wodt = 1;
+ param->reg_AC1 = 0x33302825;
+ param->reg_AC2 = 0xCC009617 | trap_AC2;
+ param->reg_DQSIC = 0x000000E2;
+ param->reg_MRS = 0x04001600 | trap_MRS;
+ param->reg_EMRS = 0x00000000;
+ param->reg_IOZ = 0x00000023;
+ param->reg_DRV = 0x000000FA;
+ param->reg_DQIDLY = 0x00000089;
+ param->reg_FREQ = 0x000050C0;
+ param->madj_max = 96;
+ param->dll2_finetune_step = 4;
+
+ switch (param->dram_chipid) {
+ default:
+ case AST_DRAM_512Mx16:
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xCC009617 | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xCC009622 | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xCC00963F | trap_AC2;
+ break;
+ }
+
+ break;
+ case 456:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0230);
+ param->wodt = 0;
+ param->reg_AC1 = 0x33302926;
+ param->reg_AC2 = 0xCD44961A;
+ param->reg_DQSIC = 0x000000FC;
+ param->reg_MRS = 0x00081830;
+ param->reg_EMRS = 0x00000000;
+ param->reg_IOZ = 0x00000045;
+ param->reg_DQIDLY = 0x00000097;
+ param->reg_FREQ = 0x000052C0;
+ param->madj_max = 88;
+ param->dll2_finetune_step = 4;
+ break;
+ case 504:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0270);
+ param->wodt = 1;
+ param->reg_AC1 = 0x33302926;
+ param->reg_AC2 = 0xDE44A61D;
+ param->reg_DQSIC = 0x00000117;
+ param->reg_MRS = 0x00081A30;
+ param->reg_EMRS = 0x00000000;
+ param->reg_IOZ = 0x070000BB;
+ param->reg_DQIDLY = 0x000000A0;
+ param->reg_FREQ = 0x000054C0;
+ param->madj_max = 79;
+ param->dll2_finetune_step = 4;
+ break;
+ case 528:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0290);
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x33302926;
+ param->reg_AC2 = 0xEF44B61E;
+ param->reg_DQSIC = 0x00000125;
+ param->reg_MRS = 0x00081A30;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x000000F5;
+ param->reg_IOZ = 0x00000023;
+ param->reg_DQIDLY = 0x00000088;
+ param->reg_FREQ = 0x000055C0;
+ param->madj_max = 76;
+ param->dll2_finetune_step = 3;
+ break;
+ case 576:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0140);
+ param->reg_MADJ = 0x00136868;
+ param->reg_SADJ = 0x00004534;
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x33302A37;
+ param->reg_AC2 = 0xEF56B61E;
+ param->reg_DQSIC = 0x0000013F;
+ param->reg_MRS = 0x00101A50;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x000000FA;
+ param->reg_IOZ = 0x00000023;
+ param->reg_DQIDLY = 0x00000078;
+ param->reg_FREQ = 0x000057C0;
+ param->madj_max = 136;
+ param->dll2_finetune_step = 3;
+ break;
+ case 600:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x02E1);
+ param->reg_MADJ = 0x00136868;
+ param->reg_SADJ = 0x00004534;
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x32302A37;
+ param->reg_AC2 = 0xDF56B61F;
+ param->reg_DQSIC = 0x0000014D;
+ param->reg_MRS = 0x00101A50;
+ param->reg_EMRS = 0x00000004;
+ param->reg_DRV = 0x000000F5;
+ param->reg_IOZ = 0x00000023;
+ param->reg_DQIDLY = 0x00000078;
+ param->reg_FREQ = 0x000058C0;
+ param->madj_max = 132;
+ param->dll2_finetune_step = 3;
+ break;
+ case 624:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0160);
+ param->reg_MADJ = 0x00136868;
+ param->reg_SADJ = 0x00004534;
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x32302A37;
+ param->reg_AC2 = 0xEF56B621;
+ param->reg_DQSIC = 0x0000015A;
+ param->reg_MRS = 0x02101A50;
+ param->reg_EMRS = 0x00000004;
+ param->reg_DRV = 0x000000F5;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DQIDLY = 0x00000078;
+ param->reg_FREQ = 0x000059C0;
+ param->madj_max = 128;
+ param->dll2_finetune_step = 3;
+ break;
+ } /* switch freq */
+
+ switch (param->dram_chipid) {
+ case AST_DRAM_512Mx16:
+ param->dram_config = 0x130;
+ break;
+ default:
+ case AST_DRAM_1Gx16:
+ param->dram_config = 0x131;
+ break;
+ case AST_DRAM_2Gx16:
+ param->dram_config = 0x132;
+ break;
+ case AST_DRAM_4Gx16:
+ param->dram_config = 0x133;
+ break;
+ } /* switch size */
+
+ switch (param->vram_size) {
+ default:
+ case SZ_8M:
+ param->dram_config |= 0x00;
+ break;
+ case SZ_16M:
+ param->dram_config |= 0x04;
+ break;
+ case SZ_32M:
+ param->dram_config |= 0x08;
+ break;
+ case SZ_64M:
+ param->dram_config |= 0x0c;
+ break;
+ }
+}
+
+static void ddr3_init(struct ast_device *ast, struct ast2300_dram_param *param)
+{
+ u32 data, data2, retry = 0;
+
+ddr3_init_start:
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00000100);
+ ast_moutdwm(ast, AST_REG_MCR24, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00000000);
+ udelay(10);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ);
+ ast_moutdwm(ast, AST_REG_MCR68, param->reg_SADJ);
+ udelay(10);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ | 0xC0000);
+ udelay(10);
+
+ ast_moutdwm(ast, AST_REG_MCR04, param->dram_config);
+ ast_moutdwm(ast, AST_REG_MCR08, 0x90040f);
+ ast_moutdwm(ast, AST_REG_MCR10, param->reg_AC1);
+ ast_moutdwm(ast, AST_REG_MCR14, param->reg_AC2);
+ ast_moutdwm(ast, AST_REG_MCR20, param->reg_DQSIC);
+ ast_moutdwm(ast, AST_REG_MCR80, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR84, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR88, param->reg_DQIDLY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x4000A170);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00002370);
+ ast_moutdwm(ast, AST_REG_MCR38, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR40, 0xFF444444);
+ ast_moutdwm(ast, AST_REG_MCR44, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR48, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR4C, 0x00000002);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR54, 0);
+ ast_moutdwm(ast, AST_REG_MCR60, param->reg_DRV);
+ ast_moutdwm(ast, AST_REG_MCR6C, param->reg_IOZ);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR74, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR78, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
+ /* Wait MCLK2X lock to MCLK */
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ } while (!(data & 0x08000000));
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ data = (data >> 8) & 0xff;
+ while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
+ data2 = (ast_mindwm(ast, AST_REG_MCR64) & 0xfff3ffff) + 4;
+ if ((data2 & 0xff) > param->madj_max)
+ break;
+ ast_moutdwm(ast, AST_REG_MCR64, data2);
+ if (data2 & 0x00100000)
+ data2 = ((data2 & 0xff) >> 3) + 3;
+ else
+ data2 = ((data2 & 0xff) >> 2) + 5;
+ data = ast_mindwm(ast, AST_REG_MCR68) & 0xffff00ff;
+ data2 += data & 0xff;
+ data = data | (data2 << 8);
+ ast_moutdwm(ast, AST_REG_MCR68, data);
+ udelay(10);
+ ast_moutdwm(ast, AST_REG_MCR64, ast_mindwm(ast, AST_REG_MCR64) | 0xC0000);
+ udelay(10);
+ data = ast_mindwm(ast, AST_REG_MCR18) & 0xfffff1ff;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
+ data = data | 0x200;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ } while (!(data & 0x08000000));
+
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ data = (data >> 8) & 0xff;
+ }
+ ast_moutdwm(ast, AST_REG_A2P58, ast_mindwm(ast, AST_REG_MCR68) & 0xffff);
+ data = ast_mindwm(ast, AST_REG_MCR18) | 0xC00;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
+
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000040);
+ udelay(50);
+ /* Mode Register Setting */
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS | 0x100);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000005);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000007);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C08);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01);
+ data = 0;
+ if (param->wodt)
+ data = 0x300;
+ if (param->rodt)
+ data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
+ ast_moutdwm(ast, AST_REG_MCR34, data | 0x3);
+
+ /* Calibrate the DQSI delay */
+ if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
+ goto ddr3_init_start;
+
+ ast_moutdwm(ast, AST_REG_MCR120, param->reg_FREQ);
+ /* ECC Memory Initialization */
+#ifdef ECC
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x221);
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR70);
+ } while (!(data & 0x00001000));
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
+#endif
+}
+
+static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *param)
+{
+ u32 trap, trap_AC2, trap_MRS;
+
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+
+ /* Ger trap info */
+ trap = (ast_mindwm(ast, AST_REG_SCU070) >> 25) & 0x3;
+ trap_AC2 = (trap << 20) | (trap << 16);
+ trap_AC2 += 0x00110000;
+ trap_MRS = 0x00000040 | (trap << 4);
+
+ param->reg_MADJ = 0x00034C4C;
+ param->reg_SADJ = 0x00001800;
+ param->reg_DRV = 0x000000F0;
+ param->reg_PERIOD = param->dram_freq;
+ param->rodt = 0;
+
+ switch (param->dram_freq) {
+ case 264:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0130);
+ param->wodt = 0;
+ param->reg_AC1 = 0x11101513;
+ param->reg_AC2 = 0x78117011;
+ param->reg_DQSIC = 0x00000092;
+ param->reg_MRS = 0x00000842;
+ param->reg_EMRS = 0x00000000;
+ param->reg_DRV = 0x000000F0;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DQIDLY = 0x0000005A;
+ param->reg_FREQ = 0x00004AC0;
+ param->madj_max = 138;
+ param->dll2_finetune_step = 3;
+ break;
+ case 336:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0190);
+ param->wodt = 1;
+ param->reg_AC1 = 0x22202613;
+ param->reg_AC2 = 0xAA009016 | trap_AC2;
+ param->reg_DQSIC = 0x000000BA;
+ param->reg_MRS = 0x00000A02 | trap_MRS;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x000000FA;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DQIDLY = 0x00000074;
+ param->reg_FREQ = 0x00004DC0;
+ param->madj_max = 96;
+ param->dll2_finetune_step = 3;
+ switch (param->dram_chipid) {
+ default:
+ case AST_DRAM_512Mx16:
+ param->reg_AC2 = 0xAA009012 | trap_AC2;
+ break;
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xAA009016 | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xAA009023 | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xAA00903B | trap_AC2;
+ break;
+ }
+ break;
+ default:
+ case 396:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x03F1);
+ param->wodt = 1;
+ param->rodt = 0;
+ param->reg_AC1 = 0x33302714;
+ param->reg_AC2 = 0xCC00B01B | trap_AC2;
+ param->reg_DQSIC = 0x000000E2;
+ param->reg_MRS = 0x00000C02 | trap_MRS;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x000000FA;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DQIDLY = 0x00000089;
+ param->reg_FREQ = 0x00005040;
+ param->madj_max = 96;
+ param->dll2_finetune_step = 4;
+
+ switch (param->dram_chipid) {
+ case AST_DRAM_512Mx16:
+ param->reg_AC2 = 0xCC00B016 | trap_AC2;
+ break;
+ default:
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xCC00B01B | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xCC00B02B | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xCC00B03F | trap_AC2;
+ break;
+ }
+
+ break;
+
+ case 408:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x01F0);
+ param->wodt = 1;
+ param->rodt = 0;
+ param->reg_AC1 = 0x33302714;
+ param->reg_AC2 = 0xCC00B01B | trap_AC2;
+ param->reg_DQSIC = 0x000000E2;
+ param->reg_MRS = 0x00000C02 | trap_MRS;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x000000FA;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DQIDLY = 0x00000089;
+ param->reg_FREQ = 0x000050C0;
+ param->madj_max = 96;
+ param->dll2_finetune_step = 4;
+
+ switch (param->dram_chipid) {
+ case AST_DRAM_512Mx16:
+ param->reg_AC2 = 0xCC00B016 | trap_AC2;
+ break;
+ default:
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xCC00B01B | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xCC00B02B | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xCC00B03F | trap_AC2;
+ break;
+ }
+
+ break;
+ case 456:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0230);
+ param->wodt = 0;
+ param->reg_AC1 = 0x33302815;
+ param->reg_AC2 = 0xCD44B01E;
+ param->reg_DQSIC = 0x000000FC;
+ param->reg_MRS = 0x00000E72;
+ param->reg_EMRS = 0x00000000;
+ param->reg_DRV = 0x00000000;
+ param->reg_IOZ = 0x00000034;
+ param->reg_DQIDLY = 0x00000097;
+ param->reg_FREQ = 0x000052C0;
+ param->madj_max = 88;
+ param->dll2_finetune_step = 3;
+ break;
+ case 504:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0261);
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x33302815;
+ param->reg_AC2 = 0xDE44C022;
+ param->reg_DQSIC = 0x00000117;
+ param->reg_MRS = 0x00000E72;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x0000000A;
+ param->reg_IOZ = 0x00000045;
+ param->reg_DQIDLY = 0x000000A0;
+ param->reg_FREQ = 0x000054C0;
+ param->madj_max = 79;
+ param->dll2_finetune_step = 3;
+ break;
+ case 528:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0120);
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x33302815;
+ param->reg_AC2 = 0xEF44D024;
+ param->reg_DQSIC = 0x00000125;
+ param->reg_MRS = 0x00000E72;
+ param->reg_EMRS = 0x00000004;
+ param->reg_DRV = 0x000000F9;
+ param->reg_IOZ = 0x00000045;
+ param->reg_DQIDLY = 0x000000A7;
+ param->reg_FREQ = 0x000055C0;
+ param->madj_max = 76;
+ param->dll2_finetune_step = 3;
+ break;
+ case 552:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x02A1);
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x43402915;
+ param->reg_AC2 = 0xFF44E025;
+ param->reg_DQSIC = 0x00000132;
+ param->reg_MRS = 0x00000E72;
+ param->reg_EMRS = 0x00000040;
+ param->reg_DRV = 0x0000000A;
+ param->reg_IOZ = 0x00000045;
+ param->reg_DQIDLY = 0x000000AD;
+ param->reg_FREQ = 0x000056C0;
+ param->madj_max = 76;
+ param->dll2_finetune_step = 3;
+ break;
+ case 576:
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0140);
+ param->wodt = 1;
+ param->rodt = 1;
+ param->reg_AC1 = 0x43402915;
+ param->reg_AC2 = 0xFF44E027;
+ param->reg_DQSIC = 0x0000013F;
+ param->reg_MRS = 0x00000E72;
+ param->reg_EMRS = 0x00000004;
+ param->reg_DRV = 0x000000F5;
+ param->reg_IOZ = 0x00000045;
+ param->reg_DQIDLY = 0x000000B3;
+ param->reg_FREQ = 0x000057C0;
+ param->madj_max = 76;
+ param->dll2_finetune_step = 3;
+ break;
+ }
+
+ switch (param->dram_chipid) {
+ case AST_DRAM_512Mx16:
+ param->dram_config = 0x100;
+ break;
+ default:
+ case AST_DRAM_1Gx16:
+ param->dram_config = 0x121;
+ break;
+ case AST_DRAM_2Gx16:
+ param->dram_config = 0x122;
+ break;
+ case AST_DRAM_4Gx16:
+ param->dram_config = 0x123;
+ break;
+ } /* switch size */
+
+ switch (param->vram_size) {
+ default:
+ case SZ_8M:
+ param->dram_config |= 0x00;
+ break;
+ case SZ_16M:
+ param->dram_config |= 0x04;
+ break;
+ case SZ_32M:
+ param->dram_config |= 0x08;
+ break;
+ case SZ_64M:
+ param->dram_config |= 0x0c;
+ break;
+ }
+}
+
+static void ddr2_init(struct ast_device *ast, struct ast2300_dram_param *param)
+{
+ u32 data, data2, retry = 0;
+
+ddr2_init_start:
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00000100);
+ ast_moutdwm(ast, AST_REG_MCR24, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ);
+ ast_moutdwm(ast, AST_REG_MCR68, param->reg_SADJ);
+ udelay(10);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ | 0xC0000);
+ udelay(10);
+
+ ast_moutdwm(ast, AST_REG_MCR04, param->dram_config);
+ ast_moutdwm(ast, AST_REG_MCR08, 0x90040f);
+ ast_moutdwm(ast, AST_REG_MCR10, param->reg_AC1);
+ ast_moutdwm(ast, AST_REG_MCR14, param->reg_AC2);
+ ast_moutdwm(ast, AST_REG_MCR20, param->reg_DQSIC);
+ ast_moutdwm(ast, AST_REG_MCR80, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR84, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR88, param->reg_DQIDLY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x4000A130);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00002330);
+ ast_moutdwm(ast, AST_REG_MCR38, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR40, 0xFF808000);
+ ast_moutdwm(ast, AST_REG_MCR44, 0x88848466);
+ ast_moutdwm(ast, AST_REG_MCR48, 0x44440008);
+ ast_moutdwm(ast, AST_REG_MCR4C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR54, 0);
+ ast_moutdwm(ast, AST_REG_MCR60, param->reg_DRV);
+ ast_moutdwm(ast, AST_REG_MCR6C, param->reg_IOZ);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR74, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR78, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
+
+ /* Wait MCLK2X lock to MCLK */
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ } while (!(data & 0x08000000));
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ data = (data >> 8) & 0xff;
+ while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
+ data2 = (ast_mindwm(ast, AST_REG_MCR64) & 0xfff3ffff) + 4;
+ if ((data2 & 0xff) > param->madj_max)
+ break;
+ ast_moutdwm(ast, AST_REG_MCR64, data2);
+ if (data2 & 0x00100000)
+ data2 = ((data2 & 0xff) >> 3) + 3;
+ else
+ data2 = ((data2 & 0xff) >> 2) + 5;
+ data = ast_mindwm(ast, AST_REG_MCR68) & 0xffff00ff;
+ data2 += data & 0xff;
+ data = data | (data2 << 8);
+ ast_moutdwm(ast, AST_REG_MCR68, data);
+ udelay(10);
+ ast_moutdwm(ast, AST_REG_MCR64, ast_mindwm(ast, AST_REG_MCR64) | 0xC0000);
+ udelay(10);
+ data = ast_mindwm(ast, AST_REG_MCR18) & 0xfffff1ff;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
+ data = data | 0x200;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ } while (!(data & 0x08000000));
+
+ data = ast_mindwm(ast, AST_REG_MCR1C);
+ data = (data >> 8) & 0xff;
+ }
+ ast_moutdwm(ast, AST_REG_A2P58, ast_mindwm(ast, AST_REG_MCR08) & 0xffff);
+ data = ast_mindwm(ast, AST_REG_MCR18) | 0xC00;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
+
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ udelay(50);
+ /* Mode Register Setting */
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS | 0x100);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000005);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000007);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C08);
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS | 0x380);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x7FFF5C01);
+ data = 0;
+ if (param->wodt)
+ data = 0x500;
+ if (param->rodt)
+ data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
+ ast_moutdwm(ast, AST_REG_MCR34, data | 0x3);
+ ast_moutdwm(ast, AST_REG_MCR120, param->reg_FREQ);
+
+ /* Calibrate the DQSI delay */
+ if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
+ goto ddr2_init_start;
+
+ /* ECC Memory Initialization */
+#ifdef ECC
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x221);
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR70);
+ } while (!(data & 0x00001000));
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
+#endif
+}
+
+static void ast_post_chip_2300(struct ast_device *ast)
+{
+ struct ast2300_dram_param param;
+ u32 temp;
+ u8 reg;
+
+ reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ if ((reg & 0x80) == 0) {/* vga only */
+ u32 scu008, scu040;
+
+ ast_moutdwm_poll(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY, 0x01);
+ ast_moutdwm_poll(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY, 0x01);
+
+ /* Slow down CPU/AHB CLK in VGA only mode */
+ scu008 = ast_mindwm(ast, AST_REG_SCU008);
+ scu008 |= 0x00000073;
+ ast_moutdwm(ast, AST_REG_SCU008, scu008);
+
+ param.dram_freq = 396;
+ param.dram_type = AST_DDR3;
+ temp = ast_mindwm(ast, AST_REG_SCU070);
+ if (temp & 0x01000000)
+ param.dram_type = AST_DDR2;
+ switch (temp & 0x18000000) {
+ case 0:
+ param.dram_chipid = AST_DRAM_512Mx16;
+ break;
+ default:
+ case 0x08000000:
+ param.dram_chipid = AST_DRAM_1Gx16;
+ break;
+ case 0x10000000:
+ param.dram_chipid = AST_DRAM_2Gx16;
+ break;
+ case 0x18000000:
+ param.dram_chipid = AST_DRAM_4Gx16;
+ break;
+ }
+ switch (temp & 0x0c) {
+ default:
+ case 0x00:
+ param.vram_size = SZ_8M;
+ break;
+ case 0x04:
+ param.vram_size = SZ_16M;
+ break;
+ case 0x08:
+ param.vram_size = SZ_32M;
+ break;
+ case 0x0c:
+ param.vram_size = SZ_64M;
+ break;
+ }
+
+ if (param.dram_type == AST_DDR3) {
+ get_ddr3_info(ast, &param);
+ ddr3_init(ast, &param);
+ } else {
+ get_ddr2_info(ast, &param);
+ ddr2_init(ast, &param);
+ }
+
+ scu040 = ast_mindwm(ast, AST_REG_SCU040);
+ scu040 |= 0x00000040;
+ ast_moutdwm(ast, AST_REG_SCU040, scu040);
+ }
+
+ /* wait ready */
+ do {
+ reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ } while ((reg & 0x40) == 0);
+}
+
+int ast_2300_post(struct ast_device *ast)
+{
+ ast_2300_set_def_ext_reg(ast);
+
+ if (ast->config_mode == ast_use_p2a) {
+ ast_post_chip_2300(ast);
+ ast_init_3rdtx(ast);
+ } else {
+ if (ast->tx_chip == AST_TX_SIL164) {
+ /* Enable DVO */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Device initialization
+ */
+
+void ast_2300_detect_tx_chip(struct ast_device *ast)
+{
+ enum ast_tx_chip tx_chip = AST_TX_NONE;
+ struct drm_device *dev = &ast->base;
+ u8 vgacrd1;
+
+ /*
+ * On AST GEN4+, look at the configuration set by the SoC in
+ * the SOC scratch register #1 bits 11:8 (interestingly marked
+ * as "reserved" in the spec)
+ */
+ vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1,
+ AST_IO_VGACRD1_TX_TYPE_MASK);
+ switch (vgacrd1) {
+ /*
+ * GEN4 to GEN6
+ */
+ case AST_IO_VGACRD1_TX_SIL164_VBIOS:
+ tx_chip = AST_TX_SIL164;
+ break;
+ case AST_IO_VGACRD1_TX_DP501_VBIOS:
+ ast->dp501_fw_addr = drmm_kzalloc(dev, SZ_32K, GFP_KERNEL);
+ if (ast->dp501_fw_addr) {
+ /* backup firmware */
+ if (ast_backup_fw(ast, ast->dp501_fw_addr, SZ_32K)) {
+ drmm_kfree(dev, ast->dp501_fw_addr);
+ ast->dp501_fw_addr = NULL;
+ }
+ }
+ fallthrough;
+ case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW:
+ tx_chip = AST_TX_DP501;
+ break;
+ /*
+ * GEN7+
+ */
+ case AST_IO_VGACRD1_TX_ASTDP:
+ tx_chip = AST_TX_ASTDP;
+ break;
+ /*
+ * Several of the listed TX chips are not explicitly supported
+ * by the ast driver. If these exist in real-world devices, they
+ * are most likely reported as VGA or SIL164 outputs. We warn here
+ * to get bug reports for these devices. If none come in for some
+ * time, we can begin to fail device probing on these values.
+ */
+ case AST_IO_VGACRD1_TX_ITE66121_VBIOS:
+ drm_warn(dev, "ITE IT66121 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
+ break;
+ case AST_IO_VGACRD1_TX_CH7003_VBIOS:
+ drm_warn(dev, "Chrontel CH7003 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
+ break;
+ case AST_IO_VGACRD1_TX_ANX9807_VBIOS:
+ drm_warn(dev, "Analogix ANX9807 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
+ break;
+ }
+
+ __ast_device_set_tx_chip(ast, tx_chip);
+}
+
+static void ast_2300_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1300) {
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2300_device_quirks = {
+ .crtc_mem_req_threshold_low = 96,
+ .crtc_mem_req_threshold_high = 120,
+};
+
+struct drm_device *ast_2300_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2300_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* map reserved buffer */
+ ast->dp501_fw_buf = NULL;
+ if (ast->vram_size < pci_resource_len(pdev, 0)) {
+ ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
+ if (!ast->dp501_fw_buf)
+ drm_info(dev, "failed to map reserved buffer!\n");
+ }
+
+ ast_2300_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c
new file mode 100644
index 000000000..2e6befd24
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2400.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+
+static void ast_2400_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1400) {
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2400_device_quirks = {
+ .crtc_mem_req_threshold_low = 96,
+ .crtc_mem_req_threshold_high = 120,
+};
+
+struct drm_device *ast_2400_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2400_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* map reserved buffer */
+ ast->dp501_fw_buf = NULL;
+ if (ast->vram_size < pci_resource_len(pdev, 0)) {
+ ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
+ if (!ast->dp501_fw_buf)
+ drm_info(dev, "failed to map reserved buffer!\n");
+ }
+
+ ast_2400_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c
new file mode 100644
index 000000000..03a67ec96
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2500.c
@@ -0,0 +1,678 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+#include "ast_post.h"
+
+/*
+ * POST
+ */
+
+/*
+ * AST2500 DRAM settings modules
+ */
+
+#define REGTBL_NUM 17
+#define REGIDX_010 0
+#define REGIDX_014 1
+#define REGIDX_018 2
+#define REGIDX_020 3
+#define REGIDX_024 4
+#define REGIDX_02C 5
+#define REGIDX_030 6
+#define REGIDX_214 7
+#define REGIDX_2E0 8
+#define REGIDX_2E4 9
+#define REGIDX_2E8 10
+#define REGIDX_2EC 11
+#define REGIDX_2F0 12
+#define REGIDX_2F4 13
+#define REGIDX_2F8 14
+#define REGIDX_RFC 15
+#define REGIDX_PLL 16
+
+static const u32 ast2500_ddr3_1600_timing_table[REGTBL_NUM] = {
+ 0x64604D38, /* 0x010 */
+ 0x29690599, /* 0x014 */
+ 0x00000300, /* 0x018 */
+ 0x00000000, /* 0x020 */
+ 0x00000000, /* 0x024 */
+ 0x02181E70, /* 0x02C */
+ 0x00000040, /* 0x030 */
+ 0x00000024, /* 0x214 */
+ 0x02001300, /* 0x2E0 */
+ 0x0E0000A0, /* 0x2E4 */
+ 0x000E001B, /* 0x2E8 */
+ 0x35B8C105, /* 0x2EC */
+ 0x08090408, /* 0x2F0 */
+ 0x9B000800, /* 0x2F4 */
+ 0x0E400A00, /* 0x2F8 */
+ 0x9971452F, /* tRFC */
+ 0x000071C1 /* PLL */
+};
+
+static const u32 ast2500_ddr4_1600_timing_table[REGTBL_NUM] = {
+ 0x63604E37, /* 0x010 */
+ 0xE97AFA99, /* 0x014 */
+ 0x00019000, /* 0x018 */
+ 0x08000000, /* 0x020 */
+ 0x00000400, /* 0x024 */
+ 0x00000410, /* 0x02C */
+ 0x00000101, /* 0x030 */
+ 0x00000024, /* 0x214 */
+ 0x03002900, /* 0x2E0 */
+ 0x0E0000A0, /* 0x2E4 */
+ 0x000E001C, /* 0x2E8 */
+ 0x35B8C106, /* 0x2EC */
+ 0x08080607, /* 0x2F0 */
+ 0x9B000900, /* 0x2F4 */
+ 0x0E400A00, /* 0x2F8 */
+ 0x99714545, /* tRFC */
+ 0x000071C1 /* PLL */
+};
+
+#define TIMEOUT 5000000
+
+void ast_2500_patch_ahb(void __iomem *regs)
+{
+ u32 data;
+
+ /* Clear bus lock condition */
+ __ast_moutdwm(regs, AST_REG_AHBC00, AST_REG_AHBC00_PROTECT_KEY);
+ __ast_moutdwm(regs, AST_REG_AHBC84, 0x00010000);
+ __ast_moutdwm(regs, AST_REG_AHBC88, 0x00000000);
+ __ast_moutdwm(regs, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+
+ data = __ast_mindwm(regs, AST_REG_SCU070);
+ if (data & 0x08000000) { /* check fast reset */
+ /*
+ * If "Fast restet" is enabled for ARM-ICE debugger,
+ * then WDT needs to enable, that
+ * WDT04 is WDT#1 Reload reg.
+ * WDT08 is WDT#1 counter restart reg to avoid system deadlock
+ * WDT0C is WDT#1 control reg
+ * [6:5]:= 01:Full chip
+ * [4]:= 1:1MHz clock source
+ * [1]:= 1:WDT will be cleeared and disabled after timeout occurs
+ * [0]:= 1:WDT enable
+ */
+ __ast_moutdwm(regs, AST_REG_WDT04(0), 0x00000010);
+ __ast_moutdwm(regs, AST_REG_WDT08(0), 0x00004755);
+ __ast_moutdwm(regs, AST_REG_WDT0C(0), 0x00000033);
+ udelay(1000);
+ }
+
+ do {
+ __ast_moutdwm(regs, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+ data = __ast_mindwm(regs, AST_REG_SCU000);
+ } while (data != 1);
+
+ __ast_moutdwm(regs, AST_REG_SCU07C, 0x08000000); /* clear fast reset */
+}
+
+static bool mmc_test_single_2500(struct ast_device *ast, u32 datagen)
+{
+ return mmc_test(ast, datagen, 0x85);
+}
+
+static bool cbr_test_2500(struct ast_device *ast)
+{
+ ast_moutdwm(ast, AST_REG_MCR74, 0x0000FFFF);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0xFF00FF00);
+ if (!mmc_test_burst(ast, 0))
+ return false;
+ if (!mmc_test_single_2500(ast, 0))
+ return false;
+ return true;
+}
+
+static bool ddr_test_2500(struct ast_device *ast)
+{
+ ast_moutdwm(ast, AST_REG_MCR74, 0x0000FFFF);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0xFF00FF00);
+ if (!mmc_test_burst(ast, 0))
+ return false;
+ if (!mmc_test_burst(ast, 1))
+ return false;
+ if (!mmc_test_burst(ast, 2))
+ return false;
+ if (!mmc_test_burst(ast, 3))
+ return false;
+ if (!mmc_test_single_2500(ast, 0))
+ return false;
+ return true;
+}
+
+static void ddr_init_common_2500(struct ast_device *ast)
+{
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020080);
+ ast_moutdwm(ast, AST_REG_MCR08, 0x2003000F);
+ ast_moutdwm(ast, AST_REG_MCR38, 0x00000FFF);
+ ast_moutdwm(ast, AST_REG_MCR40, 0x88448844);
+ ast_moutdwm(ast, AST_REG_MCR44, 0x24422288);
+ ast_moutdwm(ast, AST_REG_MCR48, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR4C, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR208, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR218, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR220, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR228, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR230, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2A8, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2B0, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR240, 0x86000000);
+ ast_moutdwm(ast, AST_REG_MCR244, 0x00008600);
+ ast_moutdwm(ast, AST_REG_MCR248, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR24C, 0x80808080);
+}
+
+static void ddr_phy_init_2500(struct ast_device *ast)
+{
+ u32 data, pass, timecnt;
+
+ pass = 0;
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000005);
+ while (!pass) {
+ for (timecnt = 0; timecnt < TIMEOUT; timecnt++) {
+ data = ast_mindwm(ast, AST_REG_MCR60) & 0x1;
+ if (!data)
+ break;
+ }
+ if (timecnt != TIMEOUT) {
+ data = ast_mindwm(ast, AST_REG_MCR300) & 0x000A0000;
+ if (!data)
+ pass = 1;
+ }
+ if (!pass) {
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
+ udelay(10); /* delay 10 us */
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000005);
+ }
+ }
+
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000006);
+}
+
+/*
+ * TODO: Review and fix the comments. The function below only detects
+ * up to 1 GiB of SDRAM.
+ *
+ * Check DRAM Size
+ * 1Gb : 0x80000000 ~ 0x87FFFFFF
+ * 2Gb : 0x80000000 ~ 0x8FFFFFFF
+ * 4Gb : 0x80000000 ~ 0x9FFFFFFF
+ * 8Gb : 0x80000000 ~ 0xBFFFFFFF
+ */
+static void check_dram_size_2500(struct ast_device *ast, u32 tRFC)
+{
+ u32 reg_04, reg_14;
+
+ reg_04 = ast_mindwm(ast, AST_REG_MCR04) & 0xfffffffc;
+ reg_14 = ast_mindwm(ast, AST_REG_MCR14) & 0xffffff00;
+
+ ast_moutdwm(ast, AST_SDRAM(0x20100000), 0x41424344);
+ ast_moutdwm(ast, AST_SDRAM(0x10100000), 0x35363738);
+ ast_moutdwm(ast, AST_SDRAM(0x08100000), 0x292A2B2C);
+ ast_moutdwm(ast, AST_SDRAM(0x00100000), 0x1D1E1F10);
+
+ /* Check 8Gbit */
+ if (ast_mindwm(ast, AST_SDRAM(0x20100000)) == 0x41424344) {
+ reg_04 |= 0x03;
+ reg_14 |= (tRFC >> 24) & 0xFF;
+ /* Check 4Gbit */
+ } else if (ast_mindwm(ast, AST_SDRAM(0x10100000)) == 0x35363738) {
+ reg_04 |= 0x02;
+ reg_14 |= (tRFC >> 16) & 0xFF;
+ /* Check 2Gbit */
+ } else if (ast_mindwm(ast, AST_SDRAM(0x08100000)) == 0x292A2B2C) {
+ reg_04 |= 0x01;
+ reg_14 |= (tRFC >> 8) & 0xFF;
+ } else {
+ reg_14 |= tRFC & 0xFF;
+ }
+ ast_moutdwm(ast, AST_REG_MCR04, reg_04);
+ ast_moutdwm(ast, AST_REG_MCR14, reg_14);
+}
+
+static void enable_cache_2500(struct ast_device *ast)
+{
+ u32 reg_04, data;
+
+ reg_04 = ast_mindwm(ast, AST_REG_MCR04);
+ ast_moutdwm(ast, AST_REG_MCR04, reg_04 | 0x1000);
+
+ do
+ data = ast_mindwm(ast, AST_REG_MCR04);
+ while (!(data & 0x80000));
+ ast_moutdwm(ast, AST_REG_MCR04, reg_04 | 0x400);
+}
+
+static void set_mpll_2500(struct ast_device *ast)
+{
+ u32 mcr, data, param;
+
+ /* Reset MMC */
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020080);
+ for (mcr = AST_REG_MCR04; mcr <= AST_REG_MCR8C; mcr += 4)
+ ast_moutdwm(ast, mcr, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020000);
+
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+ data = ast_mindwm(ast, AST_REG_SCU070) & 0x00800000;
+ if (data) {
+ /* CLKIN = 25MHz */
+ param = 0x930023E0;
+ ast_moutdwm(ast, AST_REG_SCU160, 0x00011320);
+ } else {
+ /* CLKIN = 24MHz */
+ param = 0x93002400;
+ }
+ ast_moutdwm(ast, AST_REG_SCU020, param);
+ udelay(100);
+}
+
+static void reset_mmc_2500(struct ast_device *ast)
+{
+ ast_moutdwm(ast, AST_REG_WDT1C(1), 0x00000004);
+ ast_moutdwm(ast, AST_REG_WDT04(1), 0x00000001);
+ ast_moutdwm(ast, AST_REG_WDT08(1), 0x00004755);
+ ast_moutdwm(ast, AST_REG_WDT0C(1), 0x00000013);
+ mdelay(100);
+ ast_moutdwm(ast, AST_REG_WDT14(1), 0x00000077);
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+}
+
+static void ddr3_init_2500(struct ast_device *ast, const u32 *ddr_table)
+{
+ ast_moutdwm(ast, AST_REG_MCR04, 0x00000303);
+ ast_moutdwm(ast, AST_REG_MCR10, ddr_table[REGIDX_010]);
+ ast_moutdwm(ast, AST_REG_MCR14, ddr_table[REGIDX_014]);
+ ast_moutdwm(ast, AST_REG_MCR18, ddr_table[REGIDX_018]);
+ ast_moutdwm(ast, AST_REG_MCR20, ddr_table[REGIDX_020]); /* MODEREG4/6 */
+ ast_moutdwm(ast, AST_REG_MCR24, ddr_table[REGIDX_024]); /* MODEREG5 */
+ ast_moutdwm(ast, AST_REG_MCR2C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */
+ ast_moutdwm(ast, AST_REG_MCR30, ddr_table[REGIDX_030]); /* MODEREG1/3 */
+
+ /* DDR PHY Setting */
+ ast_moutdwm(ast, AST_REG_MCR200, 0x02492AAE);
+ ast_moutdwm(ast, AST_REG_MCR204, 0x00001001);
+ ast_moutdwm(ast, AST_REG_MCR20C, 0x55E00B0B);
+ ast_moutdwm(ast, AST_REG_MCR210, 0x20000000);
+ ast_moutdwm(ast, AST_REG_MCR214, ddr_table[REGIDX_214]);
+ ast_moutdwm(ast, AST_REG_MCR2E0, ddr_table[REGIDX_2E0]);
+ ast_moutdwm(ast, AST_REG_MCR2E4, ddr_table[REGIDX_2E4]);
+ ast_moutdwm(ast, AST_REG_MCR2E8, ddr_table[REGIDX_2E8]);
+ ast_moutdwm(ast, AST_REG_MCR2EC, ddr_table[REGIDX_2EC]);
+ ast_moutdwm(ast, AST_REG_MCR2F0, ddr_table[REGIDX_2F0]);
+ ast_moutdwm(ast, AST_REG_MCR2F4, ddr_table[REGIDX_2F4]);
+ ast_moutdwm(ast, AST_REG_MCR2F8, ddr_table[REGIDX_2F8]);
+ ast_moutdwm(ast, AST_REG_MCR290, 0x00100008);
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006);
+
+ /* Controller Setting */
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020091);
+
+ /* Wait DDR PHY init done */
+ ddr_phy_init_2500(ast);
+
+ ast_moutdwm(ast, AST_REG_MCR120, ddr_table[REGIDX_PLL]);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x42AA5C81);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x0001AF93);
+
+ check_dram_size_2500(ast, ddr_table[REGIDX_RFC]);
+ enable_cache_2500(ast);
+ ast_moutdwm(ast, AST_REG_MCR1C, 0x00000008);
+ ast_moutdwm(ast, AST_REG_MCR38, 0xFFFFFF00);
+}
+
+static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
+{
+ u32 data, data2, pass, retrycnt;
+ u32 ddr_vref, phy_vref;
+ u32 min_ddr_vref = 0, min_phy_vref = 0;
+ u32 max_ddr_vref = 0, max_phy_vref = 0;
+
+ ast_moutdwm(ast, AST_REG_MCR04, 0x00000313);
+ ast_moutdwm(ast, AST_REG_MCR10, ddr_table[REGIDX_010]);
+ ast_moutdwm(ast, AST_REG_MCR14, ddr_table[REGIDX_014]);
+ ast_moutdwm(ast, AST_REG_MCR18, ddr_table[REGIDX_018]);
+ ast_moutdwm(ast, AST_REG_MCR20, ddr_table[REGIDX_020]); /* MODEREG4/6 */
+ ast_moutdwm(ast, AST_REG_MCR24, ddr_table[REGIDX_024]); /* MODEREG5 */
+ ast_moutdwm(ast, AST_REG_MCR2C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */
+ ast_moutdwm(ast, AST_REG_MCR30, ddr_table[REGIDX_030]); /* MODEREG1/3 */
+
+ /* DDR PHY Setting */
+ ast_moutdwm(ast, AST_REG_MCR200, 0x42492AAE);
+ ast_moutdwm(ast, AST_REG_MCR204, 0x09002000);
+ ast_moutdwm(ast, AST_REG_MCR20C, 0x55E00B0B);
+ ast_moutdwm(ast, AST_REG_MCR210, 0x20000000);
+ ast_moutdwm(ast, AST_REG_MCR214, ddr_table[REGIDX_214]);
+ ast_moutdwm(ast, AST_REG_MCR2E0, ddr_table[REGIDX_2E0]);
+ ast_moutdwm(ast, AST_REG_MCR2E4, ddr_table[REGIDX_2E4]);
+ ast_moutdwm(ast, AST_REG_MCR2E8, ddr_table[REGIDX_2E8]);
+ ast_moutdwm(ast, AST_REG_MCR2EC, ddr_table[REGIDX_2EC]);
+ ast_moutdwm(ast, AST_REG_MCR2F0, ddr_table[REGIDX_2F0]);
+ ast_moutdwm(ast, AST_REG_MCR2F4, ddr_table[REGIDX_2F4]);
+ ast_moutdwm(ast, AST_REG_MCR2F8, ddr_table[REGIDX_2F8]);
+ ast_moutdwm(ast, AST_REG_MCR290, 0x00100008);
+ ast_moutdwm(ast, AST_REG_MCR2C4, 0x3C183C3C);
+ ast_moutdwm(ast, AST_REG_MCR2C8, 0x00631E0E);
+
+ /* Controller Setting */
+ ast_moutdwm(ast, AST_REG_MCR34, 0x0001A991);
+
+ /* Train PHY Vref first */
+ pass = 0;
+
+ for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) {
+ max_phy_vref = 0x0;
+ pass = 0;
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00001C06);
+ for (phy_vref = 0x40; phy_vref < 0x80; phy_vref++) {
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2CC, phy_vref | (phy_vref << 8));
+ /* Fire DFI Init */
+ ddr_phy_init_2500(ast);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01);
+ if (cbr_test_2500(ast)) {
+ pass++;
+ data = ast_mindwm(ast, AST_REG_MCR3D0);
+ data2 = data >> 8;
+ data = data & 0xff;
+ if (data > data2)
+ data = data2;
+ if (max_phy_vref < data) {
+ max_phy_vref = data;
+ min_phy_vref = phy_vref;
+ }
+ } else if (pass > 0) {
+ break;
+ }
+ }
+ }
+ ast_moutdwm(ast, AST_REG_MCR2CC, min_phy_vref | (min_phy_vref << 8));
+
+ /* Train DDR Vref next */
+ pass = 0;
+
+ for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) {
+ min_ddr_vref = 0xFF;
+ max_ddr_vref = 0x0;
+ pass = 0;
+ for (ddr_vref = 0x00; ddr_vref < 0x40; ddr_vref++) {
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006 | (ddr_vref << 8));
+ /* Fire DFI Init */
+ ddr_phy_init_2500(ast);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01);
+ if (cbr_test_2500(ast)) {
+ pass++;
+ if (min_ddr_vref > ddr_vref)
+ min_ddr_vref = ddr_vref;
+ if (max_ddr_vref < ddr_vref)
+ max_ddr_vref = ddr_vref;
+ } else if (pass != 0) {
+ break;
+ }
+ }
+ }
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
+ ddr_vref = (min_ddr_vref + max_ddr_vref + 1) >> 1;
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006 | (ddr_vref << 8));
+
+ /* Wait DDR PHY init done */
+ ddr_phy_init_2500(ast);
+
+ ast_moutdwm(ast, AST_REG_MCR120, ddr_table[REGIDX_PLL]);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x42AA5C81);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x0001AF93);
+
+ check_dram_size_2500(ast, ddr_table[REGIDX_RFC]);
+ enable_cache_2500(ast);
+ ast_moutdwm(ast, AST_REG_MCR1C, 0x00000008);
+ ast_moutdwm(ast, AST_REG_MCR38, 0xFFFFFF00);
+}
+
+static bool ast_dram_init_2500(struct ast_device *ast)
+{
+ u32 data;
+ u32 max_tries = 5;
+
+ do {
+ if (max_tries-- == 0)
+ return false;
+ set_mpll_2500(ast);
+ reset_mmc_2500(ast);
+ ddr_init_common_2500(ast);
+
+ data = ast_mindwm(ast, AST_REG_SCU070);
+ if (data & 0x01000000)
+ ddr4_init_2500(ast, ast2500_ddr4_1600_timing_table);
+ else
+ ddr3_init_2500(ast, ast2500_ddr3_1600_timing_table);
+ } while (!ddr_test_2500(ast));
+
+ ast_moutdwm(ast, AST_REG_SCU040, ast_mindwm(ast, AST_REG_SCU040) | 0x41);
+
+ /* Patch code */
+ data = ast_mindwm(ast, AST_REG_SCU00C) & 0xF9FFFFFF;
+ ast_moutdwm(ast, AST_REG_SCU00C, data | 0x10000000);
+
+ return true;
+}
+
+static void ast_post_chip_2500(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ u32 temp;
+ u8 reg;
+
+ reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ if ((reg & AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK) == 0) {/* vga only */
+ u32 scu008;
+
+ /* Clear bus lock condition */
+ ast_2500_patch_ahb(ast->regs);
+
+ /* Disable watchdog */
+ ast_moutdwm(ast, AST_REG_WDT2C(0), 0x00000000);
+ ast_moutdwm(ast, AST_REG_WDT0C(1), 0x00000000);
+
+ /*
+ * Reset USB port to patch USB unknown device issue
+ * SCU90 is Multi-function Pin Control #5
+ * [29]:= 1:Enable USB2.0 Host port#1 (that the mutually shared USB2.0 Hub
+ * port).
+ * SCU94 is Multi-function Pin Control #6
+ * [14:13]:= 1x:USB2.0 Host2 controller
+ * SCU70 is Hardware Strap reg
+ * [23]:= 1:CLKIN is 25MHz and USBCK1 = 24/48 MHz (determined by
+ * [18]: 0(24)/1(48) MHz)
+ * SCU7C is Write clear reg to SCU70
+ * [23]:= write 1 and then SCU70[23] will be clear as 0b.
+ */
+ ast_moutdwm(ast, AST_REG_SCU090, 0x20000000);
+ ast_moutdwm(ast, AST_REG_SCU094, 0x00004000);
+ if (ast_mindwm(ast, AST_REG_SCU070) & 0x00800000) {
+ ast_moutdwm(ast, AST_REG_SCU07C, 0x00800000);
+ mdelay(100);
+ ast_moutdwm(ast, AST_REG_SCU070, 0x00800000);
+ }
+ /* Modify eSPI reset pin */
+ temp = ast_mindwm(ast, AST_REG_SCU070);
+ if (temp & 0x02000000)
+ ast_moutdwm(ast, AST_REG_SCU07C, 0x00004000);
+
+ /* Slow down CPU/AHB CLK in VGA only mode */
+ scu008 = ast_mindwm(ast, AST_REG_SCU008);
+ scu008 |= 0x00000073;
+ ast_moutdwm(ast, AST_REG_SCU008, scu008);
+
+ if (!ast_dram_init_2500(ast))
+ drm_err(dev, "DRAM init failed !\n");
+
+ temp = ast_mindwm(ast, AST_REG_SCU040);
+ ast_moutdwm(ast, AST_REG_SCU040, temp | 0x40);
+ }
+
+ /* wait ready */
+ do {
+ reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ } while ((reg & 0x40) == 0);
+}
+
+int ast_2500_post(struct ast_device *ast)
+{
+ ast_2300_set_def_ext_reg(ast);
+
+ if (ast->config_mode == ast_use_p2a) {
+ ast_post_chip_2500(ast);
+ } else {
+ if (ast->tx_chip == AST_TX_SIL164) {
+ /* Enable DVO */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Mode setting
+ */
+
+const struct ast_vbios_dclk_info ast_2500_dclk_table[] = {
+ {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */
+ {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
+ {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
+ {0x76, 0x63, 0x01}, /* 03: VCLK36 */
+ {0xee, 0x67, 0x01}, /* 04: VCLK40 */
+ {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
+ {0xc6, 0x64, 0x01}, /* 06: VCLK50 */
+ {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
+ {0x80, 0x64, 0x00}, /* 08: VCLK65 */
+ {0x7b, 0x63, 0x00}, /* 09: VCLK75 */
+ {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */
+ {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */
+ {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */
+ {0x85, 0x24, 0x00}, /* 0d: VCLK135 */
+ {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */
+ {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */
+ {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
+ {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */
+ {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
+ {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
+ {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
+ {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
+ {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
+ {0x58, 0x01, 0x42}, /* 17: VCLK119 */
+ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
+ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
+ {0x44, 0x20, 0x43}, /* 1a: VCLK118_25 */
+};
+
+/*
+ * Device initialization
+ */
+
+static void ast_2500_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST2510) {
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2500_device_quirks = {
+ .crtc_mem_req_threshold_low = 96,
+ .crtc_mem_req_threshold_high = 120,
+ .crtc_hsync_precatch_needed = true,
+};
+
+struct drm_device *ast_2500_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2500_device_quirks);
+
+ ast->dclk_table = ast_2500_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* map reserved buffer */
+ ast->dp501_fw_buf = NULL;
+ if (ast->vram_size < pci_resource_len(pdev, 0)) {
+ ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
+ if (!ast->dp501_fw_buf)
+ drm_info(dev, "failed to map reserved buffer!\n");
+ }
+
+ ast_2500_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c
new file mode 100644
index 000000000..dee78fd5b
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2600.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "ast_drv.h"
+#include "ast_post.h"
+
+/*
+ * POST
+ */
+
+int ast_2600_post(struct ast_device *ast)
+{
+ ast_2300_set_def_ext_reg(ast);
+
+ if (ast->tx_chip == AST_TX_ASTDP)
+ return ast_dp_launch(ast);
+
+ return 0;
+}
+
+/*
+ * Device initialization
+ */
+
+static void ast_2600_detect_widescreen(struct ast_device *ast)
+{
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2600_device_quirks = {
+ .crtc_mem_req_threshold_low = 160,
+ .crtc_mem_req_threshold_high = 224,
+ .crtc_hsync_precatch_needed = true,
+ .crtc_hsync_add4_needed = true,
+};
+
+struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2600_device_quirks);
+
+ ast->dclk_table = ast_2500_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ switch (ast->tx_chip) {
+ case AST_TX_ASTDP:
+ ret = ast_post_gpu(ast);
+ break;
+ default:
+ ret = 0;
+ if (need_post)
+ ret = ast_post_gpu(ast);
+ break;
+ }
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ast_2600_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c
new file mode 100644
index 000000000..690d4cd1d
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_cursor.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+
+#include <linux/bits.h>
+#include <linux/sizes.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+
+/*
+ * Hardware cursor
+ */
+
+/* define for signature structure */
+#define AST_HWC_SIGNATURE_SIZE SZ_32
+#define AST_HWC_SIGNATURE_CHECKSUM 0x00
+#define AST_HWC_SIGNATURE_SizeX 0x04
+#define AST_HWC_SIGNATURE_SizeY 0x08
+#define AST_HWC_SIGNATURE_X 0x0C
+#define AST_HWC_SIGNATURE_Y 0x10
+#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
+#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
+
+static unsigned long ast_cursor_vram_size(void)
+{
+ return AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE;
+}
+
+long ast_cursor_vram_offset(struct ast_device *ast)
+{
+ unsigned long size = ast_cursor_vram_size();
+
+ if (size > ast->vram_size)
+ return -EINVAL;
+
+ return ALIGN_DOWN(ast->vram_size - size, SZ_8);
+}
+
+static u32 ast_cursor_calculate_checksum(const void *src, unsigned int width, unsigned int height)
+{
+ u32 csum = 0;
+ unsigned int one_pixel_copy = width & BIT(0);
+ unsigned int two_pixel_copy = width - one_pixel_copy;
+ unsigned int trailing_bytes = (AST_MAX_HWC_WIDTH - width) * sizeof(u16);
+ unsigned int x, y;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < two_pixel_copy; x += 2) {
+ const u32 *src32 = (const u32 *)src;
+
+ csum += *src32;
+ src += SZ_4;
+ }
+ if (one_pixel_copy) {
+ const u16 *src16 = (const u16 *)src;
+
+ csum += *src16;
+ src += SZ_2;
+ }
+ src += trailing_bytes;
+ }
+
+ return csum;
+}
+
+static void ast_set_cursor_image(struct ast_device *ast, const u8 *src,
+ unsigned int width, unsigned int height)
+{
+ u8 __iomem *dst = ast_plane_vaddr(&ast->cursor_plane.base);
+ u32 csum = ast_cursor_calculate_checksum(src, width, height);
+
+ /* write pixel data */
+#if defined(__BIG_ENDIAN)
+ unsigned int i;
+
+ for (i = 0; i < AST_HWC_SIZE; i += 2)
+ writew(swab16(*(const __u16 *)&src[i]), &dst[i]);
+#else
+ memcpy_toio(dst, src, AST_HWC_SIZE);
+#endif
+
+ /* write checksum + signature */
+ dst += AST_HWC_SIZE;
+ writel(csum, dst);
+ writel(width, dst + AST_HWC_SIGNATURE_SizeX);
+ writel(height, dst + AST_HWC_SIGNATURE_SizeY);
+ writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX);
+ writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY);
+}
+
+static void ast_set_cursor_base(struct ast_device *ast, u64 address)
+{
+ u8 addr0 = (address >> 3) & 0xff;
+ u8 addr1 = (address >> 11) & 0xff;
+ u8 addr2 = (address >> 19) & 0xff;
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc8, addr0);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc9, addr1);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xca, addr2);
+}
+
+static void ast_set_cursor_location(struct ast_device *ast, u16 x, u16 y,
+ u8 x_offset, u8 y_offset)
+{
+ u8 x0 = (x & 0x00ff);
+ u8 x1 = (x & 0x0f00) >> 8;
+ u8 y0 = (y & 0x00ff);
+ u8 y1 = (y & 0x0700) >> 8;
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc2, x_offset);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc3, y_offset);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc4, x0);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc5, x1);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc6, y0);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xc7, y1);
+}
+
+static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled)
+{
+ static const u8 mask = (u8)~(AST_IO_VGACRCB_HWC_16BPP |
+ AST_IO_VGACRCB_HWC_ENABLED);
+
+ u8 vgacrcb = AST_IO_VGACRCB_HWC_16BPP;
+
+ if (enabled)
+ vgacrcb |= AST_IO_VGACRCB_HWC_ENABLED;
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xcb, mask, vgacrcb);
+}
+
+/*
+ * Cursor plane
+ */
+
+static const uint32_t ast_cursor_plane_formats[] = {
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_ARGB8888,
+};
+
+static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_framebuffer *new_fb = new_plane_state->fb;
+ struct drm_crtc_state *new_crtc_state = NULL;
+ int ret;
+
+ if (new_plane_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
+
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ true, true);
+ if (ret || !new_plane_state->visible)
+ return ret;
+
+ if (new_fb->width > AST_MAX_HWC_WIDTH || new_fb->height > AST_MAX_HWC_HEIGHT)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const u8 *ast_cursor_plane_get_argb4444(struct ast_cursor_plane *ast_cursor_plane,
+ struct drm_shadow_plane_state *shadow_plane_state,
+ const struct drm_rect *clip)
+{
+ struct drm_plane_state *plane_state = &shadow_plane_state->base;
+ struct drm_framebuffer *fb = plane_state->fb;
+ u8 *argb4444 = NULL;
+
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
+ switch (fb->format->format) {
+ case DRM_FORMAT_ARGB4444:
+ if (shadow_plane_state->data[0].is_iomem) {
+ struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = {
+ IOSYS_MAP_INIT_VADDR(ast_cursor_plane->argb4444),
+ };
+ unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
+ AST_HWC_PITCH,
+ };
+
+ drm_fb_memcpy(argb4444_dst, argb4444_dst_pitch,
+ shadow_plane_state->data, fb, clip);
+ argb4444 = argb4444_dst[0].vaddr;
+ } else {
+ argb4444 = shadow_plane_state->data[0].vaddr;
+ }
+ break;
+ case DRM_FORMAT_ARGB8888:
+ {
+ struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = {
+ IOSYS_MAP_INIT_VADDR(ast_cursor_plane->argb4444),
+ };
+ unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = {
+ AST_HWC_PITCH,
+ };
+
+ drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch,
+ shadow_plane_state->data, fb, clip,
+ &shadow_plane_state->fmtcnv_state);
+ argb4444 = argb4444_dst[0].vaddr;
+ }
+ break;
+ }
+
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+ } else {
+ /*
+ * Fall back to white square if GEM object is not ready. Gives
+ * the user an indication where the cursor is located.
+ */
+ memset(ast_cursor_plane->argb4444, 0xff, sizeof(ast_cursor_plane->argb4444));
+ argb4444 = ast_cursor_plane->argb4444;
+ }
+
+ return argb4444;
+}
+
+static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct ast_cursor_plane *ast_cursor_plane = to_ast_cursor_plane(plane);
+ struct ast_plane *ast_plane = to_ast_plane(plane);
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+ struct ast_device *ast = to_ast_device(plane->dev);
+ struct drm_rect damage;
+ u64 dst_off = ast_plane->offset;
+ u8 __iomem *dst = ast_plane_vaddr(ast_plane); /* TODO: Use mapping abstraction properly */
+ u8 __iomem *sig = dst + AST_HWC_SIZE; /* TODO: Use mapping abstraction properly */
+ unsigned int offset_x, offset_y;
+ u16 x, y;
+ u8 x_offset, y_offset;
+
+ /*
+ * Do data transfer to hardware buffer and point the scanout
+ * engine to the offset.
+ */
+
+ if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) {
+ const u8 *argb4444 = ast_cursor_plane_get_argb4444(ast_cursor_plane,
+ shadow_plane_state,
+ &damage);
+
+ if (argb4444)
+ ast_set_cursor_image(ast, argb4444, fb->width, fb->height);
+
+ ast_set_cursor_base(ast, dst_off);
+ }
+
+ /*
+ * Update location in HWC signature and registers.
+ */
+
+ writel(plane_state->crtc_x, sig + AST_HWC_SIGNATURE_X);
+ writel(plane_state->crtc_y, sig + AST_HWC_SIGNATURE_Y);
+
+ offset_x = AST_MAX_HWC_WIDTH - fb->width;
+ offset_y = AST_MAX_HWC_HEIGHT - fb->height;
+
+ if (plane_state->crtc_x < 0) {
+ x_offset = (-plane_state->crtc_x) + offset_x;
+ x = 0;
+ } else {
+ x_offset = offset_x;
+ x = plane_state->crtc_x;
+ }
+ if (plane_state->crtc_y < 0) {
+ y_offset = (-plane_state->crtc_y) + offset_y;
+ y = 0;
+ } else {
+ y_offset = offset_y;
+ y = plane_state->crtc_y;
+ }
+
+ ast_set_cursor_location(ast, x, y, x_offset, y_offset);
+
+ /* Dummy write to enable HWC and make the HW pick-up the changes. */
+ ast_set_cursor_enabled(ast, true);
+}
+
+static void ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(plane->dev);
+
+ ast_set_cursor_enabled(ast, false);
+}
+
+static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = ast_cursor_plane_helper_atomic_check,
+ .atomic_update = ast_cursor_plane_helper_atomic_update,
+ .atomic_disable = ast_cursor_plane_helper_atomic_disable,
+};
+
+static const struct drm_plane_funcs ast_cursor_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ DRM_GEM_SHADOW_PLANE_FUNCS,
+};
+
+int ast_cursor_plane_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct ast_cursor_plane *ast_cursor_plane = &ast->cursor_plane;
+ struct ast_plane *ast_plane = &ast_cursor_plane->base;
+ struct drm_plane *cursor_plane = &ast_plane->base;
+ unsigned long size;
+ long offset;
+ int ret;
+
+ size = ast_cursor_vram_size();
+ offset = ast_cursor_vram_offset(ast);
+ if (offset < 0)
+ return offset;
+
+ ret = ast_plane_init(dev, ast_plane, offset, size,
+ 0x01, &ast_cursor_plane_funcs,
+ ast_cursor_plane_formats, ARRAY_SIZE(ast_cursor_plane_formats),
+ NULL, DRM_PLANE_TYPE_CURSOR);
+ if (ret) {
+ drm_err(dev, "ast_plane_init() failed: %d\n", ret);
+ return ret;
+ }
+ drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs);
+ drm_plane_enable_fb_damage_clips(cursor_plane);
+ drm_plane_create_blend_mode_property(cursor_plane,
+ BIT(DRM_MODE_BLEND_COVERAGE));
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ast/ast_ddc.c b/drivers/gpu/drm/ast/ast_ddc.c
new file mode 100644
index 000000000..29cf5d157
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_ddc.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+
+#include <linux/i2c-algo-bit.h>
+#include <linux/i2c.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "ast_ddc.h"
+#include "ast_drv.h"
+
+struct ast_ddc {
+ struct ast_device *ast;
+
+ struct i2c_algo_bit_data bit;
+ struct i2c_adapter adapter;
+};
+
+static void ast_ddc_algo_bit_data_setsda(void *data, int state)
+{
+ struct ast_ddc *ddc = data;
+ struct ast_device *ast = ddc->ast;
+ int i;
+ u8 ujcrb7, jtemp;
+
+ for (i = 0; i < 0x10000; i++) {
+ ujcrb7 = ((state & 0x01) ? 0 : 1) << 2;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0xf1, ujcrb7);
+ jtemp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x04);
+ if (ujcrb7 == jtemp)
+ break;
+ }
+}
+
+static void ast_ddc_algo_bit_data_setscl(void *data, int state)
+{
+ struct ast_ddc *ddc = data;
+ struct ast_device *ast = ddc->ast;
+ int i;
+ u8 ujcrb7, jtemp;
+
+ for (i = 0; i < 0x10000; i++) {
+ ujcrb7 = ((state & 0x01) ? 0 : 1);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0xf4, ujcrb7);
+ jtemp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x01);
+ if (ujcrb7 == jtemp)
+ break;
+ }
+}
+
+static int ast_ddc_algo_bit_data_pre_xfer(struct i2c_adapter *adapter)
+{
+ struct ast_ddc *ddc = i2c_get_adapdata(adapter);
+ struct ast_device *ast = ddc->ast;
+
+ /*
+ * Protect access to I/O registers from concurrent modesetting
+ * by acquiring the I/O-register lock.
+ */
+ mutex_lock(&ast->modeset_lock);
+
+ return 0;
+}
+
+static void ast_ddc_algo_bit_data_post_xfer(struct i2c_adapter *adapter)
+{
+ struct ast_ddc *ddc = i2c_get_adapdata(adapter);
+ struct ast_device *ast = ddc->ast;
+
+ mutex_unlock(&ast->modeset_lock);
+}
+
+static int ast_ddc_algo_bit_data_getsda(void *data)
+{
+ struct ast_ddc *ddc = data;
+ struct ast_device *ast = ddc->ast;
+ uint32_t val, val2, count, pass;
+
+ count = 0;
+ pass = 0;
+ val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x20) >> 5) & 0x01;
+ do {
+ val2 = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x20) >> 5) & 0x01;
+ if (val == val2) {
+ pass++;
+ } else {
+ pass = 0;
+ val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x20) >> 5) & 0x01;
+ }
+ } while ((pass < 5) && (count++ < 0x10000));
+
+ return val & 1 ? 1 : 0;
+}
+
+static int ast_ddc_algo_bit_data_getscl(void *data)
+{
+ struct ast_ddc *ddc = data;
+ struct ast_device *ast = ddc->ast;
+ uint32_t val, val2, count, pass;
+
+ count = 0;
+ pass = 0;
+ val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x10) >> 4) & 0x01;
+ do {
+ val2 = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x10) >> 4) & 0x01;
+ if (val == val2) {
+ pass++;
+ } else {
+ pass = 0;
+ val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x10) >> 4) & 0x01;
+ }
+ } while ((pass < 5) && (count++ < 0x10000));
+
+ return val & 1 ? 1 : 0;
+}
+
+static void ast_ddc_release(struct drm_device *dev, void *res)
+{
+ struct ast_ddc *ddc = res;
+
+ i2c_del_adapter(&ddc->adapter);
+}
+
+struct i2c_adapter *ast_ddc_create(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct ast_ddc *ddc;
+ struct i2c_adapter *adapter;
+ struct i2c_algo_bit_data *bit;
+ int ret;
+
+ ddc = drmm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
+ if (!ddc)
+ return ERR_PTR(-ENOMEM);
+ ddc->ast = ast;
+
+ bit = &ddc->bit;
+ bit->data = ddc;
+ bit->setsda = ast_ddc_algo_bit_data_setsda;
+ bit->setscl = ast_ddc_algo_bit_data_setscl;
+ bit->getsda = ast_ddc_algo_bit_data_getsda;
+ bit->getscl = ast_ddc_algo_bit_data_getscl;
+ bit->pre_xfer = ast_ddc_algo_bit_data_pre_xfer;
+ bit->post_xfer = ast_ddc_algo_bit_data_post_xfer;
+ bit->udelay = 20;
+ bit->timeout = usecs_to_jiffies(2200);
+
+ adapter = &ddc->adapter;
+ adapter->owner = THIS_MODULE;
+ adapter->algo_data = bit;
+ adapter->dev.parent = dev->dev;
+ snprintf(adapter->name, sizeof(adapter->name), "AST DDC bus");
+ i2c_set_adapdata(adapter, ddc);
+
+ ret = i2c_bit_add_bus(adapter);
+ if (ret) {
+ drm_err(dev, "Failed to register bit i2c\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = drmm_add_action_or_reset(dev, ast_ddc_release, ddc);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &ddc->adapter;
+}
diff --git a/drivers/gpu/drm/ast/ast_ddc.h b/drivers/gpu/drm/ast/ast_ddc.h
new file mode 100644
index 000000000..85c93edc9
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_ddc.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __AST_DDC_H__
+#define __AST_DDC_H__
+
+struct ast_device;
+struct i2c_adapter;
+
+struct i2c_adapter *ast_ddc_create(struct ast_device *ast);
+
+#endif
diff --git a/drivers/gpu/drm/ast/ast_dp.c b/drivers/gpu/drm/ast/ast_dp.c
new file mode 100644
index 000000000..5f266a054
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_dp.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, ASPEED Technology Inc.
+// Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com>
+
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "ast_drv.h"
+#include "ast_vbios.h"
+
+struct ast_astdp_mode_index_table_entry {
+ unsigned int hdisplay;
+ unsigned int vdisplay;
+ unsigned int mode_index;
+};
+
+/* FIXME: Do refresh rate and flags actually matter? */
+static const struct ast_astdp_mode_index_table_entry ast_astdp_mode_index_table[] = {
+ { 320, 240, ASTDP_320x240_60 },
+ { 400, 300, ASTDP_400x300_60 },
+ { 512, 384, ASTDP_512x384_60 },
+ { 640, 480, ASTDP_640x480_60 },
+ { 800, 600, ASTDP_800x600_56 },
+ { 1024, 768, ASTDP_1024x768_60 },
+ { 1152, 864, ASTDP_1152x864_75 },
+ { 1280, 800, ASTDP_1280x800_60_RB },
+ { 1280, 1024, ASTDP_1280x1024_60 },
+ { 1360, 768, ASTDP_1366x768_60 }, // same as 1366x786
+ { 1366, 768, ASTDP_1366x768_60 },
+ { 1440, 900, ASTDP_1440x900_60_RB },
+ { 1600, 900, ASTDP_1600x900_60_RB },
+ { 1600, 1200, ASTDP_1600x1200_60 },
+ { 1680, 1050, ASTDP_1680x1050_60_RB },
+ { 1920, 1080, ASTDP_1920x1080_60 },
+ { 1920, 1200, ASTDP_1920x1200_60 },
+ { 0 }
+};
+
+struct ast_astdp_connector_state {
+ struct drm_connector_state base;
+
+ int mode_index;
+};
+
+static struct ast_astdp_connector_state *
+to_ast_astdp_connector_state(const struct drm_connector_state *state)
+{
+ return container_of(state, struct ast_astdp_connector_state, base);
+}
+
+static int ast_astdp_get_mode_index(unsigned int hdisplay, unsigned int vdisplay)
+{
+ const struct ast_astdp_mode_index_table_entry *entry = ast_astdp_mode_index_table;
+
+ while (entry->hdisplay && entry->vdisplay) {
+ if (entry->hdisplay == hdisplay && entry->vdisplay == vdisplay)
+ return entry->mode_index;
+ ++entry;
+ }
+
+ return -EINVAL;
+}
+
+static bool ast_astdp_is_connected(struct ast_device *ast)
+{
+ if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xDF, AST_IO_VGACRDF_HPD))
+ return false;
+ /*
+ * HPD might be set even if no monitor is connected, so also check that
+ * the link training was successful.
+ */
+ if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xDC, AST_IO_VGACRDC_LINK_SUCCESS))
+ return false;
+ return true;
+}
+
+static int ast_astdp_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+ struct ast_device *ast = data;
+ size_t rdlen = round_up(len, 4);
+ int ret = 0;
+ unsigned int i;
+
+ if (block > 0)
+ return -EIO; /* extension headers not supported */
+
+ /*
+ * Protect access to I/O registers from concurrent modesetting
+ * by acquiring the I/O-register lock.
+ */
+ mutex_lock(&ast->modeset_lock);
+
+ /* Start reading EDID data */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe5, (u8)~AST_IO_VGACRE5_EDID_READ_DONE, 0x00);
+
+ for (i = 0; i < rdlen; i += 4) {
+ unsigned int offset;
+ unsigned int j;
+ u8 ediddata[4];
+ u8 vgacre4;
+
+ offset = (i + block * EDID_LENGTH) / 4;
+ if (offset >= 64) {
+ ret = -EIO;
+ goto out;
+ }
+ vgacre4 = offset;
+
+ /*
+ * CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64
+ */
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xe4, vgacre4);
+
+ /*
+ * CRD7[b0]: valid flag for EDID
+ * CRD6[b0]: mirror read pointer for EDID
+ */
+ for (j = 0; j < 200; ++j) {
+ u8 vgacrd7, vgacrd6;
+
+ /*
+ * Delay are getting longer with each retry.
+ *
+ * 1. No delay on first try
+ * 2. The Delays are often 2 loops when users request "Display Settings"
+ * of right-click of mouse.
+ * 3. The Delays are often longer a lot when system resume from S3/S4.
+ */
+ if (j)
+ msleep(j + 1);
+
+ /* Wait for EDID offset to show up in mirror register */
+ vgacrd7 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd7);
+ if (vgacrd7 & AST_IO_VGACRD7_EDID_VALID_FLAG) {
+ vgacrd6 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd6);
+ if (vgacrd6 == offset)
+ break;
+ }
+ }
+ if (j == 200) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ediddata[0] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd8);
+ ediddata[1] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd9);
+ ediddata[2] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xda);
+ ediddata[3] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xdb);
+
+ if (i == 31) {
+ /*
+ * For 128-bytes EDID_1.3,
+ * 1. Add the value of Bytes-126 to Bytes-127.
+ * The Bytes-127 is Checksum. Sum of all 128bytes should
+ * equal 0 (mod 256).
+ * 2. Modify Bytes-126 to be 0.
+ * The Bytes-126 indicates the Number of extensions to
+ * follow. 0 represents noextensions.
+ */
+ ediddata[3] = ediddata[3] + ediddata[2];
+ ediddata[2] = 0;
+ }
+
+ memcpy(buf, ediddata, min((len - i), 4));
+ buf += 4;
+ }
+
+out:
+ /* Signal end of reading */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe5, (u8)~AST_IO_VGACRE5_EDID_READ_DONE,
+ AST_IO_VGACRE5_EDID_READ_DONE);
+
+ mutex_unlock(&ast->modeset_lock);
+
+ return ret;
+}
+
+/*
+ * Launch Aspeed DP
+ */
+int ast_dp_launch(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ unsigned int i = 10;
+
+ while (i) {
+ u8 vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1);
+
+ if (vgacrd1 & AST_IO_VGACRD1_MCU_FW_EXECUTING)
+ break;
+ --i;
+ msleep(100);
+ }
+ if (!i) {
+ drm_err(dev, "Wait DPMCU executing timeout\n");
+ return -ENODEV;
+ }
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe5,
+ (u8) ~AST_IO_VGACRE5_EDID_READ_DONE,
+ AST_IO_VGACRE5_EDID_READ_DONE);
+
+ return 0;
+}
+
+static bool ast_dp_get_phy_sleep(struct ast_device *ast)
+{
+ u8 vgacre3 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xe3);
+
+ return (vgacre3 & AST_IO_VGACRE3_DP_PHY_SLEEP);
+}
+
+static void ast_dp_set_phy_sleep(struct ast_device *ast, bool sleep)
+{
+ u8 vgacre3 = 0x00;
+
+ if (sleep)
+ vgacre3 |= AST_IO_VGACRE3_DP_PHY_SLEEP;
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe3, (u8)~AST_IO_VGACRE3_DP_PHY_SLEEP,
+ vgacre3);
+ msleep(50);
+}
+
+static void ast_dp_link_training(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ u8 vgacrdc;
+
+ if (i)
+ msleep(100);
+
+ vgacrdc = ast_get_index_reg(ast, AST_IO_VGACRI, 0xdc);
+ if (vgacrdc & AST_IO_VGACRDC_LINK_SUCCESS)
+ return;
+ }
+ drm_err(dev, "Link training failed\n");
+}
+
+static bool __ast_dp_wait_enable(struct ast_device *ast, bool enabled)
+{
+ u8 vgacrdf_test = 0x00;
+ u8 vgacrdf;
+ unsigned int i;
+
+ if (enabled)
+ vgacrdf_test |= AST_IO_VGACRDF_DP_VIDEO_ENABLE;
+
+ for (i = 0; i < 1000; ++i) {
+ if (i)
+ mdelay(1);
+ vgacrdf = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xdf,
+ AST_IO_VGACRDF_DP_VIDEO_ENABLE);
+ if (vgacrdf == vgacrdf_test)
+ return true;
+ }
+
+ return false;
+}
+
+static void ast_dp_set_enable(struct ast_device *ast, bool enabled)
+{
+ struct drm_device *dev = &ast->base;
+ u8 vgacre3 = 0x00;
+
+ if (enabled)
+ vgacre3 |= AST_IO_VGACRE3_DP_VIDEO_ENABLE;
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe3, (u8)~AST_IO_VGACRE3_DP_VIDEO_ENABLE,
+ vgacre3);
+
+ drm_WARN_ON(dev, !__ast_dp_wait_enable(ast, enabled));
+}
+
+static void ast_wait_for_vretrace(struct ast_device *ast)
+{
+ unsigned long timeout = jiffies + HZ;
+ u8 vgair1;
+
+ do {
+ vgair1 = ast_io_read8(ast, AST_IO_VGAIR1_R);
+ } while (!(vgair1 & AST_IO_VGAIR1_VREFRESH) && time_before(jiffies, timeout));
+}
+
+/*
+ * Encoder
+ */
+
+static const struct drm_encoder_funcs ast_astdp_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static enum drm_mode_status
+ast_astdp_encoder_helper_mode_valid(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode)
+{
+ int res;
+
+ res = ast_astdp_get_mode_index(mode->hdisplay, mode->vdisplay);
+ if (res < 0)
+ return MODE_NOMODE;
+
+ return MODE_OK;
+}
+
+static void ast_astdp_encoder_helper_atomic_mode_set(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_device *dev = encoder->dev;
+ struct ast_device *ast = to_ast_device(dev);
+ struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
+ const struct ast_vbios_enhtable *vmode = ast_crtc_state->vmode;
+ struct ast_astdp_connector_state *astdp_conn_state =
+ to_ast_astdp_connector_state(conn_state);
+ int mode_index = astdp_conn_state->mode_index;
+ u8 refresh_rate_index;
+ u8 vgacre0, vgacre1, vgacre2;
+
+ if (drm_WARN_ON(dev, vmode->refresh_rate_index < 1 || vmode->refresh_rate_index > 255))
+ return;
+ refresh_rate_index = vmode->refresh_rate_index - 1;
+
+ /* FIXME: Why are we doing this? */
+ switch (mode_index) {
+ case ASTDP_1280x800_60_RB:
+ case ASTDP_1440x900_60_RB:
+ case ASTDP_1600x900_60_RB:
+ case ASTDP_1680x1050_60_RB:
+ mode_index = (u8)(mode_index - (u8)refresh_rate_index);
+ break;
+ default:
+ mode_index = (u8)(mode_index + (u8)refresh_rate_index);
+ break;
+ }
+
+ /*
+ * CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp)
+ * CRE1[7:0]: MISC1 (default: 0x00)
+ * CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50)
+ */
+ vgacre0 = AST_IO_VGACRE0_24BPP;
+ vgacre1 = 0x00;
+ vgacre2 = mode_index & 0xff;
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xe0, vgacre0);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xe1, vgacre1);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xe2, vgacre2);
+}
+
+static void ast_astdp_encoder_helper_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(encoder->dev);
+ struct ast_connector *ast_connector = &ast->output.astdp.connector;
+
+ if (ast_connector->physical_status == connector_status_connected) {
+ ast_dp_set_phy_sleep(ast, false);
+ ast_dp_link_training(ast);
+
+ ast_wait_for_vretrace(ast);
+ ast_dp_set_enable(ast, true);
+ }
+}
+
+static void ast_astdp_encoder_helper_atomic_disable(struct drm_encoder *encoder,
+ struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(encoder->dev);
+
+ ast_dp_set_enable(ast, false);
+ ast_dp_set_phy_sleep(ast, true);
+}
+
+static int ast_astdp_encoder_helper_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ const struct drm_display_mode *mode = &crtc_state->mode;
+ struct ast_astdp_connector_state *astdp_conn_state =
+ to_ast_astdp_connector_state(conn_state);
+ int res;
+
+ if (drm_atomic_crtc_needs_modeset(crtc_state)) {
+ res = ast_astdp_get_mode_index(mode->hdisplay, mode->vdisplay);
+ if (res < 0)
+ return res;
+ astdp_conn_state->mode_index = res;
+ }
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs ast_astdp_encoder_helper_funcs = {
+ .mode_valid = ast_astdp_encoder_helper_mode_valid,
+ .atomic_mode_set = ast_astdp_encoder_helper_atomic_mode_set,
+ .atomic_enable = ast_astdp_encoder_helper_atomic_enable,
+ .atomic_disable = ast_astdp_encoder_helper_atomic_disable,
+ .atomic_check = ast_astdp_encoder_helper_atomic_check,
+};
+
+/*
+ * Connector
+ */
+
+static int ast_astdp_connector_helper_get_modes(struct drm_connector *connector)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ int count;
+
+ if (ast_connector->physical_status == connector_status_connected) {
+ struct ast_device *ast = to_ast_device(connector->dev);
+ const struct drm_edid *drm_edid;
+
+ drm_edid = drm_edid_read_custom(connector, ast_astdp_read_edid_block, ast);
+ drm_edid_connector_update(connector, drm_edid);
+ count = drm_edid_connector_add_modes(connector);
+ drm_edid_free(drm_edid);
+ } else {
+ drm_edid_connector_update(connector, NULL);
+
+ /*
+ * There's no EDID data without a connected monitor. Set BMC-
+ * compatible modes in this case. The XGA default resolution
+ * should work well for all BMCs.
+ */
+ count = drm_add_modes_noedid(connector, 4096, 4096);
+ if (count)
+ drm_set_preferred_mode(connector, 1024, 768);
+ }
+
+ return count;
+}
+
+static int ast_astdp_connector_helper_detect_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ struct ast_device *ast = to_ast_device(connector->dev);
+ enum drm_connector_status status = connector_status_disconnected;
+ bool phy_sleep;
+
+ mutex_lock(&ast->modeset_lock);
+
+ phy_sleep = ast_dp_get_phy_sleep(ast);
+ if (phy_sleep)
+ ast_dp_set_phy_sleep(ast, false);
+
+ if (ast_astdp_is_connected(ast))
+ status = connector_status_connected;
+
+ if (phy_sleep && status == connector_status_disconnected)
+ ast_dp_set_phy_sleep(ast, true);
+
+ mutex_unlock(&ast->modeset_lock);
+
+ if (status != ast_connector->physical_status)
+ ++connector->epoch_counter;
+ ast_connector->physical_status = status;
+
+ return connector_status_connected;
+}
+
+static const struct drm_connector_helper_funcs ast_astdp_connector_helper_funcs = {
+ .get_modes = ast_astdp_connector_helper_get_modes,
+ .detect_ctx = ast_astdp_connector_helper_detect_ctx,
+};
+
+static void ast_astdp_connector_reset(struct drm_connector *connector)
+{
+ struct ast_astdp_connector_state *astdp_state = kzalloc_obj(*astdp_state);
+
+ if (connector->state)
+ connector->funcs->atomic_destroy_state(connector, connector->state);
+
+ if (astdp_state)
+ __drm_atomic_helper_connector_reset(connector, &astdp_state->base);
+ else
+ __drm_atomic_helper_connector_reset(connector, NULL);
+}
+
+static struct drm_connector_state *
+ast_astdp_connector_atomic_duplicate_state(struct drm_connector *connector)
+{
+ struct ast_astdp_connector_state *new_astdp_state, *astdp_state;
+ struct drm_device *dev = connector->dev;
+
+ if (drm_WARN_ON(dev, !connector->state))
+ return NULL;
+
+ new_astdp_state = kmalloc_obj(*new_astdp_state);
+ if (!new_astdp_state)
+ return NULL;
+ __drm_atomic_helper_connector_duplicate_state(connector, &new_astdp_state->base);
+
+ astdp_state = to_ast_astdp_connector_state(connector->state);
+
+ new_astdp_state->mode_index = astdp_state->mode_index;
+
+ return &new_astdp_state->base;
+}
+
+static void ast_astdp_connector_atomic_destroy_state(struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ struct ast_astdp_connector_state *astdp_state = to_ast_astdp_connector_state(state);
+
+ __drm_atomic_helper_connector_destroy_state(&astdp_state->base);
+ kfree(astdp_state);
+}
+
+static const struct drm_connector_funcs ast_astdp_connector_funcs = {
+ .reset = ast_astdp_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = ast_astdp_connector_atomic_duplicate_state,
+ .atomic_destroy_state = ast_astdp_connector_atomic_destroy_state,
+};
+
+/*
+ * Output
+ */
+
+int ast_astdp_output_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct drm_crtc *crtc = &ast->crtc;
+ struct drm_encoder *encoder;
+ struct ast_connector *ast_connector;
+ struct drm_connector *connector;
+ int ret;
+
+ /* encoder */
+
+ encoder = &ast->output.astdp.encoder;
+ ret = drm_encoder_init(dev, encoder, &ast_astdp_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ret;
+ drm_encoder_helper_add(encoder, &ast_astdp_encoder_helper_funcs);
+
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ /* connector */
+
+ ast_connector = &ast->output.astdp.connector;
+ connector = &ast_connector->base;
+ ret = drm_connector_init(dev, connector, &ast_astdp_connector_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort);
+ if (ret)
+ return ret;
+ drm_connector_helper_add(connector, &ast_astdp_connector_helper_funcs);
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+
+ ast_connector->physical_status = connector->status;
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c
new file mode 100644
index 000000000..6d6ccfad1
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_dp501.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_probe_helper.h>
+
+#include "ast_drv.h"
+
+MODULE_FIRMWARE("ast_dp501_fw.bin");
+
+static void ast_release_firmware(void *data)
+{
+ struct ast_device *ast = data;
+
+ release_firmware(ast->dp501_fw);
+ ast->dp501_fw = NULL;
+}
+
+static int ast_load_dp501_microcode(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ int ret;
+
+ ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast);
+}
+
+static void send_ack(struct ast_device *ast)
+{
+ u8 sendack;
+ sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0xff);
+ sendack |= 0x80;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0x00, sendack);
+}
+
+static void send_nack(struct ast_device *ast)
+{
+ u8 sendack;
+ sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0xff);
+ sendack &= ~0x80;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0x00, sendack);
+}
+
+static bool wait_ack(struct ast_device *ast)
+{
+ u8 waitack;
+ u32 retry = 0;
+ do {
+ waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff);
+ waitack &= 0x80;
+ udelay(100);
+ } while ((!waitack) && (retry++ < 1000));
+
+ if (retry < 1000)
+ return true;
+ else
+ return false;
+}
+
+static bool wait_nack(struct ast_device *ast)
+{
+ u8 waitack;
+ u32 retry = 0;
+ do {
+ waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff);
+ waitack &= 0x80;
+ udelay(100);
+ } while ((waitack) && (retry++ < 1000));
+
+ if (retry < 1000)
+ return true;
+ else
+ return false;
+}
+
+static void set_cmd_trigger(struct ast_device *ast)
+{
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, ~0x40, 0x40);
+}
+
+static void clear_cmd_trigger(struct ast_device *ast)
+{
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, ~0x40, 0x00);
+}
+
+#if 0
+static bool wait_fw_ready(struct ast_device *ast)
+{
+ u8 waitready;
+ u32 retry = 0;
+ do {
+ waitready = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff);
+ waitready &= 0x40;
+ udelay(100);
+ } while ((!waitready) && (retry++ < 1000));
+
+ if (retry < 1000)
+ return true;
+ else
+ return false;
+}
+#endif
+
+static bool ast_write_cmd(struct ast_device *ast, u8 data)
+{
+ int retry = 0;
+
+ if (wait_nack(ast)) {
+ send_nack(ast);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data);
+ send_ack(ast);
+ set_cmd_trigger(ast);
+ do {
+ if (wait_ack(ast)) {
+ clear_cmd_trigger(ast);
+ send_nack(ast);
+ return true;
+ }
+ } while (retry++ < 100);
+ }
+ clear_cmd_trigger(ast);
+ send_nack(ast);
+ return false;
+}
+
+static bool ast_write_data(struct ast_device *ast, u8 data)
+{
+ if (wait_nack(ast)) {
+ send_nack(ast);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data);
+ send_ack(ast);
+ if (wait_ack(ast)) {
+ send_nack(ast);
+ return true;
+ }
+ }
+ send_nack(ast);
+ return false;
+}
+
+#if 0
+static bool ast_read_data(struct drm_device *dev, u8 *data)
+{
+ struct ast_device *ast = to_ast_device(dev);
+ u8 tmp;
+
+ *data = 0;
+
+ if (wait_ack(ast) == false)
+ return false;
+ tmp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd3, 0xff);
+ *data = tmp;
+ if (wait_nack(ast) == false) {
+ send_nack(ast);
+ return false;
+ }
+ send_nack(ast);
+ return true;
+}
+
+static void clear_cmd(struct ast_device *ast)
+{
+ send_nack(ast);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, 0x00);
+}
+#endif
+
+static void ast_set_dp501_video_output(struct ast_device *ast, u8 mode)
+{
+ ast_write_cmd(ast, 0x40);
+ ast_write_data(ast, mode);
+
+ msleep(10);
+}
+
+static u32 get_fw_base(struct ast_device *ast)
+{
+ return ast_mindwm(ast, AST_REG_SCU104) & 0x7fffffff;
+}
+
+bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size)
+{
+ u32 i, data;
+ u32 boot_address;
+
+ if (ast->config_mode != ast_use_p2a)
+ return false;
+
+ data = ast_mindwm(ast, AST_REG_SCU100) & 0x01;
+ if (data) {
+ boot_address = get_fw_base(ast);
+ for (i = 0; i < size; i += 4)
+ *(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i);
+ return true;
+ }
+ return false;
+}
+
+static bool ast_launch_m68k(struct ast_device *ast)
+{
+ u32 i, data, len = 0;
+ u32 boot_address;
+ u8 *fw_addr = NULL;
+ u8 jreg;
+
+ if (ast->config_mode != ast_use_p2a)
+ return false;
+
+ data = ast_mindwm(ast, AST_REG_SCU100) & 0x01;
+ if (!data) {
+
+ if (ast->dp501_fw_addr) {
+ fw_addr = ast->dp501_fw_addr;
+ len = 32*1024;
+ } else {
+ if (!ast->dp501_fw &&
+ ast_load_dp501_microcode(ast) < 0)
+ return false;
+
+ fw_addr = (u8 *)ast->dp501_fw->data;
+ len = ast->dp501_fw->size;
+ }
+ /* Get BootAddress */
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+ data = ast_mindwm(ast, AST_REG_MCR04);
+ switch (data & 0x03) {
+ case 0:
+ boot_address = 0x44000000;
+ break;
+ default:
+ case 1:
+ boot_address = 0x48000000;
+ break;
+ case 2:
+ boot_address = 0x50000000;
+ break;
+ case 3:
+ boot_address = 0x60000000;
+ break;
+ }
+ boot_address -= 0x200000; /* -2MB */
+
+ /* copy image to buffer */
+ for (i = 0; i < len; i += 4) {
+ data = *(u32 *)(fw_addr + i);
+ ast_moutdwm(ast, boot_address + i, data);
+ }
+
+ /* Init SCU */
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+
+ /* Launch FW */
+ ast_moutdwm(ast, AST_REG_SCU104, 0x80000000 + boot_address);
+ ast_moutdwm(ast, AST_REG_SCU100, 1);
+
+ /* Update Scratch */
+ data = ast_mindwm(ast, AST_REG_SCU040) & 0xfffff1ff;
+ data |= 0x800; /* D[11:9] = 100b: UEFI handling */
+ ast_moutdwm(ast, AST_REG_SCU040, data);
+
+ jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */
+ jreg |= 0x02;
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x99, jreg);
+ }
+ return true;
+}
+
+static bool ast_dp501_is_connected(struct ast_device *ast)
+{
+ u32 boot_address, offset, data;
+
+ if (ast->config_mode == ast_use_p2a) {
+ boot_address = get_fw_base(ast);
+
+ /* validate FW version */
+ offset = AST_DP501_GBL_VERSION;
+ data = ast_mindwm(ast, boot_address + offset);
+ if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1)
+ return false;
+
+ /* validate PnP Monitor */
+ offset = AST_DP501_PNPMONITOR;
+ data = ast_mindwm(ast, boot_address + offset);
+ if (!(data & AST_DP501_PNP_CONNECTED))
+ return false;
+ } else {
+ if (!ast->dp501_fw_buf)
+ return false;
+
+ /* dummy read */
+ offset = 0x0000;
+ data = readl(ast->dp501_fw_buf + offset);
+
+ /* validate FW version */
+ offset = AST_DP501_GBL_VERSION;
+ data = readl(ast->dp501_fw_buf + offset);
+ if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1)
+ return false;
+
+ /* validate PnP Monitor */
+ offset = AST_DP501_PNPMONITOR;
+ data = readl(ast->dp501_fw_buf + offset);
+ if (!(data & AST_DP501_PNP_CONNECTED))
+ return false;
+ }
+ return true;
+}
+
+static int ast_dp512_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+ struct ast_device *ast = data;
+ size_t rdlen = round_up(len, 4);
+ u32 i, boot_address, offset, ediddata;
+
+ if (block > (512 / EDID_LENGTH))
+ return -EIO;
+
+ offset = AST_DP501_EDID_DATA + block * EDID_LENGTH;
+
+ if (ast->config_mode == ast_use_p2a) {
+ boot_address = get_fw_base(ast);
+
+ for (i = 0; i < rdlen; i += 4) {
+ ediddata = ast_mindwm(ast, boot_address + offset + i);
+ memcpy(buf, &ediddata, min((len - i), 4));
+ buf += 4;
+ }
+ } else {
+ for (i = 0; i < rdlen; i += 4) {
+ ediddata = readl(ast->dp501_fw_buf + offset + i);
+ memcpy(buf, &ediddata, min((len - i), 4));
+ buf += 4;
+ }
+ }
+
+ return true;
+}
+
+static bool ast_init_dvo(struct ast_device *ast)
+{
+ u8 jreg;
+ u32 scu02c;
+
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+
+ jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
+ if (!(jreg & 0x80)) {
+ u32 scu008;
+
+ /* Init SCU DVO Settings */
+
+ scu008 = ast_mindwm(ast, AST_REG_SCU008);
+ scu008 &= 0xfffff8ff;
+ scu008 |= 0x00000500; /* delay phase */
+ ast_moutdwm(ast, AST_REG_SCU008, scu008);
+
+ if (IS_AST_GEN4(ast)) {
+ u32 scu084, scu088, scu090;
+
+ scu084 = ast_mindwm(ast, AST_REG_SCU084);
+ scu084 |= 0xfffe0000; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU084, scu084);
+
+ scu088 = ast_mindwm(ast, AST_REG_SCU088);
+ scu088 |= 0x000fffff; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU088, scu088);
+
+ scu090 = ast_mindwm(ast, AST_REG_SCU090);
+ scu090 &= 0xffffffcf;
+ scu090 |= 0x00000020; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU090, scu090);
+ } else { /* AST GEN5+ */
+ u32 scu088, scu08c, scu0a4, scu0a8, scu094;
+
+ scu088 = ast_mindwm(ast, AST_REG_SCU088);
+ scu088 |= 0x30000000; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU088, scu088);
+
+ scu08c = ast_mindwm(ast, AST_REG_SCU08C);
+ scu08c |= 0x000000cf; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU08C, scu08c);
+
+ scu0a4 = ast_mindwm(ast, AST_REG_SCU0A4);
+ scu0a4 |= 0xffff0000; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU0A4, scu0a4);
+
+ scu0a8 = ast_mindwm(ast, AST_REG_SCU0A8);
+ scu0a8 |= 0x0000000f; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU0A8, scu0a8);
+
+ scu094 = ast_mindwm(ast, AST_REG_SCU094);
+ scu094 |= 0x00000002; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU094, scu094);
+ }
+ }
+
+ /* Force to DVO */
+ scu02c = ast_mindwm(ast, AST_REG_SCU02C);
+ scu02c &= 0xfffbffff;
+ ast_moutdwm(ast, AST_REG_SCU02C, scu02c);
+
+ /* Init VGA DVO Settings */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80);
+ return true;
+}
+
+
+static void ast_init_analog(struct ast_device *ast)
+{
+ u32 scu02c;
+
+ /*
+ * Set DAC source to VGA mode in SCU2C via the P2A
+ * bridge.
+ */
+
+ /* Unlock the SCU with the magic password */
+ ast_moutdwm_poll(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY, 0x01);
+
+ /* Clear bits [17:16] of SCU2C */
+ scu02c = ast_mindwm(ast, AST_REG_SCU02C);
+ scu02c &= 0xfffcffff;
+ ast_moutdwm(ast, AST_REG_SCU02C, scu02c);
+
+ /* Disable DVO */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00);
+}
+
+void ast_init_3rdtx(struct ast_device *ast)
+{
+ u8 vgacrd1;
+
+ if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) {
+ vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1,
+ AST_IO_VGACRD1_TX_TYPE_MASK);
+ switch (vgacrd1) {
+ case AST_IO_VGACRD1_TX_SIL164_VBIOS:
+ ast_init_dvo(ast);
+ break;
+ case AST_IO_VGACRD1_TX_DP501_VBIOS:
+ ast_launch_m68k(ast);
+ break;
+ case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW:
+ ast_init_dvo(ast);
+ break;
+ default:
+ if (ast->tx_chip == AST_TX_SIL164)
+ ast_init_dvo(ast);
+ else
+ ast_init_analog(ast);
+ }
+ }
+}
+
+/*
+ * Encoder
+ */
+
+static const struct drm_encoder_funcs ast_dp501_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static void ast_dp501_encoder_helper_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(encoder->dev);
+
+ ast_set_dp501_video_output(ast, 1);
+}
+
+static void ast_dp501_encoder_helper_atomic_disable(struct drm_encoder *encoder,
+ struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(encoder->dev);
+
+ ast_set_dp501_video_output(ast, 0);
+}
+
+static const struct drm_encoder_helper_funcs ast_dp501_encoder_helper_funcs = {
+ .atomic_enable = ast_dp501_encoder_helper_atomic_enable,
+ .atomic_disable = ast_dp501_encoder_helper_atomic_disable,
+};
+
+/*
+ * Connector
+ */
+
+static int ast_dp501_connector_helper_get_modes(struct drm_connector *connector)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ int count;
+
+ if (ast_connector->physical_status == connector_status_connected) {
+ struct ast_device *ast = to_ast_device(connector->dev);
+ const struct drm_edid *drm_edid;
+
+ drm_edid = drm_edid_read_custom(connector, ast_dp512_read_edid_block, ast);
+ drm_edid_connector_update(connector, drm_edid);
+ count = drm_edid_connector_add_modes(connector);
+ drm_edid_free(drm_edid);
+ } else {
+ drm_edid_connector_update(connector, NULL);
+
+ /*
+ * There's no EDID data without a connected monitor. Set BMC-
+ * compatible modes in this case. The XGA default resolution
+ * should work well for all BMCs.
+ */
+ count = drm_add_modes_noedid(connector, 4096, 4096);
+ if (count)
+ drm_set_preferred_mode(connector, 1024, 768);
+ }
+
+ return count;
+}
+
+static int ast_dp501_connector_helper_detect_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ struct ast_device *ast = to_ast_device(connector->dev);
+ enum drm_connector_status status = connector_status_disconnected;
+
+ if (ast_dp501_is_connected(ast))
+ status = connector_status_connected;
+
+ if (status != ast_connector->physical_status)
+ ++connector->epoch_counter;
+ ast_connector->physical_status = status;
+
+ return connector_status_connected;
+}
+
+static const struct drm_connector_helper_funcs ast_dp501_connector_helper_funcs = {
+ .get_modes = ast_dp501_connector_helper_get_modes,
+ .detect_ctx = ast_dp501_connector_helper_detect_ctx,
+};
+
+static const struct drm_connector_funcs ast_dp501_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/*
+ * Output
+ */
+
+int ast_dp501_output_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct drm_crtc *crtc = &ast->crtc;
+ struct drm_encoder *encoder;
+ struct ast_connector *ast_connector;
+ struct drm_connector *connector;
+ int ret;
+
+ /* encoder */
+
+ encoder = &ast->output.dp501.encoder;
+ ret = drm_encoder_init(dev, encoder, &ast_dp501_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ret;
+ drm_encoder_helper_add(encoder, &ast_dp501_encoder_helper_funcs);
+
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ /* connector */
+
+ ast_connector = &ast->output.dp501.connector;
+ connector = &ast_connector->base;
+ ret = drm_connector_init(dev, connector, &ast_dp501_connector_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort);
+ if (ret)
+ return ret;
+ drm_connector_helper_add(connector, &ast_dp501_connector_helper_funcs);
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+
+ ast_connector->physical_status = connector->status;
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
new file mode 100644
index 000000000..ba6cf5fa9
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/aperture.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_shmem.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_module.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "ast_drv.h"
+
+static int ast_modeset = -1;
+
+MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
+module_param_named(modeset, ast_modeset, int, 0400);
+
+/*
+ * Register access
+ */
+
+/* Select R/W segment */
+static void __ast_selseg(void __iomem *regs, u32 r)
+{
+ u32 p2a04, p2a04_base;
+
+ p2a04 = r & AST_REG_P2A04_BASE_MASK;
+ __ast_write32(regs, AST_REG_P2A04, p2a04);
+ __ast_write32(regs, AST_REG_P2A00, AST_REG_P2A00_PROTECTION_KEY);
+
+ do {
+ cpu_relax();
+ p2a04_base = __ast_read32(regs, AST_REG_P2A04);
+ p2a04_base &= AST_REG_P2A04_BASE_MASK;
+ } while (p2a04_base != p2a04);
+}
+
+/* Read within segment */
+static u32 __ast_rdseg32(void __iomem *regs, u32 r)
+{
+ return __ast_read32(regs, AST_REG_P2A_ADDR(r));
+}
+
+/* Write within segment */
+static void __ast_wrseg32(void __iomem *regs, u32 r, u32 v)
+{
+ __ast_write32(regs, AST_REG_P2A_ADDR(r), v);
+}
+
+u32 __ast_mindwm(void __iomem *regs, u32 r)
+{
+ __ast_selseg(regs, r);
+
+ return __ast_rdseg32(regs, r);
+}
+
+void __ast_moutdwm(void __iomem *regs, u32 r, u32 v)
+{
+ __ast_selseg(regs, r);
+ __ast_wrseg32(regs, r, v);
+}
+
+u32 ast_mindwm(struct ast_device *ast, u32 r)
+{
+ return __ast_mindwm(ast->regs, r);
+}
+
+void ast_moutdwm(struct ast_device *ast, u32 r, u32 v)
+{
+ __ast_moutdwm(ast->regs, r, v);
+}
+
+void ast_moutdwm_poll(struct ast_device *ast, u32 r, u32 v, u32 res)
+{
+ void __iomem *regs = ast->regs;
+
+ __ast_selseg(regs, r);
+ __ast_wrseg32(regs, r, v);
+
+ do {
+ cpu_relax();
+ } while (__ast_rdseg32(regs, r) != res);
+}
+
+/*
+ * AST device
+ */
+
+void ast_device_init(struct ast_device *ast,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ const struct ast_device_quirks *quirks)
+{
+ ast->quirks = quirks;
+ ast->chip = chip;
+ ast->config_mode = config_mode;
+ ast->regs = regs;
+ ast->ioregs = ioregs;
+}
+
+void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip)
+{
+ static const char * const info_str[] = {
+ "analog VGA",
+ "Sil164 TMDS transmitter",
+ "DP501 DisplayPort transmitter",
+ "ASPEED DisplayPort transmitter",
+ };
+
+ drm_info(&ast->base, "Using %s\n", info_str[tx_chip]);
+
+ ast->tx_chip = tx_chip;
+}
+
+/*
+ * DRM driver
+ */
+
+DEFINE_DRM_GEM_FOPS(ast_fops);
+
+static const struct drm_driver ast_driver = {
+ .driver_features = DRIVER_ATOMIC |
+ DRIVER_GEM |
+ DRIVER_MODESET,
+
+ .fops = &ast_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+
+ DRM_GEM_SHMEM_DRIVER_OPS,
+ DRM_FBDEV_SHMEM_DRIVER_OPS,
+};
+
+/*
+ * PCI driver
+ */
+
+#define PCI_VENDOR_ASPEED 0x1a03
+
+#define AST_VGA_DEVICE(id, info) { \
+ .class = PCI_BASE_CLASS_DISPLAY << 16, \
+ .class_mask = 0xff0000, \
+ .vendor = PCI_VENDOR_ASPEED, \
+ .device = id, \
+ .subvendor = PCI_ANY_ID, \
+ .subdevice = PCI_ANY_ID, \
+ .driver_data = (unsigned long) info }
+
+static const struct pci_device_id ast_pciidlist[] = {
+ AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL),
+ AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL),
+ {0, 0, 0},
+};
+
+MODULE_DEVICE_TABLE(pci, ast_pciidlist);
+
+static bool ast_is_vga_enabled(void __iomem *ioregs)
+{
+ u8 vgaer = __ast_read8(ioregs, AST_IO_VGAER);
+
+ return vgaer & AST_IO_VGAER_VGA_ENABLE;
+}
+
+static void ast_enable_vga(void __iomem *ioregs)
+{
+ __ast_write8(ioregs, AST_IO_VGAER, AST_IO_VGAER_VGA_ENABLE);
+ __ast_write8(ioregs, AST_IO_VGAMR_W, AST_IO_VGAMR_IOSEL);
+}
+
+/*
+ * Run this function as part of the HW device cleanup; not
+ * when the DRM device gets released.
+ */
+static void ast_enable_mmio_release(void *data)
+{
+ void __iomem *ioregs = (void __force __iomem *)data;
+
+ /* enable standard VGA decode */
+ __ast_write8_i(ioregs, AST_IO_VGACRI, 0xa1, AST_IO_VGACRA1_MMIO_ENABLED);
+}
+
+static int ast_enable_mmio(struct device *dev, void __iomem *ioregs)
+{
+ void *data = (void __force *)ioregs;
+
+ __ast_write8_i(ioregs, AST_IO_VGACRI, 0xa1,
+ AST_IO_VGACRA1_MMIO_ENABLED |
+ AST_IO_VGACRA1_VGAIO_DISABLED);
+
+ return devm_add_action_or_reset(dev, ast_enable_mmio_release, data);
+}
+
+static void ast_open_key(void __iomem *ioregs)
+{
+ __ast_write8_i(ioregs, AST_IO_VGACRI, 0x80, AST_IO_VGACR80_PASSWORD);
+}
+
+static int ast_detect_chip(struct pci_dev *pdev,
+ void __iomem *regs, void __iomem *ioregs,
+ enum ast_chip *chip_out,
+ enum ast_config_mode *config_mode_out)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ enum ast_config_mode config_mode = ast_use_defaults;
+ uint32_t scu_rev = 0xffffffff;
+ enum ast_chip chip;
+ u32 data, p2a04, scu07c;
+ u8 vgacrd0, vgacrd1;
+
+ /*
+ * Find configuration mode and read SCU revision
+ */
+
+ /* Check if we have device-tree properties */
+ if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", &data)) {
+ /* We do, disable P2A access */
+ config_mode = ast_use_dt;
+ scu_rev = data;
+ } else if (pdev->device == PCI_CHIP_AST2000) { // Not all families have a P2A bridge
+ /*
+ * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge
+ * is disabled. We force using P2A if VGA only mode bit
+ * is set D[7]
+ */
+ vgacrd0 = __ast_read8_i(ioregs, AST_IO_VGACRI, 0xd0);
+ vgacrd1 = __ast_read8_i(ioregs, AST_IO_VGACRI, 0xd1);
+ if (!(vgacrd0 & 0x80) || !(vgacrd1 & 0x10)) {
+
+ /*
+ * We have a P2A bridge and it is enabled.
+ */
+
+ /* Patch AST2500/AST2510 */
+ if ((pdev->revision & 0xf0) == 0x40) {
+ if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK))
+ ast_2500_patch_ahb(regs);
+ }
+
+ /* Double check that it's actually working */
+ p2a04 = __ast_read32(regs, AST_REG_P2A04);
+ if (p2a04 != 0xffffffff && p2a04 != 0x00000000) {
+ config_mode = ast_use_p2a;
+
+ /* Read SCU7C (silicon revision register) */
+ scu07c = __ast_mindwm(regs, AST_REG_SCU07C);
+ scu_rev = scu07c & AST_REG_SCU07C_CHIP_BONDING_MASK;
+ }
+ }
+ }
+
+ switch (config_mode) {
+ case ast_use_defaults:
+ dev_info(dev, "Using default configuration\n");
+ break;
+ case ast_use_dt:
+ dev_info(dev, "Using device-tree for configuration\n");
+ break;
+ case ast_use_p2a:
+ dev_info(dev, "Using P2A bridge for configuration\n");
+ break;
+ }
+
+ /*
+ * Identify chipset
+ */
+
+ if (pdev->revision >= 0x50) {
+ chip = AST2600;
+ dev_info(dev, "AST 2600 detected\n");
+ } else if (pdev->revision >= 0x40) {
+ switch (scu_rev & 0x300) {
+ case 0x0100:
+ chip = AST2510;
+ dev_info(dev, "AST 2510 detected\n");
+ break;
+ default:
+ chip = AST2500;
+ dev_info(dev, "AST 2500 detected\n");
+ break;
+ }
+ } else if (pdev->revision >= 0x30) {
+ switch (scu_rev & 0x300) {
+ case 0x0100:
+ chip = AST1400;
+ dev_info(dev, "AST 1400 detected\n");
+ break;
+ default:
+ chip = AST2400;
+ dev_info(dev, "AST 2400 detected\n");
+ break;
+ }
+ } else if (pdev->revision >= 0x20) {
+ switch (scu_rev & 0x300) {
+ case 0x0000:
+ chip = AST1300;
+ dev_info(dev, "AST 1300 detected\n");
+ break;
+ default:
+ chip = AST2300;
+ dev_info(dev, "AST 2300 detected\n");
+ break;
+ }
+ } else if (pdev->revision >= 0x10) {
+ switch (scu_rev & 0x0300) {
+ case 0x0200:
+ chip = AST1100;
+ dev_info(dev, "AST 1100 detected\n");
+ break;
+ case 0x0100:
+ chip = AST2200;
+ dev_info(dev, "AST 2200 detected\n");
+ break;
+ case 0x0000:
+ chip = AST2150;
+ dev_info(dev, "AST 2150 detected\n");
+ break;
+ default:
+ chip = AST2100;
+ dev_info(dev, "AST 2100 detected\n");
+ break;
+ }
+ } else {
+ chip = AST2000;
+ dev_info(dev, "AST 2000 detected\n");
+ }
+
+ *chip_out = chip;
+ *config_mode_out = config_mode;
+
+ return __AST_CHIP_GEN(chip);
+}
+
+static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct device *dev = &pdev->dev;
+ int ret;
+ void __iomem *regs;
+ void __iomem *ioregs;
+ enum ast_config_mode config_mode;
+ enum ast_chip chip;
+ unsigned int chip_gen;
+ struct drm_device *drm;
+ bool need_post = false;
+
+ ret = aperture_remove_conflicting_pci_devices(pdev, ast_driver.name);
+ if (ret)
+ return ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ regs = pcim_iomap_region(pdev, 1, "ast");
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ if (pdev->revision >= 0x40) {
+ /*
+ * On AST2500 and later models, MMIO is enabled by
+ * default. Adopt it to be compatible with ARM.
+ */
+ resource_size_t len = pci_resource_len(pdev, 1);
+
+ if (len < AST_IO_MM_OFFSET)
+ return -EIO;
+ if ((len - AST_IO_MM_OFFSET) < AST_IO_MM_LENGTH)
+ return -EIO;
+ ioregs = regs + AST_IO_MM_OFFSET;
+ } else if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
+ /*
+ * Map I/O registers if we have a PCI BAR for I/O.
+ */
+ resource_size_t len = pci_resource_len(pdev, 2);
+
+ if (len < AST_IO_MM_LENGTH)
+ return -EIO;
+ ioregs = pcim_iomap_region(pdev, 2, "ast");
+ if (IS_ERR(ioregs))
+ return PTR_ERR(ioregs);
+ } else {
+ /*
+ * Anything else is best effort.
+ */
+ resource_size_t len = pci_resource_len(pdev, 1);
+
+ if (len < AST_IO_MM_OFFSET)
+ return -EIO;
+ if ((len - AST_IO_MM_OFFSET) < AST_IO_MM_LENGTH)
+ return -EIO;
+ ioregs = regs + AST_IO_MM_OFFSET;
+
+ dev_info(dev, "Platform has no I/O space, using MMIO\n");
+ }
+
+ if (!ast_is_vga_enabled(ioregs)) {
+ dev_info(dev, "VGA not enabled on entry, requesting chip POST\n");
+ need_post = true;
+ }
+
+ /*
+ * If VGA isn't enabled, we need to enable now or subsequent
+ * access to the scratch registers will fail.
+ */
+ if (need_post)
+ ast_enable_vga(ioregs);
+ /* Enable extended register access */
+ ast_open_key(ioregs);
+
+ ret = ast_enable_mmio(dev, ioregs);
+ if (ret)
+ return ret;
+
+ ret = ast_detect_chip(pdev, regs, ioregs, &chip, &config_mode);
+ if (ret < 0)
+ return ret;
+ chip_gen = ret;
+
+ switch (chip_gen) {
+ case 1:
+ drm = ast_2000_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 2:
+ drm = ast_2100_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 3:
+ drm = ast_2200_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 4:
+ drm = ast_2300_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 5:
+ drm = ast_2400_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 6:
+ drm = ast_2500_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 7:
+ drm = ast_2600_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ default:
+ dev_err(&pdev->dev, "Gen%d not supported\n", chip_gen);
+ return -ENODEV;
+ }
+ if (IS_ERR(drm))
+ return PTR_ERR(drm);
+ pci_set_drvdata(pdev, drm);
+
+ ret = drm_dev_register(drm, ent->driver_data);
+ if (ret)
+ return ret;
+
+ drm_client_setup(drm, NULL);
+
+ return 0;
+}
+
+static void ast_pci_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ drm_dev_unregister(dev);
+ drm_atomic_helper_shutdown(dev);
+}
+
+static void ast_pci_shutdown(struct pci_dev *pdev)
+{
+ drm_atomic_helper_shutdown(pci_get_drvdata(pdev));
+}
+
+static int ast_drm_freeze(struct drm_device *dev)
+{
+ int error;
+
+ error = drm_mode_config_helper_suspend(dev);
+ if (error)
+ return error;
+ pci_save_state(to_pci_dev(dev->dev));
+ return 0;
+}
+
+static int ast_drm_thaw(struct drm_device *dev)
+{
+ struct ast_device *ast = to_ast_device(dev);
+ int ret;
+
+ ast_enable_vga(ast->ioregs);
+ ast_open_key(ast->ioregs);
+ ast_enable_mmio(dev->dev, ast->ioregs);
+
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ret;
+
+ return drm_mode_config_helper_resume(dev);
+}
+
+static int ast_drm_resume(struct drm_device *dev)
+{
+ if (pci_enable_device(to_pci_dev(dev->dev)))
+ return -EIO;
+
+ return ast_drm_thaw(dev);
+}
+
+static int ast_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+ int error;
+
+ error = ast_drm_freeze(ddev);
+ if (error)
+ return error;
+
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ return 0;
+}
+
+static int ast_pm_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+ return ast_drm_resume(ddev);
+}
+
+static int ast_pm_freeze(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+ return ast_drm_freeze(ddev);
+}
+
+static int ast_pm_thaw(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+ return ast_drm_thaw(ddev);
+}
+
+static int ast_pm_poweroff(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *ddev = pci_get_drvdata(pdev);
+
+ return ast_drm_freeze(ddev);
+}
+
+static const struct dev_pm_ops ast_pm_ops = {
+ .suspend = ast_pm_suspend,
+ .resume = ast_pm_resume,
+ .freeze = ast_pm_freeze,
+ .thaw = ast_pm_thaw,
+ .poweroff = ast_pm_poweroff,
+ .restore = ast_pm_resume,
+};
+
+static struct pci_driver ast_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = ast_pciidlist,
+ .probe = ast_pci_probe,
+ .remove = ast_pci_remove,
+ .shutdown = ast_pci_shutdown,
+ .driver.pm = &ast_pm_ops,
+};
+
+drm_module_pci_driver_if_modeset(ast_pci_driver, ast_modeset);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
new file mode 100644
index 000000000..4f221b848
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+#ifndef __AST_DRV_H__
+#define __AST_DRV_H__
+
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_mode.h>
+#include <drm/drm_framebuffer.h>
+
+#include "ast_reg.h"
+
+struct ast_vbios_enhtable;
+
+#define DRIVER_AUTHOR "Dave Airlie"
+
+#define DRIVER_NAME "ast"
+#define DRIVER_DESC "AST"
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 1
+#define DRIVER_PATCHLEVEL 0
+
+#define PCI_CHIP_AST2000 0x2000
+#define PCI_CHIP_AST2100 0x2010
+
+#define __AST_CHIP(__gen, __index) ((__gen) << 16 | (__index))
+
+enum ast_chip {
+ /* 1st gen */
+ AST1000 = __AST_CHIP(1, 0), // unused
+ AST2000 = __AST_CHIP(1, 1),
+ /* 2nd gen */
+ AST1100 = __AST_CHIP(2, 0),
+ AST2100 = __AST_CHIP(2, 1),
+ AST2050 = __AST_CHIP(2, 2), // unused
+ /* 3rd gen */
+ AST2200 = __AST_CHIP(3, 0),
+ AST2150 = __AST_CHIP(3, 1),
+ /* 4th gen */
+ AST2300 = __AST_CHIP(4, 0),
+ AST1300 = __AST_CHIP(4, 1),
+ AST1050 = __AST_CHIP(4, 2), // unused
+ /* 5th gen */
+ AST2400 = __AST_CHIP(5, 0),
+ AST1400 = __AST_CHIP(5, 1),
+ AST1250 = __AST_CHIP(5, 2), // unused
+ /* 6th gen */
+ AST2500 = __AST_CHIP(6, 0),
+ AST2510 = __AST_CHIP(6, 1),
+ AST2520 = __AST_CHIP(6, 2), // unused
+ /* 7th gen */
+ AST2600 = __AST_CHIP(7, 0),
+ AST2620 = __AST_CHIP(7, 1), // unused
+};
+
+#define __AST_CHIP_GEN(__chip) (((unsigned long)(__chip)) >> 16)
+
+enum ast_tx_chip {
+ AST_TX_NONE,
+ AST_TX_SIL164,
+ AST_TX_DP501,
+ AST_TX_ASTDP,
+};
+
+enum ast_config_mode {
+ ast_use_p2a,
+ ast_use_dt,
+ ast_use_defaults
+};
+
+enum ast_dram_layout {
+ AST_DRAM_512Mx16 = 0,
+ AST_DRAM_1Gx16 = 1,
+ AST_DRAM_512Mx32 = 2,
+ AST_DRAM_1Gx32 = 3,
+ AST_DRAM_2Gx16 = 6,
+ AST_DRAM_4Gx16 = 7,
+ AST_DRAM_8Gx16 = 8,
+};
+
+/*
+ * Hardware cursor
+ */
+
+#define AST_MAX_HWC_WIDTH 64
+#define AST_MAX_HWC_HEIGHT 64
+#define AST_HWC_PITCH (AST_MAX_HWC_WIDTH * SZ_2)
+#define AST_HWC_SIZE (AST_MAX_HWC_HEIGHT * AST_HWC_PITCH)
+
+/*
+ * Planes
+ */
+
+struct ast_plane {
+ struct drm_plane base;
+
+ u64 offset;
+ unsigned long size;
+};
+
+static inline struct ast_plane *to_ast_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct ast_plane, base);
+}
+
+struct ast_cursor_plane {
+ struct ast_plane base;
+
+ u8 argb4444[AST_HWC_SIZE];
+};
+
+static inline struct ast_cursor_plane *to_ast_cursor_plane(struct drm_plane *plane)
+{
+ return container_of(to_ast_plane(plane), struct ast_cursor_plane, base);
+}
+
+/*
+ * Connector
+ */
+
+struct ast_connector {
+ struct drm_connector base;
+
+ enum drm_connector_status physical_status;
+};
+
+static inline struct ast_connector *
+to_ast_connector(struct drm_connector *connector)
+{
+ return container_of(connector, struct ast_connector, base);
+}
+
+/*
+ * Device
+ */
+
+struct ast_device_quirks {
+ /*
+ * CRTC memory request threshold
+ */
+ unsigned char crtc_mem_req_threshold_low;
+ unsigned char crtc_mem_req_threshold_high;
+
+ /*
+ * Adjust hsync values to load next scanline early. Signalled
+ * by AST2500PreCatchCRT in VBIOS mode flags.
+ */
+ bool crtc_hsync_precatch_needed;
+
+ /*
+ * Workaround for modes with HSync Time that is not a multiple
+ * of 8 (e.g., 1920x1080@60Hz, HSync +44 pixels).
+ */
+ bool crtc_hsync_add4_needed;
+};
+
+struct ast_device {
+ struct drm_device base;
+
+ const struct ast_device_quirks *quirks;
+
+ void __iomem *regs;
+ void __iomem *ioregs;
+ void __iomem *dp501_fw_buf;
+
+ enum ast_config_mode config_mode;
+ enum ast_chip chip;
+
+ const struct ast_vbios_dclk_info *dclk_table;
+
+ void __iomem *vram;
+ unsigned long vram_base;
+ unsigned long vram_size;
+
+ struct mutex modeset_lock; /* Protects access to modeset I/O registers in ioregs */
+
+ enum ast_tx_chip tx_chip;
+
+ struct ast_plane primary_plane;
+ struct ast_cursor_plane cursor_plane;
+ struct drm_crtc crtc;
+ union {
+ struct {
+ struct drm_encoder encoder;
+ struct ast_connector connector;
+ } vga;
+ struct {
+ struct drm_encoder encoder;
+ struct ast_connector connector;
+ } sil164;
+ struct {
+ struct drm_encoder encoder;
+ struct ast_connector connector;
+ } dp501;
+ struct {
+ struct drm_encoder encoder;
+ struct ast_connector connector;
+ } astdp;
+ } output;
+
+ bool support_wsxga_p; /* 1680x1050 */
+ bool support_fullhd; /* 1920x1080 */
+ bool support_wuxga; /* 1920x1200 */
+
+ u8 *dp501_fw_addr;
+ const struct firmware *dp501_fw; /* dp501 fw */
+};
+
+static inline struct ast_device *to_ast_device(struct drm_device *dev)
+{
+ return container_of(dev, struct ast_device, base);
+}
+
+static inline unsigned long __ast_gen(struct ast_device *ast)
+{
+ return __AST_CHIP_GEN(ast->chip);
+}
+#define AST_GEN(__ast) __ast_gen(__ast)
+
+static inline bool __ast_gen_is_eq(struct ast_device *ast, unsigned long gen)
+{
+ return __ast_gen(ast) == gen;
+}
+#define IS_AST_GEN1(__ast) __ast_gen_is_eq(__ast, 1)
+#define IS_AST_GEN2(__ast) __ast_gen_is_eq(__ast, 2)
+#define IS_AST_GEN3(__ast) __ast_gen_is_eq(__ast, 3)
+#define IS_AST_GEN4(__ast) __ast_gen_is_eq(__ast, 4)
+#define IS_AST_GEN5(__ast) __ast_gen_is_eq(__ast, 5)
+#define IS_AST_GEN6(__ast) __ast_gen_is_eq(__ast, 6)
+#define IS_AST_GEN7(__ast) __ast_gen_is_eq(__ast, 7)
+
+/*
+ * MMIO access
+ */
+
+static inline u8 __ast_read8(const void __iomem *addr, u32 reg)
+{
+ return ioread8(addr + reg);
+}
+
+static inline void __ast_write8(void __iomem *addr, u32 reg, u8 val)
+{
+ iowrite8(val, addr + reg);
+}
+
+static inline u8 __ast_read8_i(void __iomem *addr, u32 reg, u8 index)
+{
+ __ast_write8(addr, reg, index);
+ return __ast_read8(addr, reg + 1);
+}
+
+static inline u8 __ast_read8_i_masked(void __iomem *addr, u32 reg, u8 index, u8 read_mask)
+{
+ u8 val = __ast_read8_i(addr, reg, index);
+
+ return val & read_mask;
+}
+
+static inline void __ast_write8_i(void __iomem *addr, u32 reg, u8 index, u8 val)
+{
+ __ast_write8(addr, reg, index);
+ __ast_write8(addr, reg + 1, val);
+}
+
+static inline void __ast_write8_i_masked(void __iomem *addr, u32 reg, u8 index, u8 preserve_mask,
+ u8 val)
+{
+ u8 tmp = __ast_read8_i_masked(addr, reg, index, preserve_mask);
+
+ val &= ~preserve_mask;
+ __ast_write8_i(addr, reg, index, tmp | val);
+}
+
+static inline u8 ast_io_read8(struct ast_device *ast, u32 reg)
+{
+ return __ast_read8(ast->ioregs, reg);
+}
+
+static inline void ast_io_write8(struct ast_device *ast, u32 reg, u8 val)
+{
+ __ast_write8(ast->ioregs, reg, val);
+}
+
+static inline u8 ast_get_index_reg(struct ast_device *ast, u32 base, u8 index)
+{
+ return __ast_read8_i(ast->ioregs, base, index);
+}
+
+static inline u8 ast_get_index_reg_mask(struct ast_device *ast, u32 base, u8 index,
+ u8 preserve_mask)
+{
+ return __ast_read8_i_masked(ast->ioregs, base, index, preserve_mask);
+}
+
+static inline void ast_set_index_reg(struct ast_device *ast, u32 base, u8 index, u8 val)
+{
+ __ast_write8_i(ast->ioregs, base, index, val);
+}
+
+static inline void ast_set_index_reg_mask(struct ast_device *ast, u32 base, u8 index,
+ u8 preserve_mask, u8 val)
+{
+ __ast_write8_i_masked(ast->ioregs, base, index, preserve_mask, val);
+}
+
+/*
+ * Register access
+ */
+
+static inline u32 __ast_read32(const void __iomem *addr, u32 reg)
+{
+ return ioread32(addr + reg);
+}
+
+static inline void __ast_write32(void __iomem *addr, u32 reg, u32 val)
+{
+ iowrite32(val, addr + reg);
+}
+
+static inline u32 ast_read32(struct ast_device *ast, u32 reg)
+{
+ return __ast_read32(ast->regs, reg);
+}
+
+static inline void ast_write32(struct ast_device *ast, u32 reg, u32 val)
+{
+ __ast_write32(ast->regs, reg, val);
+}
+
+u32 __ast_mindwm(void __iomem *regs, u32 r);
+void __ast_moutdwm(void __iomem *regs, u32 r, u32 v);
+u32 ast_mindwm(struct ast_device *ast, u32 r);
+void ast_moutdwm(struct ast_device *ast, u32 r, u32 v);
+void ast_moutdwm_poll(struct ast_device *ast, u32 r, u32 v, u32 res);
+
+/*
+ * VBIOS
+ */
+
+struct ast_vbios_stdtable {
+ u8 misc;
+ u8 seq[4];
+ u8 crtc[25];
+ u8 ar[20];
+ u8 gr[9];
+};
+
+struct ast_vbios_dclk_info {
+ u8 param1;
+ u8 param2;
+ u8 param3;
+};
+
+struct ast_crtc_state {
+ struct drm_crtc_state base;
+
+ /* Last known format of primary plane */
+ const struct drm_format_info *format;
+
+ const struct ast_vbios_stdtable *std_table;
+ const struct ast_vbios_enhtable *vmode;
+};
+
+#define to_ast_crtc_state(state) container_of(state, struct ast_crtc_state, base)
+
+#define AST_MM_ALIGN_SHIFT 4
+#define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1)
+
+#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4)
+#define AST_DP501_FW_VERSION_1 BIT(4)
+#define AST_DP501_PNP_CONNECTED BIT(1)
+
+#define AST_DP501_DEFAULT_DCLK 65
+
+#define AST_DP501_GBL_VERSION 0xf000
+#define AST_DP501_PNPMONITOR 0xf010
+#define AST_DP501_LINKRATE 0xf014
+#define AST_DP501_EDID_DATA 0xf020
+
+/*
+ * ASTDP resoultion table:
+ * EX: ASTDP_A_B_C:
+ * A: Resolution
+ * B: Refresh Rate
+ * C: Misc information, such as CVT, Reduce Blanked
+ */
+#define ASTDP_640x480_60 0x00
+#define ASTDP_640x480_72 0x01
+#define ASTDP_640x480_75 0x02
+#define ASTDP_640x480_85 0x03
+#define ASTDP_800x600_56 0x04
+#define ASTDP_800x600_60 0x05
+#define ASTDP_800x600_72 0x06
+#define ASTDP_800x600_75 0x07
+#define ASTDP_800x600_85 0x08
+#define ASTDP_1024x768_60 0x09
+#define ASTDP_1024x768_70 0x0A
+#define ASTDP_1024x768_75 0x0B
+#define ASTDP_1024x768_85 0x0C
+#define ASTDP_1280x1024_60 0x0D
+#define ASTDP_1280x1024_75 0x0E
+#define ASTDP_1280x1024_85 0x0F
+#define ASTDP_1600x1200_60 0x10
+#define ASTDP_320x240_60 0x11
+#define ASTDP_400x300_60 0x12
+#define ASTDP_512x384_60 0x13
+#define ASTDP_1920x1200_60 0x14
+#define ASTDP_1920x1080_60 0x15
+#define ASTDP_1280x800_60 0x16
+#define ASTDP_1280x800_60_RB 0x17
+#define ASTDP_1440x900_60 0x18
+#define ASTDP_1440x900_60_RB 0x19
+#define ASTDP_1680x1050_60 0x1A
+#define ASTDP_1680x1050_60_RB 0x1B
+#define ASTDP_1600x900_60 0x1C
+#define ASTDP_1600x900_60_RB 0x1D
+#define ASTDP_1366x768_60 0x1E
+#define ASTDP_1152x864_75 0x1F
+
+int ast_mm_init(struct ast_device *ast);
+
+/* ast_drv.c */
+void ast_device_init(struct ast_device *ast,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ const struct ast_device_quirks *quirks);
+void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip);
+
+/* ast_2000.c */
+int ast_2000_post(struct ast_device *ast);
+extern const struct ast_vbios_dclk_info ast_2000_dclk_table[];
+void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post);
+struct drm_device *ast_2000_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2100.c */
+int ast_2100_post(struct ast_device *ast);
+bool __ast_2100_detect_wsxga_p(struct ast_device *ast);
+bool __ast_2100_detect_wuxga(struct ast_device *ast);
+struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2200.c */
+struct drm_device *ast_2200_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2300.c */
+int ast_2300_post(struct ast_device *ast);
+void ast_2300_detect_tx_chip(struct ast_device *ast);
+struct drm_device *ast_2300_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2400.c */
+struct drm_device *ast_2400_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2500.c */
+void ast_2500_patch_ahb(void __iomem *regs);
+int ast_2500_post(struct ast_device *ast);
+extern const struct ast_vbios_dclk_info ast_2500_dclk_table[];
+struct drm_device *ast_2500_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2600.c */
+int ast_2600_post(struct ast_device *ast);
+struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast post */
+int ast_post_gpu(struct ast_device *ast);
+
+int ast_vga_output_init(struct ast_device *ast);
+int ast_sil164_output_init(struct ast_device *ast);
+
+/* ast_cursor.c */
+long ast_cursor_vram_offset(struct ast_device *ast);
+int ast_cursor_plane_init(struct ast_device *ast);
+
+/* ast dp501 */
+bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size);
+void ast_init_3rdtx(struct ast_device *ast);
+int ast_dp501_output_init(struct ast_device *ast);
+
+/* aspeed DP */
+int ast_dp_launch(struct ast_device *ast);
+int ast_astdp_output_init(struct ast_device *ast);
+
+/* ast_mode.c */
+int ast_mode_config_init(struct ast_device *ast);
+int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane,
+ u64 offset, unsigned long size,
+ uint32_t possible_crtcs,
+ const struct drm_plane_funcs *funcs,
+ const uint32_t *formats, unsigned int format_count,
+ const uint64_t *format_modifiers,
+ enum drm_plane_type type);
+void __iomem *ast_plane_vaddr(struct ast_plane *ast);
+
+#endif
diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c
new file mode 100644
index 000000000..0bc140319
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_mm.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+
+static u32 ast_get_vram_size(struct ast_device *ast)
+{
+ u32 vram_size;
+ u8 vgacr99, vgacraa;
+
+ vgacraa = ast_get_index_reg(ast, AST_IO_VGACRI, 0xaa);
+ switch (vgacraa & AST_IO_VGACRAA_VGAMEM_SIZE_MASK) {
+ case 0:
+ vram_size = SZ_8M;
+ break;
+ case 1:
+ vram_size = SZ_16M;
+ break;
+ case 2:
+ vram_size = SZ_32M;
+ break;
+ case 3:
+ vram_size = SZ_64M;
+ break;
+ }
+
+ vgacr99 = ast_get_index_reg(ast, AST_IO_VGACRI, 0x99);
+ switch (vgacr99 & AST_IO_VGACR99_VGAMEM_RSRV_MASK) {
+ case 1:
+ vram_size -= SZ_1M;
+ break;
+ case 2:
+ vram_size -= SZ_2M;
+ break;
+ case 3:
+ vram_size -= SZ_4M;
+ break;
+ }
+
+ return vram_size;
+}
+
+int ast_mm_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+ resource_size_t base, size;
+ u32 vram_size;
+
+ base = pci_resource_start(pdev, 0);
+ size = pci_resource_len(pdev, 0);
+
+ /* Don't fail on errors, but performance might be reduced. */
+ devm_arch_io_reserve_memtype_wc(dev->dev, base, size);
+ devm_arch_phys_wc_add(dev->dev, base, size);
+
+ vram_size = ast_get_vram_size(ast);
+
+ ast->vram = devm_ioremap_wc(dev->dev, base, vram_size);
+ if (!ast->vram)
+ return -ENOMEM;
+
+ ast->vram_base = base;
+ ast->vram_size = vram_size;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
new file mode 100644
index 000000000..d5ed8c5c7
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -0,0 +1,1078 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ * Parts based on xf86-video-ast
+ * Copyright (c) 2005 ASPEED Technology Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_color_mgmt.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_panic.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "ast_drv.h"
+#include "ast_tables.h"
+#include "ast_vbios.h"
+
+#define AST_LUT_SIZE 256
+
+#define AST_PRIMARY_PLANE_MAX_OFFSET (BIT(16) - 1)
+
+static unsigned long ast_fb_vram_offset(void)
+{
+ return 0; // with shmem, the primary plane is always at offset 0
+}
+
+static unsigned long ast_fb_vram_size(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ unsigned long offset = ast_fb_vram_offset(); // starts at offset
+ long cursor_offset = ast_cursor_vram_offset(ast); // ends at cursor offset
+
+ if (cursor_offset < 0)
+ cursor_offset = ast->vram_size; // no cursor; it's all ours
+ if (drm_WARN_ON_ONCE(dev, offset > cursor_offset))
+ return 0; // cannot legally happen; signal error
+ return cursor_offset - offset;
+}
+
+static void ast_set_gamma_lut(struct drm_crtc *crtc, unsigned int index,
+ u16 red, u16 green, u16 blue)
+{
+ struct drm_device *dev = crtc->dev;
+ struct ast_device *ast = to_ast_device(dev);
+ u8 i8 = index & 0xff;
+ u8 r8 = red >> 8;
+ u8 g8 = green >> 8;
+ u8 b8 = blue >> 8;
+
+ if (drm_WARN_ON_ONCE(dev, index != i8))
+ return; /* driver bug */
+
+ ast_io_write8(ast, AST_IO_VGADWR, i8);
+ ast_io_read8(ast, AST_IO_VGASRI);
+ ast_io_write8(ast, AST_IO_VGAPDR, r8);
+ ast_io_read8(ast, AST_IO_VGASRI);
+ ast_io_write8(ast, AST_IO_VGAPDR, g8);
+ ast_io_read8(ast, AST_IO_VGASRI);
+ ast_io_write8(ast, AST_IO_VGAPDR, b8);
+ ast_io_read8(ast, AST_IO_VGASRI);
+}
+
+static void ast_crtc_fill_gamma(struct ast_device *ast,
+ const struct drm_format_info *format)
+{
+ struct drm_crtc *crtc = &ast->crtc;
+
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ /* gamma table is used as color palette */
+ drm_crtc_fill_palette_8(crtc, ast_set_gamma_lut);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_RGB565:
+ /* also uses 24-bit gamma correction on high-color modes */
+ fallthrough;
+ case DRM_FORMAT_XRGB8888:
+ drm_crtc_fill_gamma_888(crtc, ast_set_gamma_lut);
+ break;
+ default:
+ drm_warn_once(&ast->base, "Unsupported format %p4cc for gamma correction\n",
+ &format->format);
+ break;
+ }
+}
+
+static void ast_crtc_load_gamma(struct ast_device *ast,
+ const struct drm_format_info *format,
+ struct drm_color_lut *lut)
+{
+ struct drm_crtc *crtc = &ast->crtc;
+
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ /* gamma table is used as color palette */
+ drm_crtc_load_palette_8(crtc, lut, ast_set_gamma_lut);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_RGB565:
+ /* also uses 24-bit gamma correction on high-color modes */
+ fallthrough;
+ case DRM_FORMAT_XRGB8888:
+ drm_crtc_load_gamma_888(crtc, lut, ast_set_gamma_lut);
+ break;
+ default:
+ drm_warn_once(&ast->base, "Unsupported format %p4cc for gamma correction\n",
+ &format->format);
+ break;
+ }
+}
+
+static void ast_set_vbios_color_reg(struct ast_device *ast,
+ const struct drm_format_info *format,
+ const struct ast_vbios_enhtable *vmode)
+{
+ u8 vgacr8c = 0x00;
+ u8 vgacr92 = 0x00;
+
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_VGA;
+ vgacr92 = 8;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_15_BPP;
+ vgacr92 = 15;
+ break;
+ case DRM_FORMAT_RGB565:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_16_BPP;
+ vgacr92 = 16;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_32_BPP;
+ vgacr92 = 32;
+ break;
+ }
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x8c, vgacr8c);
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0x00);
+
+ if (vmode->flags & NewModeInfo) {
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, AST_IO_VGACR91_PASSWORD);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x92, vgacr92);
+ }
+}
+
+static void ast_set_vbios_mode_reg(struct ast_device *ast,
+ const struct drm_display_mode *adjusted_mode,
+ const struct ast_vbios_enhtable *vmode)
+{
+ u32 refresh_rate_index, mode_id;
+
+ refresh_rate_index = vmode->refresh_rate_index;
+ mode_id = vmode->mode_id;
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x8d, refresh_rate_index & 0xff);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x8e, mode_id & 0xff);
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0x00);
+
+ if (vmode->flags & NewModeInfo) {
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, AST_IO_VGACR91_PASSWORD);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x93, adjusted_mode->clock / 1000);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x94, adjusted_mode->crtc_hdisplay);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x95, adjusted_mode->crtc_hdisplay >> 8);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x96, adjusted_mode->crtc_vdisplay);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x97, adjusted_mode->crtc_vdisplay >> 8);
+ }
+}
+
+static void ast_set_std_reg(struct ast_device *ast,
+ struct drm_display_mode *mode,
+ const struct ast_vbios_stdtable *stdtable)
+{
+ u32 i;
+ u8 jreg;
+
+ jreg = stdtable->misc;
+ ast_io_write8(ast, AST_IO_VGAMR_W, jreg);
+
+ /* Set SEQ; except Screen Disable field */
+ ast_set_index_reg(ast, AST_IO_VGASRI, 0x00, 0x03);
+ ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0x20, stdtable->seq[0]);
+ for (i = 1; i < 4; i++) {
+ jreg = stdtable->seq[i];
+ ast_set_index_reg(ast, AST_IO_VGASRI, (i + 1), jreg);
+ }
+
+ /* Set CRTC; except base address and offset */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00);
+ for (i = 0; i < 12; i++)
+ ast_set_index_reg(ast, AST_IO_VGACRI, i, stdtable->crtc[i]);
+ for (i = 14; i < 19; i++)
+ ast_set_index_reg(ast, AST_IO_VGACRI, i, stdtable->crtc[i]);
+ for (i = 20; i < 25; i++)
+ ast_set_index_reg(ast, AST_IO_VGACRI, i, stdtable->crtc[i]);
+
+ /* set AR */
+ jreg = ast_io_read8(ast, AST_IO_VGAIR1_R);
+ for (i = 0; i < 20; i++) {
+ jreg = stdtable->ar[i];
+ ast_io_write8(ast, AST_IO_VGAARI_W, (u8)i);
+ ast_io_write8(ast, AST_IO_VGAARI_W, jreg);
+ }
+ ast_io_write8(ast, AST_IO_VGAARI_W, 0x14);
+ ast_io_write8(ast, AST_IO_VGAARI_W, 0x00);
+
+ jreg = ast_io_read8(ast, AST_IO_VGAIR1_R);
+ ast_io_write8(ast, AST_IO_VGAARI_W, 0x20);
+
+ /* Set GR */
+ for (i = 0; i < 9; i++)
+ ast_set_index_reg(ast, AST_IO_VGAGRI, i, stdtable->gr[i]);
+}
+
+static void ast_set_crtc_reg(struct ast_device *ast, struct drm_display_mode *mode,
+ const struct ast_vbios_enhtable *vmode)
+{
+ u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0;
+ u16 temp;
+ unsigned char crtc_hsync_precatch = 0;
+
+ if (ast->quirks->crtc_hsync_precatch_needed && (vmode->flags & AST2500PreCatchCRT))
+ crtc_hsync_precatch = 40;
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00);
+
+ temp = (mode->crtc_htotal >> 3) - 5;
+ if (temp & 0x100)
+ jregAC |= 0x01; /* HT D[8] */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x00, 0x00, temp);
+
+ temp = (mode->crtc_hdisplay >> 3) - 1;
+ if (temp & 0x100)
+ jregAC |= 0x04; /* HDE D[8] */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x01, 0x00, temp);
+
+ temp = (mode->crtc_hblank_start >> 3) - 1;
+ if (temp & 0x100)
+ jregAC |= 0x10; /* HBS D[8] */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x02, 0x00, temp);
+
+ temp = ((mode->crtc_hblank_end >> 3) - 1) & 0x7f;
+ if (temp & 0x20)
+ jreg05 |= 0x80; /* HBE D[5] */
+ if (temp & 0x40)
+ jregAD |= 0x01; /* HBE D[5] */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x03, 0xE0, (temp & 0x1f));
+
+ temp = ((mode->crtc_hsync_start - crtc_hsync_precatch) >> 3) - 1;
+ if (temp & 0x100)
+ jregAC |= 0x40; /* HRS D[5] */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x04, 0x00, temp);
+
+ temp = (((mode->crtc_hsync_end - crtc_hsync_precatch) >> 3) - 1) & 0x3f;
+ if (temp & 0x20)
+ jregAD |= 0x04; /* HRE D[5] */
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05));
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAC, 0x00, jregAC);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAD, 0x00, jregAD);
+
+ if (ast->quirks->crtc_hsync_add4_needed && mode->crtc_vdisplay == 1080)
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x02);
+ else
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x00);
+
+ /* vert timings */
+ temp = (mode->crtc_vtotal) - 2;
+ if (temp & 0x100)
+ jreg07 |= 0x01;
+ if (temp & 0x200)
+ jreg07 |= 0x20;
+ if (temp & 0x400)
+ jregAE |= 0x01;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x06, 0x00, temp);
+
+ temp = (mode->crtc_vsync_start) - 1;
+ if (temp & 0x100)
+ jreg07 |= 0x04;
+ if (temp & 0x200)
+ jreg07 |= 0x80;
+ if (temp & 0x400)
+ jregAE |= 0x08;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x10, 0x00, temp);
+
+ temp = (mode->crtc_vsync_end - 1) & 0x3f;
+ if (temp & 0x10)
+ jregAE |= 0x20;
+ if (temp & 0x20)
+ jregAE |= 0x40;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x70, temp & 0xf);
+
+ temp = mode->crtc_vdisplay - 1;
+ if (temp & 0x100)
+ jreg07 |= 0x02;
+ if (temp & 0x200)
+ jreg07 |= 0x40;
+ if (temp & 0x400)
+ jregAE |= 0x02;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x12, 0x00, temp);
+
+ temp = mode->crtc_vblank_start - 1;
+ if (temp & 0x100)
+ jreg07 |= 0x08;
+ if (temp & 0x200)
+ jreg09 |= 0x20;
+ if (temp & 0x400)
+ jregAE |= 0x04;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x15, 0x00, temp);
+
+ temp = mode->crtc_vblank_end - 1;
+ if (temp & 0x100)
+ jregAE |= 0x10;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x16, 0x00, temp);
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x07, 0x00, jreg07);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x09, 0xdf, jreg09);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAE, 0x00, (jregAE | 0x80));
+
+ if (crtc_hsync_precatch)
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x80);
+ else
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x00);
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x80);
+}
+
+static void ast_set_offset_reg(struct ast_device *ast,
+ struct drm_framebuffer *fb)
+{
+ u16 offset;
+
+ offset = fb->pitches[0] >> 3;
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x13, (offset & 0xff));
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xb0, (offset >> 8) & 0x3f);
+}
+
+static void ast_set_dclk_reg(struct ast_device *ast,
+ struct drm_display_mode *mode,
+ const struct ast_vbios_enhtable *vmode)
+{
+ const struct ast_vbios_dclk_info *clk_info = &ast->dclk_table[vmode->dclk_index];
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc0, 0x00, clk_info->param1);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc1, 0x00, clk_info->param2);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xbb, 0x0f,
+ (clk_info->param3 & 0xc0) |
+ ((clk_info->param3 & 0x3) << 4));
+}
+
+static void ast_set_color_reg(struct ast_device *ast,
+ const struct drm_format_info *format)
+{
+ u8 vgacra0 = 0x00;
+ u8 vgacra3 = 0x00;
+ u8 vgacra8 = 0x00;
+
+ vgacra0 |= AST_IO_VGACRA0_MEMORY_CHAIN4_MODE |
+ AST_IO_VGACRA0_LINEAR_EXT_ACCESS |
+ AST_IO_VGACRA0_SEGMENTED_EXT_ACCESS;
+
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ vgacra3 |= AST_IO_VGACRA3_256_COLORS;
+ vgacra8 &= ~AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ vgacra3 |= AST_IO_VGACRA3_15_BPP;
+ vgacra8 |= AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
+ break;
+ case DRM_FORMAT_RGB565:
+ vgacra3 |= AST_IO_VGACRA3_16_BPP;
+ vgacra8 |= AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ vgacra3 |= AST_IO_VGACRA3_32_BPP;
+ vgacra8 |= AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
+ break;
+ }
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa0, 0x8f, vgacra0);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xf0, vgacra3);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa8, 0xfd, vgacra8);
+}
+
+static void ast_set_crtthd_reg(struct ast_device *ast)
+{
+ u8 vgacra6 = ast->quirks->crtc_mem_req_threshold_low;
+ u8 vgacra7 = ast->quirks->crtc_mem_req_threshold_high;
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, vgacra7);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, vgacra6);
+}
+
+static void ast_set_sync_reg(struct ast_device *ast,
+ struct drm_display_mode *mode,
+ const struct ast_vbios_enhtable *vmode)
+{
+ u8 jreg;
+
+ jreg = ast_io_read8(ast, AST_IO_VGAMR_R);
+ jreg &= ~0xC0;
+ if (vmode->flags & NVSync)
+ jreg |= 0x80;
+ if (vmode->flags & NHSync)
+ jreg |= 0x40;
+ ast_io_write8(ast, AST_IO_VGAMR_W, jreg);
+}
+
+static void ast_set_start_address_crt1(struct ast_device *ast,
+ unsigned int offset)
+{
+ u32 addr;
+
+ addr = offset >> 2;
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x0d, (u8)(addr & 0xff));
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x0c, (u8)((addr >> 8) & 0xff));
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xaf, (u8)((addr >> 16) & 0xff));
+
+}
+
+static void ast_wait_for_vretrace(struct ast_device *ast)
+{
+ unsigned long timeout = jiffies + HZ;
+ u8 vgair1;
+
+ do {
+ vgair1 = ast_io_read8(ast, AST_IO_VGAIR1_R);
+ } while (!(vgair1 & AST_IO_VGAIR1_VREFRESH) && time_before(jiffies, timeout));
+}
+
+/*
+ * Planes
+ */
+
+int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane,
+ u64 offset, unsigned long size,
+ uint32_t possible_crtcs,
+ const struct drm_plane_funcs *funcs,
+ const uint32_t *formats, unsigned int format_count,
+ const uint64_t *format_modifiers,
+ enum drm_plane_type type)
+{
+ struct drm_plane *plane = &ast_plane->base;
+
+ ast_plane->offset = offset;
+ ast_plane->size = size;
+
+ return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+ formats, format_count, format_modifiers,
+ type, NULL);
+}
+
+void __iomem *ast_plane_vaddr(struct ast_plane *ast_plane)
+{
+ struct ast_device *ast = to_ast_device(ast_plane->base.dev);
+
+ return ast->vram + ast_plane->offset;
+}
+
+/*
+ * Primary plane
+ */
+
+static const uint32_t ast_primary_plane_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_C8,
+};
+
+static int ast_primary_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_device *dev = plane->dev;
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc_state *new_crtc_state = NULL;
+ struct ast_crtc_state *new_ast_crtc_state;
+ int ret;
+
+ if (new_plane_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
+
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, true);
+ if (ret) {
+ return ret;
+ } else if (!new_plane_state->visible) {
+ if (drm_WARN_ON(dev, new_plane_state->crtc)) /* cannot legally happen */
+ return -EINVAL;
+ else
+ return 0;
+ }
+
+ new_ast_crtc_state = to_ast_crtc_state(new_crtc_state);
+
+ new_ast_crtc_state->format = new_plane_state->fb->format;
+
+ return 0;
+}
+
+static void ast_handle_damage(struct ast_plane *ast_plane, struct iosys_map *src,
+ struct drm_framebuffer *fb,
+ const struct drm_rect *clip,
+ struct drm_format_conv_state *fmtcnv_state)
+{
+ struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(ast_plane_vaddr(ast_plane));
+
+ iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
+
+#if defined(__BIG_ENDIAN)
+ drm_fb_swab(&dst, fb->pitches, src, fb, clip, !src[0].is_iomem, fmtcnv_state);
+#else
+ drm_fb_memcpy(&dst, fb->pitches, src, fb, clip);
+#endif
+}
+
+static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_device *dev = plane->dev;
+ struct ast_device *ast = to_ast_device(dev);
+ struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+ struct drm_framebuffer *old_fb = old_plane_state->fb;
+ struct ast_plane *ast_plane = to_ast_plane(plane);
+ struct drm_crtc *crtc = plane_state->crtc;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_rect damage;
+ struct drm_atomic_helper_damage_iter iter;
+
+ if (!old_fb || (fb->format != old_fb->format) || crtc_state->mode_changed) {
+ struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
+
+ ast_set_color_reg(ast, fb->format);
+ ast_set_vbios_color_reg(ast, fb->format, ast_crtc_state->vmode);
+ }
+
+ /* if the buffer comes from another device */
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
+ drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_for_each_plane_damage(&iter, &damage) {
+ ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage,
+ &shadow_plane_state->fmtcnv_state);
+ }
+
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+ }
+
+ /*
+ * Some BMCs stop scanning out the video signal after the driver
+ * reprogrammed the offset. This stalls display output for several
+ * seconds and makes the display unusable. Therefore only update
+ * the offset if it changes.
+ */
+ if (!old_fb || old_fb->pitches[0] != fb->pitches[0])
+ ast_set_offset_reg(ast, fb);
+}
+
+static void ast_primary_plane_helper_atomic_enable(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(plane->dev);
+ struct ast_plane *ast_plane = to_ast_plane(plane);
+
+ /*
+ * Some BMCs stop scanning out the video signal after the driver
+ * reprogrammed the scanout address. This stalls display
+ * output for several seconds and makes the display unusable.
+ * Therefore only reprogram the address after enabling the plane.
+ */
+ ast_set_start_address_crt1(ast, (u32)ast_plane->offset);
+}
+
+static void ast_primary_plane_helper_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ /*
+ * Keep this empty function to avoid calling
+ * atomic_update when disabling the plane.
+ */
+}
+
+static int ast_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
+ struct drm_scanout_buffer *sb)
+{
+ struct ast_plane *ast_plane = to_ast_plane(plane);
+
+ if (plane->state && plane->state->fb) {
+ sb->format = plane->state->fb->format;
+ sb->width = plane->state->fb->width;
+ sb->height = plane->state->fb->height;
+ sb->pitch[0] = plane->state->fb->pitches[0];
+ iosys_map_set_vaddr_iomem(&sb->map[0], ast_plane_vaddr(ast_plane));
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static const struct drm_plane_helper_funcs ast_primary_plane_helper_funcs = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = ast_primary_plane_helper_atomic_check,
+ .atomic_update = ast_primary_plane_helper_atomic_update,
+ .atomic_enable = ast_primary_plane_helper_atomic_enable,
+ .atomic_disable = ast_primary_plane_helper_atomic_disable,
+ .get_scanout_buffer = ast_primary_plane_helper_get_scanout_buffer,
+};
+
+static const struct drm_plane_funcs ast_primary_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ DRM_GEM_SHADOW_PLANE_FUNCS,
+};
+
+static int ast_primary_plane_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct ast_plane *ast_primary_plane = &ast->primary_plane;
+ struct drm_plane *primary_plane = &ast_primary_plane->base;
+ u64 offset = ast_fb_vram_offset();
+ unsigned long size = ast_fb_vram_size(ast);
+ int ret;
+
+ ret = ast_plane_init(dev, ast_primary_plane, offset, size,
+ 0x01, &ast_primary_plane_funcs,
+ ast_primary_plane_formats, ARRAY_SIZE(ast_primary_plane_formats),
+ NULL, DRM_PLANE_TYPE_PRIMARY);
+ if (ret) {
+ drm_err(dev, "ast_plane_init() failed: %d\n", ret);
+ return ret;
+ }
+ drm_plane_helper_add(primary_plane, &ast_primary_plane_helper_funcs);
+ drm_plane_enable_fb_damage_clips(primary_plane);
+
+ return 0;
+}
+
+/*
+ * CRTC
+ */
+
+static enum drm_mode_status
+ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
+{
+ struct ast_device *ast = to_ast_device(crtc->dev);
+ const struct ast_vbios_enhtable *vmode;
+
+ vmode = ast_vbios_find_mode(ast, mode);
+ if (!vmode)
+ return MODE_NOMODE;
+
+ return MODE_OK;
+}
+
+static void ast_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct ast_device *ast = to_ast_device(dev);
+ struct drm_crtc_state *crtc_state = crtc->state;
+ struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
+ const struct ast_vbios_stdtable *std_table = ast_crtc_state->std_table;
+ const struct ast_vbios_enhtable *vmode = ast_crtc_state->vmode;
+ struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+
+ /*
+ * Ensure that no scanout takes place before reprogramming mode
+ * and format registers.
+ *
+ * TODO: Get vblank interrupts working and remove this line.
+ */
+ ast_wait_for_vretrace(ast);
+
+ ast_set_vbios_mode_reg(ast, adjusted_mode, vmode);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xa1, 0x06);
+ ast_set_std_reg(ast, adjusted_mode, std_table);
+ ast_set_crtc_reg(ast, adjusted_mode, vmode);
+ ast_set_dclk_reg(ast, adjusted_mode, vmode);
+ ast_set_crtthd_reg(ast);
+ ast_set_sync_reg(ast, adjusted_mode, vmode);
+}
+
+static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+ struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+ struct ast_crtc_state *old_ast_crtc_state = to_ast_crtc_state(old_crtc_state);
+ struct drm_device *dev = crtc->dev;
+ struct ast_device *ast = to_ast_device(dev);
+ struct ast_crtc_state *ast_state;
+ const struct drm_format_info *format;
+ const struct ast_vbios_enhtable *vmode;
+ unsigned int hborder = 0;
+ unsigned int vborder = 0;
+ int ret;
+
+ if (!crtc_state->enable)
+ return 0;
+
+ ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
+ if (ret)
+ return ret;
+
+ ast_state = to_ast_crtc_state(crtc_state);
+
+ format = ast_state->format;
+ if (drm_WARN_ON_ONCE(dev, !format))
+ return -EINVAL; /* BUG: We didn't set format in primary check(). */
+
+ /*
+ * The gamma LUT has to be reloaded after changing the primary
+ * plane's color format.
+ */
+ if (old_ast_crtc_state->format != format)
+ crtc_state->color_mgmt_changed = true;
+
+ if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) {
+ if (crtc_state->gamma_lut->length !=
+ AST_LUT_SIZE * sizeof(struct drm_color_lut)) {
+ drm_err(dev, "Wrong size for gamma_lut %zu\n",
+ crtc_state->gamma_lut->length);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Set register tables.
+ *
+ * TODO: These tables mix all kinds of fields and should
+ * probably be resolved into various helper functions.
+ */
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ ast_state->std_table = &vbios_stdtable[VGAModeIndex];
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_RGB565:
+ ast_state->std_table = &vbios_stdtable[HiCModeIndex];
+ break;
+ case DRM_FORMAT_XRGB8888:
+ ast_state->std_table = &vbios_stdtable[TrueCModeIndex];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Find the VBIOS mode and adjust the DRM display mode accordingly
+ * if a full modeset is required. Otherwise keep the existing values.
+ */
+ if (drm_atomic_crtc_needs_modeset(crtc_state)) {
+ vmode = ast_vbios_find_mode(ast, &crtc_state->mode);
+ if (!vmode)
+ return -EINVAL;
+ ast_state->vmode = vmode;
+
+ if (vmode->flags & HBorder)
+ hborder = 8;
+ if (vmode->flags & VBorder)
+ vborder = 8;
+
+ adjusted_mode->crtc_hdisplay = vmode->hde;
+ adjusted_mode->crtc_hblank_start = vmode->hde + hborder;
+ adjusted_mode->crtc_hblank_end = vmode->ht - hborder;
+ adjusted_mode->crtc_hsync_start = vmode->hde + hborder + vmode->hfp;
+ adjusted_mode->crtc_hsync_end = vmode->hde + hborder + vmode->hfp + vmode->hsync;
+ adjusted_mode->crtc_htotal = vmode->ht;
+
+ adjusted_mode->crtc_vdisplay = vmode->vde;
+ adjusted_mode->crtc_vblank_start = vmode->vde + vborder;
+ adjusted_mode->crtc_vblank_end = vmode->vt - vborder;
+ adjusted_mode->crtc_vsync_start = vmode->vde + vborder + vmode->vfp;
+ adjusted_mode->crtc_vsync_end = vmode->vde + vborder + vmode->vfp + vmode->vsync;
+ adjusted_mode->crtc_vtotal = vmode->vt;
+ }
+
+ return 0;
+}
+
+static void
+ast_crtc_helper_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
+ crtc);
+ struct drm_device *dev = crtc->dev;
+ struct ast_device *ast = to_ast_device(dev);
+ struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state);
+
+ /*
+ * The gamma LUT has to be reloaded after changing the primary
+ * plane's color format.
+ */
+ if (crtc_state->enable && crtc_state->color_mgmt_changed) {
+ if (crtc_state->gamma_lut)
+ ast_crtc_load_gamma(ast,
+ ast_crtc_state->format,
+ crtc_state->gamma_lut->data);
+ else
+ ast_crtc_fill_gamma(ast, ast_crtc_state->format);
+ }
+}
+
+static void ast_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(crtc->dev);
+ u8 vgacr17 = 0x00;
+ u8 vgacrb6 = 0xff;
+
+ vgacr17 |= AST_IO_VGACR17_SYNC_ENABLE;
+ vgacrb6 &= ~(AST_IO_VGACRB6_VSYNC_OFF | AST_IO_VGACRB6_HSYNC_OFF);
+
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x17, 0x7f, vgacr17);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, vgacrb6);
+}
+
+static void ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_commit *state)
+{
+ struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+ struct ast_device *ast = to_ast_device(crtc->dev);
+ u8 vgacr17 = 0xff;
+
+ vgacr17 &= ~AST_IO_VGACR17_SYNC_ENABLE;
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x17, 0x7f, vgacr17);
+
+ /*
+ * HW cursors require the underlying primary plane and CRTC to
+ * display a valid mode and image. This is not the case during
+ * full modeset operations. So we temporarily disable any active
+ * plane, including the HW cursor. Each plane's atomic_update()
+ * helper will re-enable it if necessary.
+ *
+ * We only do this during *full* modesets. It does not affect
+ * simple pageflips on the planes.
+ */
+ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
+}
+
+static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
+ .mode_valid = ast_crtc_helper_mode_valid,
+ .mode_set_nofb = ast_crtc_helper_mode_set_nofb,
+ .atomic_check = ast_crtc_helper_atomic_check,
+ .atomic_flush = ast_crtc_helper_atomic_flush,
+ .atomic_enable = ast_crtc_helper_atomic_enable,
+ .atomic_disable = ast_crtc_helper_atomic_disable,
+};
+
+static void ast_crtc_reset(struct drm_crtc *crtc)
+{
+ struct ast_crtc_state *ast_state = kzalloc_obj(*ast_state);
+
+ if (crtc->state)
+ crtc->funcs->atomic_destroy_state(crtc, crtc->state);
+
+ if (ast_state)
+ __drm_atomic_helper_crtc_reset(crtc, &ast_state->base);
+ else
+ __drm_atomic_helper_crtc_reset(crtc, NULL);
+}
+
+static struct drm_crtc_state *
+ast_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+ struct ast_crtc_state *new_ast_state, *ast_state;
+ struct drm_device *dev = crtc->dev;
+
+ if (drm_WARN_ON(dev, !crtc->state))
+ return NULL;
+
+ new_ast_state = kmalloc_obj(*new_ast_state);
+ if (!new_ast_state)
+ return NULL;
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &new_ast_state->base);
+
+ ast_state = to_ast_crtc_state(crtc->state);
+
+ new_ast_state->format = ast_state->format;
+ new_ast_state->std_table = ast_state->std_table;
+ new_ast_state->vmode = ast_state->vmode;
+
+ return &new_ast_state->base;
+}
+
+static void ast_crtc_atomic_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct ast_crtc_state *ast_state = to_ast_crtc_state(state);
+
+ __drm_atomic_helper_crtc_destroy_state(&ast_state->base);
+ kfree(ast_state);
+}
+
+static const struct drm_crtc_funcs ast_crtc_funcs = {
+ .reset = ast_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = ast_crtc_atomic_duplicate_state,
+ .atomic_destroy_state = ast_crtc_atomic_destroy_state,
+};
+
+static int ast_crtc_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct drm_crtc *crtc = &ast->crtc;
+ int ret;
+
+ ret = drm_crtc_init_with_planes(dev, crtc, &ast->primary_plane.base,
+ &ast->cursor_plane.base.base, &ast_crtc_funcs,
+ NULL);
+ if (ret)
+ return ret;
+
+ drm_mode_crtc_set_gamma_size(crtc, AST_LUT_SIZE);
+ drm_crtc_enable_color_mgmt(crtc, 0, false, AST_LUT_SIZE);
+
+ drm_crtc_helper_add(crtc, &ast_crtc_helper_funcs);
+
+ return 0;
+}
+
+/*
+ * Mode config
+ */
+
+static void ast_mode_config_helper_atomic_commit_tail(struct drm_atomic_commit *state)
+{
+ struct ast_device *ast = to_ast_device(state->dev);
+
+ /*
+ * Concurrent operations could possibly trigger a call to
+ * drm_connector_helper_funcs.get_modes by reading the display
+ * modes. Protect access to registers by acquiring the modeset
+ * lock.
+ */
+ mutex_lock(&ast->modeset_lock);
+ drm_atomic_helper_commit_tail(state);
+ mutex_unlock(&ast->modeset_lock);
+}
+
+static const struct drm_mode_config_helper_funcs ast_mode_config_helper_funcs = {
+ .atomic_commit_tail = ast_mode_config_helper_atomic_commit_tail,
+};
+
+static enum drm_mode_status ast_mode_config_mode_valid(struct drm_device *dev,
+ const struct drm_display_mode *mode)
+{
+ const struct drm_format_info *info = drm_format_info(DRM_FORMAT_XRGB8888);
+ struct ast_device *ast = to_ast_device(dev);
+ unsigned long max_fb_size = ast_fb_vram_size(ast);
+ u64 pitch;
+
+ if (drm_WARN_ON_ONCE(dev, !info))
+ return MODE_ERROR; /* driver bug */
+
+ pitch = drm_format_info_min_pitch(info, 0, mode->hdisplay);
+ if (!pitch)
+ return MODE_BAD_WIDTH;
+ if (pitch > AST_PRIMARY_PLANE_MAX_OFFSET)
+ return MODE_BAD_WIDTH; /* maximum programmable pitch */
+ if (pitch > max_fb_size / mode->vdisplay)
+ return MODE_MEM;
+
+ return MODE_OK;
+}
+
+static const struct drm_mode_config_funcs ast_mode_config_funcs = {
+ .fb_create = drm_gem_fb_create_with_dirty,
+ .mode_valid = ast_mode_config_mode_valid,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+int ast_mode_config_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ int ret;
+
+ ret = drmm_mutex_init(dev, &ast->modeset_lock);
+ if (ret)
+ return ret;
+
+ ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ret;
+
+ dev->mode_config.funcs = &ast_mode_config_funcs;
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+ dev->mode_config.preferred_depth = 24;
+
+ if (ast->support_fullhd) {
+ dev->mode_config.max_width = 1920;
+ dev->mode_config.max_height = 2048;
+ } else {
+ dev->mode_config.max_width = 1600;
+ dev->mode_config.max_height = 1200;
+ }
+
+ dev->mode_config.helper_private = &ast_mode_config_helper_funcs;
+
+ ret = ast_primary_plane_init(ast);
+ if (ret)
+ return ret;
+
+ ret = ast_cursor_plane_init(ast);
+ if (ret)
+ return ret;
+
+ ret = ast_crtc_init(ast);
+ if (ret)
+ return ret;
+
+ switch (ast->tx_chip) {
+ case AST_TX_NONE:
+ ret = ast_vga_output_init(ast);
+ break;
+ case AST_TX_SIL164:
+ ret = ast_sil164_output_init(ast);
+ break;
+ case AST_TX_DP501:
+ ret = ast_dp501_output_init(ast);
+ break;
+ case AST_TX_ASTDP:
+ ret = ast_astdp_output_init(ast);
+ break;
+ }
+ if (ret)
+ return ret;
+
+ drm_mode_config_reset(dev);
+ drmm_kms_helper_poll_init(dev);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
new file mode 100644
index 000000000..297230f1e
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+#include "ast_post.h"
+
+int ast_post_gpu(struct ast_device *ast)
+{
+ int ret;
+
+ if (AST_GEN(ast) >= 7) {
+ ret = ast_2600_post(ast);
+ if (ret)
+ return ret;
+ } else if (AST_GEN(ast) >= 6) {
+ ret = ast_2500_post(ast);
+ if (ret)
+ return ret;
+ } else if (AST_GEN(ast) >= 4) {
+ ret = ast_2300_post(ast);
+ if (ret)
+ return ret;
+ } else if (AST_GEN(ast) >= 2) {
+ ret = ast_2100_post(ast);
+ if (ret)
+ return ret;
+ } else {
+ ret = ast_2000_post(ast);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#define TIMEOUT 5000000
+
+bool mmc_test(struct ast_device *ast, u32 datagen, u8 test_ctl)
+{
+ u32 data, timeout;
+
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, (datagen << 3) | test_ctl);
+ timeout = 0;
+ do {
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x3000;
+ if (data & 0x2000)
+ return false;
+ if (++timeout > TIMEOUT) {
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ return false;
+ }
+ } while (!data);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x0);
+ return true;
+}
+
+bool mmc_test_burst(struct ast_device *ast, u32 datagen)
+{
+ return mmc_test(ast, datagen, 0xc1);
+}
diff --git a/drivers/gpu/drm/ast/ast_post.h b/drivers/gpu/drm/ast/ast_post.h
new file mode 100644
index 000000000..41b2db08a
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_post.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef AST_POST_H
+#define AST_POST_H
+
+#include <linux/limits.h>
+#include <linux/types.h>
+
+struct ast_device;
+
+/* DRAM timing tables */
+struct ast_dramstruct {
+ u32 index;
+ u32 data;
+};
+
+/* control commands */
+#define __AST_DRAMSTRUCT_UDELAY 0xff00
+#define __AST_DRAMSTRUCT_INVALID 0xffff
+
+#define __AST_DRAMSTRUCT_INDEX(_name) \
+ (__AST_DRAMSTRUCT_ ## _name)
+
+#define __AST_DRAMSTRUCT_INIT(_index, _value) \
+ { (_index), (_value) }
+
+#define AST_DRAMSTRUCT_REG(_reg, _value) \
+ __AST_DRAMSTRUCT_INIT(_reg, _value)
+#define AST_DRAMSTRUCT_UDELAY(_usecs) \
+ __AST_DRAMSTRUCT_INIT(__AST_DRAMSTRUCT_UDELAY, _usecs)
+#define AST_DRAMSTRUCT_INVALID \
+ __AST_DRAMSTRUCT_INIT(__AST_DRAMSTRUCT_INVALID, U32_MAX)
+
+#define AST_DRAMSTRUCT_IS_REG(_entry, _reg) \
+ ((_entry)->index == (_reg))
+#define AST_DRAMSTRUCT_IS(_entry, _name) \
+ ((_entry)->index == __AST_DRAMSTRUCT_INDEX(_name))
+
+bool mmc_test(struct ast_device *ast, u32 datagen, u8 test_ctl);
+bool mmc_test_burst(struct ast_device *ast, u32 datagen);
+
+/* ast_2000.c */
+void ast_2000_set_def_ext_reg(struct ast_device *ast);
+
+/* ast_2300.c */
+void ast_2300_set_def_ext_reg(struct ast_device *ast);
+
+#endif
diff --git a/drivers/gpu/drm/ast/ast_reg.h b/drivers/gpu/drm/ast/ast_reg.h
new file mode 100644
index 000000000..b808b0a02
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_reg.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __AST_REG_H__
+#define __AST_REG_H__
+
+#include <linux/bits.h>
+
+/*
+ * Modesetting
+ */
+
+#define AST_IO_MM_OFFSET (0x380)
+#define AST_IO_MM_LENGTH (128)
+
+#define AST_IO_VGAARI_W (0x40)
+
+#define AST_IO_VGAMR_W (0x42)
+#define AST_IO_VGAMR_R (0x4c)
+#define AST_IO_VGAMR_IOSEL BIT(0)
+
+#define AST_IO_VGAER (0x43)
+#define AST_IO_VGAER_VGA_ENABLE BIT(0)
+
+#define AST_IO_VGASRI (0x44)
+#define AST_IO_VGASR1_SD BIT(5)
+#define AST_IO_VGADRR (0x47)
+#define AST_IO_VGADWR (0x48)
+#define AST_IO_VGAPDR (0x49)
+#define AST_IO_VGAGRI (0x4E)
+
+#define AST_IO_VGACRI (0x54)
+#define AST_IO_VGACR17_SYNC_ENABLE BIT(7) /* called "Hardware reset" in docs */
+#define AST_IO_VGACR80_PASSWORD (0xa8)
+
+#define AST_IO_VGACR8C_NEW_MODE_MASK GENMASK(3, 0)
+#define AST_IO_VGACR8C_NEW_MODE_EGA (0x00)
+#define AST_IO_VGACR8C_NEW_MODE_VGA (0x01)
+#define AST_IO_VGACR8C_NEW_MODE_15_BPP (0x02)
+#define AST_IO_VGACR8C_NEW_MODE_16_BPP (0x03)
+#define AST_IO_VGACR8C_NEW_MODE_32_BPP (0x04)
+#define AST_IO_VGACR8C_NEW_MODE_CGA (0x0f)
+#define AST_IO_VGACR8C_NEW_MODE_TEXT (0x0e)
+#define AST_IO_VGACR8C_CUR_MODE_MASK GENMASK(7, 4)
+#define AST_IO_VGACR8C_CUR_MODE_EGA (0x00)
+#define AST_IO_VGACR8C_CUR_MODE_VGA (0x10)
+#define AST_IO_VGACR8C_CUR_MODE_15_BPP (0x20)
+#define AST_IO_VGACR8C_CUR_MODE_16_BPP (0x30)
+#define AST_IO_VGACR8C_CUR_MODE_32_BPP (0x40)
+#define AST_IO_VGACR8C_CUR_MODE_CGA (0xf0)
+#define AST_IO_VGACR8C_CUR_MODE_TEXT (0xe0)
+
+#define AST_IO_VGACR91_PASSWORD (0xa8)
+#define AST_IO_VGACR99_VGAMEM_RSRV_MASK GENMASK(1, 0)
+
+#define AST_IO_VGACRA0_MEMORY_CHAIN4_MODE BIT(6)
+#define AST_IO_VGACRA0_LINEAR_EXT_ACCESS BIT(5)
+#define AST_IO_VGACRA0_SEGMENTED_EXT_ACCESS BIT(4)
+
+#define AST_IO_VGACRA1_VGAIO_DISABLED BIT(1)
+#define AST_IO_VGACRA1_MMIO_ENABLED BIT(2)
+
+#define AST_IO_VGACRA3_DVO_ENABLED BIT(7)
+#define AST_IO_VGACRA3_32_BPP BIT(3)
+#define AST_IO_VGACRA3_16_BPP BIT(2)
+#define AST_IO_VGACRA3_15_BPP BIT(1)
+#define AST_IO_VGACRA3_256_COLORS BIT(0)
+
+#define AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED BIT(1)
+#define AST_IO_VGACRAA_VGAMEM_SIZE_MASK GENMASK(1, 0)
+#define AST_IO_VGACRB6_HSYNC_OFF BIT(0)
+#define AST_IO_VGACRB6_VSYNC_OFF BIT(1)
+#define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */
+#define AST_IO_VGACRCB_HWC_ENABLED BIT(1)
+
+/* mirrors SCU100[7:0] */
+#define AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK GENMASK(7, 6)
+#define AST_IO_VGACRD0_VRAM_INIT_BY_BMC BIT(7)
+#define AST_IO_VGACRD0_VRAM_INIT_READY BIT(6)
+#define AST_IO_VGACRD0_IKVM_WIDESCREEN BIT(0)
+
+#define AST_IO_VGACRD1_MCU_FW_EXECUTING BIT(5)
+/* Display Transmitter Type */
+#define AST_IO_VGACRD1_TX_TYPE_MASK GENMASK(3, 1)
+#define AST_IO_VGACRD1_NO_TX 0x00
+#define AST_IO_VGACRD1_TX_ITE66121_VBIOS 0x02
+#define AST_IO_VGACRD1_TX_SIL164_VBIOS 0x04
+#define AST_IO_VGACRD1_TX_CH7003_VBIOS 0x06
+#define AST_IO_VGACRD1_TX_DP501_VBIOS 0x08
+#define AST_IO_VGACRD1_TX_ANX9807_VBIOS 0x0a
+#define AST_IO_VGACRD1_TX_FW_EMBEDDED_FW 0x0c /* special case of DP501 */
+#define AST_IO_VGACRD1_TX_ASTDP 0x0e
+#define AST_IO_VGACRD1_SUPPORTS_WUXGA BIT(0)
+
+/*
+ * AST DisplayPort
+ */
+#define AST_IO_VGACRD7_EDID_VALID_FLAG BIT(0)
+#define AST_IO_VGACRDC_LINK_SUCCESS BIT(0)
+#define AST_IO_VGACRDF_HPD BIT(0)
+#define AST_IO_VGACRDF_DP_VIDEO_ENABLE BIT(4) /* mirrors AST_IO_VGACRE3_DP_VIDEO_ENABLE */
+#define AST_IO_VGACRE0_24BPP BIT(5) /* 18 bpp, if unset */
+#define AST_IO_VGACRE3_DP_VIDEO_ENABLE BIT(0)
+#define AST_IO_VGACRE3_DP_PHY_SLEEP BIT(4)
+#define AST_IO_VGACRE5_EDID_READ_DONE BIT(0)
+
+#define AST_IO_VGAIR1_R (0x5A)
+#define AST_IO_VGAIR1_VREFRESH BIT(3)
+
+/*
+ * P-Bus to AHB Bridge (0x00000000 - 0x0001ffff)
+ */
+
+#define AST_REG_P2A_BASE (0x00000000)
+#define AST_REG_P2A(__offset) (AST_REG_P2A_BASE + (__offset))
+#define AST_REG_P2A_ADDR(__addr) AST_REG_P2A(0x10000 + ((__addr) & GENMASK(15, 0)))
+#define AST_REG_P2A00 AST_REG_P2A(0xf000)
+#define AST_REG_P2A00_PROTECTION_KEY (0x01)
+#define AST_REG_P2A04 AST_REG_P2A(0xf004)
+#define AST_REG_P2A04_BASE_MASK GENMASK(31, 16)
+
+/*
+ * AHB Controller (0x1e600000 - 0x1e61ffff)
+ */
+
+#define AST_REG_AHBC_BASE (0x1e600000)
+#define AST_REG_AHBC(__offset) (AST_REG_AHBC_BASE + (__offset))
+#define AST_REG_AHBC00 AST_REG_AHBC(0x00)
+#define AST_REG_AHBC00_PROTECT_KEY (0xaeed1a03)
+#define AST_REG_AHBC84 AST_REG_AHBC(0x84)
+#define AST_REG_AHBC88 AST_REG_AHBC(0x88)
+
+/*
+ * SDRAM Memory Controller (0x1e6e0000 - 0x1e6e0fff)
+ */
+
+#define AST_REG_MCR_BASE (0x1e6e0000)
+#define AST_REG_MCR(__offset) (AST_REG_MCR_BASE + (__offset))
+#define AST_REG_MCR00 AST_REG_MCR(0x00)
+#define AST_REG_MCR00_PROTECTION_KEY (0xfc600309)
+#define AST_REG_MCR04 AST_REG_MCR(0x04)
+#define AST_REG_MCR08 AST_REG_MCR(0x08)
+#define AST_REG_MCR0C AST_REG_MCR(0x0c)
+#define AST_REG_MCR10 AST_REG_MCR(0x10)
+#define AST_REG_MCR14 AST_REG_MCR(0x14)
+#define AST_REG_MCR18 AST_REG_MCR(0x18)
+#define AST_REG_MCR1C AST_REG_MCR(0x1c)
+#define AST_REG_MCR20 AST_REG_MCR(0x20)
+#define AST_REG_MCR24 AST_REG_MCR(0x24)
+#define AST_REG_MCR28 AST_REG_MCR(0x28)
+#define AST_REG_MCR2C AST_REG_MCR(0x2C)
+#define AST_REG_MCR30 AST_REG_MCR(0x30)
+#define AST_REG_MCR34 AST_REG_MCR(0x34)
+#define AST_REG_MCR38 AST_REG_MCR(0x38)
+#define AST_REG_MCR3C AST_REG_MCR(0x3c)
+#define AST_REG_MCR40 AST_REG_MCR(0x40)
+#define AST_REG_MCR44 AST_REG_MCR(0x44)
+#define AST_REG_MCR48 AST_REG_MCR(0x48)
+#define AST_REG_MCR4C AST_REG_MCR(0x4C)
+#define AST_REG_MCR50 AST_REG_MCR(0x50)
+#define AST_REG_MCR54 AST_REG_MCR(0x54)
+#define AST_REG_MCR58 AST_REG_MCR(0x58)
+#define AST_REG_MCR5C AST_REG_MCR(0x5c)
+#define AST_REG_MCR60 AST_REG_MCR(0x60)
+#define AST_REG_MCR64 AST_REG_MCR(0x64)
+#define AST_REG_MCR68 AST_REG_MCR(0x68)
+#define AST_REG_MCR6C AST_REG_MCR(0x6c)
+#define AST_REG_MCR70 AST_REG_MCR(0x70)
+#define AST_REG_MCR74 AST_REG_MCR(0x74)
+#define AST_REG_MCR78 AST_REG_MCR(0x78)
+#define AST_REG_MCR7C AST_REG_MCR(0x7c)
+#define AST_REG_MCR80 AST_REG_MCR(0x80)
+#define AST_REG_MCR84 AST_REG_MCR(0x84)
+#define AST_REG_MCR88 AST_REG_MCR(0x88)
+#define AST_REG_MCR8C AST_REG_MCR(0x8c)
+#define AST_REG_MCR100 AST_REG_MCR(0x100)
+#define AST_REG_MCR108 AST_REG_MCR(0x108)
+#define AST_REG_MCR120 AST_REG_MCR(0x120)
+#define AST_REG_MCR140 AST_REG_MCR(0x140)
+#define AST_REG_MCR200 AST_REG_MCR(0x200)
+#define AST_REG_MCR204 AST_REG_MCR(0x204)
+#define AST_REG_MCR208 AST_REG_MCR(0x208)
+#define AST_REG_MCR20C AST_REG_MCR(0x20C)
+#define AST_REG_MCR210 AST_REG_MCR(0x210)
+#define AST_REG_MCR214 AST_REG_MCR(0x214)
+#define AST_REG_MCR218 AST_REG_MCR(0x218)
+#define AST_REG_MCR220 AST_REG_MCR(0x220)
+#define AST_REG_MCR228 AST_REG_MCR(0x228)
+#define AST_REG_MCR230 AST_REG_MCR(0x230)
+#define AST_REG_MCR2A8 AST_REG_MCR(0x2a8)
+#define AST_REG_MCR2B0 AST_REG_MCR(0x2b0)
+#define AST_REG_MCR240 AST_REG_MCR(0x240)
+#define AST_REG_MCR244 AST_REG_MCR(0x244)
+#define AST_REG_MCR248 AST_REG_MCR(0x248)
+#define AST_REG_MCR24C AST_REG_MCR(0x24c)
+#define AST_REG_MCR290 AST_REG_MCR(0x290)
+#define AST_REG_MCR2C0 AST_REG_MCR(0x2c0)
+#define AST_REG_MCR2C4 AST_REG_MCR(0x2c4)
+#define AST_REG_MCR2C8 AST_REG_MCR(0x2c8)
+#define AST_REG_MCR2CC AST_REG_MCR(0x2cc)
+#define AST_REG_MCR2E0 AST_REG_MCR(0x2e0)
+#define AST_REG_MCR2E4 AST_REG_MCR(0x2e4)
+#define AST_REG_MCR2E8 AST_REG_MCR(0x2e8)
+#define AST_REG_MCR2EC AST_REG_MCR(0x2ec)
+#define AST_REG_MCR2F0 AST_REG_MCR(0x2f0)
+#define AST_REG_MCR2F4 AST_REG_MCR(0x2f4)
+#define AST_REG_MCR2F8 AST_REG_MCR(0x2f8)
+#define AST_REG_MCR300 AST_REG_MCR(0x300)
+#define AST_REG_MCR3D0 AST_REG_MCR(0x3d0)
+
+/*
+ * System Control Unit (0x1e6e2000 - 0x1e6e2fff)
+ */
+
+#define AST_REG_SCU_BASE (0x1e6e2000)
+#define AST_REG_SCU(__offset) (AST_REG_SCU_BASE + (__offset))
+#define AST_REG_SCU000 AST_REG_SCU(0x000)
+#define AST_REG_SCU000_PROTECTION_KEY (0x1688a8a8)
+#define AST_REG_SCU008 AST_REG_SCU(0x008)
+#define AST_REG_SCU00C AST_REG_SCU(0x00c)
+#define AST_REG_SCU020 AST_REG_SCU(0x020)
+#define AST_REG_SCU02C AST_REG_SCU(0x02c)
+#define AST_REG_SCU040 AST_REG_SCU(0x040)
+#define AST_REG_SCU070 AST_REG_SCU(0x070)
+#define AST_REG_SCU07C AST_REG_SCU(0x07c)
+#define AST_REG_SCU07C_CHIP_BONDING_MASK GENMASK(15, 8)
+#define AST_REG_SCU084 AST_REG_SCU(0x084)
+#define AST_REG_SCU088 AST_REG_SCU(0x088)
+#define AST_REG_SCU08C AST_REG_SCU(0x08c)
+#define AST_REG_SCU090 AST_REG_SCU(0x090)
+#define AST_REG_SCU094 AST_REG_SCU(0x094)
+#define AST_REG_SCU0A4 AST_REG_SCU(0x0a4)
+#define AST_REG_SCU0A8 AST_REG_SCU(0x0a8)
+#define AST_REG_SCU100 AST_REG_SCU(0x100)
+#define AST_REG_SCU104 AST_REG_SCU(0x104)
+#define AST_REG_SCU160 AST_REG_SCU(0x160)
+
+/*
+ * AHB-to-P Bus Bridge (0x1e720000 - 0x1e73ffff)
+ */
+
+#define AST_REG_A2P_BASE (0x1e720000)
+#define AST_REG_A2P(__offset) (AST_REG_A2P_BASE + (__offset))
+#define AST_REG_A2P58 AST_REG_A2P(0x58)
+
+/*
+ * Watchdog timer (0x1e785000 - 0x1e785fff)
+ */
+
+#define AST_REG_WDT_BASE(__n) (0x1e785000 + (__n) * 0x40)
+#define AST_REG_WDT(__n, __offset) (AST_REG_WDT_BASE((__n)) + (__offset))
+#define AST_REG_WDT04(__n) AST_REG_WDT((__n), 0x04)
+#define AST_REG_WDT08(__n) AST_REG_WDT((__n), 0x08)
+#define AST_REG_WDT0C(__n) AST_REG_WDT((__n), 0x0c)
+#define AST_REG_WDT14(__n) AST_REG_WDT((__n), 0x14)
+#define AST_REG_WDT1C(__n) AST_REG_WDT((__n), 0x1c)
+#define AST_REG_WDT2C(__n) AST_REG_WDT((__n), 0x2c)
+
+/*
+ * SDRAM (0x80000000 - 0xffffffff)
+ */
+
+#define AST_SDRAM_BASE (0x80000000)
+#define AST_SDRAM(__offset) (AST_SDRAM_BASE + (__offset))
+
+#endif
diff --git a/drivers/gpu/drm/ast/ast_sil164.c b/drivers/gpu/drm/ast/ast_sil164.c
new file mode 100644
index 000000000..be01254dd
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_sil164.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: MIT
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "ast_ddc.h"
+#include "ast_drv.h"
+
+/*
+ * Encoder
+ */
+
+static const struct drm_encoder_funcs ast_sil164_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/*
+ * Connector
+ */
+
+static int ast_sil164_connector_helper_get_modes(struct drm_connector *connector)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ int count;
+
+ if (ast_connector->physical_status == connector_status_connected) {
+ count = drm_connector_helper_get_modes(connector);
+ } else {
+ drm_edid_connector_update(connector, NULL);
+
+ /*
+ * There's no EDID data without a connected monitor. Set BMC-
+ * compatible modes in this case. The XGA default resolution
+ * should work well for all BMCs.
+ */
+ count = drm_add_modes_noedid(connector, 4096, 4096);
+ if (count)
+ drm_set_preferred_mode(connector, 1024, 768);
+ }
+
+ return count;
+}
+
+static int ast_sil164_connector_helper_detect_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ enum drm_connector_status status;
+
+ status = drm_connector_helper_detect_from_ddc(connector, ctx, force);
+
+ if (status != ast_connector->physical_status)
+ ++connector->epoch_counter;
+ ast_connector->physical_status = status;
+
+ return connector_status_connected;
+}
+
+static const struct drm_connector_helper_funcs ast_sil164_connector_helper_funcs = {
+ .get_modes = ast_sil164_connector_helper_get_modes,
+ .detect_ctx = ast_sil164_connector_helper_detect_ctx,
+};
+
+static const struct drm_connector_funcs ast_sil164_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/*
+ * Output
+ */
+
+int ast_sil164_output_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct drm_crtc *crtc = &ast->crtc;
+ struct i2c_adapter *ddc;
+ struct drm_encoder *encoder;
+ struct ast_connector *ast_connector;
+ struct drm_connector *connector;
+ int ret;
+
+ /* DDC */
+
+ ddc = ast_ddc_create(ast);
+ if (IS_ERR(ddc))
+ return PTR_ERR(ddc);
+
+ /* encoder */
+
+ encoder = &ast->output.sil164.encoder;
+ ret = drm_encoder_init(dev, encoder, &ast_sil164_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ret;
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ /* connector */
+
+ ast_connector = &ast->output.sil164.connector;
+ connector = &ast_connector->base;
+ ret = drm_connector_init_with_ddc(dev, connector, &ast_sil164_connector_funcs,
+ DRM_MODE_CONNECTOR_DVII, ddc);
+ if (ret)
+ return ret;
+ drm_connector_helper_add(connector, &ast_sil164_connector_helper_funcs);
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+
+ ast_connector->physical_status = connector->status;
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h
new file mode 100644
index 000000000..7da5b5c60
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_tables.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2005 ASPEED Technology Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the authors not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. The authors makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/* Ported from xf86-video-ast driver */
+
+#ifndef AST_TABLES_H
+#define AST_TABLES_H
+
+#include "ast_drv.h"
+
+/* Std. Table Index Definition */
+#define TextModeIndex 0
+#define EGAModeIndex 1
+#define VGAModeIndex 2
+#define HiCModeIndex 3
+#define TrueCModeIndex 4
+
+static const struct ast_vbios_stdtable vbios_stdtable[] = {
+ /* MD_2_3_400 */
+ {
+ 0x67,
+ {0x00,0x03,0x00,0x02},
+ {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f,
+ 0x00,0x4f,0x0d,0x0e,0x00,0x00,0x00,0x00,
+ 0x9c,0x8e,0x8f,0x28,0x1f,0x96,0xb9,0xa3,
+ 0xff},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
+ 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+ 0x0c,0x00,0x0f,0x08},
+ {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00,
+ 0xff}
+ },
+ /* Mode12/ExtEGATable */
+ {
+ 0xe3,
+ {0x01,0x0f,0x00,0x06},
+ {0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e,
+ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xe9,0x8b,0xdf,0x28,0x00,0xe7,0x04,0xe3,
+ 0xff},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
+ 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+ 0x01,0x00,0x0f,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
+ 0xff}
+ },
+ /* ExtVGATable */
+ {
+ 0x2f,
+ {0x01,0x0f,0x00,0x0e},
+ {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
+ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
+ 0xff},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+ 0x01,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
+ 0xff}
+ },
+ /* ExtHiCTable */
+ {
+ 0x2f,
+ {0x01,0x0f,0x00,0x0e},
+ {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
+ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
+ 0xff},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+ 0x01,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
+ 0xff}
+ },
+ /* ExtTrueCTable */
+ {
+ 0x2f,
+ {0x01,0x0f,0x00,0x0e},
+ {0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
+ 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
+ 0xff},
+ {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+ 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+ 0x01,0x00,0x00,0x00},
+ {0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
+ 0xff}
+ },
+};
+
+#endif
diff --git a/drivers/gpu/drm/ast/ast_vbios.c b/drivers/gpu/drm/ast/ast_vbios.c
new file mode 100644
index 000000000..0953e6dd3
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_vbios.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2005 ASPEED Technology Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the authors not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. The authors makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ast_drv.h"
+#include "ast_vbios.h"
+
+/* 4:3 */
+
+static const struct ast_vbios_enhtable res_640x480[] = {
+ { 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60 Hz */
+ (SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2e },
+ { 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72 Hz */
+ (SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2e },
+ { 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75 Hz */
+ (SyncNN | Charx8Dot), 75, 3, 0x2e },
+ { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85 Hz */
+ (SyncNN | Charx8Dot), 85, 4, 0x2e },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_800x600[] = {
+ { 1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56 Hz */
+ (SyncPP | Charx8Dot), 56, 1, 0x30 },
+ { 1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60 Hz */
+ (SyncPP | Charx8Dot), 60, 2, 0x30 },
+ { 1040, 800, 56, 120, 666, 600, 37, 6, VCLK50, /* 72 Hz */
+ (SyncPP | Charx8Dot), 72, 3, 0x30 },
+ { 1056, 800, 16, 80, 625, 600, 1, 3, VCLK49_5, /* 75 Hz */
+ (SyncPP | Charx8Dot), 75, 4, 0x30 },
+ { 1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* 85 Hz */
+ (SyncPP | Charx8Dot), 84, 5, 0x30 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1024x768[] = {
+ { 1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60 Hz */
+ (SyncNN | Charx8Dot), 60, 1, 0x31 },
+ { 1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70 Hz */
+ (SyncNN | Charx8Dot), 70, 2, 0x31 },
+ { 1312, 1024, 16, 96, 800, 768, 1, 3, VCLK78_75, /* 75 Hz */
+ (SyncPP | Charx8Dot), 75, 3, 0x31 },
+ { 1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* 85 Hz */
+ (SyncPP | Charx8Dot), 84, 4, 0x31 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1152x864[] = {
+ { 1600, 1152, 64, 128, 900, 864, 1, 3, VCLK108, /* 75 Hz */
+ (SyncPP | Charx8Dot | NewModeInfo), 75, 1, 0x3b },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1280x1024[] = {
+ { 1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60 Hz */
+ (SyncPP | Charx8Dot), 60, 1, 0x32 },
+ { 1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75 Hz */
+ (SyncPP | Charx8Dot), 75, 2, 0x32 },
+ { 1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* 85 Hz */
+ (SyncPP | Charx8Dot), 85, 3, 0x32 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1600x1200[] = {
+ { 2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60 Hz */
+ (SyncPP | Charx8Dot), 60, 1, 0x33 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+/* 16:9 */
+
+static const struct ast_vbios_enhtable res_1360x768[] = {
+ { 1792, 1360, 64, 112, 795, 768, 3, 6, VCLK85_5, /* 60 Hz */
+ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1600x900[] = {
+ { 1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60 Hz CVT RB */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo |
+ AST2500PreCatchCRT), 60, 1, 0x3a },
+ { 2112, 1600, 88, 168, 934, 900, 3, 5, VCLK118_25, /* 60 Hz CVT */
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3a },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1920x1080[] = {
+ { 2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60 Hz */
+ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo |
+ AST2500PreCatchCRT), 60, 1, 0x38 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+/* 16:10 */
+
+static const struct ast_vbios_enhtable res_1280x800[] = {
+ { 1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60 Hz RB */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo |
+ AST2500PreCatchCRT), 60, 1, 0x35 },
+ { 1680, 1280, 72, 128, 831, 800, 3, 6, VCLK83_5, /* 60 Hz */
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1440x900[] = {
+ { 1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60 Hz RB */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo |
+ AST2500PreCatchCRT), 60, 1, 0x36 },
+ { 1904, 1440, 80, 152, 934, 900, 3, 6, VCLK106_5, /* 60 Hz */
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1680x1050[] = {
+ { 1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60 Hz RB */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo |
+ AST2500PreCatchCRT), 60, 1, 0x37 },
+ { 2240, 1680, 104, 176, 1089, 1050, 3, 6, VCLK146_25, /* 60 Hz */
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+static const struct ast_vbios_enhtable res_1920x1200[] = {
+ { 2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60 Hz RB*/
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo |
+ AST2500PreCatchCRT), 60, 1, 0x34 },
+ AST_VBIOS_INVALID_MODE, /* end */
+};
+
+/*
+ * VBIOS mode tables
+ */
+
+static const struct ast_vbios_enhtable *res_table_wuxga[] = {
+ &res_1920x1200[0],
+ NULL,
+};
+
+static const struct ast_vbios_enhtable *res_table_fullhd[] = {
+ &res_1920x1080[0],
+ NULL,
+};
+
+static const struct ast_vbios_enhtable *res_table_wsxga_p[] = {
+ &res_1280x800[0],
+ &res_1360x768[0],
+ &res_1440x900[0],
+ &res_1600x900[0],
+ &res_1680x1050[0],
+ NULL,
+};
+
+static const struct ast_vbios_enhtable *res_table[] = {
+ &res_640x480[0],
+ &res_800x600[0],
+ &res_1024x768[0],
+ &res_1152x864[0],
+ &res_1280x1024[0],
+ &res_1600x1200[0],
+ NULL,
+};
+
+static const struct ast_vbios_enhtable *
+__ast_vbios_find_mode_table(const struct ast_vbios_enhtable **vmode_tables,
+ unsigned int hdisplay,
+ unsigned int vdisplay)
+{
+ while (*vmode_tables) {
+ if ((*vmode_tables)->hde == hdisplay && (*vmode_tables)->vde == vdisplay)
+ return *vmode_tables;
+ ++vmode_tables;
+ }
+
+ return NULL;
+}
+
+static const struct ast_vbios_enhtable *ast_vbios_find_mode_table(const struct ast_device *ast,
+ unsigned int hdisplay,
+ unsigned int vdisplay)
+{
+ const struct ast_vbios_enhtable *vmode_table = NULL;
+
+ if (ast->support_wuxga)
+ vmode_table = __ast_vbios_find_mode_table(res_table_wuxga, hdisplay, vdisplay);
+ if (!vmode_table && ast->support_fullhd)
+ vmode_table = __ast_vbios_find_mode_table(res_table_fullhd, hdisplay, vdisplay);
+ if (!vmode_table && ast->support_wsxga_p)
+ vmode_table = __ast_vbios_find_mode_table(res_table_wsxga_p, hdisplay, vdisplay);
+ if (!vmode_table)
+ vmode_table = __ast_vbios_find_mode_table(res_table, hdisplay, vdisplay);
+
+ return vmode_table;
+}
+
+const struct ast_vbios_enhtable *ast_vbios_find_mode(const struct ast_device *ast,
+ const struct drm_display_mode *mode)
+{
+ const struct ast_vbios_enhtable *best_vmode = NULL;
+ const struct ast_vbios_enhtable *vmode_table;
+ const struct ast_vbios_enhtable *vmode;
+ u32 refresh_rate;
+
+ vmode_table = ast_vbios_find_mode_table(ast, mode->hdisplay, mode->vdisplay);
+ if (!vmode_table)
+ return NULL;
+
+ refresh_rate = drm_mode_vrefresh(mode);
+
+ for (vmode = vmode_table; ast_vbios_mode_is_valid(vmode); ++vmode) {
+ if (((mode->flags & DRM_MODE_FLAG_NVSYNC) && (vmode->flags & PVSync)) ||
+ ((mode->flags & DRM_MODE_FLAG_PVSYNC) && (vmode->flags & NVSync)) ||
+ ((mode->flags & DRM_MODE_FLAG_NHSYNC) && (vmode->flags & PHSync)) ||
+ ((mode->flags & DRM_MODE_FLAG_PHSYNC) && (vmode->flags & NHSync))) {
+ continue;
+ }
+ if (vmode->refresh_rate <= refresh_rate &&
+ (!best_vmode || vmode->refresh_rate > best_vmode->refresh_rate))
+ best_vmode = vmode;
+ }
+
+ return best_vmode;
+}
diff --git a/drivers/gpu/drm/ast/ast_vbios.h b/drivers/gpu/drm/ast/ast_vbios.h
new file mode 100644
index 000000000..8cf025010
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_vbios.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2005 ASPEED Technology Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the authors not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. The authors makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/* Ported from xf86-video-ast driver */
+
+#ifndef AST_VBIOS_H
+#define AST_VBIOS_H
+
+#include <linux/types.h>
+
+struct ast_device;
+struct drm_display_mode;
+
+#define Charx8Dot 0x00000001
+#define HalfDCLK 0x00000002
+#define DoubleScanMode 0x00000004
+#define LineCompareOff 0x00000008
+#define HBorder 0x00000020
+#define VBorder 0x00000010
+#define WideScreenMode 0x00000100
+#define NewModeInfo 0x00000200
+#define NHSync 0x00000400
+#define PHSync 0x00000800
+#define NVSync 0x00001000
+#define PVSync 0x00002000
+#define SyncPP (PVSync | PHSync)
+#define SyncPN (PVSync | NHSync)
+#define SyncNP (NVSync | PHSync)
+#define SyncNN (NVSync | NHSync)
+#define AST2500PreCatchCRT 0x00004000
+
+/* DCLK Index */
+#define VCLK25_175 0x00
+#define VCLK28_322 0x01
+#define VCLK31_5 0x02
+#define VCLK36 0x03
+#define VCLK40 0x04
+#define VCLK49_5 0x05
+#define VCLK50 0x06
+#define VCLK56_25 0x07
+#define VCLK65 0x08
+#define VCLK75 0x09
+#define VCLK78_75 0x0a
+#define VCLK94_5 0x0b
+#define VCLK108 0x0c
+#define VCLK135 0x0d
+#define VCLK157_5 0x0e
+#define VCLK162 0x0f
+/* #define VCLK193_25 0x10 */
+#define VCLK154 0x10
+#define VCLK83_5 0x11
+#define VCLK106_5 0x12
+#define VCLK146_25 0x13
+#define VCLK148_5 0x14
+#define VCLK71 0x15
+#define VCLK88_75 0x16
+#define VCLK119 0x17
+#define VCLK85_5 0x18
+#define VCLK97_75 0x19
+#define VCLK118_25 0x1a
+
+struct ast_vbios_enhtable {
+ u32 ht;
+ u32 hde;
+ u32 hfp;
+ u32 hsync;
+ u32 vt;
+ u32 vde;
+ u32 vfp;
+ u32 vsync;
+ u32 dclk_index;
+ u32 flags;
+ u32 refresh_rate;
+ u32 refresh_rate_index;
+ u32 mode_id;
+};
+
+#define AST_VBIOS_INVALID_MODE \
+ {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}
+
+static inline bool ast_vbios_mode_is_valid(const struct ast_vbios_enhtable *vmode)
+{
+ return vmode->ht && vmode->vt && vmode->refresh_rate;
+}
+
+const struct ast_vbios_enhtable *ast_vbios_find_mode(const struct ast_device *ast,
+ const struct drm_display_mode *mode);
+
+#endif
diff --git a/drivers/gpu/drm/ast/ast_vga.c b/drivers/gpu/drm/ast/ast_vga.c
new file mode 100644
index 000000000..abe0fff84
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_vga.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: MIT
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "ast_ddc.h"
+#include "ast_drv.h"
+
+/*
+ * Encoder
+ */
+
+static const struct drm_encoder_funcs ast_vga_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/*
+ * Connector
+ */
+
+static int ast_vga_connector_helper_get_modes(struct drm_connector *connector)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ int count;
+
+ if (ast_connector->physical_status == connector_status_connected) {
+ count = drm_connector_helper_get_modes(connector);
+ } else {
+ drm_edid_connector_update(connector, NULL);
+
+ /*
+ * There's no EDID data without a connected monitor. Set BMC-
+ * compatible modes in this case. The XGA default resolution
+ * should work well for all BMCs.
+ */
+ count = drm_add_modes_noedid(connector, 4096, 4096);
+ if (count)
+ drm_set_preferred_mode(connector, 1024, 768);
+ }
+
+ return count;
+}
+
+static int ast_vga_connector_helper_detect_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct ast_connector *ast_connector = to_ast_connector(connector);
+ enum drm_connector_status status;
+
+ status = drm_connector_helper_detect_from_ddc(connector, ctx, force);
+
+ if (status != ast_connector->physical_status)
+ ++connector->epoch_counter;
+ ast_connector->physical_status = status;
+
+ return connector_status_connected;
+}
+
+static const struct drm_connector_helper_funcs ast_vga_connector_helper_funcs = {
+ .get_modes = ast_vga_connector_helper_get_modes,
+ .detect_ctx = ast_vga_connector_helper_detect_ctx,
+};
+
+static const struct drm_connector_funcs ast_vga_connector_funcs = {
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/*
+ * Output
+ */
+
+int ast_vga_output_init(struct ast_device *ast)
+{
+ struct drm_device *dev = &ast->base;
+ struct drm_crtc *crtc = &ast->crtc;
+ struct i2c_adapter *ddc;
+ struct drm_encoder *encoder;
+ struct ast_connector *ast_connector;
+ struct drm_connector *connector;
+ int ret;
+
+ /* DDC */
+
+ ddc = ast_ddc_create(ast);
+ if (IS_ERR(ddc))
+ return PTR_ERR(ddc);
+
+ /* encoder */
+
+ encoder = &ast->output.vga.encoder;
+ ret = drm_encoder_init(dev, encoder, &ast_vga_encoder_funcs,
+ DRM_MODE_ENCODER_DAC, NULL);
+ if (ret)
+ return ret;
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ /* connector */
+
+ ast_connector = &ast->output.vga.connector;
+ connector = &ast_connector->base;
+ ret = drm_connector_init_with_ddc(dev, connector, &ast_vga_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA, ddc);
+ if (ret)
+ return ret;
+ drm_connector_helper_add(connector, &ast_vga_connector_helper_funcs);
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+
+ ast_connector->physical_status = connector->status;
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ return ret;
+
+ return 0;
+}