diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/gpu/drm/ast | |
parent | Initial commit. (diff) | |
download | linux-upstream.tar.xz linux-upstream.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/ast')
-rw-r--r-- | drivers/gpu/drm/ast/Kconfig | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/Makefile | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_cursor.c | 290 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_dp501.c | 517 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_dram_tables.h | 207 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_drv.c | 256 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_drv.h | 337 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_main.c | 466 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_mm.c | 107 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_mode.c | 1288 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_post.c | 2080 | ||||
-rw-r--r-- | drivers/gpu/drm/ast/ast_tables.h | 345 |
12 files changed, 5917 insertions, 0 deletions
diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig new file mode 100644 index 000000000..fbcf2f45c --- /dev/null +++ b/drivers/gpu/drm/ast/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_AST + tristate "AST server chips" + depends on DRM && PCI && MMU + select DRM_KMS_HELPER + select DRM_VRAM_HELPER + select DRM_TTM + select DRM_TTM_HELPER + 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..2265a8a62 --- /dev/null +++ b/drivers/gpu/drm/ast/Makefile @@ -0,0 +1,9 @@ +# 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_cursor.o ast_drv.o ast_main.o ast_mm.o ast_mode.o ast_post.o \ + ast_dp501.o + +obj-$(CONFIG_DRM_AST) := ast.o diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c new file mode 100644 index 000000000..e0f461391 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_cursor.c @@ -0,0 +1,290 @@ +/* + * 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 <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> + +#include "ast_drv.h" + +static void ast_cursor_fini(struct ast_private *ast) +{ + size_t i; + struct drm_gem_vram_object *gbo; + + for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { + gbo = ast->cursor.gbo[i]; + drm_gem_vram_vunmap(gbo, ast->cursor.vaddr[i]); + drm_gem_vram_unpin(gbo); + drm_gem_vram_put(gbo); + } +} + +static void ast_cursor_release(struct drm_device *dev, void *ptr) +{ + struct ast_private *ast = to_ast_private(dev); + + ast_cursor_fini(ast); +} + +/* + * Allocate cursor BOs and pins them at the end of VRAM. + */ +int ast_cursor_init(struct ast_private *ast) +{ + struct drm_device *dev = &ast->base; + size_t size, i; + struct drm_gem_vram_object *gbo; + void __iomem *vaddr; + int ret; + + size = roundup(AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE, PAGE_SIZE); + + for (i = 0; i < ARRAY_SIZE(ast->cursor.gbo); ++i) { + gbo = drm_gem_vram_create(dev, size, 0); + if (IS_ERR(gbo)) { + ret = PTR_ERR(gbo); + goto err_drm_gem_vram_put; + } + ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM | + DRM_GEM_VRAM_PL_FLAG_TOPDOWN); + if (ret) { + drm_gem_vram_put(gbo); + goto err_drm_gem_vram_put; + } + vaddr = drm_gem_vram_vmap(gbo); + if (IS_ERR(vaddr)) { + ret = PTR_ERR(vaddr); + drm_gem_vram_unpin(gbo); + drm_gem_vram_put(gbo); + goto err_drm_gem_vram_put; + } + + ast->cursor.gbo[i] = gbo; + ast->cursor.vaddr[i] = vaddr; + } + + return drmm_add_action_or_reset(dev, ast_cursor_release, NULL); + +err_drm_gem_vram_put: + while (i) { + --i; + gbo = ast->cursor.gbo[i]; + drm_gem_vram_vunmap(gbo, ast->cursor.vaddr[i]); + drm_gem_vram_unpin(gbo); + drm_gem_vram_put(gbo); + } + return ret; +} + +static void update_cursor_image(u8 __iomem *dst, const u8 *src, int width, int height) +{ + union { + u32 ul; + u8 b[4]; + } srcdata32[2], data32; + union { + u16 us; + u8 b[2]; + } data16; + u32 csum = 0; + s32 alpha_dst_delta, last_alpha_dst_delta; + u8 __iomem *dstxor; + const u8 *srcxor; + int i, j; + u32 per_pixel_copy, two_pixel_copy; + + alpha_dst_delta = AST_MAX_HWC_WIDTH << 1; + last_alpha_dst_delta = alpha_dst_delta - (width << 1); + + srcxor = src; + dstxor = (u8 *)dst + last_alpha_dst_delta + (AST_MAX_HWC_HEIGHT - height) * alpha_dst_delta; + per_pixel_copy = width & 1; + two_pixel_copy = width >> 1; + + for (j = 0; j < height; j++) { + for (i = 0; i < two_pixel_copy; i++) { + srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; + srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0; + data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); + data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); + data32.b[2] = srcdata32[1].b[1] | (srcdata32[1].b[0] >> 4); + data32.b[3] = srcdata32[1].b[3] | (srcdata32[1].b[2] >> 4); + + writel(data32.ul, dstxor); + csum += data32.ul; + + dstxor += 4; + srcxor += 8; + + } + + for (i = 0; i < per_pixel_copy; i++) { + srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0; + data16.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4); + data16.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4); + writew(data16.us, dstxor); + csum += (u32)data16.us; + + dstxor += 2; + srcxor += 4; + } + dstxor += last_alpha_dst_delta; + } + + /* 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); +} + +int ast_cursor_blit(struct ast_private *ast, struct drm_framebuffer *fb) +{ + struct drm_device *dev = &ast->base; + struct drm_gem_vram_object *gbo; + int ret; + void *src; + void __iomem *dst; + + if (drm_WARN_ON_ONCE(dev, fb->width > AST_MAX_HWC_WIDTH) || + drm_WARN_ON_ONCE(dev, fb->height > AST_MAX_HWC_HEIGHT)) + return -EINVAL; + + gbo = drm_gem_vram_of_gem(fb->obj[0]); + + ret = drm_gem_vram_pin(gbo, 0); + if (ret) + return ret; + src = drm_gem_vram_vmap(gbo); + if (IS_ERR(src)) { + ret = PTR_ERR(src); + goto err_drm_gem_vram_unpin; + } + + dst = ast->cursor.vaddr[ast->cursor.next_index]; + + /* do data transfer to cursor BO */ + update_cursor_image(dst, src, fb->width, fb->height); + + drm_gem_vram_vunmap(gbo, src); + drm_gem_vram_unpin(gbo); + + return 0; + +err_drm_gem_vram_unpin: + drm_gem_vram_unpin(gbo); + return ret; +} + +static void ast_cursor_set_base(struct ast_private *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_CRTC_PORT, 0xc8, addr0); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, addr1); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, addr2); +} + +void ast_cursor_page_flip(struct ast_private *ast) +{ + struct drm_device *dev = &ast->base; + struct drm_gem_vram_object *gbo; + s64 off; + + gbo = ast->cursor.gbo[ast->cursor.next_index]; + + off = drm_gem_vram_offset(gbo); + if (drm_WARN_ON_ONCE(dev, off < 0)) + return; /* Bug: we didn't pin the cursor HW BO to VRAM. */ + + ast_cursor_set_base(ast, off); + + ++ast->cursor.next_index; + ast->cursor.next_index %= ARRAY_SIZE(ast->cursor.gbo); +} + +static void ast_cursor_set_location(struct ast_private *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_CRTC_PORT, 0xc2, x_offset); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc3, y_offset); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc4, x0); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc5, x1); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc6, y0); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, y1); +} + +void ast_cursor_show(struct ast_private *ast, int x, int y, + unsigned int offset_x, unsigned int offset_y) +{ + u8 x_offset, y_offset; + u8 __iomem *dst; + u8 __iomem *sig; + u8 jreg; + + dst = ast->cursor.vaddr[ast->cursor.next_index]; + + sig = dst + AST_HWC_SIZE; + writel(x, sig + AST_HWC_SIGNATURE_X); + writel(y, sig + AST_HWC_SIGNATURE_Y); + + if (x < 0) { + x_offset = (-x) + offset_x; + x = 0; + } else { + x_offset = offset_x; + } + if (y < 0) { + y_offset = (-y) + offset_y; + y = 0; + } else { + y_offset = offset_y; + } + + ast_cursor_set_location(ast, x, y, x_offset, y_offset); + + /* dummy write to fire HWC */ + jreg = 0x02 | + 0x01; /* enable ARGB4444 cursor */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg); +} + +void ast_cursor_hide(struct ast_private *ast) +{ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00); +} diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c new file mode 100644 index 000000000..cd93c44f2 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/module.h> + +#include "ast_drv.h" + +MODULE_FIRMWARE("ast_dp501_fw.bin"); + +static void ast_release_firmware(void *data) +{ + struct ast_private *ast = data; + + release_firmware(ast->dp501_fw); + ast->dp501_fw = NULL; +} + +static int ast_load_dp501_microcode(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + 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_private *ast) +{ + u8 sendack; + sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff); + sendack |= 0x80; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack); +} + +static void send_nack(struct ast_private *ast) +{ + u8 sendack; + sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff); + sendack &= ~0x80; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack); +} + +static bool wait_ack(struct ast_private *ast) +{ + u8 waitack; + u32 retry = 0; + do { + waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); + waitack &= 0x80; + udelay(100); + } while ((!waitack) && (retry++ < 1000)); + + if (retry < 1000) + return true; + else + return false; +} + +static bool wait_nack(struct ast_private *ast) +{ + u8 waitack; + u32 retry = 0; + do { + waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 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_private *ast) +{ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40); +} + +static void clear_cmd_trigger(struct ast_private *ast) +{ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00); +} + +#if 0 +static bool wait_fw_ready(struct ast_private *ast) +{ + u8 waitready; + u32 retry = 0; + do { + waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 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 drm_device *dev, u8 data) +{ + struct ast_private *ast = to_ast_private(dev); + int retry = 0; + if (wait_nack(ast)) { + send_nack(ast); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 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 drm_device *dev, u8 data) +{ + struct ast_private *ast = to_ast_private(dev); + + if (wait_nack(ast)) { + send_nack(ast); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 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_private *ast = to_ast_private(dev); + u8 tmp; + + *data = 0; + + if (wait_ack(ast) == false) + return false; + tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 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_private *ast) +{ + send_nack(ast); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00); +} +#endif + +void ast_set_dp501_video_output(struct drm_device *dev, u8 mode) +{ + ast_write_cmd(dev, 0x40); + ast_write_data(dev, mode); + + msleep(10); +} + +static u32 get_fw_base(struct ast_private *ast) +{ + return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff; +} + +bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) +{ + struct ast_private *ast = to_ast_private(dev); + u32 i, data; + u32 boot_address; + + if (ast->config_mode != ast_use_p2a) + return false; + + data = ast_mindwm(ast, 0x1e6e2100) & 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 drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + 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, 0x1e6e2100) & 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(dev) < 0) + return false; + + fw_addr = (u8 *)ast->dp501_fw->data; + len = ast->dp501_fw->size; + } + /* Get BootAddress */ + ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8); + data = ast_mindwm(ast, 0x1e6e0004); + 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, 0x1e6e2000, 0x1688a8a8); + + /* Launch FW */ + ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address); + ast_moutdwm(ast, 0x1e6e2100, 1); + + /* Update Scratch */ + data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff; /* D[11:9] = 100b: UEFI handling */ + data |= 0x800; + ast_moutdwm(ast, 0x1e6e2040, data); + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */ + jreg |= 0x02; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg); + } + return true; +} + +u8 ast_get_dp501_max_clk(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u32 boot_address, offset, data; + u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; + u32 *plinkcap; + + 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) /* version: 1x */ + return maxclk; + + /* Read Link Capability */ + offset = AST_DP501_LINKRATE; + plinkcap = (u32 *)linkcap; + *plinkcap = ast_mindwm(ast, boot_address + offset); + if (linkcap[2] == 0) { + linkrate = linkcap[0]; + linklanes = linkcap[1]; + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); + if (data > 0xff) + data = 0xff; + maxclk = (u8)data; + } + } else { + if (!ast->dp501_fw_buf) + return AST_DP501_DEFAULT_DCLK; /* 1024x768 as default */ + + /* 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) /* version: 1x */ + return maxclk; + + /* Read Link Capability */ + offset = AST_DP501_LINKRATE; + plinkcap = (u32 *)linkcap; + *plinkcap = readl(ast->dp501_fw_buf + offset); + if (linkcap[2] == 0) { + linkrate = linkcap[0]; + linklanes = linkcap[1]; + data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes); + if (data > 0xff) + data = 0xff; + maxclk = (u8)data; + } + } + return maxclk; +} + +bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) +{ + struct ast_private *ast = to_ast_private(dev); + u32 i, boot_address, offset, data; + u32 *pEDIDidx; + + 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; + + /* Read EDID */ + offset = AST_DP501_EDID_DATA; + for (i = 0; i < 128; i += 4) { + data = ast_mindwm(ast, boot_address + offset + i); + pEDIDidx = (u32 *)(ediddata + i); + *pEDIDidx = data; + } + } 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; + + /* Read EDID */ + offset = AST_DP501_EDID_DATA; + for (i = 0; i < 128; i += 4) { + data = readl(ast->dp501_fw_buf + offset + i); + pEDIDidx = (u32 *)(ediddata + i); + *pEDIDidx = data; + } + } + + return true; +} + +static bool ast_init_dvo(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u8 jreg; + u32 data; + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688a8a8); + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if (!(jreg & 0x80)) { + /* Init SCU DVO Settings */ + data = ast_read32(ast, 0x12008); + /* delay phase */ + data &= 0xfffff8ff; + data |= 0x00000500; + ast_write32(ast, 0x12008, data); + + if (ast->chip == AST2300) { + data = ast_read32(ast, 0x12084); + /* multi-pins for DVO single-edge */ + data |= 0xfffe0000; + ast_write32(ast, 0x12084, data); + + data = ast_read32(ast, 0x12088); + /* multi-pins for DVO single-edge */ + data |= 0x000fffff; + ast_write32(ast, 0x12088, data); + + data = ast_read32(ast, 0x12090); + /* multi-pins for DVO single-edge */ + data &= 0xffffffcf; + data |= 0x00000020; + ast_write32(ast, 0x12090, data); + } else { /* AST2400 */ + data = ast_read32(ast, 0x12088); + /* multi-pins for DVO single-edge */ + data |= 0x30000000; + ast_write32(ast, 0x12088, data); + + data = ast_read32(ast, 0x1208c); + /* multi-pins for DVO single-edge */ + data |= 0x000000cf; + ast_write32(ast, 0x1208c, data); + + data = ast_read32(ast, 0x120a4); + /* multi-pins for DVO single-edge */ + data |= 0xffff0000; + ast_write32(ast, 0x120a4, data); + + data = ast_read32(ast, 0x120a8); + /* multi-pins for DVO single-edge */ + data |= 0x0000000f; + ast_write32(ast, 0x120a8, data); + + data = ast_read32(ast, 0x12094); + /* multi-pins for DVO single-edge */ + data |= 0x00000002; + ast_write32(ast, 0x12094, data); + } + } + + /* Force to DVO */ + data = ast_read32(ast, 0x1202c); + data &= 0xfffbffff; + ast_write32(ast, 0x1202c, data); + + /* Init VGA DVO Settings */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); + return true; +} + + +static void ast_init_analog(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u32 data; + + /* + * Set DAC source to VGA mode in SCU2C via the P2A + * bridge. First configure the P2U to target the SCU + * in case it isn't at this stage. + */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + + /* Then unlock the SCU with the magic password */ + ast_write32(ast, 0x12000, 0x1688a8a8); + ast_write32(ast, 0x12000, 0x1688a8a8); + ast_write32(ast, 0x12000, 0x1688a8a8); + + /* Finally, clear bits [17:16] of SCU2c */ + data = ast_read32(ast, 0x1202c); + data &= 0xfffcffff; + ast_write32(ast, 0, data); + + /* Disable DVO */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x00); +} + +void ast_init_3rdtx(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u8 jreg; + + if (ast->chip == AST2300 || ast->chip == AST2400) { + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + switch (jreg & 0x0e) { + case 0x04: + ast_init_dvo(dev); + break; + case 0x08: + ast_launch_m68k(dev); + break; + case 0x0c: + ast_init_dvo(dev); + break; + default: + if (ast->tx_chip_type == AST_TX_SIL164) + ast_init_dvo(dev); + else + ast_init_analog(dev); + } + } +} diff --git a/drivers/gpu/drm/ast/ast_dram_tables.h b/drivers/gpu/drm/ast/ast_dram_tables.h new file mode 100644 index 000000000..1e9ac9d6d --- /dev/null +++ b/drivers/gpu/drm/ast/ast_dram_tables.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef AST_DRAM_TABLES_H +#define AST_DRAM_TABLES_H + +/* DRAM timing tables */ +struct ast_dramstruct { + u16 index; + u32 data; +}; + +static const struct ast_dramstruct ast2000_dram_table_data[] = { + { 0x0108, 0x00000000 }, + { 0x0120, 0x00004a21 }, + { 0xFF00, 0x00000043 }, + { 0x0000, 0xFFFFFFFF }, + { 0x0004, 0x00000089 }, + { 0x0008, 0x22331353 }, + { 0x000C, 0x0d07000b }, + { 0x0010, 0x11113333 }, + { 0x0020, 0x00110350 }, + { 0x0028, 0x1e0828f0 }, + { 0x0024, 0x00000001 }, + { 0x001C, 0x00000000 }, + { 0x0014, 0x00000003 }, + { 0xFF00, 0x00000043 }, + { 0x0018, 0x00000131 }, + { 0x0014, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x0018, 0x00000031 }, + { 0x0014, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x0028, 0x1e0828f1 }, + { 0x0024, 0x00000003 }, + { 0x002C, 0x1f0f28fb }, + { 0x0030, 0xFFFFFE01 }, + { 0xFFFF, 0xFFFFFFFF } +}; + +static const struct ast_dramstruct ast1100_dram_table_data[] = { + { 0x2000, 0x1688a8a8 }, + { 0x2020, 0x000041f0 }, + { 0xFF00, 0x00000043 }, + { 0x0000, 0xfc600309 }, + { 0x006C, 0x00909090 }, + { 0x0064, 0x00050000 }, + { 0x0004, 0x00000585 }, + { 0x0008, 0x0011030f }, + { 0x0010, 0x22201724 }, + { 0x0018, 0x1e29011a }, + { 0x0020, 0x00c82222 }, + { 0x0014, 0x01001523 }, + { 0x001C, 0x1024010d }, + { 0x0024, 0x00cb2522 }, + { 0x0038, 0xffffff82 }, + { 0x003C, 0x00000000 }, + { 0x0040, 0x00000000 }, + { 0x0044, 0x00000000 }, + { 0x0048, 0x00000000 }, + { 0x004C, 0x00000000 }, + { 0x0050, 0x00000000 }, + { 0x0054, 0x00000000 }, + { 0x0058, 0x00000000 }, + { 0x005C, 0x00000000 }, + { 0x0060, 0x032aa02a }, + { 0x0064, 0x002d3000 }, + { 0x0068, 0x00000000 }, + { 0x0070, 0x00000000 }, + { 0x0074, 0x00000000 }, + { 0x0078, 0x00000000 }, + { 0x007C, 0x00000000 }, + { 0x0034, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x002C, 0x00000732 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000005 }, + { 0x0028, 0x00000007 }, + { 0x0028, 0x00000003 }, + { 0x0028, 0x00000001 }, + { 0x000C, 0x00005a08 }, + { 0x002C, 0x00000632 }, + { 0x0028, 0x00000001 }, + { 0x0030, 0x000003c0 }, + { 0x0028, 0x00000003 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000003 }, + { 0x000C, 0x00005a21 }, + { 0x0034, 0x00007c03 }, + { 0x0120, 0x00004c41 }, + { 0xffff, 0xffffffff }, +}; + +static const struct ast_dramstruct ast2100_dram_table_data[] = { + { 0x2000, 0x1688a8a8 }, + { 0x2020, 0x00004120 }, + { 0xFF00, 0x00000043 }, + { 0x0000, 0xfc600309 }, + { 0x006C, 0x00909090 }, + { 0x0064, 0x00070000 }, + { 0x0004, 0x00000489 }, + { 0x0008, 0x0011030f }, + { 0x0010, 0x32302926 }, + { 0x0018, 0x274c0122 }, + { 0x0020, 0x00ce2222 }, + { 0x0014, 0x01001523 }, + { 0x001C, 0x1024010d }, + { 0x0024, 0x00cb2522 }, + { 0x0038, 0xffffff82 }, + { 0x003C, 0x00000000 }, + { 0x0040, 0x00000000 }, + { 0x0044, 0x00000000 }, + { 0x0048, 0x00000000 }, + { 0x004C, 0x00000000 }, + { 0x0050, 0x00000000 }, + { 0x0054, 0x00000000 }, + { 0x0058, 0x00000000 }, + { 0x005C, 0x00000000 }, + { 0x0060, 0x0f2aa02a }, + { 0x0064, 0x003f3005 }, + { 0x0068, 0x02020202 }, + { 0x0070, 0x00000000 }, + { 0x0074, 0x00000000 }, + { 0x0078, 0x00000000 }, + { 0x007C, 0x00000000 }, + { 0x0034, 0x00000001 }, + { 0xFF00, 0x00000043 }, + { 0x002C, 0x00000942 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000005 }, + { 0x0028, 0x00000007 }, + { 0x0028, 0x00000003 }, + { 0x0028, 0x00000001 }, + { 0x000C, 0x00005a08 }, + { 0x002C, 0x00000842 }, + { 0x0028, 0x00000001 }, + { 0x0030, 0x000003c0 }, + { 0x0028, 0x00000003 }, + { 0x0030, 0x00000040 }, + { 0x0028, 0x00000003 }, + { 0x000C, 0x00005a21 }, + { 0x0034, 0x00007c03 }, + { 0x0120, 0x00005061 }, + { 0xffff, 0xffffffff }, +}; + +/* + * 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 */ +}; + +#endif diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c new file mode 100644 index 000000000..59d2466d4 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -0,0 +1,256 @@ +/* + * 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/console.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_probe_helper.h> + +#include "ast_drv.h" + +int ast_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, ast_modeset, int, 0400); + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(ast_fops); + +static struct drm_driver ast_driver = { + .driver_features = DRIVER_ATOMIC | + DRIVER_GEM | + DRIVER_MODESET, + + .fops = &ast_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + DRM_GEM_VRAM_DRIVER +}; + +/* + * 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 void ast_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + if (!ap) + return; + + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + drm_fb_helper_remove_conflicting_framebuffers(ap, "astdrmfb", primary); + kfree(ap); +} + +static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct ast_private *ast; + struct drm_device *dev; + int ret; + + ast_kick_out_firmware_fb(pdev); + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ast = ast_device_create(&ast_driver, pdev, ent->driver_data); + if (IS_ERR(ast)) + return PTR_ERR(ast); + dev = &ast->base; + + ret = drm_dev_register(dev, ent->driver_data); + if (ret) + return ret; + + drm_fbdev_generic_setup(dev, 32); + + 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 int ast_drm_freeze(struct drm_device *dev) +{ + int error; + + error = drm_mode_config_helper_suspend(dev); + if (error) + return error; + pci_save_state(dev->pdev); + return 0; +} + +static int ast_drm_thaw(struct drm_device *dev) +{ + ast_post_gpu(dev); + + return drm_mode_config_helper_resume(dev); +} + +static int ast_drm_resume(struct drm_device *dev) +{ + int ret; + + if (pci_enable_device(dev->pdev)) + return -EIO; + + ret = ast_drm_thaw(dev); + if (ret) + return ret; + return 0; +} + +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, + .driver.pm = &ast_pm_ops, +}; + +static int __init ast_init(void) +{ + if (vgacon_text_force() && ast_modeset == -1) + return -EINVAL; + + if (ast_modeset == 0) + return -EINVAL; + return pci_register_driver(&ast_pci_driver); +} +static void __exit ast_exit(void) +{ + pci_unregister_driver(&ast_pci_driver); +} + +module_init(ast_init); +module_exit(ast_exit); + +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..b68b1ddfe --- /dev/null +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -0,0 +1,337 @@ +/* + * 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/types.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.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 <drm/drm_fb_helper.h> + +#define DRIVER_AUTHOR "Dave Airlie" + +#define DRIVER_NAME "ast" +#define DRIVER_DESC "AST" +#define DRIVER_DATE "20120228" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 0 + +#define PCI_CHIP_AST2000 0x2000 +#define PCI_CHIP_AST2100 0x2010 + + +enum ast_chip { + AST2000, + AST2100, + AST1100, + AST2200, + AST2150, + AST2300, + AST2400, + AST2500, +}; + +enum ast_tx_chip { + AST_TX_NONE, + AST_TX_SIL164, + AST_TX_ITE66121, + AST_TX_DP501, +}; + +#define AST_DRAM_512Mx16 0 +#define AST_DRAM_1Gx16 1 +#define AST_DRAM_512Mx32 2 +#define AST_DRAM_1Gx32 3 +#define AST_DRAM_2Gx16 6 +#define AST_DRAM_4Gx16 7 +#define AST_DRAM_8Gx16 8 + + +#define AST_MAX_HWC_WIDTH 64 +#define AST_MAX_HWC_HEIGHT 64 + +#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH * AST_MAX_HWC_HEIGHT * 2) +#define AST_HWC_SIGNATURE_SIZE 32 + +#define AST_DEFAULT_HWC_NUM 2 + +/* define for signature structure */ +#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 + +struct ast_i2c_chan { + struct i2c_adapter adapter; + struct drm_device *dev; + struct i2c_algo_bit_data bit; +}; + +struct ast_connector { + struct drm_connector base; + struct ast_i2c_chan *i2c; +}; + +static inline struct ast_connector * +to_ast_connector(struct drm_connector *connector) +{ + return container_of(connector, struct ast_connector, base); +} + +struct ast_private { + struct drm_device base; + + void __iomem *regs; + void __iomem *ioregs; + void __iomem *dp501_fw_buf; + + enum ast_chip chip; + bool vga2_clone; + uint32_t dram_bus_width; + uint32_t dram_type; + uint32_t mclk; + + int fb_mtrr; + + struct { + struct drm_gem_vram_object *gbo[AST_DEFAULT_HWC_NUM]; + void __iomem *vaddr[AST_DEFAULT_HWC_NUM]; + unsigned int next_index; + } cursor; + + struct drm_plane primary_plane; + struct drm_plane cursor_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct ast_connector connector; + + bool support_wide_screen; + enum { + ast_use_p2a, + ast_use_dt, + ast_use_defaults + } config_mode; + + enum ast_tx_chip tx_chip_type; + u8 dp501_maxclk; + u8 *dp501_fw_addr; + const struct firmware *dp501_fw; /* dp501 fw */ +}; + +static inline struct ast_private *to_ast_private(struct drm_device *dev) +{ + return container_of(dev, struct ast_private, base); +} + +struct ast_private *ast_device_create(struct drm_driver *drv, + struct pci_dev *pdev, + unsigned long flags); + +#define AST_IO_AR_PORT_WRITE (0x40) +#define AST_IO_MISC_PORT_WRITE (0x42) +#define AST_IO_VGA_ENABLE_PORT (0x43) +#define AST_IO_SEQ_PORT (0x44) +#define AST_IO_DAC_INDEX_READ (0x47) +#define AST_IO_DAC_INDEX_WRITE (0x48) +#define AST_IO_DAC_DATA (0x49) +#define AST_IO_GR_PORT (0x4E) +#define AST_IO_CRTC_PORT (0x54) +#define AST_IO_INPUT_STATUS1_READ (0x5A) +#define AST_IO_MISC_PORT_READ (0x4C) + +#define AST_IO_MM_OFFSET (0x380) + +#define AST_IO_VGAIR1_VREFRESH BIT(3) + +#define __ast_read(x) \ +static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \ +u##x val = 0;\ +val = ioread##x(ast->regs + reg); \ +return val;\ +} + +__ast_read(8); +__ast_read(16); +__ast_read(32) + +#define __ast_io_read(x) \ +static inline u##x ast_io_read##x(struct ast_private *ast, u32 reg) { \ +u##x val = 0;\ +val = ioread##x(ast->ioregs + reg); \ +return val;\ +} + +__ast_io_read(8); +__ast_io_read(16); +__ast_io_read(32); + +#define __ast_write(x) \ +static inline void ast_write##x(struct ast_private *ast, u32 reg, u##x val) {\ + iowrite##x(val, ast->regs + reg);\ + } + +__ast_write(8); +__ast_write(16); +__ast_write(32); + +#define __ast_io_write(x) \ +static inline void ast_io_write##x(struct ast_private *ast, u32 reg, u##x val) {\ + iowrite##x(val, ast->ioregs + reg);\ + } + +__ast_io_write(8); +__ast_io_write(16); +#undef __ast_io_write + +static inline void ast_set_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t val) +{ + ast_io_write16(ast, base, ((u16)val << 8) | index); +} + +void ast_set_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t mask, uint8_t val); +uint8_t ast_get_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index); +uint8_t ast_get_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, uint8_t mask); + +static inline void ast_open_key(struct ast_private *ast) +{ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8); +} + +#define AST_VIDMEM_SIZE_8M 0x00800000 +#define AST_VIDMEM_SIZE_16M 0x01000000 +#define AST_VIDMEM_SIZE_32M 0x02000000 +#define AST_VIDMEM_SIZE_64M 0x04000000 +#define AST_VIDMEM_SIZE_128M 0x08000000 + +#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M + +struct ast_vbios_stdtable { + u8 misc; + u8 seq[4]; + u8 crtc[25]; + u8 ar[20]; + u8 gr[9]; +}; + +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; +}; + +struct ast_vbios_dclk_info { + u8 param1; + u8 param2; + u8 param3; +}; + +struct ast_vbios_mode_info { + const struct ast_vbios_stdtable *std_table; + const struct ast_vbios_enhtable *enh_table; +}; + +struct ast_crtc_state { + struct drm_crtc_state base; + + /* Last known format of primary plane */ + const struct drm_format_info *format; + + struct ast_vbios_mode_info vbios_mode_info; +}; + +#define to_ast_crtc_state(state) container_of(state, struct ast_crtc_state, base) + +int ast_mode_config_init(struct ast_private *ast); + +#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 + +int ast_mm_init(struct ast_private *ast); + +/* ast post */ +void ast_enable_vga(struct drm_device *dev); +void ast_enable_mmio(struct drm_device *dev); +bool ast_is_vga_enabled(struct drm_device *dev); +void ast_post_gpu(struct drm_device *dev); +u32 ast_mindwm(struct ast_private *ast, u32 r); +void ast_moutdwm(struct ast_private *ast, u32 r, u32 v); +/* ast dp501 */ +void ast_set_dp501_video_output(struct drm_device *dev, u8 mode); +bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size); +bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata); +u8 ast_get_dp501_max_clk(struct drm_device *dev); +void ast_init_3rdtx(struct drm_device *dev); + +/* ast_cursor.c */ +int ast_cursor_init(struct ast_private *ast); +int ast_cursor_blit(struct ast_private *ast, struct drm_framebuffer *fb); +void ast_cursor_page_flip(struct ast_private *ast); +void ast_cursor_show(struct ast_private *ast, int x, int y, + unsigned int offset_x, unsigned int offset_y); +void ast_cursor_hide(struct ast_private *ast); + +#endif diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c new file mode 100644 index 000000000..0d1635115 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_main.c @@ -0,0 +1,466 @@ +/* + * 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_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_managed.h> + +#include "ast_drv.h" + +void ast_set_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t mask, uint8_t val) +{ + u8 tmp; + ast_io_write8(ast, base, index); + tmp = (ast_io_read8(ast, base + 1) & mask) | val; + ast_set_index_reg(ast, base, index, tmp); +} + +uint8_t ast_get_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index) +{ + uint8_t ret; + ast_io_write8(ast, base, index); + ret = ast_io_read8(ast, base + 1); + return ret; +} + +uint8_t ast_get_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, uint8_t mask) +{ + uint8_t ret; + ast_io_write8(ast, base, index); + ret = ast_io_read8(ast, base + 1) & mask; + return ret; +} + +static void ast_detect_config_mode(struct drm_device *dev, u32 *scu_rev) +{ + struct device_node *np = dev->pdev->dev.of_node; + struct ast_private *ast = to_ast_private(dev); + uint32_t data, jregd0, jregd1; + + /* Defaults */ + ast->config_mode = ast_use_defaults; + *scu_rev = 0xffffffff; + + /* Check if we have device-tree properties */ + if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", + scu_rev)) { + /* We do, disable P2A access */ + ast->config_mode = ast_use_dt; + drm_info(dev, "Using device-tree for configuration\n"); + return; + } + + /* Not all families have a P2A bridge */ + if (dev->pdev->device != PCI_CHIP_AST2000) + return; + + /* + * 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] + */ + jregd0 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + jregd1 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (!(jregd0 & 0x80) || !(jregd1 & 0x10)) { + /* Double check it's actually working */ + data = ast_read32(ast, 0xf004); + if ((data != 0xFFFFFFFF) && (data != 0x00)) { + /* P2A works, grab silicon revision */ + ast->config_mode = ast_use_p2a; + + drm_info(dev, "Using P2A bridge for configuration\n"); + + /* Read SCU7c (silicon revision register) */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + *scu_rev = ast_read32(ast, 0x1207c); + return; + } + } + + /* We have a P2A bridge but it's disabled */ + drm_info(dev, "P2A bridge disabled, using default configuration\n"); +} + +static int ast_detect_chip(struct drm_device *dev, bool *need_post) +{ + struct ast_private *ast = to_ast_private(dev); + uint32_t jreg, scu_rev; + + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. We also inform + * our caller that it needs to POST the chip + * (Assumption: VGA not enabled -> need to POST) + */ + if (!ast_is_vga_enabled(dev)) { + ast_enable_vga(dev); + drm_info(dev, "VGA not enabled on entry, requesting chip POST\n"); + *need_post = true; + } else + *need_post = false; + + + /* Enable extended register access */ + ast_open_key(ast); + ast_enable_mmio(dev); + + /* Find out whether P2A works or whether to use device-tree */ + ast_detect_config_mode(dev, &scu_rev); + + /* Identify chipset */ + if (dev->pdev->revision >= 0x40) { + ast->chip = AST2500; + drm_info(dev, "AST 2500 detected\n"); + } else if (dev->pdev->revision >= 0x30) { + ast->chip = AST2400; + drm_info(dev, "AST 2400 detected\n"); + } else if (dev->pdev->revision >= 0x20) { + ast->chip = AST2300; + drm_info(dev, "AST 2300 detected\n"); + } else if (dev->pdev->revision >= 0x10) { + switch (scu_rev & 0x0300) { + case 0x0200: + ast->chip = AST1100; + drm_info(dev, "AST 1100 detected\n"); + break; + case 0x0100: + ast->chip = AST2200; + drm_info(dev, "AST 2200 detected\n"); + break; + case 0x0000: + ast->chip = AST2150; + drm_info(dev, "AST 2150 detected\n"); + break; + default: + ast->chip = AST2100; + drm_info(dev, "AST 2100 detected\n"); + break; + } + ast->vga2_clone = false; + } else { + ast->chip = AST2000; + drm_info(dev, "AST 2000 detected\n"); + } + + /* Check if we support wide screen */ + switch (ast->chip) { + case AST2000: + ast->support_wide_screen = false; + break; + default: + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if (!(jreg & 0x80)) + ast->support_wide_screen = true; + else if (jreg & 0x01) + ast->support_wide_screen = true; + else { + ast->support_wide_screen = false; + if (ast->chip == AST2300 && + (scu_rev & 0x300) == 0x0) /* ast1300 */ + ast->support_wide_screen = true; + if (ast->chip == AST2400 && + (scu_rev & 0x300) == 0x100) /* ast1400 */ + ast->support_wide_screen = true; + if (ast->chip == AST2500 && + scu_rev == 0x100) /* ast2510 */ + ast->support_wide_screen = true; + } + break; + } + + /* Check 3rd Tx option (digital output afaik) */ + ast->tx_chip_type = AST_TX_NONE; + + /* + * 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) { + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff); + if (jreg & 0x80) + ast->tx_chip_type = AST_TX_SIL164; + } + + if ((ast->chip == AST2300) || (ast->chip == AST2400)) { + /* + * On AST2300 and 2400, look the configuration set by the SoC in + * the SOC scratch register #1 bits 11:8 (interestingly marked + * as "reserved" in the spec) + */ + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + switch (jreg) { + case 0x04: + ast->tx_chip_type = AST_TX_SIL164; + break; + case 0x08: + ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL); + if (ast->dp501_fw_addr) { + /* backup firmware */ + if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) { + drmm_kfree(dev, ast->dp501_fw_addr); + ast->dp501_fw_addr = NULL; + } + } + fallthrough; + case 0x0c: + ast->tx_chip_type = AST_TX_DP501; + } + } + + /* Print stuff for diagnostic purposes */ + switch(ast->tx_chip_type) { + case AST_TX_SIL164: + drm_info(dev, "Using Sil164 TMDS transmitter\n"); + break; + case AST_TX_DP501: + drm_info(dev, "Using DP501 DisplayPort transmitter\n"); + break; + default: + drm_info(dev, "Analog VGA only\n"); + } + return 0; +} + +static int ast_get_dram_info(struct drm_device *dev) +{ + struct device_node *np = dev->pdev->dev.of_node; + struct ast_private *ast = to_ast_private(dev); + uint32_t mcr_cfg, mcr_scu_mpll, mcr_scu_strap; + uint32_t denum, num, div, ref_pll, dsel; + + switch (ast->config_mode) { + case ast_use_dt: + /* + * If some properties are missing, use reasonable + * defaults for AST2400 + */ + if (of_property_read_u32(np, "aspeed,mcr-configuration", + &mcr_cfg)) + mcr_cfg = 0x00000577; + if (of_property_read_u32(np, "aspeed,mcr-scu-mpll", + &mcr_scu_mpll)) + mcr_scu_mpll = 0x000050C0; + if (of_property_read_u32(np, "aspeed,mcr-scu-strap", + &mcr_scu_strap)) + mcr_scu_strap = 0; + break; + case ast_use_p2a: + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + mcr_cfg = ast_read32(ast, 0x10004); + mcr_scu_mpll = ast_read32(ast, 0x10120); + mcr_scu_strap = ast_read32(ast, 0x10170); + break; + case ast_use_defaults: + default: + ast->dram_bus_width = 16; + ast->dram_type = AST_DRAM_1Gx16; + if (ast->chip == AST2500) + ast->mclk = 800; + else + ast->mclk = 396; + return 0; + } + + if (mcr_cfg & 0x40) + ast->dram_bus_width = 16; + else + ast->dram_bus_width = 32; + + if (ast->chip == AST2500) { + switch (mcr_cfg & 0x03) { + case 0: + ast->dram_type = AST_DRAM_1Gx16; + break; + default: + case 1: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_4Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_8Gx16; + break; + } + } else if (ast->chip == AST2300 || ast->chip == AST2400) { + switch (mcr_cfg & 0x03) { + case 0: + ast->dram_type = AST_DRAM_512Mx16; + break; + default: + case 1: + ast->dram_type = AST_DRAM_1Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_4Gx16; + break; + } + } else { + switch (mcr_cfg & 0x0c) { + case 0: + case 4: + ast->dram_type = AST_DRAM_512Mx16; + break; + case 8: + if (mcr_cfg & 0x40) + ast->dram_type = AST_DRAM_1Gx16; + else + ast->dram_type = AST_DRAM_512Mx32; + break; + case 0xc: + ast->dram_type = AST_DRAM_1Gx32; + break; + } + } + + if (mcr_scu_strap & 0x2000) + ref_pll = 14318; + else + ref_pll = 12000; + + denum = mcr_scu_mpll & 0x1f; + num = (mcr_scu_mpll & 0x3fe0) >> 5; + dsel = (mcr_scu_mpll & 0xc000) >> 14; + switch (dsel) { + case 3: + div = 0x4; + break; + case 2: + case 1: + div = 0x2; + break; + default: + div = 0x1; + break; + } + ast->mclk = ref_pll * (num + 2) / ((denum + 2) * (div * 1000)); + return 0; +} + +/* + * Run this function as part of the HW device cleanup; not + * when the DRM device gets released. + */ +static void ast_device_release(void *data) +{ + struct ast_private *ast = data; + + /* enable standard VGA decode */ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04); +} + +struct ast_private *ast_device_create(struct drm_driver *drv, + struct pci_dev *pdev, + unsigned long flags) +{ + struct drm_device *dev; + struct ast_private *ast; + bool need_post; + int ret = 0; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_private, base); + if (IS_ERR(ast)) + return ast; + dev = &ast->base; + + dev->pdev = pdev; + pci_set_drvdata(pdev, dev); + + ast->regs = pcim_iomap(pdev, 1, 0); + if (!ast->regs) + return ERR_PTR(-EIO); + + /* + * If we don't have IO space at all, use MMIO now and + * assume the chip has MMIO enabled by default (rev 0x20 + * and higher). + */ + if (!(pci_resource_flags(dev->pdev, 2) & IORESOURCE_IO)) { + drm_info(dev, "platform has no IO space, trying MMIO\n"); + ast->ioregs = ast->regs + AST_IO_MM_OFFSET; + } + + /* "map" IO regs if the above hasn't done so already */ + if (!ast->ioregs) { + ast->ioregs = pcim_iomap(pdev, 2, 0); + if (!ast->ioregs) + return ERR_PTR(-EIO); + } + + ast_detect_chip(dev, &need_post); + + ret = ast_get_dram_info(dev); + if (ret) + return ERR_PTR(ret); + + drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n", + ast->mclk, ast->dram_type, ast->dram_bus_width); + + if (need_post) + ast_post_gpu(dev); + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (dev->vram_mm->vram_size < pci_resource_len(dev->pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(dev->pdev, 0, dev->vram_mm->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + ret = devm_add_action_or_reset(dev->dev, ast_device_release, ast); + if (ret) + return ERR_PTR(ret); + + return ast; +} diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c new file mode 100644 index 000000000..8392ebde5 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -0,0 +1,107 @@ +/* + * 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_gem_vram_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" + +static u32 ast_get_vram_size(struct ast_private *ast) +{ + u8 jreg; + u32 vram_size; + + ast_open_key(ast); + + vram_size = AST_VIDMEM_DEFAULT_SIZE; + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); + switch (jreg & 3) { + case 0: + vram_size = AST_VIDMEM_SIZE_8M; + break; + case 1: + vram_size = AST_VIDMEM_SIZE_16M; + break; + case 2: + vram_size = AST_VIDMEM_SIZE_32M; + break; + case 3: + vram_size = AST_VIDMEM_SIZE_64M; + break; + } + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff); + switch (jreg & 0x03) { + case 1: + vram_size -= 0x100000; + break; + case 2: + vram_size -= 0x200000; + break; + case 3: + vram_size -= 0x400000; + break; + } + + return vram_size; +} + +static void ast_mm_release(struct drm_device *dev, void *ptr) +{ + struct ast_private *ast = to_ast_private(dev); + + arch_phys_wc_del(ast->fb_mtrr); + arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); +} + +int ast_mm_init(struct ast_private *ast) +{ + struct drm_device *dev = &ast->base; + u32 vram_size; + int ret; + + vram_size = ast_get_vram_size(ast); + + ret = drmm_vram_helper_init(dev, pci_resource_start(dev->pdev, 0), + vram_size); + if (ret) { + drm_err(dev, "Error initializing VRAM MM; %d\n", ret); + return ret; + } + + arch_io_reserve_memtype_wc(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); + ast->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); + + return drmm_add_action_or_reset(dev, ast_mm_release, NULL); +} diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c new file mode 100644 index 000000000..d27f2840b --- /dev/null +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -0,0 +1,1288 @@ +/* + * 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/export.h> +#include <linux/pci.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + +#include "ast_drv.h" +#include "ast_tables.h" + +static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev); +static void ast_i2c_destroy(struct ast_i2c_chan *i2c); + +static inline void ast_load_palette_index(struct ast_private *ast, + u8 index, u8 red, u8 green, + u8 blue) +{ + ast_io_write8(ast, AST_IO_DAC_INDEX_WRITE, index); + ast_io_read8(ast, AST_IO_SEQ_PORT); + ast_io_write8(ast, AST_IO_DAC_DATA, red); + ast_io_read8(ast, AST_IO_SEQ_PORT); + ast_io_write8(ast, AST_IO_DAC_DATA, green); + ast_io_read8(ast, AST_IO_SEQ_PORT); + ast_io_write8(ast, AST_IO_DAC_DATA, blue); + ast_io_read8(ast, AST_IO_SEQ_PORT); +} + +static void ast_crtc_load_lut(struct ast_private *ast, struct drm_crtc *crtc) +{ + u16 *r, *g, *b; + int i; + + if (!crtc->enabled) + return; + + r = crtc->gamma_store; + g = r + crtc->gamma_size; + b = g + crtc->gamma_size; + + for (i = 0; i < 256; i++) + ast_load_palette_index(ast, i, *r++ >> 8, *g++ >> 8, *b++ >> 8); +} + +static bool ast_get_vbios_mode_info(const struct drm_format_info *format, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + struct ast_vbios_mode_info *vbios_mode) +{ + u32 refresh_rate_index = 0, refresh_rate; + const struct ast_vbios_enhtable *best = NULL; + u32 hborder, vborder; + bool check_sync; + + switch (format->cpp[0] * 8) { + case 8: + vbios_mode->std_table = &vbios_stdtable[VGAModeIndex]; + break; + case 16: + vbios_mode->std_table = &vbios_stdtable[HiCModeIndex]; + break; + case 24: + case 32: + vbios_mode->std_table = &vbios_stdtable[TrueCModeIndex]; + break; + default: + return false; + } + + switch (mode->crtc_hdisplay) { + case 640: + vbios_mode->enh_table = &res_640x480[refresh_rate_index]; + break; + case 800: + vbios_mode->enh_table = &res_800x600[refresh_rate_index]; + break; + case 1024: + vbios_mode->enh_table = &res_1024x768[refresh_rate_index]; + break; + case 1280: + if (mode->crtc_vdisplay == 800) + vbios_mode->enh_table = &res_1280x800[refresh_rate_index]; + else + vbios_mode->enh_table = &res_1280x1024[refresh_rate_index]; + break; + case 1360: + vbios_mode->enh_table = &res_1360x768[refresh_rate_index]; + break; + case 1440: + vbios_mode->enh_table = &res_1440x900[refresh_rate_index]; + break; + case 1600: + if (mode->crtc_vdisplay == 900) + vbios_mode->enh_table = &res_1600x900[refresh_rate_index]; + else + vbios_mode->enh_table = &res_1600x1200[refresh_rate_index]; + break; + case 1680: + vbios_mode->enh_table = &res_1680x1050[refresh_rate_index]; + break; + case 1920: + if (mode->crtc_vdisplay == 1080) + vbios_mode->enh_table = &res_1920x1080[refresh_rate_index]; + else + vbios_mode->enh_table = &res_1920x1200[refresh_rate_index]; + break; + default: + return false; + } + + refresh_rate = drm_mode_vrefresh(mode); + check_sync = vbios_mode->enh_table->flags & WideScreenMode; + + while (1) { + const struct ast_vbios_enhtable *loop = vbios_mode->enh_table; + + while (loop->refresh_rate != 0xff) { + if ((check_sync) && + (((mode->flags & DRM_MODE_FLAG_NVSYNC) && + (loop->flags & PVSync)) || + ((mode->flags & DRM_MODE_FLAG_PVSYNC) && + (loop->flags & NVSync)) || + ((mode->flags & DRM_MODE_FLAG_NHSYNC) && + (loop->flags & PHSync)) || + ((mode->flags & DRM_MODE_FLAG_PHSYNC) && + (loop->flags & NHSync)))) { + loop++; + continue; + } + if (loop->refresh_rate <= refresh_rate + && (!best || loop->refresh_rate > best->refresh_rate)) + best = loop; + loop++; + } + if (best || !check_sync) + break; + check_sync = 0; + } + + if (best) + vbios_mode->enh_table = best; + + hborder = (vbios_mode->enh_table->flags & HBorder) ? 8 : 0; + vborder = (vbios_mode->enh_table->flags & VBorder) ? 8 : 0; + + adjusted_mode->crtc_htotal = vbios_mode->enh_table->ht; + adjusted_mode->crtc_hblank_start = vbios_mode->enh_table->hde + hborder; + adjusted_mode->crtc_hblank_end = vbios_mode->enh_table->ht - hborder; + adjusted_mode->crtc_hsync_start = vbios_mode->enh_table->hde + hborder + + vbios_mode->enh_table->hfp; + adjusted_mode->crtc_hsync_end = (vbios_mode->enh_table->hde + hborder + + vbios_mode->enh_table->hfp + + vbios_mode->enh_table->hsync); + + adjusted_mode->crtc_vtotal = vbios_mode->enh_table->vt; + adjusted_mode->crtc_vblank_start = vbios_mode->enh_table->vde + vborder; + adjusted_mode->crtc_vblank_end = vbios_mode->enh_table->vt - vborder; + adjusted_mode->crtc_vsync_start = vbios_mode->enh_table->vde + vborder + + vbios_mode->enh_table->vfp; + adjusted_mode->crtc_vsync_end = (vbios_mode->enh_table->vde + vborder + + vbios_mode->enh_table->vfp + + vbios_mode->enh_table->vsync); + + return true; +} + +static void ast_set_vbios_color_reg(struct ast_private *ast, + const struct drm_format_info *format, + const struct ast_vbios_mode_info *vbios_mode) +{ + u32 color_index; + + switch (format->cpp[0]) { + case 1: + color_index = VGAModeIndex - 1; + break; + case 2: + color_index = HiCModeIndex; + break; + case 3: + case 4: + color_index = TrueCModeIndex; + break; + default: + return; + } + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8c, (u8)((color_index & 0x0f) << 4)); + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0x00); + + if (vbios_mode->enh_table->flags & NewModeInfo) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, format->cpp[0] * 8); + } +} + +static void ast_set_vbios_mode_reg(struct ast_private *ast, + const struct drm_display_mode *adjusted_mode, + const struct ast_vbios_mode_info *vbios_mode) +{ + u32 refresh_rate_index, mode_id; + + refresh_rate_index = vbios_mode->enh_table->refresh_rate_index; + mode_id = vbios_mode->enh_table->mode_id; + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8d, refresh_rate_index & 0xff); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff); + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0x00); + + if (vbios_mode->enh_table->flags & NewModeInfo) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8); + } +} + +static void ast_set_std_reg(struct ast_private *ast, + struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + const struct ast_vbios_stdtable *stdtable; + u32 i; + u8 jreg; + + stdtable = vbios_mode->std_table; + + jreg = stdtable->misc; + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg); + + /* Set SEQ; except Screen Disable field */ + ast_set_index_reg(ast, AST_IO_SEQ_PORT, 0x00, 0x03); + ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x01, 0xdf, stdtable->seq[0]); + for (i = 1; i < 4; i++) { + jreg = stdtable->seq[i]; + ast_set_index_reg(ast, AST_IO_SEQ_PORT, (i + 1) , jreg); + } + + /* Set CRTC; except base address and offset */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00); + for (i = 0; i < 12; i++) + ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, stdtable->crtc[i]); + for (i = 14; i < 19; i++) + ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, stdtable->crtc[i]); + for (i = 20; i < 25; i++) + ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, stdtable->crtc[i]); + + /* set AR */ + jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); + for (i = 0; i < 20; i++) { + jreg = stdtable->ar[i]; + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, (u8)i); + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, jreg); + } + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x14); + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x00); + + jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); + ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x20); + + /* Set GR */ + for (i = 0; i < 9; i++) + ast_set_index_reg(ast, AST_IO_GR_PORT, i, stdtable->gr[i]); +} + +static void ast_set_crtc_reg(struct ast_private *ast, + struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0; + u16 temp, precache = 0; + + if ((ast->chip == AST2500) && + (vbios_mode->enh_table->flags & AST2500PreCatchCRT)) + precache = 40; + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 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_CRTC_PORT, 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_CRTC_PORT, 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_CRTC_PORT, 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_CRTC_PORT, 0x03, 0xE0, (temp & 0x1f)); + + temp = ((mode->crtc_hsync_start-precache) >> 3) - 1; + if (temp & 0x100) + jregAC |= 0x40; /* HRS D[5] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x04, 0x00, temp); + + temp = (((mode->crtc_hsync_end-precache) >> 3) - 1) & 0x3f; + if (temp & 0x20) + jregAD |= 0x04; /* HRE D[5] */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05)); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAC, 0x00, jregAC); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAD, 0x00, jregAD); + + /* 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_CRTC_PORT, 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_CRTC_PORT, 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_CRTC_PORT, 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_CRTC_PORT, 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_CRTC_PORT, 0x15, 0x00, temp); + + temp = mode->crtc_vblank_end - 1; + if (temp & 0x100) + jregAE |= 0x10; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x16, 0x00, temp); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x07, 0x00, jreg07); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x09, 0xdf, jreg09); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAE, 0x00, (jregAE | 0x80)); + + if (precache) + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0x3f, 0x80); + else + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0x3f, 0x00); + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x80); +} + +static void ast_set_offset_reg(struct ast_private *ast, + struct drm_framebuffer *fb) +{ + u16 offset; + + offset = fb->pitches[0] >> 3; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x13, (offset & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xb0, (offset >> 8) & 0x3f); +} + +static void ast_set_dclk_reg(struct ast_private *ast, + struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + const struct ast_vbios_dclk_info *clk_info; + + if (ast->chip == AST2500) + clk_info = &dclk_table_ast2500[vbios_mode->enh_table->dclk_index]; + else + clk_info = &dclk_table[vbios_mode->enh_table->dclk_index]; + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc0, 0x00, clk_info->param1); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc1, 0x00, clk_info->param2); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xbb, 0x0f, + (clk_info->param3 & 0xc0) | + ((clk_info->param3 & 0x3) << 4)); +} + +static void ast_set_color_reg(struct ast_private *ast, + const struct drm_format_info *format) +{ + u8 jregA0 = 0, jregA3 = 0, jregA8 = 0; + + switch (format->cpp[0] * 8) { + case 8: + jregA0 = 0x70; + jregA3 = 0x01; + jregA8 = 0x00; + break; + case 15: + case 16: + jregA0 = 0x70; + jregA3 = 0x04; + jregA8 = 0x02; + break; + case 32: + jregA0 = 0x70; + jregA3 = 0x08; + jregA8 = 0x02; + break; + } + + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa0, 0x8f, jregA0); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xf0, jregA3); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8); +} + +static void ast_set_crtthd_reg(struct ast_private *ast) +{ + /* Set Threshold */ + if (ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60); + } else if (ast->chip == AST2100 || + ast->chip == AST1100 || + ast->chip == AST2200 || + ast->chip == AST2150) { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x3f); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x2f); + } else { + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x2f); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x1f); + } +} + +static void ast_set_sync_reg(struct ast_private *ast, + struct drm_display_mode *mode, + struct ast_vbios_mode_info *vbios_mode) +{ + u8 jreg; + + jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ); + jreg &= ~0xC0; + if (vbios_mode->enh_table->flags & NVSync) jreg |= 0x80; + if (vbios_mode->enh_table->flags & NHSync) jreg |= 0x40; + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg); +} + +static void ast_set_start_address_crt1(struct ast_private *ast, + unsigned offset) +{ + u32 addr; + + addr = offset >> 2; + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x0d, (u8)(addr & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x0c, (u8)((addr >> 8) & 0xff)); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xaf, (u8)((addr >> 16) & 0xff)); + +} + +static void ast_wait_for_vretrace(struct ast_private *ast) +{ + unsigned long timeout = jiffies + HZ; + u8 vgair1; + + do { + vgair1 = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); + } while (!(vgair1 & AST_IO_VGAIR1_VREFRESH) && time_before(jiffies, timeout)); +} + +/* + * Primary plane + */ + +static const uint32_t ast_primary_plane_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_C8, +}; + +static int ast_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + struct ast_crtc_state *ast_crtc_state; + int ret; + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); + if (ret) + return ret; + + if (!state->visible) + return 0; + + ast_crtc_state = to_ast_crtc_state(crtc_state); + + ast_crtc_state->format = state->fb->format; + + return 0; +} + +static void +ast_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct ast_private *ast = to_ast_private(dev); + struct drm_plane_state *state = plane->state; + struct drm_gem_vram_object *gbo; + s64 gpu_addr; + struct drm_framebuffer *fb = state->fb; + struct drm_framebuffer *old_fb = old_state->fb; + + if (!old_fb || (fb->format != old_fb->format)) { + struct drm_crtc_state *crtc_state = state->crtc->state; + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + struct ast_vbios_mode_info *vbios_mode_info = &ast_crtc_state->vbios_mode_info; + + ast_set_color_reg(ast, fb->format); + ast_set_vbios_color_reg(ast, fb->format, vbios_mode_info); + } + + gbo = drm_gem_vram_of_gem(fb->obj[0]); + gpu_addr = drm_gem_vram_offset(gbo); + if (drm_WARN_ON_ONCE(dev, gpu_addr < 0)) + return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */ + + ast_set_offset_reg(ast, fb); + ast_set_start_address_crt1(ast, (u32)gpu_addr); + + ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x00); +} + +static void +ast_primary_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ast_private *ast = to_ast_private(plane->dev); + + ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20); +} + +static const struct drm_plane_helper_funcs ast_primary_plane_helper_funcs = { + .prepare_fb = drm_gem_vram_plane_helper_prepare_fb, + .cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb, + .atomic_check = ast_primary_plane_helper_atomic_check, + .atomic_update = ast_primary_plane_helper_atomic_update, + .atomic_disable = ast_primary_plane_helper_atomic_disable, +}; + +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, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +/* + * Cursor plane + */ + +static const uint32_t ast_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static int +ast_cursor_plane_helper_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct drm_crtc *crtc = new_state->crtc; + struct ast_private *ast; + int ret; + + if (!crtc || !fb) + return 0; + + ast = to_ast_private(plane->dev); + + ret = ast_cursor_blit(ast, fb); + if (ret) + return ret; + + return 0; +} + +static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_crtc_state *crtc_state; + int ret; + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); + if (ret) + return ret; + + if (!state->visible) + return 0; + + if (fb->width > AST_MAX_HWC_WIDTH || fb->height > AST_MAX_HWC_HEIGHT) + return -EINVAL; + + return 0; +} + +static void +ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct ast_private *ast = to_ast_private(plane->dev); + unsigned int offset_x, offset_y; + + offset_x = AST_MAX_HWC_WIDTH - fb->width; + offset_y = AST_MAX_HWC_HEIGHT - fb->height; + + if (state->fb != old_state->fb) { + /* A new cursor image was installed. */ + ast_cursor_page_flip(ast); + } + + ast_cursor_show(ast, state->crtc_x, state->crtc_y, + offset_x, offset_y); +} + +static void +ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ast_private *ast = to_ast_private(plane->dev); + + ast_cursor_hide(ast); +} + +static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = { + .prepare_fb = ast_cursor_plane_helper_prepare_fb, + .cleanup_fb = NULL, /* not required for cursor plane */ + .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, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +/* + * CRTC + */ + +static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct ast_private *ast = to_ast_private(crtc->dev); + + /* TODO: Maybe control display signal generation with + * Sync Enable (bit CR17.7). + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + if (ast->tx_chip_type == AST_TX_DP501) + ast_set_dp501_video_output(crtc->dev, 1); + break; + case DRM_MODE_DPMS_OFF: + if (ast->tx_chip_type == AST_TX_DP501) + ast_set_dp501_video_output(crtc->dev, 0); + break; + } +} + +static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_device *dev = crtc->dev; + struct ast_crtc_state *ast_state; + const struct drm_format_info *format; + bool succ; + + if (!state->enable) + return 0; /* no mode checks if CRTC is being disabled */ + + ast_state = to_ast_crtc_state(state); + + format = ast_state->format; + if (drm_WARN_ON_ONCE(dev, !format)) + return -EINVAL; /* BUG: We didn't set format in primary check(). */ + + succ = ast_get_vbios_mode_info(format, &state->mode, + &state->adjusted_mode, + &ast_state->vbios_mode_info); + if (!succ) + return -EINVAL; + + return 0; +} + +static void +ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) +{ + struct ast_private *ast = to_ast_private(crtc->dev); + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc->state); + struct ast_crtc_state *old_ast_crtc_state = to_ast_crtc_state(old_crtc_state); + + /* + * The gamma LUT has to be reloaded after changing the primary + * plane's color format. + */ + if (old_ast_crtc_state->format != ast_crtc_state->format) + ast_crtc_load_lut(ast, crtc); +} + +static void +ast_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *dev = crtc->dev; + struct ast_private *ast = to_ast_private(dev); + struct drm_crtc_state *crtc_state = crtc->state; + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + struct ast_vbios_mode_info *vbios_mode_info = + &ast_crtc_state->vbios_mode_info; + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + + ast_set_vbios_mode_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); + ast_set_std_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_crtc_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_dclk_reg(ast, adjusted_mode, vbios_mode_info); + ast_set_crtthd_reg(ast); + ast_set_sync_reg(ast, adjusted_mode, vbios_mode_info); + + ast_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +} + +static void +ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *dev = crtc->dev; + struct ast_private *ast = to_ast_private(dev); + + ast_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + + /* + * 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); + + /* + * Ensure that no scanout takes place before reprogramming mode + * and format registers. + */ + ast_wait_for_vretrace(ast); +} + +static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { + .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(sizeof(*ast_state), GFP_KERNEL); + + 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(sizeof(*new_ast_state), GFP_KERNEL); + 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; + memcpy(&new_ast_state->vbios_mode_info, &ast_state->vbios_mode_info, + sizeof(new_ast_state->vbios_mode_info)); + + 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, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .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 drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + struct drm_crtc *crtc = &ast->crtc; + int ret; + + ret = drm_crtc_init_with_planes(dev, crtc, &ast->primary_plane, + &ast->cursor_plane, &ast_crtc_funcs, + NULL); + if (ret) + return ret; + + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_helper_add(crtc, &ast_crtc_helper_funcs); + + return 0; +} + +/* + * Encoder + */ + +static int ast_encoder_init(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + struct drm_encoder *encoder = &ast->encoder; + int ret; + + ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC); + if (ret) + return ret; + + encoder->possible_crtcs = 1; + + return 0; +} + +/* + * Connector + */ + +static int ast_get_modes(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + struct ast_private *ast = to_ast_private(connector->dev); + struct edid *edid; + int ret; + bool flags = false; + if (ast->tx_chip_type == AST_TX_DP501) { + ast->dp501_maxclk = 0xff; + edid = kmalloc(128, GFP_KERNEL); + if (!edid) + return -ENOMEM; + + flags = ast_dp501_read_edid(connector->dev, (u8 *)edid); + if (flags) + ast->dp501_maxclk = ast_get_dp501_max_clk(connector->dev); + else + kfree(edid); + } + if (!flags) + edid = drm_get_edid(connector, &ast_connector->i2c->adapter); + if (edid) { + drm_connector_update_edid_property(&ast_connector->base, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + return ret; + } else + drm_connector_update_edid_property(&ast_connector->base, NULL); + return 0; +} + +static enum drm_mode_status ast_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct ast_private *ast = to_ast_private(connector->dev); + int flags = MODE_NOMODE; + uint32_t jtemp; + + if (ast->support_wide_screen) { + if ((mode->hdisplay == 1680) && (mode->vdisplay == 1050)) + return MODE_OK; + if ((mode->hdisplay == 1280) && (mode->vdisplay == 800)) + return MODE_OK; + if ((mode->hdisplay == 1440) && (mode->vdisplay == 900)) + return MODE_OK; + if ((mode->hdisplay == 1360) && (mode->vdisplay == 768)) + return MODE_OK; + if ((mode->hdisplay == 1600) && (mode->vdisplay == 900)) + return MODE_OK; + + if ((ast->chip == AST2100) || (ast->chip == AST2200) || + (ast->chip == AST2300) || (ast->chip == AST2400) || + (ast->chip == AST2500)) { + if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080)) + return MODE_OK; + + if ((mode->hdisplay == 1920) && (mode->vdisplay == 1200)) { + jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff); + if (jtemp & 0x01) + return MODE_NOMODE; + else + return MODE_OK; + } + } + } + switch (mode->hdisplay) { + case 640: + if (mode->vdisplay == 480) flags = MODE_OK; + break; + case 800: + if (mode->vdisplay == 600) flags = MODE_OK; + break; + case 1024: + if (mode->vdisplay == 768) flags = MODE_OK; + break; + case 1280: + if (mode->vdisplay == 1024) flags = MODE_OK; + break; + case 1600: + if (mode->vdisplay == 1200) flags = MODE_OK; + break; + default: + return flags; + } + + return flags; +} + +static void ast_connector_destroy(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + ast_i2c_destroy(ast_connector->i2c); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_helper_funcs ast_connector_helper_funcs = { + .get_modes = ast_get_modes, + .mode_valid = ast_mode_valid, +}; + +static const struct drm_connector_funcs ast_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = ast_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int ast_connector_init(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + struct ast_connector *ast_connector = &ast->connector; + struct drm_connector *connector = &ast_connector->base; + struct drm_encoder *encoder = &ast->encoder; + + ast_connector->i2c = ast_i2c_create(dev); + if (!ast_connector->i2c) + drm_err(dev, "failed to add ddc bus for connector\n"); + + drm_connector_init_with_ddc(dev, connector, + &ast_connector_funcs, + DRM_MODE_CONNECTOR_VGA, + &ast_connector->i2c->adapter); + + drm_connector_helper_add(connector, &ast_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} + +/* + * Mode config + */ + +static const struct drm_mode_config_helper_funcs +ast_mode_config_helper_funcs = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static const struct drm_mode_config_funcs ast_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .mode_valid = drm_vram_helper_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int ast_mode_config_init(struct ast_private *ast) +{ + struct drm_device *dev = &ast->base; + int ret; + + ret = ast_cursor_init(ast); + 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; + dev->mode_config.prefer_shadow = 1; + dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0); + + if (ast->chip == AST2100 || + ast->chip == AST2200 || + ast->chip == AST2300 || + ast->chip == AST2400 || + ast->chip == AST2500) { + 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; + + memset(&ast->primary_plane, 0, sizeof(ast->primary_plane)); + ret = drm_universal_plane_init(dev, &ast->primary_plane, 0x01, + &ast_primary_plane_funcs, + ast_primary_plane_formats, + ARRAY_SIZE(ast_primary_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + drm_err(dev, "ast: drm_universal_plane_init() failed: %d\n", ret); + return ret; + } + drm_plane_helper_add(&ast->primary_plane, + &ast_primary_plane_helper_funcs); + + ret = drm_universal_plane_init(dev, &ast->cursor_plane, 0x01, + &ast_cursor_plane_funcs, + ast_cursor_plane_formats, + ARRAY_SIZE(ast_cursor_plane_formats), + NULL, DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + drm_err(dev, "drm_universal_plane_failed(): %d\n", ret); + return ret; + } + drm_plane_helper_add(&ast->cursor_plane, + &ast_cursor_plane_helper_funcs); + + ast_crtc_init(dev); + ast_encoder_init(dev); + ast_connector_init(dev); + + drm_mode_config_reset(dev); + + return 0; +} + +static int get_clock(void *i2c_priv) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = to_ast_private(i2c->dev); + uint32_t val, val2, count, pass; + + count = 0; + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4) & 0x01; + do { + val2 = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4) & 0x01; + if (val == val2) { + pass++; + } else { + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4) & 0x01; + } + } while ((pass < 5) && (count++ < 0x10000)); + + return val & 1 ? 1 : 0; +} + +static int get_data(void *i2c_priv) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = to_ast_private(i2c->dev); + uint32_t val, val2, count, pass; + + count = 0; + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5) & 0x01; + do { + val2 = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5) & 0x01; + if (val == val2) { + pass++; + } else { + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5) & 0x01; + } + } while ((pass < 5) && (count++ < 0x10000)); + + return val & 1 ? 1 : 0; +} + +static void set_clock(void *i2c_priv, int clock) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = to_ast_private(i2c->dev); + int i; + u8 ujcrb7, jtemp; + + for (i = 0; i < 0x10000; i++) { + ujcrb7 = ((clock & 0x01) ? 0 : 1); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xf4, ujcrb7); + jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x01); + if (ujcrb7 == jtemp) + break; + } +} + +static void set_data(void *i2c_priv, int data) +{ + struct ast_i2c_chan *i2c = i2c_priv; + struct ast_private *ast = to_ast_private(i2c->dev); + int i; + u8 ujcrb7, jtemp; + + for (i = 0; i < 0x10000; i++) { + ujcrb7 = ((data & 0x01) ? 0 : 1) << 2; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xf1, ujcrb7); + jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x04); + if (ujcrb7 == jtemp) + break; + } +} + +static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev) +{ + struct ast_i2c_chan *i2c; + int ret; + + i2c = kzalloc(sizeof(struct ast_i2c_chan), GFP_KERNEL); + if (!i2c) + return NULL; + + i2c->adapter.owner = THIS_MODULE; + i2c->adapter.class = I2C_CLASS_DDC; + i2c->adapter.dev.parent = &dev->pdev->dev; + i2c->dev = dev; + i2c_set_adapdata(&i2c->adapter, i2c); + snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), + "AST i2c bit bus"); + i2c->adapter.algo_data = &i2c->bit; + + i2c->bit.udelay = 20; + i2c->bit.timeout = 2; + i2c->bit.data = i2c; + i2c->bit.setsda = set_data; + i2c->bit.setscl = set_clock; + i2c->bit.getsda = get_data; + i2c->bit.getscl = get_clock; + ret = i2c_bit_add_bus(&i2c->adapter); + if (ret) { + drm_err(dev, "Failed to register bit i2c\n"); + goto out_free; + } + + return i2c; +out_free: + kfree(i2c); + return NULL; +} + +static void ast_i2c_destroy(struct ast_i2c_chan *i2c) +{ + if (!i2c) + return; + i2c_del_adapter(&i2c->adapter); + kfree(i2c); +} diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c new file mode 100644 index 000000000..1bc05bf0f --- /dev/null +++ b/drivers/gpu/drm/ast/ast_post.c @@ -0,0 +1,2080 @@ +/* + * 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_dram_tables.h" +#include "ast_drv.h" + +static void ast_post_chip_2300(struct drm_device *dev); +static void ast_post_chip_2500(struct drm_device *dev); + +void ast_enable_vga(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + + ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01); + ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01); +} + +void ast_enable_mmio(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); +} + + +bool ast_is_vga_enabled(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u8 ch; + + ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); + + return !!(ch & 0x01); +} + +static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; +static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff }; +static const u8 extreginfo_ast2300[] = { 0x0f, 0x04, 0x1f, 0xff }; + +static void +ast_set_def_ext_reg(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u8 i, index, reg; + const u8 *ext_reg_info; + + /* reset scratch */ + for (i = 0x81; i <= 0x9f; i++) + ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00); + + if (ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500) { + if (dev->pdev->revision >= 0x20) + ext_reg_info = extreginfo_ast2300; + else + ext_reg_info = extreginfo_ast2300a0; + } else + ext_reg_info = extreginfo; + + index = 0xa0; + while (*ext_reg_info != 0xff) { + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, index, 0x00, *ext_reg_info); + index++; + ext_reg_info++; + } + + /* disable standard IO/MEM decode if secondary */ + /* ast_set_index_reg-mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x3); */ + + /* Set Ext. Default */ + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x8c, 0x00, 0x01); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x00, 0x00); + + /* Enable RAMDAC for A1 */ + reg = 0x04; + if (ast->chip == AST2300 || ast->chip == AST2400 || + ast->chip == AST2500) + reg |= 0x20; + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg); +} + +u32 ast_mindwm(struct ast_private *ast, u32 r) +{ + uint32_t data; + + ast_write32(ast, 0xf004, r & 0xffff0000); + ast_write32(ast, 0xf000, 0x1); + + do { + data = ast_read32(ast, 0xf004) & 0xffff0000; + } while (data != (r & 0xffff0000)); + return ast_read32(ast, 0x10000 + (r & 0x0000ffff)); +} + +void ast_moutdwm(struct ast_private *ast, u32 r, u32 v) +{ + uint32_t data; + ast_write32(ast, 0xf004, r & 0xffff0000); + ast_write32(ast, 0xf000, 0x1); + do { + data = ast_read32(ast, 0xf004) & 0xffff0000; + } while (data != (r & 0xffff0000)); + ast_write32(ast, 0x10000 + (r & 0x0000ffff), v); +} + +/* + * 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_private *ast, u32 datagen) +{ + u32 data, timeout; + + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3)); + timeout = 0; + do { + data = ast_mindwm(ast, 0x1e6e0070) & 0x40; + if (++timeout > TIMEOUT_AST2150) { + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0xffffffff; + } + } while (!data); + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3)); + timeout = 0; + do { + data = ast_mindwm(ast, 0x1e6e0070) & 0x40; + if (++timeout > TIMEOUT_AST2150) { + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0xffffffff; + } + } while (!data); + data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7; + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return data; +} + +#if 0 /* unused in DDX driver - here for completeness */ +static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen) +{ + u32 data, timeout; + + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3)); + timeout = 0; + do { + data = ast_mindwm(ast, 0x1e6e0070) & 0x40; + if (++timeout > TIMEOUT_AST2150) { + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return 0xffffffff; + } + } while (!data); + data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7; + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return data; +} +#endif + +static int cbrtest_ast2150(struct ast_private *ast) +{ + int i; + + for (i = 0; i < 8; i++) + if (mmctestburst2_ast2150(ast, i)) + return 0; + return 1; +} + +static int cbrscan_ast2150(struct ast_private *ast, int busw) +{ + u32 patcnt, loop; + + for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, 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_private *ast, int busw) +{ + u32 dll_min[4], dll_max[4], dlli, data, passcnt; + +cbr_start: + dll_min[0] = dll_min[1] = dll_min[2] = dll_min[3] = 0xff; + dll_max[0] = dll_max[1] = dll_max[2] = dll_max[3] = 0x0; + passcnt = 0; + + for (dlli = 0; dlli < 100; dlli++) { + ast_moutdwm(ast, 0x1e6e0068, 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, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24)); +} + + + +static void ast_init_dram_reg(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u8 j; + u32 data, temp, i; + const struct ast_dramstruct *dram_reg_info; + + j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + + if ((j & 0x80) == 0) { /* VGA only */ + if (ast->chip == AST2000) { + dram_reg_info = ast2000_dram_table_data; + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x10100, 0xa8); + + do { + ; + } while (ast_read32(ast, 0x10100) != 0xa8); + } else {/* AST2100/1100 */ + if (ast->chip == AST2100 || ast->chip == AST2200) + dram_reg_info = ast2100_dram_table_data; + else + dram_reg_info = ast1100_dram_table_data; + + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688A8A8); + do { + ; + } while (ast_read32(ast, 0x12000) != 0x01); + + ast_write32(ast, 0x10000, 0xfc600309); + do { + ; + } while (ast_read32(ast, 0x10000) != 0x01); + } + + while (dram_reg_info->index != 0xffff) { + if (dram_reg_info->index == 0xff00) {/* delay fn */ + for (i = 0; i < 15; i++) + udelay(dram_reg_info->data); + } else if (dram_reg_info->index == 0x4 && ast->chip != AST2000) { + data = dram_reg_info->data; + if (ast->dram_type == AST_DRAM_1Gx16) + data = 0x00000d89; + else if (ast->dram_type == AST_DRAM_1Gx32) + data = 0x00000c8d; + + temp = ast_read32(ast, 0x12070); + temp &= 0xc; + temp <<= 2; + ast_write32(ast, 0x10000 + dram_reg_info->index, data | temp); + } else + ast_write32(ast, 0x10000 + dram_reg_info->index, dram_reg_info->data); + dram_reg_info++; + } + + /* AST 2100/2150 DRAM calibration */ + data = ast_read32(ast, 0x10120); + if (data == 0x5061) { /* 266Mhz */ + data = ast_read32(ast, 0x10004); + if (data & 0x40) + cbrdlli_ast2150(ast, 16); /* 16 bits */ + else + cbrdlli_ast2150(ast, 32); /* 32 bits */ + } + + switch (ast->chip) { + case AST2000: + temp = ast_read32(ast, 0x10140); + ast_write32(ast, 0x10140, temp | 0x40); + break; + case AST1100: + case AST2100: + case AST2200: + case AST2150: + temp = ast_read32(ast, 0x1200c); + ast_write32(ast, 0x1200c, temp & 0xfffffffd); + temp = ast_read32(ast, 0x12040); + ast_write32(ast, 0x12040, temp | 0x40); + break; + default: + break; + } + } + + /* wait ready */ + do { + j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + } while ((j & 0x40) == 0); +} + +void ast_post_gpu(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u32 reg; + + pci_read_config_dword(dev->pdev, 0x04, ®); + reg |= 0x3; + pci_write_config_dword(dev->pdev, 0x04, reg); + + ast_enable_vga(dev); + ast_open_key(ast); + ast_enable_mmio(dev); + ast_set_def_ext_reg(dev); + + if (ast->config_mode == ast_use_p2a) { + if (ast->chip == AST2500) + ast_post_chip_2500(dev); + else if (ast->chip == AST2300 || ast->chip == AST2400) + ast_post_chip_2300(dev); + else + ast_init_dram_reg(dev); + + ast_init_3rdtx(dev); + } else { + if (ast->tx_chip_type != AST_TX_NONE) + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */ + } +} + +/* 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 bool mmc_test(struct ast_private *ast, u32 datagen, u8 test_ctl) +{ + u32 data, timeout; + + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, (datagen << 3) | test_ctl); + timeout = 0; + do { + data = ast_mindwm(ast, 0x1e6e0070) & 0x3000; + if (data & 0x2000) + return false; + if (++timeout > TIMEOUT) { + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return false; + } + } while (!data); + ast_moutdwm(ast, 0x1e6e0070, 0x0); + return true; +} + +static u32 mmc_test2(struct ast_private *ast, u32 datagen, u8 test_ctl) +{ + u32 data, timeout; + + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + ast_moutdwm(ast, 0x1e6e0070, (datagen << 3) | test_ctl); + timeout = 0; + do { + data = ast_mindwm(ast, 0x1e6e0070) & 0x1000; + if (++timeout > TIMEOUT) { + ast_moutdwm(ast, 0x1e6e0070, 0x0); + return 0xffffffff; + } + } while (!data); + data = ast_mindwm(ast, 0x1e6e0078); + data = (data | (data >> 16)) & 0xffff; + ast_moutdwm(ast, 0x1e6e0070, 0x00000000); + return data; +} + + +static bool mmc_test_burst(struct ast_private *ast, u32 datagen) +{ + return mmc_test(ast, datagen, 0xc1); +} + +static u32 mmc_test_burst2(struct ast_private *ast, u32 datagen) +{ + return mmc_test2(ast, datagen, 0x41); +} + +static bool mmc_test_single(struct ast_private *ast, u32 datagen) +{ + return mmc_test(ast, datagen, 0xc5); +} + +static u32 mmc_test_single2(struct ast_private *ast, u32 datagen) +{ + return mmc_test2(ast, datagen, 0x05); +} + +static bool mmc_test_single_2500(struct ast_private *ast, u32 datagen) +{ + return mmc_test(ast, datagen, 0x85); +} + +static int cbr_test(struct ast_private *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_private *ast) +{ + u32 data, data2, patcnt, loop; + + data2 = 3; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < CBR_PASSNUM2; loop++) { + if ((data = cbr_test(ast)) != 0) { + data2 &= data; + if (!data2) + return 0; + break; + } + } + if (loop == CBR_PASSNUM2) + return 0; + } + return data2; +} + +static u32 cbr_test2(struct ast_private *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_private *ast) +{ + u32 data, data2, patcnt, loop; + + data2 = 0xffff; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < CBR_PASSNUM2; loop++) { + if ((data = cbr_test2(ast)) != 0) { + data2 &= data; + if (!data2) + return 0; + break; + } + } + if (loop == CBR_PASSNUM2) + return 0; + } + return data2; +} + +static bool cbr_test3(struct ast_private *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_private *ast) +{ + u32 patcnt, loop; + + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, 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_private *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, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1E6E0074, 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, 0x1E6E0080, 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, 0x1E6E0084, data); + return status; +} /* finetuneDQI_L */ + +static void finetuneDQSI(struct ast_private *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, 0x1E6E000C); + reg_mcr18 = ast_mindwm(ast, 0x1E6E0018); + reg_mcr18 &= 0x0000ffff; + ast_moutdwm(ast, 0x1E6E0018, 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] = passcnt[1] = 0; + for (dqsip = 0; dqsip < 2; dqsip++) { + ast_moutdwm(ast, 0x1E6E000C, 0); + ast_moutdwm(ast, 0x1E6E0018, reg_mcr18 | (dqidly << 16) | (dqsip << 23)); + ast_moutdwm(ast, 0x1E6E000C, reg_mcr0c); + for (dlli = 0; dlli < 76; dlli++) { + ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1E6E0070, 0); + ast_moutdwm(ast, 0x1E6E0074, 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 = g_dqsip = g_margin = 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] = 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, 0x1E6E0018, reg_mcr18); + +} +static bool cbr_dll2(struct ast_private *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] = dllmin[1] = 0xff; + dllmax[0] = dllmax[1] = 0x0; + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24)); + ast_moutdwm(ast, 0x1E6E0074, 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, 0x1E6E0068, ast_mindwm(ast, 0x1E720058) | (dlli << 16)); + return status; +} /* CBRDLL2 */ + +static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 trap, trap_AC2, trap_MRS; + + ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + + /* Ger trap info */ + trap = (ast_mindwm(ast, 0x1E6E2070) >> 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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 AST_VIDMEM_SIZE_8M: + param->dram_config |= 0x00; + break; + case AST_VIDMEM_SIZE_16M: + param->dram_config |= 0x04; + break; + case AST_VIDMEM_SIZE_32M: + param->dram_config |= 0x08; + break; + case AST_VIDMEM_SIZE_64M: + param->dram_config |= 0x0c; + break; + } + +} + +static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 data, data2, retry = 0; + +ddr3_init_start: + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); + ast_moutdwm(ast, 0x1E6E0018, 0x00000100); + ast_moutdwm(ast, 0x1E6E0024, 0x00000000); + ast_moutdwm(ast, 0x1E6E0034, 0x00000000); + udelay(10); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ); + ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ); + udelay(10); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); + udelay(10); + + ast_moutdwm(ast, 0x1E6E0004, param->dram_config); + ast_moutdwm(ast, 0x1E6E0008, 0x90040f); + ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1); + ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2); + ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); + ast_moutdwm(ast, 0x1E6E0080, 0x00000000); + ast_moutdwm(ast, 0x1E6E0084, 0x00000000); + ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); + ast_moutdwm(ast, 0x1E6E0018, 0x4000A170); + ast_moutdwm(ast, 0x1E6E0018, 0x00002370); + ast_moutdwm(ast, 0x1E6E0038, 0x00000000); + ast_moutdwm(ast, 0x1E6E0040, 0xFF444444); + ast_moutdwm(ast, 0x1E6E0044, 0x22222222); + ast_moutdwm(ast, 0x1E6E0048, 0x22222222); + ast_moutdwm(ast, 0x1E6E004C, 0x00000002); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); + ast_moutdwm(ast, 0x1E6E0054, 0); + ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV); + ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0074, 0x00000000); + ast_moutdwm(ast, 0x1E6E0078, 0x00000000); + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); + /* Wait MCLK2X lock to MCLK */ + do { + data = ast_mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + data = ast_mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) { + data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; + if ((data2 & 0xff) > param->madj_max) { + break; + } + ast_moutdwm(ast, 0x1E6E0064, data2); + if (data2 & 0x00100000) { + data2 = ((data2 & 0xff) >> 3) + 3; + } else { + data2 = ((data2 & 0xff) >> 2) + 5; + } + data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff; + data2 += data & 0xff; + data = data | (data2 << 8); + ast_moutdwm(ast, 0x1E6E0068, data); + udelay(10); + ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000); + udelay(10); + data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff; + ast_moutdwm(ast, 0x1E6E0018, data); + data = data | 0x200; + ast_moutdwm(ast, 0x1E6E0018, data); + do { + data = ast_mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + + data = ast_mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + } + ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0068) & 0xffff); + data = ast_mindwm(ast, 0x1E6E0018) | 0xC00; + ast_moutdwm(ast, 0x1E6E0018, data); + + ast_moutdwm(ast, 0x1E6E0034, 0x00000001); + ast_moutdwm(ast, 0x1E6E000C, 0x00000040); + udelay(50); + /* Mode Register Setting */ + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000005); + ast_moutdwm(ast, 0x1E6E0028, 0x00000007); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS); + ast_moutdwm(ast, 0x1E6E000C, 0x00005C08); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + + ast_moutdwm(ast, 0x1E6E000C, 0x00005C01); + data = 0; + if (param->wodt) { + data = 0x300; + } + if (param->rodt) { + data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3); + } + ast_moutdwm(ast, 0x1E6E0034, data | 0x3); + + /* Calibrate the DQSI delay */ + if ((cbr_dll2(ast, param) == false) && (retry++ < 10)) + goto ddr3_init_start; + + ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ); + /* ECC Memory Initialization */ +#ifdef ECC + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0070, 0x221); + do { + data = ast_mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); +#endif + + +} + +static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 trap, trap_AC2, trap_MRS; + + ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + + /* Ger trap info */ + trap = (ast_mindwm(ast, 0x1E6E2070) >> 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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, 0x1E6E2020, 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 AST_VIDMEM_SIZE_8M: + param->dram_config |= 0x00; + break; + case AST_VIDMEM_SIZE_16M: + param->dram_config |= 0x04; + break; + case AST_VIDMEM_SIZE_32M: + param->dram_config |= 0x08; + break; + case AST_VIDMEM_SIZE_64M: + param->dram_config |= 0x0c; + break; + } +} + +static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param) +{ + u32 data, data2, retry = 0; + +ddr2_init_start: + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); + ast_moutdwm(ast, 0x1E6E0018, 0x00000100); + ast_moutdwm(ast, 0x1E6E0024, 0x00000000); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ); + ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ); + udelay(10); + ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000); + udelay(10); + + ast_moutdwm(ast, 0x1E6E0004, param->dram_config); + ast_moutdwm(ast, 0x1E6E0008, 0x90040f); + ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1); + ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2); + ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC); + ast_moutdwm(ast, 0x1E6E0080, 0x00000000); + ast_moutdwm(ast, 0x1E6E0084, 0x00000000); + ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY); + ast_moutdwm(ast, 0x1E6E0018, 0x4000A130); + ast_moutdwm(ast, 0x1E6E0018, 0x00002330); + ast_moutdwm(ast, 0x1E6E0038, 0x00000000); + ast_moutdwm(ast, 0x1E6E0040, 0xFF808000); + ast_moutdwm(ast, 0x1E6E0044, 0x88848466); + ast_moutdwm(ast, 0x1E6E0048, 0x44440008); + ast_moutdwm(ast, 0x1E6E004C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); + ast_moutdwm(ast, 0x1E6E0054, 0); + ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV); + ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0074, 0x00000000); + ast_moutdwm(ast, 0x1E6E0078, 0x00000000); + ast_moutdwm(ast, 0x1E6E007C, 0x00000000); + + /* Wait MCLK2X lock to MCLK */ + do { + data = ast_mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + data = ast_mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) { + data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4; + if ((data2 & 0xff) > param->madj_max) { + break; + } + ast_moutdwm(ast, 0x1E6E0064, data2); + if (data2 & 0x00100000) { + data2 = ((data2 & 0xff) >> 3) + 3; + } else { + data2 = ((data2 & 0xff) >> 2) + 5; + } + data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff; + data2 += data & 0xff; + data = data | (data2 << 8); + ast_moutdwm(ast, 0x1E6E0068, data); + udelay(10); + ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000); + udelay(10); + data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff; + ast_moutdwm(ast, 0x1E6E0018, data); + data = data | 0x200; + ast_moutdwm(ast, 0x1E6E0018, data); + do { + data = ast_mindwm(ast, 0x1E6E001C); + } while (!(data & 0x08000000)); + + data = ast_mindwm(ast, 0x1E6E001C); + data = (data >> 8) & 0xff; + } + ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0008) & 0xffff); + data = ast_mindwm(ast, 0x1E6E0018) | 0xC00; + ast_moutdwm(ast, 0x1E6E0018, data); + + ast_moutdwm(ast, 0x1E6E0034, 0x00000001); + ast_moutdwm(ast, 0x1E6E000C, 0x00000000); + udelay(50); + /* Mode Register Setting */ + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000005); + ast_moutdwm(ast, 0x1E6E0028, 0x00000007); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + + ast_moutdwm(ast, 0x1E6E000C, 0x00005C08); + ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000001); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS); + ast_moutdwm(ast, 0x1E6E0028, 0x00000003); + + ast_moutdwm(ast, 0x1E6E000C, 0x7FFF5C01); + data = 0; + if (param->wodt) { + data = 0x500; + } + if (param->rodt) { + data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3); + } + ast_moutdwm(ast, 0x1E6E0034, data | 0x3); + ast_moutdwm(ast, 0x1E6E0120, 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, 0x1E6E007C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0070, 0x221); + do { + data = ast_mindwm(ast, 0x1E6E0070); + } while (!(data & 0x00001000)); + ast_moutdwm(ast, 0x1E6E0070, 0x00000000); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0050, 0x00000000); +#endif + +} + +static void ast_post_chip_2300(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + struct ast2300_dram_param param; + u32 temp; + u8 reg; + + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if ((reg & 0x80) == 0) {/* vga only */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688a8a8); + do { + ; + } while (ast_read32(ast, 0x12000) != 0x1); + + ast_write32(ast, 0x10000, 0xfc600309); + do { + ; + } while (ast_read32(ast, 0x10000) != 0x1); + + /* Slow down CPU/AHB CLK in VGA only mode */ + temp = ast_read32(ast, 0x12008); + temp |= 0x73; + ast_write32(ast, 0x12008, temp); + + param.dram_freq = 396; + param.dram_type = AST_DDR3; + temp = ast_mindwm(ast, 0x1e6e2070); + 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 = AST_VIDMEM_SIZE_8M; + break; + + case 0x04: + param.vram_size = AST_VIDMEM_SIZE_16M; + break; + + case 0x08: + param.vram_size = AST_VIDMEM_SIZE_32M; + break; + + case 0x0c: + param.vram_size = AST_VIDMEM_SIZE_64M; + break; + } + + if (param.dram_type == AST_DDR3) { + get_ddr3_info(ast, ¶m); + ddr3_init(ast, ¶m); + } else { + get_ddr2_info(ast, ¶m); + ddr2_init(ast, ¶m); + } + + temp = ast_mindwm(ast, 0x1e6e2040); + ast_moutdwm(ast, 0x1e6e2040, temp | 0x40); + } + + /* wait ready */ + do { + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + } while ((reg & 0x40) == 0); +} + +static bool cbr_test_2500(struct ast_private *ast) +{ + ast_moutdwm(ast, 0x1E6E0074, 0x0000FFFF); + ast_moutdwm(ast, 0x1E6E007C, 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_private *ast) +{ + ast_moutdwm(ast, 0x1E6E0074, 0x0000FFFF); + ast_moutdwm(ast, 0x1E6E007C, 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_private *ast) +{ + ast_moutdwm(ast, 0x1E6E0034, 0x00020080); + ast_moutdwm(ast, 0x1E6E0008, 0x2003000F); + ast_moutdwm(ast, 0x1E6E0038, 0x00000FFF); + ast_moutdwm(ast, 0x1E6E0040, 0x88448844); + ast_moutdwm(ast, 0x1E6E0044, 0x24422288); + ast_moutdwm(ast, 0x1E6E0048, 0x22222222); + ast_moutdwm(ast, 0x1E6E004C, 0x22222222); + ast_moutdwm(ast, 0x1E6E0050, 0x80000000); + ast_moutdwm(ast, 0x1E6E0208, 0x00000000); + ast_moutdwm(ast, 0x1E6E0218, 0x00000000); + ast_moutdwm(ast, 0x1E6E0220, 0x00000000); + ast_moutdwm(ast, 0x1E6E0228, 0x00000000); + ast_moutdwm(ast, 0x1E6E0230, 0x00000000); + ast_moutdwm(ast, 0x1E6E02A8, 0x00000000); + ast_moutdwm(ast, 0x1E6E02B0, 0x00000000); + ast_moutdwm(ast, 0x1E6E0240, 0x86000000); + ast_moutdwm(ast, 0x1E6E0244, 0x00008600); + ast_moutdwm(ast, 0x1E6E0248, 0x80000000); + ast_moutdwm(ast, 0x1E6E024C, 0x80808080); +} + +static void ddr_phy_init_2500(struct ast_private *ast) +{ + u32 data, pass, timecnt; + + pass = 0; + ast_moutdwm(ast, 0x1E6E0060, 0x00000005); + while (!pass) { + for (timecnt = 0; timecnt < TIMEOUT; timecnt++) { + data = ast_mindwm(ast, 0x1E6E0060) & 0x1; + if (!data) + break; + } + if (timecnt != TIMEOUT) { + data = ast_mindwm(ast, 0x1E6E0300) & 0x000A0000; + if (!data) + pass = 1; + } + if (!pass) { + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + udelay(10); /* delay 10 us */ + ast_moutdwm(ast, 0x1E6E0060, 0x00000005); + } + } + + ast_moutdwm(ast, 0x1E6E0060, 0x00000006); +} + +/* + * Check DRAM Size + * 1Gb : 0x80000000 ~ 0x87FFFFFF + * 2Gb : 0x80000000 ~ 0x8FFFFFFF + * 4Gb : 0x80000000 ~ 0x9FFFFFFF + * 8Gb : 0x80000000 ~ 0xBFFFFFFF + */ +static void check_dram_size_2500(struct ast_private *ast, u32 tRFC) +{ + u32 reg_04, reg_14; + + reg_04 = ast_mindwm(ast, 0x1E6E0004) & 0xfffffffc; + reg_14 = ast_mindwm(ast, 0x1E6E0014) & 0xffffff00; + + ast_moutdwm(ast, 0xA0100000, 0x41424344); + ast_moutdwm(ast, 0x90100000, 0x35363738); + ast_moutdwm(ast, 0x88100000, 0x292A2B2C); + ast_moutdwm(ast, 0x80100000, 0x1D1E1F10); + + /* Check 8Gbit */ + if (ast_mindwm(ast, 0xA0100000) == 0x41424344) { + reg_04 |= 0x03; + reg_14 |= (tRFC >> 24) & 0xFF; + /* Check 4Gbit */ + } else if (ast_mindwm(ast, 0x90100000) == 0x35363738) { + reg_04 |= 0x02; + reg_14 |= (tRFC >> 16) & 0xFF; + /* Check 2Gbit */ + } else if (ast_mindwm(ast, 0x88100000) == 0x292A2B2C) { + reg_04 |= 0x01; + reg_14 |= (tRFC >> 8) & 0xFF; + } else { + reg_14 |= tRFC & 0xFF; + } + ast_moutdwm(ast, 0x1E6E0004, reg_04); + ast_moutdwm(ast, 0x1E6E0014, reg_14); +} + +static void enable_cache_2500(struct ast_private *ast) +{ + u32 reg_04, data; + + reg_04 = ast_mindwm(ast, 0x1E6E0004); + ast_moutdwm(ast, 0x1E6E0004, reg_04 | 0x1000); + + do + data = ast_mindwm(ast, 0x1E6E0004); + while (!(data & 0x80000)); + ast_moutdwm(ast, 0x1E6E0004, reg_04 | 0x400); +} + +static void set_mpll_2500(struct ast_private *ast) +{ + u32 addr, data, param; + + /* Reset MMC */ + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); + ast_moutdwm(ast, 0x1E6E0034, 0x00020080); + for (addr = 0x1e6e0004; addr < 0x1e6e0090;) { + ast_moutdwm(ast, addr, 0x0); + addr += 4; + } + ast_moutdwm(ast, 0x1E6E0034, 0x00020000); + + ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8); + data = ast_mindwm(ast, 0x1E6E2070) & 0x00800000; + if (data) { + /* CLKIN = 25MHz */ + param = 0x930023E0; + ast_moutdwm(ast, 0x1E6E2160, 0x00011320); + } else { + /* CLKIN = 24MHz */ + param = 0x93002400; + } + ast_moutdwm(ast, 0x1E6E2020, param); + udelay(100); +} + +static void reset_mmc_2500(struct ast_private *ast) +{ + ast_moutdwm(ast, 0x1E78505C, 0x00000004); + ast_moutdwm(ast, 0x1E785044, 0x00000001); + ast_moutdwm(ast, 0x1E785048, 0x00004755); + ast_moutdwm(ast, 0x1E78504C, 0x00000013); + mdelay(100); + ast_moutdwm(ast, 0x1E785054, 0x00000077); + ast_moutdwm(ast, 0x1E6E0000, 0xFC600309); +} + +static void ddr3_init_2500(struct ast_private *ast, const u32 *ddr_table) +{ + + ast_moutdwm(ast, 0x1E6E0004, 0x00000303); + ast_moutdwm(ast, 0x1E6E0010, ddr_table[REGIDX_010]); + ast_moutdwm(ast, 0x1E6E0014, ddr_table[REGIDX_014]); + ast_moutdwm(ast, 0x1E6E0018, ddr_table[REGIDX_018]); + ast_moutdwm(ast, 0x1E6E0020, ddr_table[REGIDX_020]); /* MODEREG4/6 */ + ast_moutdwm(ast, 0x1E6E0024, ddr_table[REGIDX_024]); /* MODEREG5 */ + ast_moutdwm(ast, 0x1E6E002C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */ + ast_moutdwm(ast, 0x1E6E0030, ddr_table[REGIDX_030]); /* MODEREG1/3 */ + + /* DDR PHY Setting */ + ast_moutdwm(ast, 0x1E6E0200, 0x02492AAE); + ast_moutdwm(ast, 0x1E6E0204, 0x00001001); + ast_moutdwm(ast, 0x1E6E020C, 0x55E00B0B); + ast_moutdwm(ast, 0x1E6E0210, 0x20000000); + ast_moutdwm(ast, 0x1E6E0214, ddr_table[REGIDX_214]); + ast_moutdwm(ast, 0x1E6E02E0, ddr_table[REGIDX_2E0]); + ast_moutdwm(ast, 0x1E6E02E4, ddr_table[REGIDX_2E4]); + ast_moutdwm(ast, 0x1E6E02E8, ddr_table[REGIDX_2E8]); + ast_moutdwm(ast, 0x1E6E02EC, ddr_table[REGIDX_2EC]); + ast_moutdwm(ast, 0x1E6E02F0, ddr_table[REGIDX_2F0]); + ast_moutdwm(ast, 0x1E6E02F4, ddr_table[REGIDX_2F4]); + ast_moutdwm(ast, 0x1E6E02F8, ddr_table[REGIDX_2F8]); + ast_moutdwm(ast, 0x1E6E0290, 0x00100008); + ast_moutdwm(ast, 0x1E6E02C0, 0x00000006); + + /* Controller Setting */ + ast_moutdwm(ast, 0x1E6E0034, 0x00020091); + + /* Wait DDR PHY init done */ + ddr_phy_init_2500(ast); + + ast_moutdwm(ast, 0x1E6E0120, ddr_table[REGIDX_PLL]); + ast_moutdwm(ast, 0x1E6E000C, 0x42AA5C81); + ast_moutdwm(ast, 0x1E6E0034, 0x0001AF93); + + check_dram_size_2500(ast, ddr_table[REGIDX_RFC]); + enable_cache_2500(ast); + ast_moutdwm(ast, 0x1E6E001C, 0x00000008); + ast_moutdwm(ast, 0x1E6E0038, 0xFFFFFF00); +} + +static void ddr4_init_2500(struct ast_private *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, 0x1E6E0004, 0x00000313); + ast_moutdwm(ast, 0x1E6E0010, ddr_table[REGIDX_010]); + ast_moutdwm(ast, 0x1E6E0014, ddr_table[REGIDX_014]); + ast_moutdwm(ast, 0x1E6E0018, ddr_table[REGIDX_018]); + ast_moutdwm(ast, 0x1E6E0020, ddr_table[REGIDX_020]); /* MODEREG4/6 */ + ast_moutdwm(ast, 0x1E6E0024, ddr_table[REGIDX_024]); /* MODEREG5 */ + ast_moutdwm(ast, 0x1E6E002C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */ + ast_moutdwm(ast, 0x1E6E0030, ddr_table[REGIDX_030]); /* MODEREG1/3 */ + + /* DDR PHY Setting */ + ast_moutdwm(ast, 0x1E6E0200, 0x42492AAE); + ast_moutdwm(ast, 0x1E6E0204, 0x09002000); + ast_moutdwm(ast, 0x1E6E020C, 0x55E00B0B); + ast_moutdwm(ast, 0x1E6E0210, 0x20000000); + ast_moutdwm(ast, 0x1E6E0214, ddr_table[REGIDX_214]); + ast_moutdwm(ast, 0x1E6E02E0, ddr_table[REGIDX_2E0]); + ast_moutdwm(ast, 0x1E6E02E4, ddr_table[REGIDX_2E4]); + ast_moutdwm(ast, 0x1E6E02E8, ddr_table[REGIDX_2E8]); + ast_moutdwm(ast, 0x1E6E02EC, ddr_table[REGIDX_2EC]); + ast_moutdwm(ast, 0x1E6E02F0, ddr_table[REGIDX_2F0]); + ast_moutdwm(ast, 0x1E6E02F4, ddr_table[REGIDX_2F4]); + ast_moutdwm(ast, 0x1E6E02F8, ddr_table[REGIDX_2F8]); + ast_moutdwm(ast, 0x1E6E0290, 0x00100008); + ast_moutdwm(ast, 0x1E6E02C4, 0x3C183C3C); + ast_moutdwm(ast, 0x1E6E02C8, 0x00631E0E); + + /* Controller Setting */ + ast_moutdwm(ast, 0x1E6E0034, 0x0001A991); + + /* Train PHY Vref first */ + pass = 0; + + for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) { + max_phy_vref = 0x0; + pass = 0; + ast_moutdwm(ast, 0x1E6E02C0, 0x00001C06); + for (phy_vref = 0x40; phy_vref < 0x80; phy_vref++) { + ast_moutdwm(ast, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + ast_moutdwm(ast, 0x1E6E02CC, phy_vref | (phy_vref << 8)); + /* Fire DFI Init */ + ddr_phy_init_2500(ast); + ast_moutdwm(ast, 0x1E6E000C, 0x00005C01); + if (cbr_test_2500(ast)) { + pass++; + data = ast_mindwm(ast, 0x1E6E03D0); + 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, 0x1E6E02CC, 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, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + ast_moutdwm(ast, 0x1E6E02C0, 0x00000006 | (ddr_vref << 8)); + /* Fire DFI Init */ + ddr_phy_init_2500(ast); + ast_moutdwm(ast, 0x1E6E000C, 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, 0x1E6E000C, 0x00000000); + ast_moutdwm(ast, 0x1E6E0060, 0x00000000); + ddr_vref = (min_ddr_vref + max_ddr_vref + 1) >> 1; + ast_moutdwm(ast, 0x1E6E02C0, 0x00000006 | (ddr_vref << 8)); + + /* Wait DDR PHY init done */ + ddr_phy_init_2500(ast); + + ast_moutdwm(ast, 0x1E6E0120, ddr_table[REGIDX_PLL]); + ast_moutdwm(ast, 0x1E6E000C, 0x42AA5C81); + ast_moutdwm(ast, 0x1E6E0034, 0x0001AF93); + + check_dram_size_2500(ast, ddr_table[REGIDX_RFC]); + enable_cache_2500(ast); + ast_moutdwm(ast, 0x1E6E001C, 0x00000008); + ast_moutdwm(ast, 0x1E6E0038, 0xFFFFFF00); +} + +static bool ast_dram_init_2500(struct ast_private *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, 0x1E6E2070); + 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, 0x1E6E2040, ast_mindwm(ast, 0x1E6E2040) | 0x41); + + /* Patch code */ + data = ast_mindwm(ast, 0x1E6E200C) & 0xF9FFFFFF; + ast_moutdwm(ast, 0x1E6E200C, data | 0x10000000); + + return true; +} + +void ast_post_chip_2500(struct drm_device *dev) +{ + struct ast_private *ast = to_ast_private(dev); + u32 temp; + u8 reg; + + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if ((reg & 0x80) == 0) {/* vga only */ + /* Clear bus lock condition */ + ast_moutdwm(ast, 0x1e600000, 0xAEED1A03); + ast_moutdwm(ast, 0x1e600084, 0x00010000); + ast_moutdwm(ast, 0x1e600088, 0x00000000); + ast_moutdwm(ast, 0x1e6e2000, 0x1688A8A8); + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + ast_write32(ast, 0x12000, 0x1688a8a8); + while (ast_read32(ast, 0x12000) != 0x1) + ; + + ast_write32(ast, 0x10000, 0xfc600309); + while (ast_read32(ast, 0x10000) != 0x1) + ; + + /* Slow down CPU/AHB CLK in VGA only mode */ + temp = ast_read32(ast, 0x12008); + temp |= 0x73; + ast_write32(ast, 0x12008, temp); + + /* Reset USB port to patch USB unknown device issue */ + ast_moutdwm(ast, 0x1e6e2090, 0x20000000); + temp = ast_mindwm(ast, 0x1e6e2094); + temp |= 0x00004000; + ast_moutdwm(ast, 0x1e6e2094, temp); + temp = ast_mindwm(ast, 0x1e6e2070); + if (temp & 0x00800000) { + ast_moutdwm(ast, 0x1e6e207c, 0x00800000); + mdelay(100); + ast_moutdwm(ast, 0x1e6e2070, 0x00800000); + } + + if (!ast_dram_init_2500(ast)) + drm_err(dev, "DRAM init failed !\n"); + + temp = ast_mindwm(ast, 0x1e6e2040); + ast_moutdwm(ast, 0x1e6e2040, temp | 0x40); + } + + /* wait ready */ + do { + reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + } while ((reg & 0x40) == 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..d665dd5af --- /dev/null +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -0,0 +1,345 @@ +/* + * 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 + +/* Std. Table Index Definition */ +#define TextModeIndex 0 +#define EGAModeIndex 1 +#define VGAModeIndex 2 +#define HiCModeIndex 3 +#define TrueCModeIndex 4 + +#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 + +static const struct ast_vbios_dclk_info 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 */ +}; + +static const struct ast_vbios_dclk_info dclk_table_ast2500[] = { + {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 */ +}; + +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} + }, +}; + +static const struct ast_vbios_enhtable res_640x480[] = { + { 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60Hz */ + (SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2E }, + { 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72Hz */ + (SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2E }, + { 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75Hz */ + (SyncNN | Charx8Dot) , 75, 3, 0x2E }, + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85Hz */ + (SyncNN | Charx8Dot) , 85, 4, 0x2E }, + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* end */ + (SyncNN | Charx8Dot) , 0xFF, 4, 0x2E }, +}; + +static const struct ast_vbios_enhtable res_800x600[] = { + {1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56Hz */ + (SyncPP | Charx8Dot), 56, 1, 0x30 }, + {1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60Hz */ + (SyncPP | Charx8Dot), 60, 2, 0x30 }, + {1040, 800, 56, 120, 666, 600, 37, 6, VCLK50, /* 72Hz */ + (SyncPP | Charx8Dot), 72, 3, 0x30 }, + {1056, 800, 16, 80, 625, 600, 1, 3, VCLK49_5, /* 75Hz */ + (SyncPP | Charx8Dot), 75, 4, 0x30 }, + {1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* 85Hz */ + (SyncPP | Charx8Dot), 84, 5, 0x30 }, + {1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* end */ + (SyncPP | Charx8Dot), 0xFF, 5, 0x30 }, +}; + + +static const struct ast_vbios_enhtable res_1024x768[] = { + {1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60Hz */ + (SyncNN | Charx8Dot), 60, 1, 0x31 }, + {1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70Hz */ + (SyncNN | Charx8Dot), 70, 2, 0x31 }, + {1312, 1024, 16, 96, 800, 768, 1, 3, VCLK78_75, /* 75Hz */ + (SyncPP | Charx8Dot), 75, 3, 0x31 }, + {1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* 85Hz */ + (SyncPP | Charx8Dot), 84, 4, 0x31 }, + {1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* end */ + (SyncPP | Charx8Dot), 0xFF, 4, 0x31 }, +}; + +static const struct ast_vbios_enhtable res_1280x1024[] = { + {1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60Hz */ + (SyncPP | Charx8Dot), 60, 1, 0x32 }, + {1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75Hz */ + (SyncPP | Charx8Dot), 75, 2, 0x32 }, + {1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* 85Hz */ + (SyncPP | Charx8Dot), 85, 3, 0x32 }, + {1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* end */ + (SyncPP | Charx8Dot), 0xFF, 3, 0x32 }, +}; + +static const struct ast_vbios_enhtable res_1600x1200[] = { + {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60Hz */ + (SyncPP | Charx8Dot), 60, 1, 0x33 }, + {2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* end */ + (SyncPP | Charx8Dot), 0xFF, 1, 0x33 }, +}; + +/* 16:9 */ +static const struct ast_vbios_enhtable res_1360x768[] = { + {1792, 1360, 64, 112, 795, 768, 3, 6, VCLK85_5, /* 60Hz */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 }, + {1792, 1360, 64, 112, 795, 768, 3, 6, VCLK85_5, /* end */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 0xFF, 1, 0x39 }, +}; + +static const struct ast_vbios_enhtable res_1600x900[] = { + {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x3A }, + {2112, 1600, 88, 168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3A }, + {2112, 1600, 88, 168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x3A }, +}; + +static const struct ast_vbios_enhtable res_1920x1080[] = { + {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x38 }, + {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 0xFF, 1, 0x38 }, +}; + + +/* 16:10 */ +static const struct ast_vbios_enhtable res_1280x800[] = { + {1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x35 }, + {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 }, + {1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x35 }, + +}; + +static const struct ast_vbios_enhtable res_1440x900[] = { + {1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x36 }, + {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 }, + {1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x36 }, +}; + +static const struct ast_vbios_enhtable res_1680x1050[] = { + {1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x37 }, + {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 }, + {2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x37 }, +}; + +static const struct ast_vbios_enhtable res_1920x1200[] = { + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB*/ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x34 }, + {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 0xFF, 1, 0x34 }, +}; + +#endif |