diff options
Diffstat (limited to 'drivers/gpu/drm/ast')
30 files changed, 6993 insertions, 5282 deletions
diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig index 9647e1f07088..242fbccdf844 100644 --- a/drivers/gpu/drm/ast/Kconfig +++ b/drivers/gpu/drm/ast/Kconfig @@ -1,9 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only config DRM_AST tristate "AST server chips" - depends on DRM && PCI && MMU - select DRM_TTM + depends on DRM && PCI + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER - select DRM_TTM + select I2C + select I2C_ALGOBIT help Say yes for experimental AST GPU driver. Do not enable this driver without having a working -modesetting, diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 617fdd39519c..cdbcba3b43ad 100644 --- a/drivers/gpu/drm/ast/Makefile +++ b/drivers/gpu/drm/ast/Makefile @@ -1,7 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o ast_dp501.o +ast-y := \ + ast_2000.o \ + ast_2100.o \ + ast_2200.o \ + ast_2300.o \ + ast_2400.o \ + ast_2500.o \ + ast_2600.o \ + ast_cursor.o \ + ast_ddc.o \ + ast_dp501.o \ + ast_dp.o \ + ast_drv.o \ + ast_mm.o \ + ast_mode.o \ + ast_post.o \ + ast_sil164.o \ + ast_vbios.o \ + ast_vga.o obj-$(CONFIG_DRM_AST) := ast.o diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c new file mode 100644 index 000000000000..fa3bc23ce098 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2000.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "ast_drv.h" +#include "ast_post.h" + +/* + * POST + */ + +void ast_2000_set_def_ext_reg(struct ast_device *ast) +{ + static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff }; + u8 i, index, reg; + const u8 *ext_reg_info; + + /* reset scratch */ + for (i = 0x81; i <= 0x9f; i++) + ast_set_index_reg(ast, AST_IO_VGACRI, i, 0x00); + + ext_reg_info = extreginfo; + index = 0xa0; + while (*ext_reg_info != 0xff) { + ast_set_index_reg_mask(ast, AST_IO_VGACRI, index, 0x00, *ext_reg_info); + index++; + ext_reg_info++; + } + + /* disable standard IO/MEM decode if secondary */ + /* ast_set_index_reg-mask(ast, AST_IO_VGACRI, 0xa1, 0xff, 0x3); */ + + /* Set Ext. Default */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x8c, 0x00, 0x01); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x00, 0x00); + + /* Enable RAMDAC for A1 */ + reg = 0x04; + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xff, reg); +} + +static const struct ast_dramstruct ast2000_dram_table_data[] = { + { 0x0108, 0x00000000 }, + { 0x0120, 0x00004a21 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 0x0000, 0xFFFFFFFF }, + AST_DRAMSTRUCT_INIT(DRAM_TYPE, 0x00000089), + { 0x0008, 0x22331353 }, + { 0x000C, 0x0d07000b }, + { 0x0010, 0x11113333 }, + { 0x0020, 0x00110350 }, + { 0x0028, 0x1e0828f0 }, + { 0x0024, 0x00000001 }, + { 0x001C, 0x00000000 }, + { 0x0014, 0x00000003 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 0x0018, 0x00000131 }, + { 0x0014, 0x00000001 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 0x0018, 0x00000031 }, + { 0x0014, 0x00000001 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 0x0028, 0x1e0828f1 }, + { 0x0024, 0x00000003 }, + { 0x002C, 0x1f0f28fb }, + { 0x0030, 0xFFFFFE01 }, + AST_DRAMSTRUCT_INVALID, +}; + +static void ast_post_chip_2000(struct ast_device *ast) +{ + u8 j; + u32 temp, i; + const struct ast_dramstruct *dram_reg_info; + + j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); + + if ((j & 0x80) == 0) { /* VGA only */ + 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); + + while (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) { + if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) { + for (i = 0; i < 15; i++) + udelay(dram_reg_info->data); + } else { + ast_write32(ast, 0x10000 + dram_reg_info->index, + dram_reg_info->data); + } + dram_reg_info++; + } + + temp = ast_read32(ast, 0x10140); + ast_write32(ast, 0x10140, temp | 0x40); + } + + /* wait ready */ + do { + j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); + } while ((j & 0x40) == 0); +} + +int ast_2000_post(struct ast_device *ast) +{ + ast_2000_set_def_ext_reg(ast); + + if (ast->config_mode == ast_use_p2a) { + ast_post_chip_2000(ast); + } else { + if (ast->tx_chip == AST_TX_SIL164) { + /* Enable DVO */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); + } + } + + return 0; +} + +/* + * Mode setting + */ + +const struct ast_vbios_dclk_info ast_2000_dclk_table[] = { + {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xee, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xc6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7b, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */ + {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */ + {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0d: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */ + {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x77, 0x58, 0x80}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ + {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x3b, 0x2c, 0x81}, /* 1a: VCLK118_25 */ +}; + +/* + * Device initialization + */ + +void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post) +{ + enum ast_tx_chip tx_chip = AST_TX_NONE; + u8 vgacra3; + + /* + * VGACRA3 Enhanced Color Mode Register, check if DVO is already + * enabled, in that case, assume we have a SIL164 TMDS transmitter + * + * Don't make that assumption if we the chip wasn't enabled and + * is at power-on reset, otherwise we'll incorrectly "detect" a + * SIL164 when there is none. + */ + if (!need_post) { + vgacra3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff); + if (vgacra3 & AST_IO_VGACRA3_DVO_ENABLED) + tx_chip = AST_TX_SIL164; + } + + __ast_device_set_tx_chip(ast, tx_chip); +} + +static const struct ast_device_quirks ast_2000_device_quirks = { + .crtc_mem_req_threshold_low = 31, + .crtc_mem_req_threshold_high = 47, +}; + +struct drm_device *ast_2000_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2000_device_quirks); + + ast->dclk_table = ast_2000_dclk_table; + + ast_2000_detect_tx_chip(ast, need_post); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c new file mode 100644 index 000000000000..05aeb0624d41 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2100.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "ast_drv.h" +#include "ast_post.h" + +/* + * DRAM type + */ + +static enum ast_dram_layout ast_2100_get_dram_layout_p2a(struct ast_device *ast) +{ + u32 mcr_cfg; + enum ast_dram_layout dram_layout; + + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + mcr_cfg = ast_read32(ast, 0x10004); + + switch (mcr_cfg & 0x0c) { + case 0: + case 4: + default: + dram_layout = AST_DRAM_512Mx16; + break; + case 8: + if (mcr_cfg & 0x40) + dram_layout = AST_DRAM_1Gx16; + else + dram_layout = AST_DRAM_512Mx32; + break; + case 0xc: + dram_layout = AST_DRAM_1Gx32; + break; + } + + return dram_layout; +} + +/* + * POST + */ + +static const struct ast_dramstruct ast1100_dram_table_data[] = { + { 0x2000, 0x1688a8a8 }, + { 0x2020, 0x000041f0 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 0x0000, 0xfc600309 }, + { 0x006C, 0x00909090 }, + { 0x0064, 0x00050000 }, + AST_DRAMSTRUCT_INIT(DRAM_TYPE, 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 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 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 }, + AST_DRAMSTRUCT_INVALID, +}; + +static const struct ast_dramstruct ast2100_dram_table_data[] = { + { 0x2000, 0x1688a8a8 }, + { 0x2020, 0x00004120 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 0x0000, 0xfc600309 }, + { 0x006C, 0x00909090 }, + { 0x0064, 0x00070000 }, + AST_DRAMSTRUCT_INIT(DRAM_TYPE, 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 }, + AST_DRAMSTRUCT_UDELAY(67u), + { 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 }, + AST_DRAMSTRUCT_INVALID, +}; + +/* + * AST2100/2150 DLL CBR Setting + */ +#define CBR_SIZE_AST2150 ((16 << 10) - 1) +#define CBR_PASSNUM_AST2150 5 +#define CBR_THRESHOLD_AST2150 10 +#define CBR_THRESHOLD2_AST2150 10 +#define TIMEOUT_AST2150 5000000 + +#define CBR_PATNUM_AST2150 8 + +static const u32 pattern_AST2150[14] = { + 0xFF00FF00, + 0xCC33CC33, + 0xAA55AA55, + 0xFFFE0001, + 0x683501FE, + 0x0F1929B0, + 0x2D0B4346, + 0x60767F02, + 0x6FBE36A6, + 0x3A253035, + 0x3019686D, + 0x41C6167E, + 0x620152BF, + 0x20F050E0 +}; + +static u32 mmctestburst2_ast2150(struct ast_device *ast, u32 datagen) +{ + u32 data, timeout; + + ast_moutdwm(ast, 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; +} + +static int cbrtest_ast2150(struct ast_device *ast) +{ + int i; + + for (i = 0; i < 8; i++) + if (mmctestburst2_ast2150(ast, i)) + return 0; + return 1; +} + +static int cbrscan_ast2150(struct ast_device *ast, int busw) +{ + u32 patcnt, loop; + + for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) { + ast_moutdwm(ast, 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_device *ast, int busw) +{ + u32 dll_min[4], dll_max[4], dlli, data, passcnt; + +cbr_start: + dll_min[0] = 0xff; + dll_min[1] = 0xff; + dll_min[2] = 0xff; + dll_min[3] = 0xff; + dll_max[0] = 0x00; + dll_max[1] = 0x00; + dll_max[2] = 0x00; + dll_max[3] = 0x00; + passcnt = 0; + + for (dlli = 0; dlli < 100; dlli++) { + ast_moutdwm(ast, 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_post_chip_2100(struct ast_device *ast) +{ + u8 j; + u32 data, temp, i; + const struct ast_dramstruct *dram_reg_info; + enum ast_dram_layout dram_layout = ast_2100_get_dram_layout_p2a(ast); + + j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); + + if ((j & 0x80) == 0) { /* VGA only */ + 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 (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) { + if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) { + for (i = 0; i < 15; i++) + udelay(dram_reg_info->data); + } else if (AST_DRAMSTRUCT_IS(dram_reg_info, DRAM_TYPE)) { + switch (dram_layout) { + case AST_DRAM_1Gx16: + data = 0x00000d89; + break; + case AST_DRAM_1Gx32: + data = 0x00000c8d; + break; + default: + data = dram_reg_info->data; + break; + } + + 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 */ + } + + temp = ast_read32(ast, 0x1200c); + ast_write32(ast, 0x1200c, temp & 0xfffffffd); + temp = ast_read32(ast, 0x12040); + ast_write32(ast, 0x12040, temp | 0x40); + } + + /* wait ready */ + do { + j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); + } while ((j & 0x40) == 0); +} + +int ast_2100_post(struct ast_device *ast) +{ + ast_2000_set_def_ext_reg(ast); + + if (ast->config_mode == ast_use_p2a) { + ast_post_chip_2100(ast); + } else { + if (ast->tx_chip == AST_TX_SIL164) { + /* Enable DVO */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); + } + } + + return 0; +} + +/* + * Widescreen detection + */ + +/* Try to detect WSXGA+ on Gen2+ */ +bool __ast_2100_detect_wsxga_p(struct ast_device *ast) +{ + u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd0); + + if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC)) + return true; + if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN) + return true; + + return false; +} + +/* Try to detect WUXGA on Gen2+ */ +bool __ast_2100_detect_wuxga(struct ast_device *ast) +{ + u8 vgacrd1; + + if (ast->support_fullhd) { + vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1); + if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA)) + return true; + } + + return false; +} + +static void ast_2100_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast)) { + ast->support_wsxga_p = true; + if (ast->chip == AST2100) + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +static const struct ast_device_quirks ast_2100_device_quirks = { + .crtc_mem_req_threshold_low = 47, + .crtc_mem_req_threshold_high = 63, +}; + +struct drm_device *ast_2100_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2100_device_quirks); + + ast->dclk_table = ast_2000_dclk_table; + + ast_2000_detect_tx_chip(ast, need_post); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_2100_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c new file mode 100644 index 000000000000..b64345d11ffa --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2200.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "ast_drv.h" + +static void ast_2200_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast)) { + ast->support_wsxga_p = true; + if (ast->chip == AST2200) + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +static const struct ast_device_quirks ast_2200_device_quirks = { + .crtc_mem_req_threshold_low = 47, + .crtc_mem_req_threshold_high = 63, +}; + +struct drm_device *ast_2200_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2200_device_quirks); + + ast->dclk_table = ast_2000_dclk_table; + + ast_2000_detect_tx_chip(ast, need_post); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_2200_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} + diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c new file mode 100644 index 000000000000..5f50d9f91ffd --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2300.c @@ -0,0 +1,1463 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/sizes.h> + +#include <drm/drm_drv.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" +#include "ast_post.h" + +/* + * POST + */ + +void ast_2300_set_def_ext_reg(struct ast_device *ast) +{ + static const u8 extreginfo[] = { 0x0f, 0x04, 0x1f, 0xff }; + u8 i, index, reg; + const u8 *ext_reg_info; + + /* reset scratch */ + for (i = 0x81; i <= 0x9f; i++) + ast_set_index_reg(ast, AST_IO_VGACRI, i, 0x00); + + ext_reg_info = extreginfo; + index = 0xa0; + while (*ext_reg_info != 0xff) { + ast_set_index_reg_mask(ast, AST_IO_VGACRI, index, 0x00, *ext_reg_info); + index++; + ext_reg_info++; + } + + /* disable standard IO/MEM decode if secondary */ + /* ast_set_index_reg-mask(ast, AST_IO_VGACRI, 0xa1, 0xff, 0x3); */ + + /* Set Ext. Default */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x8c, 0x00, 0x01); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x00, 0x00); + + /* Enable RAMDAC for A1 */ + reg = 0x04; + reg |= 0x20; + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xff, reg); +} + +/* AST 2300 DRAM settings */ +#define AST_DDR3 0 +#define AST_DDR2 1 + +struct ast2300_dram_param { + u32 dram_type; + u32 dram_chipid; + u32 dram_freq; + u32 vram_size; + u32 odt; + u32 wodt; + u32 rodt; + u32 dram_config; + u32 reg_PERIOD; + u32 reg_MADJ; + u32 reg_SADJ; + u32 reg_MRS; + u32 reg_EMRS; + u32 reg_AC1; + u32 reg_AC2; + u32 reg_DQSIC; + u32 reg_DRV; + u32 reg_IOZ; + u32 reg_DQIDLY; + u32 reg_FREQ; + u32 madj_max; + u32 dll2_finetune_step; +}; + +/* + * DQSI DLL CBR Setting + */ +#define CBR_SIZE0 ((1 << 10) - 1) +#define CBR_SIZE1 ((4 << 10) - 1) +#define CBR_SIZE2 ((64 << 10) - 1) +#define CBR_PASSNUM 5 +#define CBR_PASSNUM2 5 +#define CBR_THRESHOLD 10 +#define CBR_THRESHOLD2 10 +#define TIMEOUT 5000000 +#define CBR_PATNUM 8 + +static const u32 pattern[8] = { + 0xFF00FF00, + 0xCC33CC33, + 0xAA55AA55, + 0x88778877, + 0x92CC4D6E, + 0x543D3CDE, + 0xF1E843C7, + 0x7C61D253 +}; + +static u32 mmc_test2(struct ast_device *ast, u32 datagen, u8 test_ctl) +{ + u32 data, timeout; + + ast_moutdwm(ast, 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 u32 mmc_test_burst2(struct ast_device *ast, u32 datagen) +{ + return mmc_test2(ast, datagen, 0x41); +} + +static bool mmc_test_single(struct ast_device *ast, u32 datagen) +{ + return mmc_test(ast, datagen, 0xc5); +} + +static u32 mmc_test_single2(struct ast_device *ast, u32 datagen) +{ + return mmc_test2(ast, datagen, 0x05); +} + +static int cbr_test(struct ast_device *ast) +{ + u32 data; + int i; + + data = mmc_test_single2(ast, 0); + if ((data & 0xff) && (data & 0xff00)) + return 0; + for (i = 0; i < 8; i++) { + data = mmc_test_burst2(ast, i); + if ((data & 0xff) && (data & 0xff00)) + return 0; + } + if (!data) + return 3; + else if (data & 0xff) + return 2; + return 1; +} + +static int cbr_scan(struct ast_device *ast) +{ + u32 data, data2, patcnt, loop; + + data2 = 3; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < CBR_PASSNUM2; loop++) { + data = cbr_test(ast); + if (data != 0) { + data2 &= data; + if (!data2) + return 0; + break; + } + } + if (loop == CBR_PASSNUM2) + return 0; + } + return data2; +} + +static u32 cbr_test2(struct ast_device *ast) +{ + u32 data; + + data = mmc_test_burst2(ast, 0); + if (data == 0xffff) + return 0; + data |= mmc_test_single2(ast, 0); + if (data == 0xffff) + return 0; + + return ~data & 0xffff; +} + +static u32 cbr_scan2(struct ast_device *ast) +{ + u32 data, data2, patcnt, loop; + + data2 = 0xffff; + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]); + for (loop = 0; loop < CBR_PASSNUM2; loop++) { + data = cbr_test2(ast); + if (data != 0) { + data2 &= data; + if (!data2) + return 0; + break; + } + } + if (loop == CBR_PASSNUM2) + return 0; + } + return data2; +} + +static bool cbr_test3(struct ast_device *ast) +{ + if (!mmc_test_burst(ast, 0)) + return false; + if (!mmc_test_single(ast, 0)) + return false; + return true; +} + +static bool cbr_scan3(struct ast_device *ast) +{ + u32 patcnt, loop; + + for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) { + ast_moutdwm(ast, 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_device *ast, struct ast2300_dram_param *param) +{ + u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, retry = 0; + bool status = false; +FINETUNE_START: + for (cnt = 0; cnt < 16; cnt++) { + dllmin[cnt] = 0xff; + dllmax[cnt] = 0x0; + } + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + ast_moutdwm(ast, 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_device *ast) +{ + u32 dlli, dqsip, dqidly; + u32 reg_mcr18, reg_mcr0c, passcnt[2], diff; + u32 g_dqidly, g_dqsip, g_margin, g_side; + u16 pass[32][2][2]; + char tag[2][76]; + + /* Disable DQI CBR */ + reg_mcr0c = ast_mindwm(ast, 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] = 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 = 0; + g_dqsip = 0; + g_margin = 0; + g_side = 0; + + for (dqidly = 0; dqidly < 32; dqidly++) { + for (dqsip = 0; dqsip < 2; dqsip++) { + if (pass[dqidly][dqsip][0] > pass[dqidly][dqsip][1]) + continue; + diff = pass[dqidly][dqsip][1] - pass[dqidly][dqsip][0]; + if ((diff + 2) < g_margin) + continue; + passcnt[0] = 0; + passcnt[1] = 0; + for (dlli = pass[dqidly][dqsip][0]; + dlli > 0 && tag[dqsip][dlli] != 0; + dlli--, passcnt[0]++) { + } + for (dlli = pass[dqidly][dqsip][1]; + dlli < 76 && tag[dqsip][dlli] != 0; + dlli++, passcnt[1]++) { + } + if (passcnt[0] > passcnt[1]) + passcnt[0] = passcnt[1]; + passcnt[1] = 0; + if (passcnt[0] > g_side) + passcnt[1] = passcnt[0] - g_side; + if (diff > (g_margin + 1) && (passcnt[1] > 0 || passcnt[0] > 8)) { + g_margin = diff; + g_dqidly = dqidly; + g_dqsip = dqsip; + g_side = passcnt[0]; + } else if (passcnt[1] > 1 && g_side < 8) { + if (diff > g_margin) + g_margin = diff; + g_dqidly = dqidly; + g_dqsip = dqsip; + g_side = passcnt[0]; + } + } + } + reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23); + ast_moutdwm(ast, 0x1E6E0018, reg_mcr18); +} + +static bool cbr_dll2(struct ast_device *ast, struct ast2300_dram_param *param) +{ + u32 dllmin[2], dllmax[2], dlli, data, passcnt, retry = 0; + bool status = false; + + finetuneDQSI(ast); + if (finetuneDQI_L(ast, param) == false) + return status; + +CBR_START2: + dllmin[0] = 0xff; + dllmin[1] = 0xff; + dllmax[0] = 0x0; + dllmax[1] = 0x0; + passcnt = 0; + for (dlli = 0; dlli < 76; dlli++) { + ast_moutdwm(ast, 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_device *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 SZ_8M: + param->dram_config |= 0x00; + break; + case SZ_16M: + param->dram_config |= 0x04; + break; + case SZ_32M: + param->dram_config |= 0x08; + break; + case SZ_64M: + param->dram_config |= 0x0c; + break; + } +} + +static void ddr3_init(struct ast_device *ast, struct ast2300_dram_param *param) +{ + u32 data, data2, retry = 0; + +ddr3_init_start: + ast_moutdwm(ast, 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_device *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 SZ_8M: + param->dram_config |= 0x00; + break; + case SZ_16M: + param->dram_config |= 0x04; + break; + case SZ_32M: + param->dram_config |= 0x08; + break; + case SZ_64M: + param->dram_config |= 0x0c; + break; + } +} + +static void ddr2_init(struct ast_device *ast, struct ast2300_dram_param *param) +{ + u32 data, data2, retry = 0; + +ddr2_init_start: + ast_moutdwm(ast, 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 ast_device *ast) +{ + struct ast2300_dram_param param; + u32 temp; + u8 reg; + + reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); + if ((reg & 0x80) == 0) {/* vga only */ + 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 = SZ_8M; + break; + case 0x04: + param.vram_size = SZ_16M; + break; + case 0x08: + param.vram_size = SZ_32M; + break; + case 0x0c: + param.vram_size = SZ_64M; + break; + } + + if (param.dram_type == AST_DDR3) { + get_ddr3_info(ast, ¶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_VGACRI, 0xd0, 0xff); + } while ((reg & 0x40) == 0); +} + +int ast_2300_post(struct ast_device *ast) +{ + ast_2300_set_def_ext_reg(ast); + + if (ast->config_mode == ast_use_p2a) { + ast_post_chip_2300(ast); + ast_init_3rdtx(ast); + } else { + if (ast->tx_chip == AST_TX_SIL164) { + /* Enable DVO */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); + } + } + + return 0; +} + +/* + * Device initialization + */ + +void ast_2300_detect_tx_chip(struct ast_device *ast) +{ + enum ast_tx_chip tx_chip = AST_TX_NONE; + struct drm_device *dev = &ast->base; + u8 vgacrd1; + + /* + * On AST GEN4+, look at the configuration set by the SoC in + * the SOC scratch register #1 bits 11:8 (interestingly marked + * as "reserved" in the spec) + */ + vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, + AST_IO_VGACRD1_TX_TYPE_MASK); + switch (vgacrd1) { + /* + * GEN4 to GEN6 + */ + case AST_IO_VGACRD1_TX_SIL164_VBIOS: + tx_chip = AST_TX_SIL164; + break; + case AST_IO_VGACRD1_TX_DP501_VBIOS: + ast->dp501_fw_addr = drmm_kzalloc(dev, SZ_32K, GFP_KERNEL); + if (ast->dp501_fw_addr) { + /* backup firmware */ + if (ast_backup_fw(ast, ast->dp501_fw_addr, SZ_32K)) { + drmm_kfree(dev, ast->dp501_fw_addr); + ast->dp501_fw_addr = NULL; + } + } + fallthrough; + case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW: + tx_chip = AST_TX_DP501; + break; + /* + * GEN7+ + */ + case AST_IO_VGACRD1_TX_ASTDP: + tx_chip = AST_TX_ASTDP; + break; + /* + * Several of the listed TX chips are not explicitly supported + * by the ast driver. If these exist in real-world devices, they + * are most likely reported as VGA or SIL164 outputs. We warn here + * to get bug reports for these devices. If none come in for some + * time, we can begin to fail device probing on these values. + */ + case AST_IO_VGACRD1_TX_ITE66121_VBIOS: + drm_warn(dev, "ITE IT66121 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast)); + break; + case AST_IO_VGACRD1_TX_CH7003_VBIOS: + drm_warn(dev, "Chrontel CH7003 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast)); + break; + case AST_IO_VGACRD1_TX_ANX9807_VBIOS: + drm_warn(dev, "Analogix ANX9807 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast)); + break; + } + + __ast_device_set_tx_chip(ast, tx_chip); +} + +static void ast_2300_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1300) { + ast->support_wsxga_p = true; + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +static const struct ast_device_quirks ast_2300_device_quirks = { + .crtc_mem_req_threshold_low = 96, + .crtc_mem_req_threshold_high = 120, +}; + +struct drm_device *ast_2300_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2300_device_quirks); + + ast->dclk_table = ast_2000_dclk_table; + + ast_2300_detect_tx_chip(ast); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (ast->vram_size < pci_resource_len(pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ast_2300_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c new file mode 100644 index 000000000000..2e6befd24f91 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2400.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_drv.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" + +static void ast_2400_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1400) { + ast->support_wsxga_p = true; + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +static const struct ast_device_quirks ast_2400_device_quirks = { + .crtc_mem_req_threshold_low = 96, + .crtc_mem_req_threshold_high = 120, +}; + +struct drm_device *ast_2400_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2400_device_quirks); + + ast->dclk_table = ast_2000_dclk_table; + + ast_2300_detect_tx_chip(ast); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (ast->vram_size < pci_resource_len(pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ast_2400_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c new file mode 100644 index 000000000000..2a52af0ded56 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2500.c @@ -0,0 +1,675 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" +#include "ast_post.h" + +/* + * POST + */ + +/* + * AST2500 DRAM settings modules + */ + +#define REGTBL_NUM 17 +#define REGIDX_010 0 +#define REGIDX_014 1 +#define REGIDX_018 2 +#define REGIDX_020 3 +#define REGIDX_024 4 +#define REGIDX_02C 5 +#define REGIDX_030 6 +#define REGIDX_214 7 +#define REGIDX_2E0 8 +#define REGIDX_2E4 9 +#define REGIDX_2E8 10 +#define REGIDX_2EC 11 +#define REGIDX_2F0 12 +#define REGIDX_2F4 13 +#define REGIDX_2F8 14 +#define REGIDX_RFC 15 +#define REGIDX_PLL 16 + +static const u32 ast2500_ddr3_1600_timing_table[REGTBL_NUM] = { + 0x64604D38, /* 0x010 */ + 0x29690599, /* 0x014 */ + 0x00000300, /* 0x018 */ + 0x00000000, /* 0x020 */ + 0x00000000, /* 0x024 */ + 0x02181E70, /* 0x02C */ + 0x00000040, /* 0x030 */ + 0x00000024, /* 0x214 */ + 0x02001300, /* 0x2E0 */ + 0x0E0000A0, /* 0x2E4 */ + 0x000E001B, /* 0x2E8 */ + 0x35B8C105, /* 0x2EC */ + 0x08090408, /* 0x2F0 */ + 0x9B000800, /* 0x2F4 */ + 0x0E400A00, /* 0x2F8 */ + 0x9971452F, /* tRFC */ + 0x000071C1 /* PLL */ +}; + +static const u32 ast2500_ddr4_1600_timing_table[REGTBL_NUM] = { + 0x63604E37, /* 0x010 */ + 0xE97AFA99, /* 0x014 */ + 0x00019000, /* 0x018 */ + 0x08000000, /* 0x020 */ + 0x00000400, /* 0x024 */ + 0x00000410, /* 0x02C */ + 0x00000101, /* 0x030 */ + 0x00000024, /* 0x214 */ + 0x03002900, /* 0x2E0 */ + 0x0E0000A0, /* 0x2E4 */ + 0x000E001C, /* 0x2E8 */ + 0x35B8C106, /* 0x2EC */ + 0x08080607, /* 0x2F0 */ + 0x9B000900, /* 0x2F4 */ + 0x0E400A00, /* 0x2F8 */ + 0x99714545, /* tRFC */ + 0x000071C1 /* PLL */ +}; + +#define TIMEOUT 5000000 + +void ast_2500_patch_ahb(void __iomem *regs) +{ + u32 data; + + /* Clear bus lock condition */ + __ast_moutdwm(regs, 0x1e600000, 0xAEED1A03); + __ast_moutdwm(regs, 0x1e600084, 0x00010000); + __ast_moutdwm(regs, 0x1e600088, 0x00000000); + __ast_moutdwm(regs, 0x1e6e2000, 0x1688A8A8); + + data = __ast_mindwm(regs, 0x1e6e2070); + if (data & 0x08000000) { /* check fast reset */ + /* + * If "Fast restet" is enabled for ARM-ICE debugger, + * then WDT needs to enable, that + * WDT04 is WDT#1 Reload reg. + * WDT08 is WDT#1 counter restart reg to avoid system deadlock + * WDT0C is WDT#1 control reg + * [6:5]:= 01:Full chip + * [4]:= 1:1MHz clock source + * [1]:= 1:WDT will be cleeared and disabled after timeout occurs + * [0]:= 1:WDT enable + */ + __ast_moutdwm(regs, 0x1E785004, 0x00000010); + __ast_moutdwm(regs, 0x1E785008, 0x00004755); + __ast_moutdwm(regs, 0x1E78500c, 0x00000033); + udelay(1000); + } + + do { + __ast_moutdwm(regs, 0x1e6e2000, 0x1688A8A8); + data = __ast_mindwm(regs, 0x1e6e2000); + } while (data != 1); + + __ast_moutdwm(regs, 0x1e6e207c, 0x08000000); /* clear fast reset */ +} + +static bool mmc_test_single_2500(struct ast_device *ast, u32 datagen) +{ + return mmc_test(ast, datagen, 0x85); +} + +static bool cbr_test_2500(struct ast_device *ast) +{ + ast_moutdwm(ast, 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_device *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_device *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_device *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_device *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_device *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_device *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_device *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_device *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_device *ast, const u32 *ddr_table) +{ + u32 data, data2, pass, retrycnt; + u32 ddr_vref, phy_vref; + u32 min_ddr_vref = 0, min_phy_vref = 0; + u32 max_ddr_vref = 0, max_phy_vref = 0; + + ast_moutdwm(ast, 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_device *ast) +{ + u32 data; + u32 max_tries = 5; + + do { + if (max_tries-- == 0) + return false; + set_mpll_2500(ast); + reset_mmc_2500(ast); + ddr_init_common_2500(ast); + + data = ast_mindwm(ast, 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; +} + +static void ast_post_chip_2500(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + u32 temp; + u8 reg; + + reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); + if ((reg & AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK) == 0) {/* vga only */ + /* Clear bus lock condition */ + ast_2500_patch_ahb(ast->regs); + + /* Disable watchdog */ + ast_moutdwm(ast, 0x1E78502C, 0x00000000); + ast_moutdwm(ast, 0x1E78504C, 0x00000000); + + /* + * Reset USB port to patch USB unknown device issue + * SCU90 is Multi-function Pin Control #5 + * [29]:= 1:Enable USB2.0 Host port#1 (that the mutually shared USB2.0 Hub + * port). + * SCU94 is Multi-function Pin Control #6 + * [14:13]:= 1x:USB2.0 Host2 controller + * SCU70 is Hardware Strap reg + * [23]:= 1:CLKIN is 25MHz and USBCK1 = 24/48 MHz (determined by + * [18]: 0(24)/1(48) MHz) + * SCU7C is Write clear reg to SCU70 + * [23]:= write 1 and then SCU70[23] will be clear as 0b. + */ + ast_moutdwm(ast, 0x1E6E2090, 0x20000000); + ast_moutdwm(ast, 0x1E6E2094, 0x00004000); + if (ast_mindwm(ast, 0x1E6E2070) & 0x00800000) { + ast_moutdwm(ast, 0x1E6E207C, 0x00800000); + mdelay(100); + ast_moutdwm(ast, 0x1E6E2070, 0x00800000); + } + /* Modify eSPI reset pin */ + temp = ast_mindwm(ast, 0x1E6E2070); + if (temp & 0x02000000) + ast_moutdwm(ast, 0x1E6E207C, 0x00004000); + + /* Slow down CPU/AHB CLK in VGA only mode */ + temp = ast_read32(ast, 0x12008); + temp |= 0x73; + ast_write32(ast, 0x12008, temp); + + 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_VGACRI, 0xd0, 0xff); + } while ((reg & 0x40) == 0); +} + +int ast_2500_post(struct ast_device *ast) +{ + ast_2300_set_def_ext_reg(ast); + + if (ast->config_mode == ast_use_p2a) { + ast_post_chip_2500(ast); + } else { + if (ast->tx_chip == AST_TX_SIL164) { + /* Enable DVO */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); + } + } + + return 0; +} + +/* + * Mode setting + */ + +const struct ast_vbios_dclk_info ast_2500_dclk_table[] = { + {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xee, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xc6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7b, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */ + {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */ + {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0d: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */ + {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x58, 0x01, 0x42}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ + {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x44, 0x20, 0x43}, /* 1a: VCLK118_25 */ +}; + +/* + * Device initialization + */ + +static void ast_2500_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST2510) { + ast->support_wsxga_p = true; + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +static const struct ast_device_quirks ast_2500_device_quirks = { + .crtc_mem_req_threshold_low = 96, + .crtc_mem_req_threshold_high = 120, + .crtc_hsync_precatch_needed = true, +}; + +struct drm_device *ast_2500_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2500_device_quirks); + + ast->dclk_table = ast_2500_dclk_table; + + ast_2300_detect_tx_chip(ast); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (ast->vram_size < pci_resource_len(pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ast_2500_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c new file mode 100644 index 000000000000..dee78fd5b022 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "ast_drv.h" +#include "ast_post.h" + +/* + * POST + */ + +int ast_2600_post(struct ast_device *ast) +{ + ast_2300_set_def_ext_reg(ast); + + if (ast->tx_chip == AST_TX_ASTDP) + return ast_dp_launch(ast); + + return 0; +} + +/* + * Device initialization + */ + +static void ast_2600_detect_widescreen(struct ast_device *ast) +{ + ast->support_wsxga_p = true; + ast->support_fullhd = true; + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +static const struct ast_device_quirks ast_2600_device_quirks = { + .crtc_mem_req_threshold_low = 160, + .crtc_mem_req_threshold_high = 224, + .crtc_hsync_precatch_needed = true, + .crtc_hsync_add4_needed = true, +}; + +struct drm_device *ast_2600_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2600_device_quirks); + + ast->dclk_table = ast_2500_dclk_table; + + ast_2300_detect_tx_chip(ast); + + switch (ast->tx_chip) { + case AST_TX_ASTDP: + ret = ast_post_gpu(ast); + break; + default: + ret = 0; + if (need_post) + ret = ast_post_gpu(ast); + break; + } + if (ret) + return ERR_PTR(ret); + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_2600_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c new file mode 100644 index 000000000000..2d3ad7610c2e --- /dev/null +++ b/drivers/gpu/drm/ast/ast_cursor.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: MIT +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ + +#include <linux/bits.h> +#include <linux/sizes.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_format_helper.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" + +/* + * Hardware cursor + */ + +/* define for signature structure */ +#define AST_HWC_SIGNATURE_SIZE SZ_32 +#define AST_HWC_SIGNATURE_CHECKSUM 0x00 +#define AST_HWC_SIGNATURE_SizeX 0x04 +#define AST_HWC_SIGNATURE_SizeY 0x08 +#define AST_HWC_SIGNATURE_X 0x0C +#define AST_HWC_SIGNATURE_Y 0x10 +#define AST_HWC_SIGNATURE_HOTSPOTX 0x14 +#define AST_HWC_SIGNATURE_HOTSPOTY 0x18 + +static unsigned long ast_cursor_vram_size(void) +{ + return AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE; +} + +long ast_cursor_vram_offset(struct ast_device *ast) +{ + unsigned long size = ast_cursor_vram_size(); + + if (size > ast->vram_size) + return -EINVAL; + + return ALIGN_DOWN(ast->vram_size - size, SZ_8); +} + +static u32 ast_cursor_calculate_checksum(const void *src, unsigned int width, unsigned int height) +{ + u32 csum = 0; + unsigned int one_pixel_copy = width & BIT(0); + unsigned int two_pixel_copy = width - one_pixel_copy; + unsigned int trailing_bytes = (AST_MAX_HWC_WIDTH - width) * sizeof(u16); + unsigned int x, y; + + for (y = 0; y < height; y++) { + for (x = 0; x < two_pixel_copy; x += 2) { + const u32 *src32 = (const u32 *)src; + + csum += *src32; + src += SZ_4; + } + if (one_pixel_copy) { + const u16 *src16 = (const u16 *)src; + + csum += *src16; + src += SZ_2; + } + src += trailing_bytes; + } + + return csum; +} + +static void ast_set_cursor_image(struct ast_device *ast, const u8 *src, + unsigned int width, unsigned int height) +{ + u8 __iomem *dst = ast_plane_vaddr(&ast->cursor_plane.base); + u32 csum; + + csum = ast_cursor_calculate_checksum(src, width, height); + + /* write pixel data */ + memcpy_toio(dst, src, AST_HWC_SIZE); + + /* write checksum + signature */ + dst += AST_HWC_SIZE; + writel(csum, dst); + writel(width, dst + AST_HWC_SIGNATURE_SizeX); + writel(height, dst + AST_HWC_SIGNATURE_SizeY); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX); + writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY); +} + +static void ast_set_cursor_base(struct ast_device *ast, u64 address) +{ + u8 addr0 = (address >> 3) & 0xff; + u8 addr1 = (address >> 11) & 0xff; + u8 addr2 = (address >> 19) & 0xff; + + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc8, addr0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc9, addr1); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xca, addr2); +} + +static void ast_set_cursor_location(struct ast_device *ast, u16 x, u16 y, + u8 x_offset, u8 y_offset) +{ + u8 x0 = (x & 0x00ff); + u8 x1 = (x & 0x0f00) >> 8; + u8 y0 = (y & 0x00ff); + u8 y1 = (y & 0x0700) >> 8; + + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc2, x_offset); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc3, y_offset); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc4, x0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc5, x1); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc6, y0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xc7, y1); +} + +static void ast_set_cursor_enabled(struct ast_device *ast, bool enabled) +{ + static const u8 mask = (u8)~(AST_IO_VGACRCB_HWC_16BPP | + AST_IO_VGACRCB_HWC_ENABLED); + + u8 vgacrcb = AST_IO_VGACRCB_HWC_16BPP; + + if (enabled) + vgacrcb |= AST_IO_VGACRCB_HWC_ENABLED; + + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xcb, mask, vgacrcb); +} + +/* + * Cursor plane + */ + +static const uint32_t ast_cursor_plane_formats[] = { + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB8888, +}; + +static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; + + if (new_plane_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); + if (ret || !new_plane_state->visible) + return ret; + + if (new_fb->width > AST_MAX_HWC_WIDTH || new_fb->height > AST_MAX_HWC_HEIGHT) + return -EINVAL; + + return 0; +} + +static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct ast_cursor_plane *ast_cursor_plane = to_ast_cursor_plane(plane); + struct ast_plane *ast_plane = to_ast_plane(plane); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct ast_device *ast = to_ast_device(plane->dev); + struct drm_rect damage; + u64 dst_off = ast_plane->offset; + u8 __iomem *dst = ast_plane_vaddr(ast_plane); /* TODO: Use mapping abstraction properly */ + u8 __iomem *sig = dst + AST_HWC_SIZE; /* TODO: Use mapping abstraction properly */ + unsigned int offset_x, offset_y; + u16 x, y; + u8 x_offset, y_offset; + + /* + * Do data transfer to hardware buffer and point the scanout + * engine to the offset. + */ + + if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) { + u8 *argb4444; + + switch (fb->format->format) { + case DRM_FORMAT_ARGB4444: + argb4444 = shadow_plane_state->data[0].vaddr; + break; + default: + argb4444 = ast_cursor_plane->argb4444; + { + struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { + IOSYS_MAP_INIT_VADDR(argb4444), + }; + unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { + AST_HWC_PITCH, + }; + + drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, + shadow_plane_state->data, fb, &damage, + &shadow_plane_state->fmtcnv_state); + } + break; + } + ast_set_cursor_image(ast, argb4444, fb->width, fb->height); + ast_set_cursor_base(ast, dst_off); + } + + /* + * Update location in HWC signature and registers. + */ + + writel(plane_state->crtc_x, sig + AST_HWC_SIGNATURE_X); + writel(plane_state->crtc_y, sig + AST_HWC_SIGNATURE_Y); + + offset_x = AST_MAX_HWC_WIDTH - fb->width; + offset_y = AST_MAX_HWC_HEIGHT - fb->height; + + if (plane_state->crtc_x < 0) { + x_offset = (-plane_state->crtc_x) + offset_x; + x = 0; + } else { + x_offset = offset_x; + x = plane_state->crtc_x; + } + if (plane_state->crtc_y < 0) { + y_offset = (-plane_state->crtc_y) + offset_y; + y = 0; + } else { + y_offset = offset_y; + y = plane_state->crtc_y; + } + + ast_set_cursor_location(ast, x, y, x_offset, y_offset); + + /* Dummy write to enable HWC and make the HW pick-up the changes. */ + ast_set_cursor_enabled(ast, true); +} + +static void ast_cursor_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct ast_device *ast = to_ast_device(plane->dev); + + ast_set_cursor_enabled(ast, false); +} + +static const struct drm_plane_helper_funcs ast_cursor_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = ast_cursor_plane_helper_atomic_check, + .atomic_update = ast_cursor_plane_helper_atomic_update, + .atomic_disable = ast_cursor_plane_helper_atomic_disable, +}; + +static const struct drm_plane_funcs ast_cursor_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +int ast_cursor_plane_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct ast_cursor_plane *ast_cursor_plane = &ast->cursor_plane; + struct ast_plane *ast_plane = &ast_cursor_plane->base; + struct drm_plane *cursor_plane = &ast_plane->base; + unsigned long size; + long offset; + int ret; + + size = ast_cursor_vram_size(); + offset = ast_cursor_vram_offset(ast); + if (offset < 0) + return offset; + + ret = ast_plane_init(dev, ast_plane, offset, size, + 0x01, &ast_cursor_plane_funcs, + ast_cursor_plane_formats, ARRAY_SIZE(ast_cursor_plane_formats), + NULL, DRM_PLANE_TYPE_CURSOR); + if (ret) { + drm_err(dev, "ast_plane_init() failed: %d\n", ret); + return ret; + } + drm_plane_helper_add(cursor_plane, &ast_cursor_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(cursor_plane); + + return 0; +} diff --git a/drivers/gpu/drm/ast/ast_ddc.c b/drivers/gpu/drm/ast/ast_ddc.c new file mode 100644 index 000000000000..29cf5d157f34 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_ddc.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ + +#include <linux/i2c-algo-bit.h> +#include <linux/i2c.h> + +#include <drm/drm_managed.h> +#include <drm/drm_print.h> + +#include "ast_ddc.h" +#include "ast_drv.h" + +struct ast_ddc { + struct ast_device *ast; + + struct i2c_algo_bit_data bit; + struct i2c_adapter adapter; +}; + +static void ast_ddc_algo_bit_data_setsda(void *data, int state) +{ + struct ast_ddc *ddc = data; + struct ast_device *ast = ddc->ast; + int i; + u8 ujcrb7, jtemp; + + for (i = 0; i < 0x10000; i++) { + ujcrb7 = ((state & 0x01) ? 0 : 1) << 2; + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0xf1, ujcrb7); + jtemp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x04); + if (ujcrb7 == jtemp) + break; + } +} + +static void ast_ddc_algo_bit_data_setscl(void *data, int state) +{ + struct ast_ddc *ddc = data; + struct ast_device *ast = ddc->ast; + int i; + u8 ujcrb7, jtemp; + + for (i = 0; i < 0x10000; i++) { + ujcrb7 = ((state & 0x01) ? 0 : 1); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0xf4, ujcrb7); + jtemp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x01); + if (ujcrb7 == jtemp) + break; + } +} + +static int ast_ddc_algo_bit_data_pre_xfer(struct i2c_adapter *adapter) +{ + struct ast_ddc *ddc = i2c_get_adapdata(adapter); + struct ast_device *ast = ddc->ast; + + /* + * Protect access to I/O registers from concurrent modesetting + * by acquiring the I/O-register lock. + */ + mutex_lock(&ast->modeset_lock); + + return 0; +} + +static void ast_ddc_algo_bit_data_post_xfer(struct i2c_adapter *adapter) +{ + struct ast_ddc *ddc = i2c_get_adapdata(adapter); + struct ast_device *ast = ddc->ast; + + mutex_unlock(&ast->modeset_lock); +} + +static int ast_ddc_algo_bit_data_getsda(void *data) +{ + struct ast_ddc *ddc = data; + struct ast_device *ast = ddc->ast; + uint32_t val, val2, count, pass; + + count = 0; + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x20) >> 5) & 0x01; + do { + val2 = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x20) >> 5) & 0x01; + if (val == val2) { + pass++; + } else { + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x20) >> 5) & 0x01; + } + } while ((pass < 5) && (count++ < 0x10000)); + + return val & 1 ? 1 : 0; +} + +static int ast_ddc_algo_bit_data_getscl(void *data) +{ + struct ast_ddc *ddc = data; + struct ast_device *ast = ddc->ast; + uint32_t val, val2, count, pass; + + count = 0; + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x10) >> 4) & 0x01; + do { + val2 = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x10) >> 4) & 0x01; + if (val == val2) { + pass++; + } else { + pass = 0; + val = (ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xb7, 0x10) >> 4) & 0x01; + } + } while ((pass < 5) && (count++ < 0x10000)); + + return val & 1 ? 1 : 0; +} + +static void ast_ddc_release(struct drm_device *dev, void *res) +{ + struct ast_ddc *ddc = res; + + i2c_del_adapter(&ddc->adapter); +} + +struct i2c_adapter *ast_ddc_create(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct ast_ddc *ddc; + struct i2c_adapter *adapter; + struct i2c_algo_bit_data *bit; + int ret; + + ddc = drmm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL); + if (!ddc) + return ERR_PTR(-ENOMEM); + ddc->ast = ast; + + bit = &ddc->bit; + bit->data = ddc; + bit->setsda = ast_ddc_algo_bit_data_setsda; + bit->setscl = ast_ddc_algo_bit_data_setscl; + bit->getsda = ast_ddc_algo_bit_data_getsda; + bit->getscl = ast_ddc_algo_bit_data_getscl; + bit->pre_xfer = ast_ddc_algo_bit_data_pre_xfer; + bit->post_xfer = ast_ddc_algo_bit_data_post_xfer; + bit->udelay = 20; + bit->timeout = usecs_to_jiffies(2200); + + adapter = &ddc->adapter; + adapter->owner = THIS_MODULE; + adapter->algo_data = bit; + adapter->dev.parent = dev->dev; + snprintf(adapter->name, sizeof(adapter->name), "AST DDC bus"); + i2c_set_adapdata(adapter, ddc); + + ret = i2c_bit_add_bus(adapter); + if (ret) { + drm_err(dev, "Failed to register bit i2c\n"); + return ERR_PTR(ret); + } + + ret = drmm_add_action_or_reset(dev, ast_ddc_release, ddc); + if (ret) + return ERR_PTR(ret); + + return &ddc->adapter; +} diff --git a/drivers/gpu/drm/ast/ast_ddc.h b/drivers/gpu/drm/ast/ast_ddc.h new file mode 100644 index 000000000000..85c93edc9ae1 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_ddc.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AST_DDC_H__ +#define __AST_DDC_H__ + +struct ast_device; +struct i2c_adapter; + +struct i2c_adapter *ast_ddc_create(struct ast_device *ast); + +#endif diff --git a/drivers/gpu/drm/ast/ast_dp.c b/drivers/gpu/drm/ast/ast_dp.c new file mode 100644 index 000000000000..8e650a02c528 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_dp.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021, ASPEED Technology Inc. +// Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> + +#include <linux/firmware.h> +#include <linux/delay.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include "ast_drv.h" +#include "ast_vbios.h" + +struct ast_astdp_mode_index_table_entry { + unsigned int hdisplay; + unsigned int vdisplay; + unsigned int mode_index; +}; + +/* FIXME: Do refresh rate and flags actually matter? */ +static const struct ast_astdp_mode_index_table_entry ast_astdp_mode_index_table[] = { + { 320, 240, ASTDP_320x240_60 }, + { 400, 300, ASTDP_400x300_60 }, + { 512, 384, ASTDP_512x384_60 }, + { 640, 480, ASTDP_640x480_60 }, + { 800, 600, ASTDP_800x600_56 }, + { 1024, 768, ASTDP_1024x768_60 }, + { 1152, 864, ASTDP_1152x864_75 }, + { 1280, 800, ASTDP_1280x800_60_RB }, + { 1280, 1024, ASTDP_1280x1024_60 }, + { 1360, 768, ASTDP_1366x768_60 }, // same as 1366x786 + { 1366, 768, ASTDP_1366x768_60 }, + { 1440, 900, ASTDP_1440x900_60_RB }, + { 1600, 900, ASTDP_1600x900_60_RB }, + { 1600, 1200, ASTDP_1600x1200_60 }, + { 1680, 1050, ASTDP_1680x1050_60_RB }, + { 1920, 1080, ASTDP_1920x1080_60 }, + { 1920, 1200, ASTDP_1920x1200_60 }, + { 0 } +}; + +struct ast_astdp_connector_state { + struct drm_connector_state base; + + int mode_index; +}; + +static struct ast_astdp_connector_state * +to_ast_astdp_connector_state(const struct drm_connector_state *state) +{ + return container_of(state, struct ast_astdp_connector_state, base); +} + +static int ast_astdp_get_mode_index(unsigned int hdisplay, unsigned int vdisplay) +{ + const struct ast_astdp_mode_index_table_entry *entry = ast_astdp_mode_index_table; + + while (entry->hdisplay && entry->vdisplay) { + if (entry->hdisplay == hdisplay && entry->vdisplay == vdisplay) + return entry->mode_index; + ++entry; + } + + return -EINVAL; +} + +static bool ast_astdp_is_connected(struct ast_device *ast) +{ + if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xDF, AST_IO_VGACRDF_HPD)) + return false; + /* + * HPD might be set even if no monitor is connected, so also check that + * the link training was successful. + */ + if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xDC, AST_IO_VGACRDC_LINK_SUCCESS)) + return false; + return true; +} + +static int ast_astdp_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len) +{ + struct ast_device *ast = data; + size_t rdlen = round_up(len, 4); + int ret = 0; + unsigned int i; + + if (block > 0) + return -EIO; /* extension headers not supported */ + + /* + * Protect access to I/O registers from concurrent modesetting + * by acquiring the I/O-register lock. + */ + mutex_lock(&ast->modeset_lock); + + /* Start reading EDID data */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe5, (u8)~AST_IO_VGACRE5_EDID_READ_DONE, 0x00); + + for (i = 0; i < rdlen; i += 4) { + unsigned int offset; + unsigned int j; + u8 ediddata[4]; + u8 vgacre4; + + offset = (i + block * EDID_LENGTH) / 4; + if (offset >= 64) { + ret = -EIO; + goto out; + } + vgacre4 = offset; + + /* + * CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64 + */ + ast_set_index_reg(ast, AST_IO_VGACRI, 0xe4, vgacre4); + + /* + * CRD7[b0]: valid flag for EDID + * CRD6[b0]: mirror read pointer for EDID + */ + for (j = 0; j < 200; ++j) { + u8 vgacrd7, vgacrd6; + + /* + * Delay are getting longer with each retry. + * + * 1. No delay on first try + * 2. The Delays are often 2 loops when users request "Display Settings" + * of right-click of mouse. + * 3. The Delays are often longer a lot when system resume from S3/S4. + */ + if (j) + msleep(j + 1); + + /* Wait for EDID offset to show up in mirror register */ + vgacrd7 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd7); + if (vgacrd7 & AST_IO_VGACRD7_EDID_VALID_FLAG) { + vgacrd6 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd6); + if (vgacrd6 == offset) + break; + } + } + if (j == 200) { + ret = -EBUSY; + goto out; + } + + ediddata[0] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd8); + ediddata[1] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd9); + ediddata[2] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xda); + ediddata[3] = ast_get_index_reg(ast, AST_IO_VGACRI, 0xdb); + + if (i == 31) { + /* + * For 128-bytes EDID_1.3, + * 1. Add the value of Bytes-126 to Bytes-127. + * The Bytes-127 is Checksum. Sum of all 128bytes should + * equal 0 (mod 256). + * 2. Modify Bytes-126 to be 0. + * The Bytes-126 indicates the Number of extensions to + * follow. 0 represents noextensions. + */ + ediddata[3] = ediddata[3] + ediddata[2]; + ediddata[2] = 0; + } + + memcpy(buf, ediddata, min((len - i), 4)); + buf += 4; + } + +out: + /* Signal end of reading */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe5, (u8)~AST_IO_VGACRE5_EDID_READ_DONE, + AST_IO_VGACRE5_EDID_READ_DONE); + + mutex_unlock(&ast->modeset_lock); + + return ret; +} + +/* + * Launch Aspeed DP + */ +int ast_dp_launch(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + unsigned int i = 10; + + while (i) { + u8 vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1); + + if (vgacrd1 & AST_IO_VGACRD1_MCU_FW_EXECUTING) + break; + --i; + msleep(100); + } + if (!i) { + drm_err(dev, "Wait DPMCU executing timeout\n"); + return -ENODEV; + } + + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe5, + (u8) ~AST_IO_VGACRE5_EDID_READ_DONE, + AST_IO_VGACRE5_EDID_READ_DONE); + + return 0; +} + +static bool ast_dp_get_phy_sleep(struct ast_device *ast) +{ + u8 vgacre3 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xe3); + + return (vgacre3 & AST_IO_VGACRE3_DP_PHY_SLEEP); +} + +static void ast_dp_set_phy_sleep(struct ast_device *ast, bool sleep) +{ + u8 vgacre3 = 0x00; + + if (sleep) + vgacre3 |= AST_IO_VGACRE3_DP_PHY_SLEEP; + + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe3, (u8)~AST_IO_VGACRE3_DP_PHY_SLEEP, + vgacre3); + msleep(50); +} + +static void ast_dp_link_training(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + int i; + + for (i = 0; i < 10; i++) { + u8 vgacrdc; + + if (i) + msleep(100); + + vgacrdc = ast_get_index_reg(ast, AST_IO_VGACRI, 0xdc); + if (vgacrdc & AST_IO_VGACRDC_LINK_SUCCESS) + return; + } + drm_err(dev, "Link training failed\n"); +} + +static bool __ast_dp_wait_enable(struct ast_device *ast, bool enabled) +{ + u8 vgacrdf_test = 0x00; + u8 vgacrdf; + unsigned int i; + + if (enabled) + vgacrdf_test |= AST_IO_VGACRDF_DP_VIDEO_ENABLE; + + for (i = 0; i < 1000; ++i) { + if (i) + mdelay(1); + vgacrdf = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xdf, + AST_IO_VGACRDF_DP_VIDEO_ENABLE); + if (vgacrdf == vgacrdf_test) + return true; + } + + return false; +} + +static void ast_dp_set_enable(struct ast_device *ast, bool enabled) +{ + struct drm_device *dev = &ast->base; + u8 vgacre3 = 0x00; + + if (enabled) + vgacre3 |= AST_IO_VGACRE3_DP_VIDEO_ENABLE; + + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xe3, (u8)~AST_IO_VGACRE3_DP_VIDEO_ENABLE, + vgacre3); + + drm_WARN_ON(dev, !__ast_dp_wait_enable(ast, enabled)); +} + +static void ast_wait_for_vretrace(struct ast_device *ast) +{ + unsigned long timeout = jiffies + HZ; + u8 vgair1; + + do { + vgair1 = ast_io_read8(ast, AST_IO_VGAIR1_R); + } while (!(vgair1 & AST_IO_VGAIR1_VREFRESH) && time_before(jiffies, timeout)); +} + +/* + * Encoder + */ + +static const struct drm_encoder_funcs ast_astdp_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static enum drm_mode_status +ast_astdp_encoder_helper_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + int res; + + res = ast_astdp_get_mode_index(mode->hdisplay, mode->vdisplay); + if (res < 0) + return MODE_NOMODE; + + return MODE_OK; +} + +static void ast_astdp_encoder_helper_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_device *dev = encoder->dev; + struct ast_device *ast = to_ast_device(dev); + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + const struct ast_vbios_enhtable *vmode = ast_crtc_state->vmode; + struct ast_astdp_connector_state *astdp_conn_state = + to_ast_astdp_connector_state(conn_state); + int mode_index = astdp_conn_state->mode_index; + u8 refresh_rate_index; + u8 vgacre0, vgacre1, vgacre2; + + if (drm_WARN_ON(dev, vmode->refresh_rate_index < 1 || vmode->refresh_rate_index > 255)) + return; + refresh_rate_index = vmode->refresh_rate_index - 1; + + /* FIXME: Why are we doing this? */ + switch (mode_index) { + case ASTDP_1280x800_60_RB: + case ASTDP_1440x900_60_RB: + case ASTDP_1600x900_60_RB: + case ASTDP_1680x1050_60_RB: + mode_index = (u8)(mode_index - (u8)refresh_rate_index); + break; + default: + mode_index = (u8)(mode_index + (u8)refresh_rate_index); + break; + } + + /* + * CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp) + * CRE1[7:0]: MISC1 (default: 0x00) + * CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50) + */ + vgacre0 = AST_IO_VGACRE0_24BPP; + vgacre1 = 0x00; + vgacre2 = mode_index & 0xff; + + ast_set_index_reg(ast, AST_IO_VGACRI, 0xe0, vgacre0); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xe1, vgacre1); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xe2, vgacre2); +} + +static void ast_astdp_encoder_helper_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct ast_device *ast = to_ast_device(encoder->dev); + struct ast_connector *ast_connector = &ast->output.astdp.connector; + + if (ast_connector->physical_status == connector_status_connected) { + ast_dp_set_phy_sleep(ast, false); + ast_dp_link_training(ast); + + ast_wait_for_vretrace(ast); + ast_dp_set_enable(ast, true); + } +} + +static void ast_astdp_encoder_helper_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct ast_device *ast = to_ast_device(encoder->dev); + + ast_dp_set_enable(ast, false); + ast_dp_set_phy_sleep(ast, true); +} + +static int ast_astdp_encoder_helper_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + const struct drm_display_mode *mode = &crtc_state->mode; + struct ast_astdp_connector_state *astdp_conn_state = + to_ast_astdp_connector_state(conn_state); + int res; + + if (drm_atomic_crtc_needs_modeset(crtc_state)) { + res = ast_astdp_get_mode_index(mode->hdisplay, mode->vdisplay); + if (res < 0) + return res; + astdp_conn_state->mode_index = res; + } + + return 0; +} + +static const struct drm_encoder_helper_funcs ast_astdp_encoder_helper_funcs = { + .mode_valid = ast_astdp_encoder_helper_mode_valid, + .atomic_mode_set = ast_astdp_encoder_helper_atomic_mode_set, + .atomic_enable = ast_astdp_encoder_helper_atomic_enable, + .atomic_disable = ast_astdp_encoder_helper_atomic_disable, + .atomic_check = ast_astdp_encoder_helper_atomic_check, +}; + +/* + * Connector + */ + +static int ast_astdp_connector_helper_get_modes(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + int count; + + if (ast_connector->physical_status == connector_status_connected) { + struct ast_device *ast = to_ast_device(connector->dev); + const struct drm_edid *drm_edid; + + drm_edid = drm_edid_read_custom(connector, ast_astdp_read_edid_block, ast); + drm_edid_connector_update(connector, drm_edid); + count = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); + } else { + drm_edid_connector_update(connector, NULL); + + /* + * There's no EDID data without a connected monitor. Set BMC- + * compatible modes in this case. The XGA default resolution + * should work well for all BMCs. + */ + count = drm_add_modes_noedid(connector, 4096, 4096); + if (count) + drm_set_preferred_mode(connector, 1024, 768); + } + + return count; +} + +static int ast_astdp_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + struct ast_device *ast = to_ast_device(connector->dev); + enum drm_connector_status status = connector_status_disconnected; + bool phy_sleep; + + mutex_lock(&ast->modeset_lock); + + phy_sleep = ast_dp_get_phy_sleep(ast); + if (phy_sleep) + ast_dp_set_phy_sleep(ast, false); + + if (ast_astdp_is_connected(ast)) + status = connector_status_connected; + + if (phy_sleep && status == connector_status_disconnected) + ast_dp_set_phy_sleep(ast, true); + + mutex_unlock(&ast->modeset_lock); + + if (status != ast_connector->physical_status) + ++connector->epoch_counter; + ast_connector->physical_status = status; + + return connector_status_connected; +} + +static const struct drm_connector_helper_funcs ast_astdp_connector_helper_funcs = { + .get_modes = ast_astdp_connector_helper_get_modes, + .detect_ctx = ast_astdp_connector_helper_detect_ctx, +}; + +static void ast_astdp_connector_reset(struct drm_connector *connector) +{ + struct ast_astdp_connector_state *astdp_state = + kzalloc(sizeof(*astdp_state), GFP_KERNEL); + + if (connector->state) + connector->funcs->atomic_destroy_state(connector, connector->state); + + if (astdp_state) + __drm_atomic_helper_connector_reset(connector, &astdp_state->base); + else + __drm_atomic_helper_connector_reset(connector, NULL); +} + +static struct drm_connector_state * +ast_astdp_connector_atomic_duplicate_state(struct drm_connector *connector) +{ + struct ast_astdp_connector_state *new_astdp_state, *astdp_state; + struct drm_device *dev = connector->dev; + + if (drm_WARN_ON(dev, !connector->state)) + return NULL; + + new_astdp_state = kmalloc(sizeof(*new_astdp_state), GFP_KERNEL); + if (!new_astdp_state) + return NULL; + __drm_atomic_helper_connector_duplicate_state(connector, &new_astdp_state->base); + + astdp_state = to_ast_astdp_connector_state(connector->state); + + new_astdp_state->mode_index = astdp_state->mode_index; + + return &new_astdp_state->base; +} + +static void ast_astdp_connector_atomic_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + struct ast_astdp_connector_state *astdp_state = to_ast_astdp_connector_state(state); + + __drm_atomic_helper_connector_destroy_state(&astdp_state->base); + kfree(astdp_state); +} + +static const struct drm_connector_funcs ast_astdp_connector_funcs = { + .reset = ast_astdp_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = ast_astdp_connector_atomic_duplicate_state, + .atomic_destroy_state = ast_astdp_connector_atomic_destroy_state, +}; + +/* + * Output + */ + +int ast_astdp_output_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct drm_crtc *crtc = &ast->crtc; + struct drm_encoder *encoder; + struct ast_connector *ast_connector; + struct drm_connector *connector; + int ret; + + /* encoder */ + + encoder = &ast->output.astdp.encoder; + ret = drm_encoder_init(dev, encoder, &ast_astdp_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + return ret; + drm_encoder_helper_add(encoder, &ast_astdp_encoder_helper_funcs); + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* connector */ + + ast_connector = &ast->output.astdp.connector; + connector = &ast_connector->base; + ret = drm_connector_init(dev, connector, &ast_astdp_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) + return ret; + drm_connector_helper_add(connector, &ast_astdp_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + + ast_connector->physical_status = connector->status; + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index 4c7375b45281..9e19d8c17730 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -1,39 +1,60 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/delay.h> #include <linux/firmware.h> -#include <drm/drmP.h> +#include <linux/module.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + #include "ast_drv.h" + MODULE_FIRMWARE("ast_dp501_fw.bin"); -static int ast_load_dp501_microcode(struct drm_device *dev) +static void ast_release_firmware(void *data) +{ + struct ast_device *ast = data; + + release_firmware(ast->dp501_fw); + ast->dp501_fw = NULL; +} + +static int ast_load_dp501_microcode(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; + struct drm_device *dev = &ast->base; + int ret; + + ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev); + if (ret) + return ret; - return request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev); + return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast); } -static void send_ack(struct ast_private *ast) +static void send_ack(struct ast_device *ast) { u8 sendack; - sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff); + sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0xff); sendack |= 0x80; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0x00, sendack); } -static void send_nack(struct ast_private *ast) +static void send_nack(struct ast_device *ast) { u8 sendack; - sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff); + sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0xff); sendack &= ~0x80; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0x00, sendack); } -static bool wait_ack(struct ast_private *ast) +static bool wait_ack(struct ast_device *ast) { u8 waitack; u32 retry = 0; do { - waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); + waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); waitack &= 0x80; udelay(100); } while ((!waitack) && (retry++ < 1000)); @@ -44,12 +65,12 @@ static bool wait_ack(struct ast_private *ast) return false; } -static bool wait_nack(struct ast_private *ast) +static bool wait_nack(struct ast_device *ast) { u8 waitack; u32 retry = 0; do { - waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); + waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); waitack &= 0x80; udelay(100); } while ((waitack) && (retry++ < 1000)); @@ -60,23 +81,23 @@ static bool wait_nack(struct ast_private *ast) return false; } -static void set_cmd_trigger(struct ast_private *ast) +static void set_cmd_trigger(struct ast_device *ast) { - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, ~0x40, 0x40); } -static void clear_cmd_trigger(struct ast_private *ast) +static void clear_cmd_trigger(struct ast_device *ast) { - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, ~0x40, 0x00); } #if 0 -static bool wait_fw_ready(struct ast_private *ast) +static bool wait_fw_ready(struct ast_device *ast) { u8 waitready; u32 retry = 0; do { - waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff); + waitready = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); waitready &= 0x40; udelay(100); } while ((!waitready) && (retry++ < 1000)); @@ -88,13 +109,13 @@ static bool wait_fw_ready(struct ast_private *ast) } #endif -static bool ast_write_cmd(struct drm_device *dev, u8 data) +static bool ast_write_cmd(struct ast_device *ast, u8 data) { - struct ast_private *ast = dev->dev_private; int retry = 0; + if (wait_nack(ast)) { send_nack(ast); - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data); send_ack(ast); set_cmd_trigger(ast); do { @@ -110,13 +131,11 @@ static bool ast_write_cmd(struct drm_device *dev, u8 data) return false; } -static bool ast_write_data(struct drm_device *dev, u8 data) +static bool ast_write_data(struct ast_device *ast, u8 data) { - struct ast_private *ast = dev->dev_private; - if (wait_nack(ast)) { send_nack(ast); - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data); send_ack(ast); if (wait_ack(ast)) { send_nack(ast); @@ -130,14 +149,14 @@ static bool ast_write_data(struct drm_device *dev, u8 data) #if 0 static bool ast_read_data(struct drm_device *dev, u8 *data) { - struct ast_private *ast = dev->dev_private; + struct ast_device *ast = to_ast_device(dev); u8 tmp; *data = 0; if (wait_ack(ast) == false) return false; - tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd3, 0xff); + tmp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd3, 0xff); *data = tmp; if (wait_nack(ast) == false) { send_nack(ast); @@ -147,32 +166,34 @@ static bool ast_read_data(struct drm_device *dev, u8 *data) return true; } -static void clear_cmd(struct ast_private *ast) +static void clear_cmd(struct ast_device *ast) { send_nack(ast); - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, 0x00); } #endif -void ast_set_dp501_video_output(struct drm_device *dev, u8 mode) +static void ast_set_dp501_video_output(struct ast_device *ast, u8 mode) { - ast_write_cmd(dev, 0x40); - ast_write_data(dev, mode); + ast_write_cmd(ast, 0x40); + ast_write_data(ast, mode); msleep(10); } -static u32 get_fw_base(struct ast_private *ast) +static u32 get_fw_base(struct ast_device *ast) { return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff; } -bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) +bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size) { - struct ast_private *ast = dev->dev_private; 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); @@ -183,14 +204,16 @@ bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) return false; } -static bool ast_launch_m68k(struct drm_device *dev) +static bool ast_launch_m68k(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; 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) { @@ -199,7 +222,7 @@ static bool ast_launch_m68k(struct drm_device *dev) len = 32*1024; } else { if (!ast->dp501_fw && - ast_load_dp501_microcode(dev) < 0) + ast_load_dp501_microcode(ast) < 0) return false; fw_addr = (u8 *)ast->dp501_fw->data; @@ -243,80 +266,93 @@ static bool ast_launch_m68k(struct drm_device *dev) 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 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */ jreg |= 0x02; - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x99, jreg); } return true; } -u8 ast_get_dp501_max_clk(struct drm_device *dev) +static bool ast_dp501_is_connected(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; u32 boot_address, offset, data; - u8 linkcap[4], linkrate, linklanes, maxclk = 0xff; - - boot_address = get_fw_base(ast); - - /* validate FW version */ - offset = 0xf000; - data = ast_mindwm(ast, boot_address + offset); - if ((data & 0xf0) != 0x10) /* version: 1x */ - return maxclk; - - /* Read Link Capability */ - offset = 0xf014; - *(u32 *)linkcap = 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; + + if (ast->config_mode == ast_use_p2a) { + boot_address = get_fw_base(ast); + + /* validate FW version */ + offset = AST_DP501_GBL_VERSION; + data = ast_mindwm(ast, boot_address + offset); + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) + return false; + + /* validate PnP Monitor */ + offset = AST_DP501_PNPMONITOR; + data = ast_mindwm(ast, boot_address + offset); + if (!(data & AST_DP501_PNP_CONNECTED)) + return false; + } else { + if (!ast->dp501_fw_buf) + return false; + + /* dummy read */ + offset = 0x0000; + data = readl(ast->dp501_fw_buf + offset); + + /* validate FW version */ + offset = AST_DP501_GBL_VERSION; + data = readl(ast->dp501_fw_buf + offset); + if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) + return false; + + /* validate PnP Monitor */ + offset = AST_DP501_PNPMONITOR; + data = readl(ast->dp501_fw_buf + offset); + if (!(data & AST_DP501_PNP_CONNECTED)) + return false; } - return maxclk; + return true; } -bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) +static int ast_dp512_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len) { - struct ast_private *ast = dev->dev_private; - u32 i, boot_address, offset, data; + struct ast_device *ast = data; + size_t rdlen = round_up(len, 4); + u32 i, boot_address, offset, ediddata; - boot_address = get_fw_base(ast); + if (block > (512 / EDID_LENGTH)) + return -EIO; - /* validate FW version */ - offset = 0xf000; - data = ast_mindwm(ast, boot_address + offset); - if ((data & 0xf0) != 0x10) - return false; + offset = AST_DP501_EDID_DATA + block * EDID_LENGTH; - /* validate PnP Monitor */ - offset = 0xf010; - data = ast_mindwm(ast, boot_address + offset); - if (!(data & 0x01)) - return false; + if (ast->config_mode == ast_use_p2a) { + boot_address = get_fw_base(ast); - /* Read EDID */ - offset = 0xf020; - for (i = 0; i < 128; i += 4) { - data = ast_mindwm(ast, boot_address + offset + i); - *(u32 *)(ediddata + i) = data; + for (i = 0; i < rdlen; i += 4) { + ediddata = ast_mindwm(ast, boot_address + offset + i); + memcpy(buf, &ediddata, min((len - i), 4)); + buf += 4; + } + } else { + for (i = 0; i < rdlen; i += 4) { + ediddata = readl(ast->dp501_fw_buf + offset + i); + memcpy(buf, &ediddata, min((len - i), 4)); + buf += 4; + } } return true; } -static bool ast_init_dvo(struct drm_device *dev) +static bool ast_init_dvo(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; 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); + jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); if (!(jreg & 0x80)) { /* Init SCU DVO Settings */ data = ast_read32(ast, 0x12008); @@ -325,7 +361,7 @@ static bool ast_init_dvo(struct drm_device *dev) data |= 0x00000500; ast_write32(ast, 0x12008, data); - if (ast->chip == AST2300) { + if (IS_AST_GEN4(ast)) { data = ast_read32(ast, 0x12084); /* multi-pins for DVO single-edge */ data |= 0xfffe0000; @@ -341,7 +377,7 @@ static bool ast_init_dvo(struct drm_device *dev) data &= 0xffffffcf; data |= 0x00000020; ast_write32(ast, 0x12090, data); - } else { /* AST2400 */ + } else { /* AST GEN5+ */ data = ast_read32(ast, 0x12088); /* multi-pins for DVO single-edge */ data |= 0x30000000; @@ -375,14 +411,13 @@ static bool ast_init_dvo(struct drm_device *dev) ast_write32(ast, 0x1202c, data); /* Init VGA DVO Settings */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); return true; } -static void ast_init_analog(struct drm_device *dev) +static void ast_init_analog(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; u32 data; /* @@ -404,39 +439,171 @@ static void ast_init_analog(struct drm_device *dev) ast_write32(ast, 0, data); /* Disable DVO */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x00); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00); } -void ast_init_3rdtx(struct drm_device *dev) +void ast_init_3rdtx(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; - 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); + u8 vgacrd1; + + if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { + vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, + AST_IO_VGACRD1_TX_TYPE_MASK); + switch (vgacrd1) { + case AST_IO_VGACRD1_TX_SIL164_VBIOS: + ast_init_dvo(ast); break; - case 0x08: - ast_launch_m68k(dev); + case AST_IO_VGACRD1_TX_DP501_VBIOS: + ast_launch_m68k(ast); break; - case 0x0c: - ast_init_dvo(dev); + case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW: + ast_init_dvo(ast); break; default: - if (ast->tx_chip_type == AST_TX_SIL164) - ast_init_dvo(dev); + if (ast->tx_chip == AST_TX_SIL164) + ast_init_dvo(ast); else - ast_init_analog(dev); + ast_init_analog(ast); } } } -void ast_release_firmware(struct drm_device *dev) +/* + * Encoder + */ + +static const struct drm_encoder_funcs ast_dp501_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static void ast_dp501_encoder_helper_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { - struct ast_private *ast = dev->dev_private; + struct ast_device *ast = to_ast_device(encoder->dev); - release_firmware(ast->dp501_fw); - ast->dp501_fw = NULL; + ast_set_dp501_video_output(ast, 1); +} + +static void ast_dp501_encoder_helper_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) +{ + struct ast_device *ast = to_ast_device(encoder->dev); + + ast_set_dp501_video_output(ast, 0); +} + +static const struct drm_encoder_helper_funcs ast_dp501_encoder_helper_funcs = { + .atomic_enable = ast_dp501_encoder_helper_atomic_enable, + .atomic_disable = ast_dp501_encoder_helper_atomic_disable, +}; + +/* + * Connector + */ + +static int ast_dp501_connector_helper_get_modes(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + int count; + + if (ast_connector->physical_status == connector_status_connected) { + struct ast_device *ast = to_ast_device(connector->dev); + const struct drm_edid *drm_edid; + + drm_edid = drm_edid_read_custom(connector, ast_dp512_read_edid_block, ast); + drm_edid_connector_update(connector, drm_edid); + count = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); + } else { + drm_edid_connector_update(connector, NULL); + + /* + * There's no EDID data without a connected monitor. Set BMC- + * compatible modes in this case. The XGA default resolution + * should work well for all BMCs. + */ + count = drm_add_modes_noedid(connector, 4096, 4096); + if (count) + drm_set_preferred_mode(connector, 1024, 768); + } + + return count; +} + +static int ast_dp501_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + struct ast_device *ast = to_ast_device(connector->dev); + enum drm_connector_status status = connector_status_disconnected; + + if (ast_dp501_is_connected(ast)) + status = connector_status_connected; + + if (status != ast_connector->physical_status) + ++connector->epoch_counter; + ast_connector->physical_status = status; + + return connector_status_connected; +} + +static const struct drm_connector_helper_funcs ast_dp501_connector_helper_funcs = { + .get_modes = ast_dp501_connector_helper_get_modes, + .detect_ctx = ast_dp501_connector_helper_detect_ctx, +}; + +static const struct drm_connector_funcs ast_dp501_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/* + * Output + */ + +int ast_dp501_output_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct drm_crtc *crtc = &ast->crtc; + struct drm_encoder *encoder; + struct ast_connector *ast_connector; + struct drm_connector *connector; + int ret; + + /* encoder */ + + encoder = &ast->output.dp501.encoder; + ret = drm_encoder_init(dev, encoder, &ast_dp501_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + return ret; + drm_encoder_helper_add(encoder, &ast_dp501_encoder_helper_funcs); + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* connector */ + + ast_connector = &ast->output.dp501.connector; + connector = &ast_connector->base; + ret = drm_connector_init(dev, connector, &ast_dp501_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) + return ret; + drm_connector_helper_add(connector, &ast_dp501_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + + ast_connector->physical_status = connector->status; + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; } diff --git a/drivers/gpu/drm/ast/ast_dram_tables.h b/drivers/gpu/drm/ast/ast_dram_tables.h deleted file mode 100644 index 1e9ac9d6d26c..000000000000 --- a/drivers/gpu/drm/ast/ast_dram_tables.h +++ /dev/null @@ -1,207 +0,0 @@ -/* 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 index bf589c53b908..b9a9b050b546 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -25,22 +25,83 @@ /* * Authors: Dave Airlie <airlied@redhat.com> */ -#include <linux/module.h> -#include <linux/console.h> -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> +#include <linux/aperture.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pci.h> + +#include <drm/clients/drm_client_setup.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_shmem.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_module.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> #include "ast_drv.h" -int ast_modeset = -1; +static int ast_modeset = -1; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, ast_modeset, int, 0400); -#define PCI_VENDOR_ASPEED 0x1a03 +void ast_device_init(struct ast_device *ast, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + const struct ast_device_quirks *quirks) +{ + ast->quirks = quirks; + ast->chip = chip; + ast->config_mode = config_mode; + ast->regs = regs; + ast->ioregs = ioregs; +} + +void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip) +{ + static const char * const info_str[] = { + "analog VGA", + "Sil164 TMDS transmitter", + "DP501 DisplayPort transmitter", + "ASPEED DisplayPort transmitter", + }; + + drm_info(&ast->base, "Using %s\n", info_str[tx_chip]); + + ast->tx_chip = tx_chip; +} + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(ast_fops); + +static const struct drm_driver ast_driver = { + .driver_features = DRIVER_ATOMIC | + DRIVER_GEM | + DRIVER_MODESET, + + .fops = &ast_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + DRM_GEM_SHMEM_DRIVER_OPS, + DRM_FBDEV_SHMEM_DRIVER_OPS, +}; + +/* + * PCI driver + */ -static struct drm_driver driver; +#define PCI_VENDOR_ASPEED 0x1a03 #define AST_VGA_DEVICE(id, info) { \ .class = PCI_BASE_CLASS_DISPLAY << 16, \ @@ -51,91 +112,369 @@ static struct drm_driver driver; .subdevice = PCI_ANY_ID, \ .driver_data = (unsigned long) info } -static const struct pci_device_id pciidlist[] = { +static const struct pci_device_id ast_pciidlist[] = { AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL), AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL), - /* AST_VGA_DEVICE(PCI_CHIP_AST1180, NULL), - don't bind to 1180 for now */ {0, 0, 0}, }; -MODULE_DEVICE_TABLE(pci, pciidlist); +MODULE_DEVICE_TABLE(pci, ast_pciidlist); -static void ast_kick_out_firmware_fb(struct pci_dev *pdev) +static bool ast_is_vga_enabled(void __iomem *ioregs) { - struct apertures_struct *ap; - bool primary = false; + u8 vgaer = __ast_read8(ioregs, AST_IO_VGAER); - 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); + return vgaer & AST_IO_VGAER_VGA_ENABLE; +} -#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 void ast_enable_vga(void __iomem *ioregs) +{ + __ast_write8(ioregs, AST_IO_VGAER, AST_IO_VGAER_VGA_ENABLE); + __ast_write8(ioregs, AST_IO_VGAMR_W, AST_IO_VGAMR_IOSEL); } -static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +/* + * Run this function as part of the HW device cleanup; not + * when the DRM device gets released. + */ +static void ast_enable_mmio_release(void *data) { - ast_kick_out_firmware_fb(pdev); + void __iomem *ioregs = (void __force __iomem *)data; - return drm_get_pci_dev(pdev, ent, &driver); + /* enable standard VGA decode */ + __ast_write8_i(ioregs, AST_IO_VGACRI, 0xa1, AST_IO_VGACRA1_MMIO_ENABLED); } -static void -ast_pci_remove(struct pci_dev *pdev) +static int ast_enable_mmio(struct device *dev, void __iomem *ioregs) { - struct drm_device *dev = pci_get_drvdata(pdev); + void *data = (void __force *)ioregs; + + __ast_write8_i(ioregs, AST_IO_VGACRI, 0xa1, + AST_IO_VGACRA1_MMIO_ENABLED | + AST_IO_VGACRA1_VGAIO_DISABLED); - drm_put_dev(dev); + return devm_add_action_or_reset(dev, ast_enable_mmio_release, data); } +static void ast_open_key(void __iomem *ioregs) +{ + __ast_write8_i(ioregs, AST_IO_VGACRI, 0x80, AST_IO_VGACR80_PASSWORD); +} +static int ast_detect_chip(struct pci_dev *pdev, + void __iomem *regs, void __iomem *ioregs, + enum ast_chip *chip_out, + enum ast_config_mode *config_mode_out) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + enum ast_config_mode config_mode = ast_use_defaults; + uint32_t scu_rev = 0xffffffff; + enum ast_chip chip; + u32 data; + u8 vgacrd0, vgacrd1; + + /* + * Find configuration mode and read SCU revision + */ + + /* Check if we have device-tree properties */ + if (np && !of_property_read_u32(np, "aspeed,scu-revision-id", &data)) { + /* We do, disable P2A access */ + config_mode = ast_use_dt; + scu_rev = data; + } else if (pdev->device == PCI_CHIP_AST2000) { // Not all families have a P2A bridge + /* + * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge + * is disabled. We force using P2A if VGA only mode bit + * is set D[7] + */ + vgacrd0 = __ast_read8_i(ioregs, AST_IO_VGACRI, 0xd0); + vgacrd1 = __ast_read8_i(ioregs, AST_IO_VGACRI, 0xd1); + if (!(vgacrd0 & 0x80) || !(vgacrd1 & 0x10)) { + + /* + * We have a P2A bridge and it is enabled. + */ + + /* Patch AST2500/AST2510 */ + if ((pdev->revision & 0xf0) == 0x40) { + if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK)) + ast_2500_patch_ahb(regs); + } + + /* Double check that it's actually working */ + data = __ast_read32(regs, 0xf004); + if ((data != 0xffffffff) && (data != 0x00)) { + config_mode = ast_use_p2a; + + /* Read SCU7c (silicon revision register) */ + __ast_write32(regs, 0xf004, 0x1e6e0000); + __ast_write32(regs, 0xf000, 0x1); + scu_rev = __ast_read32(regs, 0x1207c); + } + } + } + + switch (config_mode) { + case ast_use_defaults: + dev_info(dev, "Using default configuration\n"); + break; + case ast_use_dt: + dev_info(dev, "Using device-tree for configuration\n"); + break; + case ast_use_p2a: + dev_info(dev, "Using P2A bridge for configuration\n"); + break; + } + + /* + * Identify chipset + */ + + if (pdev->revision >= 0x50) { + chip = AST2600; + dev_info(dev, "AST 2600 detected\n"); + } else if (pdev->revision >= 0x40) { + switch (scu_rev & 0x300) { + case 0x0100: + chip = AST2510; + dev_info(dev, "AST 2510 detected\n"); + break; + default: + chip = AST2500; + dev_info(dev, "AST 2500 detected\n"); + break; + } + } else if (pdev->revision >= 0x30) { + switch (scu_rev & 0x300) { + case 0x0100: + chip = AST1400; + dev_info(dev, "AST 1400 detected\n"); + break; + default: + chip = AST2400; + dev_info(dev, "AST 2400 detected\n"); + break; + } + } else if (pdev->revision >= 0x20) { + switch (scu_rev & 0x300) { + case 0x0000: + chip = AST1300; + dev_info(dev, "AST 1300 detected\n"); + break; + default: + chip = AST2300; + dev_info(dev, "AST 2300 detected\n"); + break; + } + } else if (pdev->revision >= 0x10) { + switch (scu_rev & 0x0300) { + case 0x0200: + chip = AST1100; + dev_info(dev, "AST 1100 detected\n"); + break; + case 0x0100: + chip = AST2200; + dev_info(dev, "AST 2200 detected\n"); + break; + case 0x0000: + chip = AST2150; + dev_info(dev, "AST 2150 detected\n"); + break; + default: + chip = AST2100; + dev_info(dev, "AST 2100 detected\n"); + break; + } + } else { + chip = AST2000; + dev_info(dev, "AST 2000 detected\n"); + } + + *chip_out = chip; + *config_mode_out = config_mode; + + return __AST_CHIP_GEN(chip); +} -static int ast_drm_freeze(struct drm_device *dev) +static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - drm_kms_helper_poll_disable(dev); + struct device *dev = &pdev->dev; + int ret; + void __iomem *regs; + void __iomem *ioregs; + enum ast_config_mode config_mode; + enum ast_chip chip; + unsigned int chip_gen; + struct drm_device *drm; + bool need_post = false; + + ret = aperture_remove_conflicting_pci_devices(pdev, ast_driver.name); + if (ret) + return ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + regs = pcim_iomap_region(pdev, 1, "ast"); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + if (pdev->revision >= 0x40) { + /* + * On AST2500 and later models, MMIO is enabled by + * default. Adopt it to be compatible with ARM. + */ + resource_size_t len = pci_resource_len(pdev, 1); + + if (len < AST_IO_MM_OFFSET) + return -EIO; + if ((len - AST_IO_MM_OFFSET) < AST_IO_MM_LENGTH) + return -EIO; + ioregs = regs + AST_IO_MM_OFFSET; + } else if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { + /* + * Map I/O registers if we have a PCI BAR for I/O. + */ + resource_size_t len = pci_resource_len(pdev, 2); + + if (len < AST_IO_MM_LENGTH) + return -EIO; + ioregs = pcim_iomap_region(pdev, 2, "ast"); + if (IS_ERR(ioregs)) + return PTR_ERR(ioregs); + } else { + /* + * Anything else is best effort. + */ + resource_size_t len = pci_resource_len(pdev, 1); + + if (len < AST_IO_MM_OFFSET) + return -EIO; + if ((len - AST_IO_MM_OFFSET) < AST_IO_MM_LENGTH) + return -EIO; + ioregs = regs + AST_IO_MM_OFFSET; + + dev_info(dev, "Platform has no I/O space, using MMIO\n"); + } + + if (!ast_is_vga_enabled(ioregs)) { + dev_info(dev, "VGA not enabled on entry, requesting chip POST\n"); + need_post = true; + } + + /* + * If VGA isn't enabled, we need to enable now or subsequent + * access to the scratch registers will fail. + */ + if (need_post) + ast_enable_vga(ioregs); + /* Enable extended register access */ + ast_open_key(ioregs); + + ret = ast_enable_mmio(dev, ioregs); + if (ret) + return ret; + + ret = ast_detect_chip(pdev, regs, ioregs, &chip, &config_mode); + if (ret < 0) + return ret; + chip_gen = ret; + + switch (chip_gen) { + case 1: + drm = ast_2000_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 2: + drm = ast_2100_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 3: + drm = ast_2200_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 4: + drm = ast_2300_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 5: + drm = ast_2400_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 6: + drm = ast_2500_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 7: + drm = ast_2600_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + default: + dev_err(&pdev->dev, "Gen%d not supported\n", chip_gen); + return -ENODEV; + } + if (IS_ERR(drm)) + return PTR_ERR(drm); + pci_set_drvdata(pdev, drm); - pci_save_state(dev->pdev); + ret = drm_dev_register(drm, ent->driver_data); + if (ret) + return ret; + + drm_client_setup(drm, NULL); - console_lock(); - ast_fbdev_set_suspend(dev, 1); - console_unlock(); return 0; } -static int ast_drm_thaw(struct drm_device *dev) +static void ast_pci_remove(struct pci_dev *pdev) { - int error = 0; + struct drm_device *dev = pci_get_drvdata(pdev); - ast_post_gpu(dev); + drm_dev_unregister(dev); + drm_atomic_helper_shutdown(dev); +} + +static void ast_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} - drm_mode_config_reset(dev); - drm_helper_resume_force_mode(dev); +static int ast_drm_freeze(struct drm_device *dev) +{ + int error; - console_lock(); - ast_fbdev_set_suspend(dev, 0); - console_unlock(); - return error; + error = drm_mode_config_helper_suspend(dev); + if (error) + return error; + pci_save_state(to_pci_dev(dev->dev)); + return 0; } -static int ast_drm_resume(struct drm_device *dev) +static int ast_drm_thaw(struct drm_device *dev) { + struct ast_device *ast = to_ast_device(dev); int ret; - if (pci_enable_device(dev->pdev)) - return -EIO; + ast_enable_vga(ast->ioregs); + ast_open_key(ast->ioregs); + ast_enable_mmio(dev->dev, ast->ioregs); - ret = ast_drm_thaw(dev); + ret = ast_post_gpu(ast); if (ret) return ret; - drm_kms_helper_poll_enable(dev); - return 0; + return drm_mode_config_helper_resume(dev); +} + +static int ast_drm_resume(struct drm_device *dev) +{ + if (pci_enable_device(to_pci_dev(dev->dev))) + return -EIO; + + return ast_drm_thaw(dev); } static int ast_pm_suspend(struct device *dev) @@ -152,6 +491,7 @@ static int ast_pm_suspend(struct device *dev) 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); @@ -163,11 +503,7 @@ static int ast_pm_freeze(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct drm_device *ddev = pci_get_drvdata(pdev); - - if (!ddev || !ddev->dev_private) - return -ENODEV; return ast_drm_freeze(ddev); - } static int ast_pm_thaw(struct device *dev) @@ -196,61 +532,15 @@ static const struct dev_pm_ops ast_pm_ops = { static struct pci_driver ast_pci_driver = { .name = DRIVER_NAME, - .id_table = pciidlist, + .id_table = ast_pciidlist, .probe = ast_pci_probe, .remove = ast_pci_remove, + .shutdown = ast_pci_shutdown, .driver.pm = &ast_pm_ops, }; -static const struct file_operations ast_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = ast_mmap, - .poll = drm_poll, - .compat_ioctl = drm_compat_ioctl, - .read = drm_read, -}; - -static struct drm_driver driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM, - - .load = ast_driver_load, - .unload = ast_driver_unload, - - .fops = &ast_fops, - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCHLEVEL, - - .gem_free_object_unlocked = ast_gem_free_object, - .dumb_create = ast_dumb_create, - .dumb_map_offset = ast_dumb_mmap_offset, - -}; - -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); +drm_module_pci_driver_if_modeset(ast_pci_driver, ast_modeset); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL and additional rights"); - diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index bfc65040dfcb..787e38c6c17d 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -28,25 +28,23 @@ #ifndef __AST_DRV_H__ #define __AST_DRV_H__ -#include <drm/drm_encoder.h> -#include <drm/drm_fb_helper.h> +#include <linux/io.h> +#include <linux/types.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> -#include <drm/ttm/ttm_placement.h> -#include <drm/ttm/ttm_memory.h> -#include <drm/ttm/ttm_module.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_gem.h> +#include "ast_reg.h" -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> +struct ast_vbios_enhtable; #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 @@ -54,224 +52,302 @@ #define PCI_CHIP_AST2000 0x2000 #define PCI_CHIP_AST2100 0x2010 -#define PCI_CHIP_AST1180 0x1180 +#define __AST_CHIP(__gen, __index) ((__gen) << 16 | (__index)) enum ast_chip { - AST2000, - AST2100, - AST1100, - AST2200, - AST2150, - AST2300, - AST2400, - AST2500, - AST1180, + /* 1st gen */ + AST1000 = __AST_CHIP(1, 0), // unused + AST2000 = __AST_CHIP(1, 1), + /* 2nd gen */ + AST1100 = __AST_CHIP(2, 0), + AST2100 = __AST_CHIP(2, 1), + AST2050 = __AST_CHIP(2, 2), // unused + /* 3rd gen */ + AST2200 = __AST_CHIP(3, 0), + AST2150 = __AST_CHIP(3, 1), + /* 4th gen */ + AST2300 = __AST_CHIP(4, 0), + AST1300 = __AST_CHIP(4, 1), + AST1050 = __AST_CHIP(4, 2), // unused + /* 5th gen */ + AST2400 = __AST_CHIP(5, 0), + AST1400 = __AST_CHIP(5, 1), + AST1250 = __AST_CHIP(5, 2), // unused + /* 6th gen */ + AST2500 = __AST_CHIP(6, 0), + AST2510 = __AST_CHIP(6, 1), + AST2520 = __AST_CHIP(6, 2), // unused + /* 7th gen */ + AST2600 = __AST_CHIP(7, 0), + AST2620 = __AST_CHIP(7, 1), // unused }; +#define __AST_CHIP_GEN(__chip) (((unsigned long)(__chip)) >> 16) + enum ast_tx_chip { AST_TX_NONE, AST_TX_SIL164, - AST_TX_ITE66121, AST_TX_DP501, + AST_TX_ASTDP, +}; + +enum ast_config_mode { + ast_use_p2a, + ast_use_dt, + ast_use_defaults +}; + +enum ast_dram_layout { + AST_DRAM_512Mx16 = 0, + AST_DRAM_1Gx16 = 1, + AST_DRAM_512Mx32 = 2, + AST_DRAM_1Gx32 = 3, + AST_DRAM_2Gx16 = 6, + AST_DRAM_4Gx16 = 7, + AST_DRAM_8Gx16 = 8, +}; + +/* + * Hardware cursor + */ + +#define AST_MAX_HWC_WIDTH 64 +#define AST_MAX_HWC_HEIGHT 64 +#define AST_HWC_PITCH (AST_MAX_HWC_WIDTH * SZ_2) +#define AST_HWC_SIZE (AST_MAX_HWC_HEIGHT * AST_HWC_PITCH) + +/* + * Planes + */ + +struct ast_plane { + struct drm_plane base; + + u64 offset; + unsigned long size; }; -#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 +static inline struct ast_plane *to_ast_plane(struct drm_plane *plane) +{ + return container_of(plane, struct ast_plane, base); +} + +struct ast_cursor_plane { + struct ast_plane base; + + u8 argb4444[AST_HWC_SIZE]; +}; -struct ast_fbdev; +static inline struct ast_cursor_plane *to_ast_cursor_plane(struct drm_plane *plane) +{ + return container_of(to_ast_plane(plane), struct ast_cursor_plane, base); +} -struct ast_private { - struct drm_device *dev; +/* + * Connector + */ + +struct ast_connector { + struct drm_connector base; + + enum drm_connector_status physical_status; +}; + +static inline struct ast_connector * +to_ast_connector(struct drm_connector *connector) +{ + return container_of(connector, struct ast_connector, base); +} + +/* + * Device + */ + +struct ast_device_quirks { + /* + * CRTC memory request threshold + */ + unsigned char crtc_mem_req_threshold_low; + unsigned char crtc_mem_req_threshold_high; + + /* + * Adjust hsync values to load next scanline early. Signalled + * by AST2500PreCatchCRT in VBIOS mode flags. + */ + bool crtc_hsync_precatch_needed; + + /* + * Workaround for modes with HSync Time that is not a multiple + * of 8 (e.g., 1920x1080@60Hz, HSync +44 pixels). + */ + bool crtc_hsync_add4_needed; +}; + +struct ast_device { + struct drm_device base; + + const struct ast_device_quirks *quirks; void __iomem *regs; void __iomem *ioregs; + void __iomem *dp501_fw_buf; + enum ast_config_mode config_mode; enum ast_chip chip; - bool vga2_clone; - uint32_t dram_bus_width; - uint32_t dram_type; - uint32_t mclk; - uint32_t vram_size; - - struct ast_fbdev *fbdev; - - int fb_mtrr; - - struct { - struct ttm_bo_device bdev; - } ttm; - - struct drm_gem_object *cursor_cache; - uint64_t cursor_cache_gpu_addr; - /* Acces to this cache is protected by the crtc->mutex of the only crtc - * we have. */ - struct ttm_bo_kmap_obj cache_kmap; - int next_cursor; - 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; + + const struct ast_vbios_dclk_info *dclk_table; + + void __iomem *vram; + unsigned long vram_base; + unsigned long vram_size; + + struct mutex modeset_lock; /* Protects access to modeset I/O registers in ioregs */ + + enum ast_tx_chip tx_chip; + + struct ast_plane primary_plane; + struct ast_cursor_plane cursor_plane; + struct drm_crtc crtc; + union { + struct { + struct drm_encoder encoder; + struct ast_connector connector; + } vga; + struct { + struct drm_encoder encoder; + struct ast_connector connector; + } sil164; + struct { + struct drm_encoder encoder; + struct ast_connector connector; + } dp501; + struct { + struct drm_encoder encoder; + struct ast_connector connector; + } astdp; + } output; + + bool support_wsxga_p; /* 1680x1050 */ + bool support_fullhd; /* 1920x1080 */ + bool support_wuxga; /* 1920x1200 */ + u8 *dp501_fw_addr; const struct firmware *dp501_fw; /* dp501 fw */ }; -int ast_driver_load(struct drm_device *dev, unsigned long flags); -void ast_driver_unload(struct drm_device *dev); - -struct ast_gem_object; - -#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_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;\ +static inline struct ast_device *to_ast_device(struct drm_device *dev) +{ + return container_of(dev, struct ast_device, base); } -__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;\ +static inline unsigned long __ast_gen(struct ast_device *ast) +{ + return __AST_CHIP_GEN(ast->chip); } +#define AST_GEN(__ast) __ast_gen(__ast) -__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);\ - } +static inline bool __ast_gen_is_eq(struct ast_device *ast, unsigned long gen) +{ + return __ast_gen(ast) == gen; +} +#define IS_AST_GEN1(__ast) __ast_gen_is_eq(__ast, 1) +#define IS_AST_GEN2(__ast) __ast_gen_is_eq(__ast, 2) +#define IS_AST_GEN3(__ast) __ast_gen_is_eq(__ast, 3) +#define IS_AST_GEN4(__ast) __ast_gen_is_eq(__ast, 4) +#define IS_AST_GEN5(__ast) __ast_gen_is_eq(__ast, 5) +#define IS_AST_GEN6(__ast) __ast_gen_is_eq(__ast, 6) +#define IS_AST_GEN7(__ast) __ast_gen_is_eq(__ast, 7) + +static inline u8 __ast_read8(const void __iomem *addr, u32 reg) +{ + return ioread8(addr + reg); +} -__ast_io_write(8); -__ast_io_write(16); -#undef __ast_io_write +static inline u32 __ast_read32(const void __iomem *addr, u32 reg) +{ + return ioread32(addr + reg); +} -static inline void ast_set_index_reg(struct ast_private *ast, - uint32_t base, uint8_t index, - uint8_t val) +static inline void __ast_write8(void __iomem *addr, u32 reg, u8 val) { - ast_io_write16(ast, base, ((u16)val << 8) | index); + iowrite8(val, addr + reg); } -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_write32(void __iomem *addr, u32 reg, u32 val) +{ + iowrite32(val, addr + reg); +} -static inline void ast_open_key(struct ast_private *ast) +static inline u8 __ast_read8_i(void __iomem *addr, u32 reg, u8 index) { - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8); + __ast_write8(addr, reg, index); + return __ast_read8(addr, reg + 1); } -#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 +static inline u8 __ast_read8_i_masked(void __iomem *addr, u32 reg, u8 index, u8 read_mask) +{ + u8 val = __ast_read8_i(addr, reg, index); -#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M + return val & read_mask; +} -#define AST_MAX_HWC_WIDTH 64 -#define AST_MAX_HWC_HEIGHT 64 +static inline void __ast_write8_i(void __iomem *addr, u32 reg, u8 index, u8 val) +{ + __ast_write8(addr, reg, index); + __ast_write8(addr, reg + 1, val); +} -#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH*AST_MAX_HWC_HEIGHT*2) -#define AST_HWC_SIGNATURE_SIZE 32 +static inline void __ast_write8_i_masked(void __iomem *addr, u32 reg, u8 index, u8 preserve_mask, + u8 val) +{ + u8 tmp = __ast_read8_i_masked(addr, reg, index, preserve_mask); -#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 + val &= ~preserve_mask; + __ast_write8_i(addr, reg, index, tmp | val); +} +static inline u32 ast_read32(struct ast_device *ast, u32 reg) +{ + return __ast_read32(ast->regs, reg); +} -struct ast_i2c_chan { - struct i2c_adapter adapter; - struct drm_device *dev; - struct i2c_algo_bit_data bit; -}; +static inline void ast_write32(struct ast_device *ast, u32 reg, u32 val) +{ + __ast_write32(ast->regs, reg, val); +} -struct ast_connector { - struct drm_connector base; - struct ast_i2c_chan *i2c; -}; +static inline u8 ast_io_read8(struct ast_device *ast, u32 reg) +{ + return __ast_read8(ast->ioregs, reg); +} -struct ast_crtc { - struct drm_crtc base; - struct drm_gem_object *cursor_bo; - uint64_t cursor_addr; - int cursor_width, cursor_height; - u8 offset_x, offset_y; -}; +static inline void ast_io_write8(struct ast_device *ast, u32 reg, u8 val) +{ + __ast_write8(ast->ioregs, reg, val); +} -struct ast_encoder { - struct drm_encoder base; -}; +static inline u8 ast_get_index_reg(struct ast_device *ast, u32 base, u8 index) +{ + return __ast_read8_i(ast->ioregs, base, index); +} -struct ast_framebuffer { - struct drm_framebuffer base; - struct drm_gem_object *obj; -}; +static inline u8 ast_get_index_reg_mask(struct ast_device *ast, u32 base, u8 index, + u8 preserve_mask) +{ + return __ast_read8_i_masked(ast->ioregs, base, index, preserve_mask); +} -struct ast_fbdev { - struct drm_fb_helper helper; - struct ast_framebuffer afb; - void *sysram; - int size; - struct ttm_bo_kmap_obj mapping; - int x1, y1, x2, y2; /* dirty rect */ - spinlock_t dirty_lock; -}; +static inline void ast_set_index_reg(struct ast_device *ast, u32 base, u8 index, u8 val) +{ + __ast_write8_i(ast->ioregs, base, index, val); +} -#define to_ast_crtc(x) container_of(x, struct ast_crtc, base) -#define to_ast_connector(x) container_of(x, struct ast_connector, base) -#define to_ast_encoder(x) container_of(x, struct ast_encoder, base) -#define to_ast_framebuffer(x) container_of(x, struct ast_framebuffer, base) +static inline void ast_set_index_reg_mask(struct ast_device *ast, u32 base, u8 index, + u8 preserve_mask, u8 val) +{ + __ast_write8_i_masked(ast->ioregs, base, index, preserve_mask, val); +} struct ast_vbios_stdtable { u8 misc; @@ -281,127 +357,194 @@ struct ast_vbios_stdtable { 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; -extern int ast_mode_init(struct drm_device *dev); -extern void ast_mode_fini(struct drm_device *dev); - -int ast_framebuffer_init(struct drm_device *dev, - struct ast_framebuffer *ast_fb, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj); - -int ast_fbdev_init(struct drm_device *dev); -void ast_fbdev_fini(struct drm_device *dev); -void ast_fbdev_set_suspend(struct drm_device *dev, int state); -void ast_fbdev_set_base(struct ast_private *ast, unsigned long gpu_addr); - -struct ast_bo { - struct ttm_buffer_object bo; - struct ttm_placement placement; - struct ttm_bo_kmap_obj kmap; - struct drm_gem_object gem; - struct ttm_place placements[3]; - int pin_count; -}; -#define gem_to_ast_bo(gobj) container_of((gobj), struct ast_bo, gem) - -static inline struct ast_bo * -ast_bo(struct ttm_buffer_object *bo) -{ - return container_of(bo, struct ast_bo, bo); -} + /* Last known format of primary plane */ + const struct drm_format_info *format; + const struct ast_vbios_stdtable *std_table; + const struct ast_vbios_enhtable *vmode; +}; -#define to_ast_obj(x) container_of(x, struct ast_gem_object, base) +#define to_ast_crtc_state(state) container_of(state, struct ast_crtc_state, base) #define AST_MM_ALIGN_SHIFT 4 #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) -extern int ast_dumb_create(struct drm_file *file, - struct drm_device *dev, - struct drm_mode_create_dumb *args); - -extern void ast_gem_free_object(struct drm_gem_object *obj); -extern int ast_dumb_mmap_offset(struct drm_file *file, - struct drm_device *dev, - uint32_t handle, - uint64_t *offset); - -#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) - -int ast_mm_init(struct ast_private *ast); -void ast_mm_fini(struct ast_private *ast); +#define AST_DP501_FW_VERSION_MASK GENMASK(7, 4) +#define AST_DP501_FW_VERSION_1 BIT(4) +#define AST_DP501_PNP_CONNECTED BIT(1) -int ast_bo_create(struct drm_device *dev, int size, int align, - uint32_t flags, struct ast_bo **pastbo); +#define AST_DP501_DEFAULT_DCLK 65 -int ast_gem_create(struct drm_device *dev, - u32 size, bool iskernel, - struct drm_gem_object **obj); +#define AST_DP501_GBL_VERSION 0xf000 +#define AST_DP501_PNPMONITOR 0xf010 +#define AST_DP501_LINKRATE 0xf014 +#define AST_DP501_EDID_DATA 0xf020 -int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr); -int ast_bo_unpin(struct ast_bo *bo); +/* + * ASTDP resoultion table: + * EX: ASTDP_A_B_C: + * A: Resolution + * B: Refresh Rate + * C: Misc information, such as CVT, Reduce Blanked + */ +#define ASTDP_640x480_60 0x00 +#define ASTDP_640x480_72 0x01 +#define ASTDP_640x480_75 0x02 +#define ASTDP_640x480_85 0x03 +#define ASTDP_800x600_56 0x04 +#define ASTDP_800x600_60 0x05 +#define ASTDP_800x600_72 0x06 +#define ASTDP_800x600_75 0x07 +#define ASTDP_800x600_85 0x08 +#define ASTDP_1024x768_60 0x09 +#define ASTDP_1024x768_70 0x0A +#define ASTDP_1024x768_75 0x0B +#define ASTDP_1024x768_85 0x0C +#define ASTDP_1280x1024_60 0x0D +#define ASTDP_1280x1024_75 0x0E +#define ASTDP_1280x1024_85 0x0F +#define ASTDP_1600x1200_60 0x10 +#define ASTDP_320x240_60 0x11 +#define ASTDP_400x300_60 0x12 +#define ASTDP_512x384_60 0x13 +#define ASTDP_1920x1200_60 0x14 +#define ASTDP_1920x1080_60 0x15 +#define ASTDP_1280x800_60 0x16 +#define ASTDP_1280x800_60_RB 0x17 +#define ASTDP_1440x900_60 0x18 +#define ASTDP_1440x900_60_RB 0x19 +#define ASTDP_1680x1050_60 0x1A +#define ASTDP_1680x1050_60_RB 0x1B +#define ASTDP_1600x900_60 0x1C +#define ASTDP_1600x900_60_RB 0x1D +#define ASTDP_1366x768_60 0x1E +#define ASTDP_1152x864_75 0x1F + +int ast_mm_init(struct ast_device *ast); + +/* ast_drv.c */ +void ast_device_init(struct ast_device *ast, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + const struct ast_device_quirks *quirks); +void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip); + +/* ast_2000.c */ +int ast_2000_post(struct ast_device *ast); +extern const struct ast_vbios_dclk_info ast_2000_dclk_table[]; +void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post); +struct drm_device *ast_2000_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2100.c */ +int ast_2100_post(struct ast_device *ast); +bool __ast_2100_detect_wsxga_p(struct ast_device *ast); +bool __ast_2100_detect_wuxga(struct ast_device *ast); +struct drm_device *ast_2100_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2200.c */ +struct drm_device *ast_2200_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2300.c */ +int ast_2300_post(struct ast_device *ast); +void ast_2300_detect_tx_chip(struct ast_device *ast); +struct drm_device *ast_2300_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2400.c */ +struct drm_device *ast_2400_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2500.c */ +void ast_2500_patch_ahb(void __iomem *regs); +int ast_2500_post(struct ast_device *ast); +extern const struct ast_vbios_dclk_info ast_2500_dclk_table[]; +struct drm_device *ast_2500_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2600.c */ +int ast_2600_post(struct ast_device *ast); +struct drm_device *ast_2600_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); -static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait) -{ - int ret; - - ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL); - if (ret) { - if (ret != -ERESTARTSYS && ret != -EBUSY) - DRM_ERROR("reserve failed %p\n", bo); - return ret; - } - return 0; -} +/* ast post */ +int ast_post_gpu(struct ast_device *ast); +u32 ast_mindwm(struct ast_device *ast, u32 r); +void ast_moutdwm(struct ast_device *ast, u32 r, u32 v); -static inline void ast_bo_unreserve(struct ast_bo *bo) -{ - ttm_bo_unreserve(&bo->bo); -} +int ast_vga_output_init(struct ast_device *ast); +int ast_sil164_output_init(struct ast_device *ast); -void ast_ttm_placement(struct ast_bo *bo, int domain); -int ast_bo_push_sysram(struct ast_bo *bo); -int ast_mmap(struct file *filp, struct vm_area_struct *vma); +/* ast_cursor.c */ +long ast_cursor_vram_offset(struct ast_device *ast); +int ast_cursor_plane_init(struct ast_device *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); -void ast_release_firmware(struct drm_device *dev); +bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size); +void ast_init_3rdtx(struct ast_device *ast); +int ast_dp501_output_init(struct ast_device *ast); + +/* aspeed DP */ +int ast_dp_launch(struct ast_device *ast); +int ast_astdp_output_init(struct ast_device *ast); + +/* ast_mode.c */ +int ast_mode_config_init(struct ast_device *ast); +int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane, + u64 offset, unsigned long size, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type); +void __iomem *ast_plane_vaddr(struct ast_plane *ast); + #endif diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c deleted file mode 100644 index de26df0c6044..000000000000 --- a/drivers/gpu/drm/ast/ast_fb.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * 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/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/tty.h> -#include <linux/sysrq.h> -#include <linux/delay.h> -#include <linux/init.h> - - -#include <drm/drmP.h> -#include <drm/drm_crtc.h> -#include <drm/drm_fb_helper.h> -#include <drm/drm_crtc_helper.h> -#include "ast_drv.h" - -static void ast_dirty_update(struct ast_fbdev *afbdev, - int x, int y, int width, int height) -{ - int i; - struct drm_gem_object *obj; - struct ast_bo *bo; - int src_offset, dst_offset; - int bpp = afbdev->afb.base.format->cpp[0]; - int ret = -EBUSY; - bool unmap = false; - bool store_for_later = false; - int x2, y2; - unsigned long flags; - - obj = afbdev->afb.obj; - bo = gem_to_ast_bo(obj); - - /* - * try and reserve the BO, if we fail with busy - * then the BO is being moved and we should - * store up the damage until later. - */ - if (drm_can_sleep()) - ret = ast_bo_reserve(bo, true); - if (ret) { - if (ret != -EBUSY) - return; - - store_for_later = true; - } - - x2 = x + width - 1; - y2 = y + height - 1; - spin_lock_irqsave(&afbdev->dirty_lock, flags); - - if (afbdev->y1 < y) - y = afbdev->y1; - if (afbdev->y2 > y2) - y2 = afbdev->y2; - if (afbdev->x1 < x) - x = afbdev->x1; - if (afbdev->x2 > x2) - x2 = afbdev->x2; - - if (store_for_later) { - afbdev->x1 = x; - afbdev->x2 = x2; - afbdev->y1 = y; - afbdev->y2 = y2; - spin_unlock_irqrestore(&afbdev->dirty_lock, flags); - return; - } - - afbdev->x1 = afbdev->y1 = INT_MAX; - afbdev->x2 = afbdev->y2 = 0; - spin_unlock_irqrestore(&afbdev->dirty_lock, flags); - - if (!bo->kmap.virtual) { - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); - if (ret) { - DRM_ERROR("failed to kmap fb updates\n"); - ast_bo_unreserve(bo); - return; - } - unmap = true; - } - for (i = y; i <= y2; i++) { - /* assume equal stride for now */ - src_offset = dst_offset = i * afbdev->afb.base.pitches[0] + (x * bpp); - memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, (x2 - x + 1) * bpp); - - } - if (unmap) - ttm_bo_kunmap(&bo->kmap); - - ast_bo_unreserve(bo); -} - -static void ast_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - struct ast_fbdev *afbdev = info->par; - drm_fb_helper_sys_fillrect(info, rect); - ast_dirty_update(afbdev, rect->dx, rect->dy, rect->width, - rect->height); -} - -static void ast_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ - struct ast_fbdev *afbdev = info->par; - drm_fb_helper_sys_copyarea(info, area); - ast_dirty_update(afbdev, area->dx, area->dy, area->width, - area->height); -} - -static void ast_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct ast_fbdev *afbdev = info->par; - drm_fb_helper_sys_imageblit(info, image); - ast_dirty_update(afbdev, image->dx, image->dy, image->width, - image->height); -} - -static struct fb_ops astfb_ops = { - .owner = THIS_MODULE, - .fb_check_var = drm_fb_helper_check_var, - .fb_set_par = drm_fb_helper_set_par, - .fb_fillrect = ast_fillrect, - .fb_copyarea = ast_copyarea, - .fb_imageblit = ast_imageblit, - .fb_pan_display = drm_fb_helper_pan_display, - .fb_blank = drm_fb_helper_blank, - .fb_setcmap = drm_fb_helper_setcmap, - .fb_debug_enter = drm_fb_helper_debug_enter, - .fb_debug_leave = drm_fb_helper_debug_leave, -}; - -static int astfb_create_object(struct ast_fbdev *afbdev, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object **gobj_p) -{ - struct drm_device *dev = afbdev->helper.dev; - u32 size; - struct drm_gem_object *gobj; - int ret = 0; - - size = mode_cmd->pitches[0] * mode_cmd->height; - ret = ast_gem_create(dev, size, true, &gobj); - if (ret) - return ret; - - *gobj_p = gobj; - return ret; -} - -static int astfb_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct ast_fbdev *afbdev = - container_of(helper, struct ast_fbdev, helper); - struct drm_device *dev = afbdev->helper.dev; - struct drm_mode_fb_cmd2 mode_cmd; - struct drm_framebuffer *fb; - struct fb_info *info; - int size, ret; - void *sysram; - struct drm_gem_object *gobj = NULL; - struct ast_bo *bo = NULL; - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7)/8); - - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - size = mode_cmd.pitches[0] * mode_cmd.height; - - ret = astfb_create_object(afbdev, &mode_cmd, &gobj); - if (ret) { - DRM_ERROR("failed to create fbcon backing object %d\n", ret); - return ret; - } - bo = gem_to_ast_bo(gobj); - - sysram = vmalloc(size); - if (!sysram) - return -ENOMEM; - - info = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto out; - } - info->par = afbdev; - - ret = ast_framebuffer_init(dev, &afbdev->afb, &mode_cmd, gobj); - if (ret) - goto out; - - afbdev->sysram = sysram; - afbdev->size = size; - - fb = &afbdev->afb.base; - afbdev->helper.fb = fb; - - strcpy(info->fix.id, "astdrmfb"); - - info->fbops = &astfb_ops; - - info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0); - info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0); - - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); - drm_fb_helper_fill_var(info, &afbdev->helper, sizes->fb_width, sizes->fb_height); - - info->screen_base = sysram; - info->screen_size = size; - - info->pixmap.flags = FB_PIXMAP_SYSTEM; - - DRM_DEBUG_KMS("allocated %dx%d\n", - fb->width, fb->height); - - return 0; - -out: - vfree(sysram); - return ret; -} - -static const struct drm_fb_helper_funcs ast_fb_helper_funcs = { - .fb_probe = astfb_create, -}; - -static void ast_fbdev_destroy(struct drm_device *dev, - struct ast_fbdev *afbdev) -{ - struct ast_framebuffer *afb = &afbdev->afb; - - drm_crtc_force_disable_all(dev); - drm_fb_helper_unregister_fbi(&afbdev->helper); - - if (afb->obj) { - drm_gem_object_put_unlocked(afb->obj); - afb->obj = NULL; - } - drm_fb_helper_fini(&afbdev->helper); - - vfree(afbdev->sysram); - drm_framebuffer_unregister_private(&afb->base); - drm_framebuffer_cleanup(&afb->base); -} - -int ast_fbdev_init(struct drm_device *dev) -{ - struct ast_private *ast = dev->dev_private; - struct ast_fbdev *afbdev; - int ret; - - afbdev = kzalloc(sizeof(struct ast_fbdev), GFP_KERNEL); - if (!afbdev) - return -ENOMEM; - - ast->fbdev = afbdev; - spin_lock_init(&afbdev->dirty_lock); - - drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs); - - ret = drm_fb_helper_init(dev, &afbdev->helper, 1); - if (ret) - goto free; - - ret = drm_fb_helper_single_add_all_connectors(&afbdev->helper); - if (ret) - goto fini; - - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - - ret = drm_fb_helper_initial_config(&afbdev->helper, 32); - if (ret) - goto fini; - - return 0; - -fini: - drm_fb_helper_fini(&afbdev->helper); -free: - kfree(afbdev); - return ret; -} - -void ast_fbdev_fini(struct drm_device *dev) -{ - struct ast_private *ast = dev->dev_private; - - if (!ast->fbdev) - return; - - ast_fbdev_destroy(dev, ast->fbdev); - kfree(ast->fbdev); - ast->fbdev = NULL; -} - -void ast_fbdev_set_suspend(struct drm_device *dev, int state) -{ - struct ast_private *ast = dev->dev_private; - - if (!ast->fbdev) - return; - - drm_fb_helper_set_suspend(&ast->fbdev->helper, state); -} - -void ast_fbdev_set_base(struct ast_private *ast, unsigned long gpu_addr) -{ - ast->fbdev->helper.fbdev->fix.smem_start = - ast->fbdev->helper.fbdev->apertures->ranges[0].base + gpu_addr; - ast->fbdev->helper.fbdev->fix.smem_len = ast->vram_size - gpu_addr; -} diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c deleted file mode 100644 index 373700c05a00..000000000000 --- a/drivers/gpu/drm/ast/ast_main.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - * 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 <drm/drmP.h> -#include "ast_drv.h" - - -#include <drm/drm_fb_helper.h> -#include <drm/drm_crtc_helper.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 = dev->dev_private; - 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("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) { - /* P2A works, grab silicon revision */ - ast->config_mode = ast_use_p2a; - - DRM_INFO("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("P2A bridge disabled, using default configuration\n"); -} - -static int ast_detect_chip(struct drm_device *dev, bool *need_post) -{ - struct ast_private *ast = dev->dev_private; - 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("VGA not enabled on entry, requesting chip POST\n"); - *need_post = true; - } else - *need_post = false; - - - /* Enable extended register access */ - ast_enable_mmio(dev); - ast_open_key(ast); - - /* Find out whether P2A works or whether to use device-tree */ - ast_detect_config_mode(dev, &scu_rev); - - /* Identify chipset */ - if (dev->pdev->device == PCI_CHIP_AST1180) { - ast->chip = AST1100; - DRM_INFO("AST 1180 detected\n"); - } else { - if (dev->pdev->revision >= 0x40) { - ast->chip = AST2500; - DRM_INFO("AST 2500 detected\n"); - } else if (dev->pdev->revision >= 0x30) { - ast->chip = AST2400; - DRM_INFO("AST 2400 detected\n"); - } else if (dev->pdev->revision >= 0x20) { - ast->chip = AST2300; - DRM_INFO("AST 2300 detected\n"); - } else if (dev->pdev->revision >= 0x10) { - switch (scu_rev & 0x0300) { - case 0x0200: - ast->chip = AST1100; - DRM_INFO("AST 1100 detected\n"); - break; - case 0x0100: - ast->chip = AST2200; - DRM_INFO("AST 2200 detected\n"); - break; - case 0x0000: - ast->chip = AST2150; - DRM_INFO("AST 2150 detected\n"); - break; - default: - ast->chip = AST2100; - DRM_INFO("AST 2100 detected\n"); - break; - } - ast->vga2_clone = false; - } else { - ast->chip = AST2000; - DRM_INFO("AST 2000 detected\n"); - } - } - - /* Check if we support wide screen */ - switch (ast->chip) { - case AST1180: - ast->support_wide_screen = true; - break; - 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 = kzalloc(32*1024, GFP_KERNEL); - if (ast->dp501_fw_addr) { - /* backup firmware */ - if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) { - kfree(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("Using Sil164 TMDS transmitter\n"); - break; - case AST_TX_DP501: - DRM_INFO("Using DP501 DisplayPort transmitter\n"); - break; - default: - DRM_INFO("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 = dev->dev_private; - 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; -} - -static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) -{ - struct ast_framebuffer *ast_fb = to_ast_framebuffer(fb); - - drm_gem_object_put_unlocked(ast_fb->obj); - drm_framebuffer_cleanup(fb); - kfree(ast_fb); -} - -static const struct drm_framebuffer_funcs ast_fb_funcs = { - .destroy = ast_user_framebuffer_destroy, -}; - - -int ast_framebuffer_init(struct drm_device *dev, - struct ast_framebuffer *ast_fb, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object *obj) -{ - int ret; - - drm_helper_mode_fill_fb_struct(dev, &ast_fb->base, mode_cmd); - ast_fb->obj = obj; - ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); - if (ret) { - DRM_ERROR("framebuffer init failed %d\n", ret); - return ret; - } - return 0; -} - -static struct drm_framebuffer * -ast_user_framebuffer_create(struct drm_device *dev, - struct drm_file *filp, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct drm_gem_object *obj; - struct ast_framebuffer *ast_fb; - int ret; - - obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]); - if (obj == NULL) - return ERR_PTR(-ENOENT); - - ast_fb = kzalloc(sizeof(*ast_fb), GFP_KERNEL); - if (!ast_fb) { - drm_gem_object_put_unlocked(obj); - return ERR_PTR(-ENOMEM); - } - - ret = ast_framebuffer_init(dev, ast_fb, mode_cmd, obj); - if (ret) { - drm_gem_object_put_unlocked(obj); - kfree(ast_fb); - return ERR_PTR(ret); - } - return &ast_fb->base; -} - -static const struct drm_mode_config_funcs ast_mode_funcs = { - .fb_create = ast_user_framebuffer_create, -}; - -static u32 ast_get_vram_info(struct drm_device *dev) -{ - struct ast_private *ast = dev->dev_private; - 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; -} - -int ast_driver_load(struct drm_device *dev, unsigned long flags) -{ - struct ast_private *ast; - bool need_post; - int ret = 0; - - ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL); - if (!ast) - return -ENOMEM; - - dev->dev_private = ast; - ast->dev = dev; - - ast->regs = pci_iomap(dev->pdev, 1, 0); - if (!ast->regs) { - ret = -EIO; - goto out_free; - } - - /* - * 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("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 = pci_iomap(dev->pdev, 2, 0); - if (!ast->ioregs) { - ret = -EIO; - goto out_free; - } - } - - ast_detect_chip(dev, &need_post); - - if (need_post) - ast_post_gpu(dev); - - if (ast->chip != AST1180) { - ret = ast_get_dram_info(dev); - if (ret) - goto out_free; - ast->vram_size = ast_get_vram_info(dev); - DRM_INFO("dram MCLK=%u Mhz type=%d bus_width=%d size=%08x\n", - ast->mclk, ast->dram_type, - ast->dram_bus_width, ast->vram_size); - } - - ret = ast_mm_init(ast); - if (ret) - goto out_free; - - drm_mode_config_init(dev); - - dev->mode_config.funcs = (void *)&ast_mode_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(ast->dev->pdev, 0); - - if (ast->chip == AST2100 || - ast->chip == AST2200 || - ast->chip == AST2300 || - ast->chip == AST2400 || - ast->chip == AST2500 || - ast->chip == AST1180) { - 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; - } - - ret = ast_mode_init(dev); - if (ret) - goto out_free; - - ret = ast_fbdev_init(dev); - if (ret) - goto out_free; - - return 0; -out_free: - kfree(ast); - dev->dev_private = NULL; - return ret; -} - -void ast_driver_unload(struct drm_device *dev) -{ - struct ast_private *ast = dev->dev_private; - - ast_release_firmware(dev); - kfree(ast->dp501_fw_addr); - ast_mode_fini(dev); - ast_fbdev_fini(dev); - drm_mode_config_cleanup(dev); - - ast_mm_fini(ast); - if (ast->ioregs != ast->regs + AST_IO_MM_OFFSET) - pci_iounmap(dev->pdev, ast->ioregs); - pci_iounmap(dev->pdev, ast->regs); - kfree(ast); -} - -int ast_gem_create(struct drm_device *dev, - u32 size, bool iskernel, - struct drm_gem_object **obj) -{ - struct ast_bo *astbo; - int ret; - - *obj = NULL; - - size = roundup(size, PAGE_SIZE); - if (size == 0) - return -EINVAL; - - ret = ast_bo_create(dev, size, 0, 0, &astbo); - if (ret) { - if (ret != -ERESTARTSYS) - DRM_ERROR("failed to allocate GEM object\n"); - return ret; - } - *obj = &astbo->gem; - return 0; -} - -int ast_dumb_create(struct drm_file *file, - struct drm_device *dev, - struct drm_mode_create_dumb *args) -{ - int ret; - struct drm_gem_object *gobj; - u32 handle; - - args->pitch = args->width * ((args->bpp + 7) / 8); - args->size = args->pitch * args->height; - - ret = ast_gem_create(dev, args->size, false, - &gobj); - if (ret) - return ret; - - ret = drm_gem_handle_create(file, gobj, &handle); - drm_gem_object_put_unlocked(gobj); - if (ret) - return ret; - - args->handle = handle; - return 0; -} - -static void ast_bo_unref(struct ast_bo **bo) -{ - struct ttm_buffer_object *tbo; - - if ((*bo) == NULL) - return; - - tbo = &((*bo)->bo); - ttm_bo_unref(&tbo); - *bo = NULL; -} - -void ast_gem_free_object(struct drm_gem_object *obj) -{ - struct ast_bo *ast_bo = gem_to_ast_bo(obj); - - ast_bo_unref(&ast_bo); -} - - -static inline u64 ast_bo_mmap_offset(struct ast_bo *bo) -{ - return drm_vma_node_offset_addr(&bo->bo.vma_node); -} -int -ast_dumb_mmap_offset(struct drm_file *file, - struct drm_device *dev, - uint32_t handle, - uint64_t *offset) -{ - struct drm_gem_object *obj; - struct ast_bo *bo; - - obj = drm_gem_object_lookup(file, handle); - if (obj == NULL) - return -ENOENT; - - bo = gem_to_ast_bo(obj); - *offset = ast_bo_mmap_offset(bo); - - drm_gem_object_put_unlocked(obj); - - return 0; - -} - diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c new file mode 100644 index 000000000000..0bc140319464 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -0,0 +1,97 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_managed.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" + +static u32 ast_get_vram_size(struct ast_device *ast) +{ + u32 vram_size; + u8 vgacr99, vgacraa; + + vgacraa = ast_get_index_reg(ast, AST_IO_VGACRI, 0xaa); + switch (vgacraa & AST_IO_VGACRAA_VGAMEM_SIZE_MASK) { + case 0: + vram_size = SZ_8M; + break; + case 1: + vram_size = SZ_16M; + break; + case 2: + vram_size = SZ_32M; + break; + case 3: + vram_size = SZ_64M; + break; + } + + vgacr99 = ast_get_index_reg(ast, AST_IO_VGACRI, 0x99); + switch (vgacr99 & AST_IO_VGACR99_VGAMEM_RSRV_MASK) { + case 1: + vram_size -= SZ_1M; + break; + case 2: + vram_size -= SZ_2M; + break; + case 3: + vram_size -= SZ_4M; + break; + } + + return vram_size; +} + +int ast_mm_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct pci_dev *pdev = to_pci_dev(dev->dev); + resource_size_t base, size; + u32 vram_size; + + base = pci_resource_start(pdev, 0); + size = pci_resource_len(pdev, 0); + + /* Don't fail on errors, but performance might be reduced. */ + devm_arch_io_reserve_memtype_wc(dev->dev, base, size); + devm_arch_phys_wc_add(dev->dev, base, size); + + vram_size = ast_get_vram_size(ast); + + ast->vram = devm_ioremap_wc(dev->dev, base, vram_size); + if (!ast->vram) + return -ENOMEM; + + ast->vram_base = base; + ast->vram_size = vram_size; + + return 0; +} diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 8bb355d5d43d..cd08990a10f9 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -27,297 +27,272 @@ /* * Authors: Dave Airlie <airlied@redhat.com> */ -#include <linux/export.h> -#include <drm/drmP.h> + +#include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_color_mgmt.h> #include <drm/drm_crtc.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_plane_helper.h> -#include "ast_drv.h" +#include <drm/drm_damage_helper.h> +#include <drm/drm_format_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_panic.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include "ast_drv.h" #include "ast_tables.h" +#include "ast_vbios.h" + +#define AST_LUT_SIZE 256 -static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev); -static void ast_i2c_destroy(struct ast_i2c_chan *i2c); -static int ast_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, - uint32_t height); -static int ast_cursor_move(struct drm_crtc *crtc, - int x, int y); - -static inline void ast_load_palette_index(struct ast_private *ast, - u8 index, u8 red, u8 green, - u8 blue) +#define AST_PRIMARY_PLANE_MAX_OFFSET (BIT(16) - 1) + +static unsigned long ast_fb_vram_offset(void) { - 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); + return 0; // with shmem, the primary plane is always at offset 0 } -static void ast_crtc_load_lut(struct drm_crtc *crtc) +static unsigned long ast_fb_vram_size(struct ast_device *ast) { - struct ast_private *ast = crtc->dev->dev_private; - u16 *r, *g, *b; - int i; - - if (!crtc->enabled) - return; - - r = crtc->gamma_store; - g = r + crtc->gamma_size; - b = g + crtc->gamma_size; + struct drm_device *dev = &ast->base; + unsigned long offset = ast_fb_vram_offset(); // starts at offset + long cursor_offset = ast_cursor_vram_offset(ast); // ends at cursor offset + + if (cursor_offset < 0) + cursor_offset = ast->vram_size; // no cursor; it's all ours + if (drm_WARN_ON_ONCE(dev, offset > cursor_offset)) + return 0; // cannot legally happen; signal error + return cursor_offset - offset; +} - for (i = 0; i < 256; i++) - ast_load_palette_index(ast, i, *r++ >> 8, *g++ >> 8, *b++ >> 8); +static void ast_set_gamma_lut(struct drm_crtc *crtc, unsigned int index, + u16 red, u16 green, u16 blue) +{ + struct drm_device *dev = crtc->dev; + struct ast_device *ast = to_ast_device(dev); + u8 i8 = index & 0xff; + u8 r8 = red >> 8; + u8 g8 = green >> 8; + u8 b8 = blue >> 8; + + if (drm_WARN_ON_ONCE(dev, index != i8)) + return; /* driver bug */ + + ast_io_write8(ast, AST_IO_VGADWR, i8); + ast_io_read8(ast, AST_IO_VGASRI); + ast_io_write8(ast, AST_IO_VGAPDR, r8); + ast_io_read8(ast, AST_IO_VGASRI); + ast_io_write8(ast, AST_IO_VGAPDR, g8); + ast_io_read8(ast, AST_IO_VGASRI); + ast_io_write8(ast, AST_IO_VGAPDR, b8); + ast_io_read8(ast, AST_IO_VGASRI); } -static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - struct ast_vbios_mode_info *vbios_mode) +static void ast_crtc_fill_gamma(struct ast_device *ast, + const struct drm_format_info *format) { - struct ast_private *ast = crtc->dev->dev_private; - const struct drm_framebuffer *fb = crtc->primary->fb; - u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate; - const struct ast_vbios_enhtable *best = NULL; - u32 hborder, vborder; - bool check_sync; - - switch (fb->format->cpp[0] * 8) { - case 8: - vbios_mode->std_table = &vbios_stdtable[VGAModeIndex]; - color_index = VGAModeIndex - 1; - break; - case 16: - vbios_mode->std_table = &vbios_stdtable[HiCModeIndex]; - color_index = HiCModeIndex; + struct drm_crtc *crtc = &ast->crtc; + + switch (format->format) { + case DRM_FORMAT_C8: + /* gamma table is used as color palette */ + drm_crtc_fill_palette_8(crtc, ast_set_gamma_lut); break; - case 24: - case 32: - vbios_mode->std_table = &vbios_stdtable[TrueCModeIndex]; - color_index = TrueCModeIndex; + case DRM_FORMAT_RGB565: + /* also uses 8-bit gamma ramp on low-color modes */ + fallthrough; + case DRM_FORMAT_XRGB8888: + drm_crtc_fill_gamma_888(crtc, ast_set_gamma_lut); break; default: - return false; + drm_warn_once(&ast->base, "Unsupported format %p4cc for gamma correction\n", + &format->format); + break; } +} - switch (crtc->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 (crtc->mode.crtc_vdisplay == 800) - vbios_mode->enh_table = &res_1280x800[refresh_rate_index]; - else - vbios_mode->enh_table = &res_1280x1024[refresh_rate_index]; +static void ast_crtc_load_gamma(struct ast_device *ast, + const struct drm_format_info *format, + struct drm_color_lut *lut) +{ + struct drm_crtc *crtc = &ast->crtc; + + switch (format->format) { + case DRM_FORMAT_C8: + /* gamma table is used as color palette */ + drm_crtc_load_palette_8(crtc, lut, ast_set_gamma_lut); break; - case 1360: - vbios_mode->enh_table = &res_1360x768[refresh_rate_index]; + case DRM_FORMAT_RGB565: + /* also uses 8-bit gamma ramp on low-color modes */ + fallthrough; + case DRM_FORMAT_XRGB8888: + drm_crtc_load_gamma_888(crtc, lut, ast_set_gamma_lut); break; - case 1440: - vbios_mode->enh_table = &res_1440x900[refresh_rate_index]; + default: + drm_warn_once(&ast->base, "Unsupported format %p4cc for gamma correction\n", + &format->format); break; - case 1600: - if (crtc->mode.crtc_vdisplay == 900) - vbios_mode->enh_table = &res_1600x900[refresh_rate_index]; - else - vbios_mode->enh_table = &res_1600x1200[refresh_rate_index]; + } +} + +static void ast_set_vbios_color_reg(struct ast_device *ast, + const struct drm_format_info *format, + const struct ast_vbios_enhtable *vmode) +{ + u32 color_index; + + switch (format->cpp[0]) { + case 1: + color_index = VGAModeIndex - 1; break; - case 1680: - vbios_mode->enh_table = &res_1680x1050[refresh_rate_index]; + case 2: + color_index = HiCModeIndex; break; - case 1920: - if (crtc->mode.crtc_vdisplay == 1080) - vbios_mode->enh_table = &res_1920x1080[refresh_rate_index]; - else - vbios_mode->enh_table = &res_1920x1200[refresh_rate_index]; + case 3: + case 4: + color_index = TrueCModeIndex; break; default: - return false; + return; } - refresh_rate = drm_mode_vrefresh(mode); - check_sync = vbios_mode->enh_table->flags & WideScreenMode; - do { - 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; - } while (1); - 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); - - refresh_rate_index = vbios_mode->enh_table->refresh_rate_index; - mode_id = vbios_mode->enh_table->mode_id; - - if (ast->chip == AST1180) { - /* TODO 1180 */ - } else { - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8c, (u8)((color_index & 0xf) << 4)); - 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, 0x92, - fb->format->cpp[0] * 8); - 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); - } + ast_set_index_reg(ast, AST_IO_VGACRI, 0x8c, (u8)((color_index & 0x0f) << 4)); + + ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0x00); + + if (vmode->flags & NewModeInfo) { + ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0xa8); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x92, format->cpp[0] * 8); } +} + +static void ast_set_vbios_mode_reg(struct ast_device *ast, + const struct drm_display_mode *adjusted_mode, + const struct ast_vbios_enhtable *vmode) +{ + u32 refresh_rate_index, mode_id; + + refresh_rate_index = vmode->refresh_rate_index; + mode_id = vmode->mode_id; - return true; + ast_set_index_reg(ast, AST_IO_VGACRI, 0x8d, refresh_rate_index & 0xff); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x8e, mode_id & 0xff); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0x00); + if (vmode->flags & NewModeInfo) { + ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0xa8); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x93, adjusted_mode->clock / 1000); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x94, adjusted_mode->crtc_hdisplay); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x95, adjusted_mode->crtc_hdisplay >> 8); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x96, adjusted_mode->crtc_vdisplay); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x97, adjusted_mode->crtc_vdisplay >> 8); + } } -static void ast_set_std_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct ast_vbios_mode_info *vbios_mode) + +static void ast_set_std_reg(struct ast_device *ast, + struct drm_display_mode *mode, + const struct ast_vbios_stdtable *stdtable) { - struct ast_private *ast = crtc->dev->dev_private; - 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); + ast_io_write8(ast, AST_IO_VGAMR_W, jreg); - /* Set SEQ */ - ast_set_index_reg(ast, AST_IO_SEQ_PORT, 0x00, 0x03); - for (i = 0; i < 4; i++) { + /* Set SEQ; except Screen Disable field */ + ast_set_index_reg(ast, AST_IO_VGASRI, 0x00, 0x03); + ast_set_index_reg_mask(ast, AST_IO_VGASRI, 0x01, 0x20, stdtable->seq[0]); + for (i = 1; i < 4; i++) { jreg = stdtable->seq[i]; - if (!i) - jreg |= 0x20; - ast_set_index_reg(ast, AST_IO_SEQ_PORT, (i + 1) , jreg); + ast_set_index_reg(ast, AST_IO_VGASRI, (i + 1), jreg); } - /* Set CRTC */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00); - for (i = 0; i < 25; i++) - ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, stdtable->crtc[i]); + /* Set CRTC; except base address and offset */ + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00); + for (i = 0; i < 12; i++) + ast_set_index_reg(ast, AST_IO_VGACRI, i, stdtable->crtc[i]); + for (i = 14; i < 19; i++) + ast_set_index_reg(ast, AST_IO_VGACRI, i, stdtable->crtc[i]); + for (i = 20; i < 25; i++) + ast_set_index_reg(ast, AST_IO_VGACRI, i, stdtable->crtc[i]); /* set AR */ - jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); + jreg = ast_io_read8(ast, AST_IO_VGAIR1_R); for (i = 0; i < 20; i++) { jreg = stdtable->ar[i]; - ast_io_write8(ast, AST_IO_AR_PORT_WRITE, (u8)i); - ast_io_write8(ast, AST_IO_AR_PORT_WRITE, jreg); + ast_io_write8(ast, AST_IO_VGAARI_W, (u8)i); + ast_io_write8(ast, AST_IO_VGAARI_W, jreg); } - ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x14); - ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x00); + ast_io_write8(ast, AST_IO_VGAARI_W, 0x14); + ast_io_write8(ast, AST_IO_VGAARI_W, 0x00); - jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ); - ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x20); + jreg = ast_io_read8(ast, AST_IO_VGAIR1_R); + ast_io_write8(ast, AST_IO_VGAARI_W, 0x20); /* Set GR */ for (i = 0; i < 9; i++) - ast_set_index_reg(ast, AST_IO_GR_PORT, i, stdtable->gr[i]); + ast_set_index_reg(ast, AST_IO_VGAGRI, i, stdtable->gr[i]); } -static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct ast_vbios_mode_info *vbios_mode) +static void ast_set_crtc_reg(struct ast_device *ast, struct drm_display_mode *mode, + const struct ast_vbios_enhtable *vmode) { - struct ast_private *ast = crtc->dev->dev_private; u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0; - u16 temp, precache = 0; + u16 temp; + unsigned char crtc_hsync_precatch = 0; - if ((ast->chip == AST2500) && - (vbios_mode->enh_table->flags & AST2500PreCatchCRT)) - precache = 40; + if (ast->quirks->crtc_hsync_precatch_needed && (vmode->flags & AST2500PreCatchCRT)) + crtc_hsync_precatch = 40; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00); temp = (mode->crtc_htotal >> 3) - 5; if (temp & 0x100) jregAC |= 0x01; /* HT D[8] */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x00, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x00, 0x00, temp); temp = (mode->crtc_hdisplay >> 3) - 1; if (temp & 0x100) jregAC |= 0x04; /* HDE D[8] */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x01, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x01, 0x00, temp); temp = (mode->crtc_hblank_start >> 3) - 1; if (temp & 0x100) jregAC |= 0x10; /* HBS D[8] */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x02, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x02, 0x00, temp); temp = ((mode->crtc_hblank_end >> 3) - 1) & 0x7f; if (temp & 0x20) jreg05 |= 0x80; /* HBE D[5] */ if (temp & 0x40) jregAD |= 0x01; /* HBE D[5] */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x03, 0xE0, (temp & 0x1f)); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x03, 0xE0, (temp & 0x1f)); - temp = ((mode->crtc_hsync_start-precache) >> 3) - 1; + temp = ((mode->crtc_hsync_start - crtc_hsync_precatch) >> 3) - 1; if (temp & 0x100) jregAC |= 0x40; /* HRS D[5] */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x04, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x04, 0x00, temp); - temp = (((mode->crtc_hsync_end-precache) >> 3) - 1) & 0x3f; + temp = (((mode->crtc_hsync_end - crtc_hsync_precatch) >> 3) - 1) & 0x3f; if (temp & 0x20) jregAD |= 0x04; /* HRE D[5] */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05)); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 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); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAC, 0x00, jregAC); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAD, 0x00, jregAD); + + if (ast->quirks->crtc_hsync_add4_needed && mode->crtc_vdisplay == 1080) + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x02); + else + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x00); /* vert timings */ temp = (mode->crtc_vtotal) - 2; @@ -327,7 +302,7 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod jreg07 |= 0x20; if (temp & 0x400) jregAE |= 0x01; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x06, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x06, 0x00, temp); temp = (mode->crtc_vsync_start) - 1; if (temp & 0x100) @@ -336,14 +311,14 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod jreg07 |= 0x80; if (temp & 0x400) jregAE |= 0x08; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x10, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x10, 0x00, temp); temp = (mode->crtc_vsync_end - 1) & 0x3f; if (temp & 0x10) jregAE |= 0x20; if (temp & 0x20) jregAE |= 0x40; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x70, temp & 0xf); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x70, temp & 0xf); temp = mode->crtc_vdisplay - 1; if (temp & 0x100) @@ -352,7 +327,7 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod jreg07 |= 0x40; if (temp & 0x400) jregAE |= 0x02; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x12, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x12, 0x00, temp); temp = mode->crtc_vblank_start - 1; if (temp & 0x100) @@ -361,63 +336,54 @@ static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mod jreg09 |= 0x20; if (temp & 0x400) jregAE |= 0x04; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x15, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x15, 0x00, temp); temp = mode->crtc_vblank_end - 1; if (temp & 0x100) jregAE |= 0x10; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x16, 0x00, temp); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 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)); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x07, 0x00, jreg07); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x09, 0xdf, jreg09); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAE, 0x00, (jregAE | 0x80)); - if (precache) - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0x3f, 0x80); + if (crtc_hsync_precatch) + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x80); else - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0x3f, 0x00); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x00); - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x80); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x80); } -static void ast_set_offset_reg(struct drm_crtc *crtc) +static void ast_set_offset_reg(struct ast_device *ast, + struct drm_framebuffer *fb) { - struct ast_private *ast = crtc->dev->dev_private; - const struct drm_framebuffer *fb = crtc->primary->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); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x13, (offset & 0xff)); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xb0, (offset >> 8) & 0x3f); } -static void ast_set_dclk_reg(struct drm_device *dev, struct drm_display_mode *mode, - struct ast_vbios_mode_info *vbios_mode) +static void ast_set_dclk_reg(struct ast_device *ast, + struct drm_display_mode *mode, + const struct ast_vbios_enhtable *vmode) { - struct ast_private *ast = dev->dev_private; - 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]; + const struct ast_vbios_dclk_info *clk_info = &ast->dclk_table[vmode->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, + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc0, 0x00, clk_info->param1); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc1, 0x00, clk_info->param2); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xbb, 0x0f, (clk_info->param3 & 0xc0) | ((clk_info->param3 & 0x3) << 4)); } -static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct ast_vbios_mode_info *vbios_mode) +static void ast_set_color_reg(struct ast_device *ast, + const struct drm_format_info *format) { - struct ast_private *ast = crtc->dev->dev_private; - const struct drm_framebuffer *fb = crtc->primary->fb; u8 jregA0 = 0, jregA3 = 0, jregA8 = 0; - switch (fb->format->cpp[0] * 8) { + switch (format->cpp[0] * 8) { case 8: jregA0 = 0x70; jregA3 = 0x01; @@ -436,850 +402,657 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode 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); - - /* 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); - } + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa0, 0x8f, jregA0); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xf0, jregA3); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa8, 0xfd, jregA8); } -static void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mode, - struct ast_vbios_mode_info *vbios_mode) +static void ast_set_crtthd_reg(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; - u8 jreg; + u8 vgacra6 = ast->quirks->crtc_mem_req_threshold_low; + u8 vgacra7 = ast->quirks->crtc_mem_req_threshold_high; - 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); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, vgacra7); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, vgacra6); } -static bool ast_set_dac_reg(struct drm_crtc *crtc, struct drm_display_mode *mode, - struct ast_vbios_mode_info *vbios_mode) +static void ast_set_sync_reg(struct ast_device *ast, + struct drm_display_mode *mode, + const struct ast_vbios_enhtable *vmode) { - const struct drm_framebuffer *fb = crtc->primary->fb; + u8 jreg; - switch (fb->format->cpp[0] * 8) { - case 8: - break; - default: - return false; - } - return true; + jreg = ast_io_read8(ast, AST_IO_VGAMR_R); + jreg &= ~0xC0; + if (vmode->flags & NVSync) + jreg |= 0x80; + if (vmode->flags & NHSync) + jreg |= 0x40; + ast_io_write8(ast, AST_IO_VGAMR_W, jreg); } -static void ast_set_start_address_crt1(struct drm_crtc *crtc, unsigned offset) +static void ast_set_start_address_crt1(struct ast_device *ast, + unsigned int offset) { - struct ast_private *ast = crtc->dev->dev_private; 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)); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x0d, (u8)(addr & 0xff)); + ast_set_index_reg(ast, AST_IO_VGACRI, 0x0c, (u8)((addr >> 8) & 0xff)); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xaf, (u8)((addr >> 16) & 0xff)); } -static void ast_crtc_dpms(struct drm_crtc *crtc, int mode) +static void ast_wait_for_vretrace(struct ast_device *ast) { - struct ast_private *ast = crtc->dev->dev_private; - - if (ast->chip == AST1180) - return; + unsigned long timeout = jiffies + HZ; + u8 vgair1; - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0); - if (ast->tx_chip_type == AST_TX_DP501) - ast_set_dp501_video_output(crtc->dev, 1); - ast_crtc_load_lut(crtc); - break; - case DRM_MODE_DPMS_OFF: - if (ast->tx_chip_type == AST_TX_DP501) - ast_set_dp501_video_output(crtc->dev, 0); - ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20); - break; - } + do { + vgair1 = ast_io_read8(ast, AST_IO_VGAIR1_R); + } while (!(vgair1 & AST_IO_VGAIR1_VREFRESH) && time_before(jiffies, timeout)); } -/* ast is different - we will force move buffers out of VRAM */ -static int ast_crtc_do_set_base(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int x, int y, int atomic) -{ - struct ast_private *ast = crtc->dev->dev_private; - struct drm_gem_object *obj; - struct ast_framebuffer *ast_fb; - struct ast_bo *bo; - int ret; - u64 gpu_addr; - - /* push the previous fb to system ram */ - if (!atomic && fb) { - ast_fb = to_ast_framebuffer(fb); - obj = ast_fb->obj; - bo = gem_to_ast_bo(obj); - ret = ast_bo_reserve(bo, false); - if (ret) - return ret; - ast_bo_push_sysram(bo); - ast_bo_unreserve(bo); - } - - ast_fb = to_ast_framebuffer(crtc->primary->fb); - obj = ast_fb->obj; - bo = gem_to_ast_bo(obj); - - ret = ast_bo_reserve(bo, false); - if (ret) - return ret; - - ret = ast_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); - if (ret) { - ast_bo_unreserve(bo); - return ret; - } - - if (&ast->fbdev->afb == ast_fb) { - /* if pushing console in kmap it */ - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); - if (ret) - DRM_ERROR("failed to kmap fbcon\n"); - else - ast_fbdev_set_base(ast, gpu_addr); - } - ast_bo_unreserve(bo); +/* + * Planes + */ - ast_set_offset_reg(crtc); - ast_set_start_address_crt1(crtc, (u32)gpu_addr); +int ast_plane_init(struct drm_device *dev, struct ast_plane *ast_plane, + u64 offset, unsigned long size, + uint32_t possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, unsigned int format_count, + const uint64_t *format_modifiers, + enum drm_plane_type type) +{ + struct drm_plane *plane = &ast_plane->base; - return 0; -} + ast_plane->offset = offset; + ast_plane->size = size; -static int ast_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - return ast_crtc_do_set_base(crtc, old_fb, x, y, 0); + return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, format_modifiers, + type, NULL); } -static int ast_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) +void __iomem *ast_plane_vaddr(struct ast_plane *ast_plane) { - struct drm_device *dev = crtc->dev; - struct ast_private *ast = crtc->dev->dev_private; - struct ast_vbios_mode_info vbios_mode; - bool ret; - if (ast->chip == AST1180) { - DRM_ERROR("AST 1180 modesetting not supported\n"); - return -EINVAL; - } - - ret = ast_get_vbios_mode_info(crtc, mode, adjusted_mode, &vbios_mode); - if (ret == false) - return -EINVAL; - ast_open_key(ast); - - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04); + struct ast_device *ast = to_ast_device(ast_plane->base.dev); - ast_set_std_reg(crtc, adjusted_mode, &vbios_mode); - ast_set_crtc_reg(crtc, adjusted_mode, &vbios_mode); - ast_set_offset_reg(crtc); - ast_set_dclk_reg(dev, adjusted_mode, &vbios_mode); - ast_set_ext_reg(crtc, adjusted_mode, &vbios_mode); - ast_set_sync_reg(dev, adjusted_mode, &vbios_mode); - ast_set_dac_reg(crtc, adjusted_mode, &vbios_mode); + return ast->vram + ast_plane->offset; +} - ast_crtc_mode_set_base(crtc, x, y, old_fb); +/* + * Primary plane + */ - return 0; -} +static const uint32_t ast_primary_plane_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_C8, +}; -static void ast_crtc_disable(struct drm_crtc *crtc) +static int ast_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) { + struct drm_device *dev = plane->dev; + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *new_crtc_state = NULL; + struct ast_crtc_state *new_ast_crtc_state; int ret; - DRM_DEBUG_KMS("\n"); - ast_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - if (crtc->primary->fb) { - struct ast_framebuffer *ast_fb = to_ast_framebuffer(crtc->primary->fb); - struct drm_gem_object *obj = ast_fb->obj; - struct ast_bo *bo = gem_to_ast_bo(obj); - - ret = ast_bo_reserve(bo, false); - if (ret) - return; + if (new_plane_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); - ast_bo_push_sysram(bo); - ast_bo_unreserve(bo); + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); + if (ret) { + return ret; + } else if (!new_plane_state->visible) { + if (drm_WARN_ON(dev, new_plane_state->crtc)) /* cannot legally happen */ + return -EINVAL; + else + return 0; } - crtc->primary->fb = NULL; -} -static void ast_crtc_prepare(struct drm_crtc *crtc) -{ + new_ast_crtc_state = to_ast_crtc_state(new_crtc_state); -} + new_ast_crtc_state->format = new_plane_state->fb->format; -static void ast_crtc_commit(struct drm_crtc *crtc) -{ - struct ast_private *ast = crtc->dev->dev_private; - ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0); - ast_crtc_load_lut(crtc); + return 0; } - -static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { - .dpms = ast_crtc_dpms, - .mode_set = ast_crtc_mode_set, - .mode_set_base = ast_crtc_mode_set_base, - .disable = ast_crtc_disable, - .prepare = ast_crtc_prepare, - .commit = ast_crtc_commit, - -}; - -static void ast_crtc_reset(struct drm_crtc *crtc) +static void ast_handle_damage(struct ast_plane *ast_plane, struct iosys_map *src, + struct drm_framebuffer *fb, + const struct drm_rect *clip) { + struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(ast_plane_vaddr(ast_plane)); + iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip)); + drm_fb_memcpy(&dst, fb->pitches, src, fb, clip); } -static int ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size, - struct drm_modeset_acquire_ctx *ctx) +static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - ast_crtc_load_lut(crtc); + struct drm_device *dev = plane->dev; + struct ast_device *ast = to_ast_device(dev); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_framebuffer *old_fb = old_plane_state->fb; + struct ast_plane *ast_plane = to_ast_plane(plane); + struct drm_crtc *crtc = plane_state->crtc; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_rect damage; + struct drm_atomic_helper_damage_iter iter; + + if (!old_fb || (fb->format != old_fb->format) || crtc_state->mode_changed) { + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + + ast_set_color_reg(ast, fb->format); + ast_set_vbios_color_reg(ast, fb->format, ast_crtc_state->vmode); + } - return 0; -} + /* if the buffer comes from another device */ + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) { + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage); + } + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + } -static void ast_crtc_destroy(struct drm_crtc *crtc) -{ - drm_crtc_cleanup(crtc); - kfree(crtc); + /* + * Some BMCs stop scanning out the video signal after the driver + * reprogrammed the offset. This stalls display output for several + * seconds and makes the display unusable. Therefore only update + * the offset if it changes. + */ + if (!old_fb || old_fb->pitches[0] != fb->pitches[0]) + ast_set_offset_reg(ast, fb); } -static const struct drm_crtc_funcs ast_crtc_funcs = { - .cursor_set = ast_cursor_set, - .cursor_move = ast_cursor_move, - .reset = ast_crtc_reset, - .set_config = drm_crtc_helper_set_config, - .gamma_set = ast_crtc_gamma_set, - .destroy = ast_crtc_destroy, -}; - -static int ast_crtc_init(struct drm_device *dev) +static void ast_primary_plane_helper_atomic_enable(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct ast_crtc *crtc; - - crtc = kzalloc(sizeof(struct ast_crtc), GFP_KERNEL); - if (!crtc) - return -ENOMEM; - - drm_crtc_init(dev, &crtc->base, &ast_crtc_funcs); - drm_mode_crtc_set_gamma_size(&crtc->base, 256); - drm_crtc_helper_add(&crtc->base, &ast_crtc_helper_funcs); - return 0; + struct ast_device *ast = to_ast_device(plane->dev); + struct ast_plane *ast_plane = to_ast_plane(plane); + + /* + * Some BMCs stop scanning out the video signal after the driver + * reprogrammed the scanout address. This stalls display + * output for several seconds and makes the display unusable. + * Therefore only reprogram the address after enabling the plane. + */ + ast_set_start_address_crt1(ast, (u32)ast_plane->offset); } -static void ast_encoder_destroy(struct drm_encoder *encoder) +static void ast_primary_plane_helper_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) { - drm_encoder_cleanup(encoder); - kfree(encoder); + /* + * Keep this empty function to avoid calling + * atomic_update when disabling the plane. + */ } - -static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector) +static int ast_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane, + struct drm_scanout_buffer *sb) { - int enc_id = connector->encoder_ids[0]; - /* pick the encoder ids */ - if (enc_id) - return drm_encoder_find(connector->dev, NULL, enc_id); - return NULL; + struct ast_plane *ast_plane = to_ast_plane(plane); + + if (plane->state && plane->state->fb) { + sb->format = plane->state->fb->format; + sb->width = plane->state->fb->width; + sb->height = plane->state->fb->height; + sb->pitch[0] = plane->state->fb->pitches[0]; + iosys_map_set_vaddr_iomem(&sb->map[0], ast_plane_vaddr(ast_plane)); + return 0; + } + return -ENODEV; } +static const struct drm_plane_helper_funcs ast_primary_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = ast_primary_plane_helper_atomic_check, + .atomic_update = ast_primary_plane_helper_atomic_update, + .atomic_enable = ast_primary_plane_helper_atomic_enable, + .atomic_disable = ast_primary_plane_helper_atomic_disable, + .get_scanout_buffer = ast_primary_plane_helper_get_scanout_buffer, +}; -static const struct drm_encoder_funcs ast_enc_funcs = { - .destroy = ast_encoder_destroy, +static const struct drm_plane_funcs ast_primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, }; -static void ast_encoder_dpms(struct drm_encoder *encoder, int mode) +static int ast_primary_plane_init(struct ast_device *ast) { + struct drm_device *dev = &ast->base; + struct ast_plane *ast_primary_plane = &ast->primary_plane; + struct drm_plane *primary_plane = &ast_primary_plane->base; + u64 offset = ast_fb_vram_offset(); + unsigned long size = ast_fb_vram_size(ast); + int ret; -} + ret = ast_plane_init(dev, ast_primary_plane, offset, size, + 0x01, &ast_primary_plane_funcs, + ast_primary_plane_formats, ARRAY_SIZE(ast_primary_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY); + if (ret) { + drm_err(dev, "ast_plane_init() failed: %d\n", ret); + return ret; + } + drm_plane_helper_add(primary_plane, &ast_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); -static void ast_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ + return 0; } -static void ast_encoder_prepare(struct drm_encoder *encoder) -{ - -} +/* + * CRTC + */ -static void ast_encoder_commit(struct drm_encoder *encoder) +static enum drm_mode_status +ast_crtc_helper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) { + struct ast_device *ast = to_ast_device(crtc->dev); + const struct ast_vbios_enhtable *vmode; -} + vmode = ast_vbios_find_mode(ast, mode); + if (!vmode) + return MODE_NOMODE; + return MODE_OK; +} -static const struct drm_encoder_helper_funcs ast_enc_helper_funcs = { - .dpms = ast_encoder_dpms, - .prepare = ast_encoder_prepare, - .commit = ast_encoder_commit, - .mode_set = ast_encoder_mode_set, -}; - -static int ast_encoder_init(struct drm_device *dev) +static void ast_crtc_helper_mode_set_nofb(struct drm_crtc *crtc) { - struct ast_encoder *ast_encoder; - - ast_encoder = kzalloc(sizeof(struct ast_encoder), GFP_KERNEL); - if (!ast_encoder) - return -ENOMEM; - - drm_encoder_init(dev, &ast_encoder->base, &ast_enc_funcs, - DRM_MODE_ENCODER_DAC, NULL); - drm_encoder_helper_add(&ast_encoder->base, &ast_enc_helper_funcs); - - ast_encoder->base.possible_crtcs = 1; - return 0; + struct drm_device *dev = crtc->dev; + struct ast_device *ast = to_ast_device(dev); + struct drm_crtc_state *crtc_state = crtc->state; + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + const struct ast_vbios_stdtable *std_table = ast_crtc_state->std_table; + const struct ast_vbios_enhtable *vmode = ast_crtc_state->vmode; + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + + /* + * Ensure that no scanout takes place before reprogramming mode + * and format registers. + * + * TODO: Get vblank interrupts working and remove this line. + */ + ast_wait_for_vretrace(ast); + + ast_set_vbios_mode_reg(ast, adjusted_mode, vmode); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xa1, 0x06); + ast_set_std_reg(ast, adjusted_mode, std_table); + ast_set_crtc_reg(ast, adjusted_mode, vmode); + ast_set_dclk_reg(ast, adjusted_mode, vmode); + ast_set_crtthd_reg(ast); + ast_set_sync_reg(ast, adjusted_mode, vmode); } -static int ast_get_modes(struct drm_connector *connector) +static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct ast_connector *ast_connector = to_ast_connector(connector); - struct ast_private *ast = connector->dev->dev_private; - struct edid *edid; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + struct ast_crtc_state *old_ast_crtc_state = to_ast_crtc_state(old_crtc_state); + struct drm_device *dev = crtc->dev; + struct ast_device *ast = to_ast_device(dev); + struct ast_crtc_state *ast_state; + const struct drm_format_info *format; + const struct ast_vbios_enhtable *vmode; + unsigned int hborder = 0; + unsigned int vborder = 0; int ret; - 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); + + if (!crtc_state->enable) + return 0; + + ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); + if (ret) 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 = connector->dev->dev_private; - 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) || (ast->chip == AST1180)) { - 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; - } + ast_state = to_ast_crtc_state(crtc_state); + + format = ast_state->format; + if (drm_WARN_ON_ONCE(dev, !format)) + return -EINVAL; /* BUG: We didn't set format in primary check(). */ + + /* + * The gamma LUT has to be reloaded after changing the primary + * plane's color format. + */ + if (old_ast_crtc_state->format != format) + crtc_state->color_mgmt_changed = true; + + if (crtc_state->color_mgmt_changed && crtc_state->gamma_lut) { + if (crtc_state->gamma_lut->length != + AST_LUT_SIZE * sizeof(struct drm_color_lut)) { + drm_err(dev, "Wrong size for gamma_lut %zu\n", + crtc_state->gamma_lut->length); + return -EINVAL; } } - 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; + + /* + * Set register tables. + * + * TODO: These tables mix all kinds of fields and should + * probably be resolved into various helper functions. + */ + switch (format->format) { + case DRM_FORMAT_C8: + ast_state->std_table = &vbios_stdtable[VGAModeIndex]; break; - case 1280: - if (mode->vdisplay == 1024) flags = MODE_OK; + case DRM_FORMAT_RGB565: + ast_state->std_table = &vbios_stdtable[HiCModeIndex]; break; - case 1600: - if (mode->vdisplay == 1200) flags = MODE_OK; + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + ast_state->std_table = &vbios_stdtable[TrueCModeIndex]; break; default: - return flags; + return -EINVAL; } - return flags; -} + /* + * Find the VBIOS mode and adjust the DRM display mode accordingly + * if a full modeset is required. Otherwise keep the existing values. + */ + if (drm_atomic_crtc_needs_modeset(crtc_state)) { + vmode = ast_vbios_find_mode(ast, &crtc_state->mode); + if (!vmode) + return -EINVAL; + ast_state->vmode = vmode; + + if (vmode->flags & HBorder) + hborder = 8; + if (vmode->flags & VBorder) + vborder = 8; + + adjusted_mode->crtc_hdisplay = vmode->hde; + adjusted_mode->crtc_hblank_start = vmode->hde + hborder; + adjusted_mode->crtc_hblank_end = vmode->ht - hborder; + adjusted_mode->crtc_hsync_start = vmode->hde + hborder + vmode->hfp; + adjusted_mode->crtc_hsync_end = vmode->hde + hborder + vmode->hfp + vmode->hsync; + adjusted_mode->crtc_htotal = vmode->ht; + + adjusted_mode->crtc_vdisplay = vmode->vde; + adjusted_mode->crtc_vblank_start = vmode->vde + vborder; + adjusted_mode->crtc_vblank_end = vmode->vt - vborder; + adjusted_mode->crtc_vsync_start = vmode->vde + vborder + vmode->vfp; + adjusted_mode->crtc_vsync_end = vmode->vde + vborder + vmode->vfp + vmode->vsync; + adjusted_mode->crtc_vtotal = vmode->vt; + } -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_unregister(connector); - drm_connector_cleanup(connector); - kfree(connector); + return 0; } -static const struct drm_connector_helper_funcs ast_connector_helper_funcs = { - .mode_valid = ast_mode_valid, - .get_modes = ast_get_modes, - .best_encoder = ast_best_single_encoder, -}; - -static const struct drm_connector_funcs ast_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = ast_connector_destroy, -}; - -static int ast_connector_init(struct drm_device *dev) +static void +ast_crtc_helper_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct ast_connector *ast_connector; - struct drm_connector *connector; - struct drm_encoder *encoder; - - ast_connector = kzalloc(sizeof(struct ast_connector), GFP_KERNEL); - if (!ast_connector) - return -ENOMEM; - - connector = &ast_connector->base; - drm_connector_init(dev, connector, &ast_connector_funcs, DRM_MODE_CONNECTOR_VGA); - - drm_connector_helper_add(connector, &ast_connector_helper_funcs); - - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - - drm_connector_register(connector); - - connector->polled = DRM_CONNECTOR_POLL_CONNECT; - - encoder = list_first_entry(&dev->mode_config.encoder_list, struct drm_encoder, head); - drm_connector_attach_encoder(connector, encoder); - - ast_connector->i2c = ast_i2c_create(dev); - if (!ast_connector->i2c) - DRM_ERROR("failed to add ddc bus for connector\n"); - - return 0; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); + struct drm_device *dev = crtc->dev; + struct ast_device *ast = to_ast_device(dev); + struct ast_crtc_state *ast_crtc_state = to_ast_crtc_state(crtc_state); + + /* + * The gamma LUT has to be reloaded after changing the primary + * plane's color format. + */ + if (crtc_state->enable && crtc_state->color_mgmt_changed) { + if (crtc_state->gamma_lut) + ast_crtc_load_gamma(ast, + ast_crtc_state->format, + crtc_state->gamma_lut->data); + else + ast_crtc_fill_gamma(ast, ast_crtc_state->format); + } } -/* allocate cursor cache and pin at start of VRAM */ -static int ast_cursor_init(struct drm_device *dev) +static void ast_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct ast_private *ast = dev->dev_private; - int size; - int ret; - struct drm_gem_object *obj; - struct ast_bo *bo; - uint64_t gpu_addr; - - size = (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE) * AST_DEFAULT_HWC_NUM; + struct ast_device *ast = to_ast_device(crtc->dev); + u8 vgacr17 = 0x00; + u8 vgacrb6 = 0xff; - ret = ast_gem_create(dev, size, true, &obj); - if (ret) - return ret; - bo = gem_to_ast_bo(obj); - ret = ast_bo_reserve(bo, false); - if (unlikely(ret != 0)) - goto fail; - - ret = ast_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); - ast_bo_unreserve(bo); - if (ret) - goto fail; - - /* kmap the object */ - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &ast->cache_kmap); - if (ret) - goto fail; + vgacr17 |= AST_IO_VGACR17_SYNC_ENABLE; + vgacrb6 &= ~(AST_IO_VGACRB6_VSYNC_OFF | AST_IO_VGACRB6_HSYNC_OFF); - ast->cursor_cache = obj; - ast->cursor_cache_gpu_addr = gpu_addr; - DRM_DEBUG_KMS("pinned cursor cache at %llx\n", ast->cursor_cache_gpu_addr); - return 0; -fail: - return ret; + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x17, 0x7f, vgacr17); + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0xfc, vgacrb6); } -static void ast_cursor_fini(struct drm_device *dev) +static void ast_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct ast_private *ast = dev->dev_private; - ttm_bo_kunmap(&ast->cache_kmap); - drm_gem_object_put_unlocked(ast->cursor_cache); + struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); + struct ast_device *ast = to_ast_device(crtc->dev); + u8 vgacr17 = 0xff; + + vgacr17 &= ~AST_IO_VGACR17_SYNC_ENABLE; + ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x17, 0x7f, vgacr17); + + /* + * HW cursors require the underlying primary plane and CRTC to + * display a valid mode and image. This is not the case during + * full modeset operations. So we temporarily disable any active + * plane, including the HW cursor. Each plane's atomic_update() + * helper will re-enable it if necessary. + * + * We only do this during *full* modesets. It does not affect + * simple pageflips on the planes. + */ + drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); } -int ast_mode_init(struct drm_device *dev) -{ - ast_cursor_init(dev); - ast_crtc_init(dev); - ast_encoder_init(dev); - ast_connector_init(dev); - return 0; -} +static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = { + .mode_valid = ast_crtc_helper_mode_valid, + .mode_set_nofb = ast_crtc_helper_mode_set_nofb, + .atomic_check = ast_crtc_helper_atomic_check, + .atomic_flush = ast_crtc_helper_atomic_flush, + .atomic_enable = ast_crtc_helper_atomic_enable, + .atomic_disable = ast_crtc_helper_atomic_disable, +}; -void ast_mode_fini(struct drm_device *dev) +static void ast_crtc_reset(struct drm_crtc *crtc) { - ast_cursor_fini(dev); + 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 int get_clock(void *i2c_priv) +static struct drm_crtc_state * +ast_crtc_atomic_duplicate_state(struct drm_crtc *crtc) { - struct ast_i2c_chan *i2c = i2c_priv; - struct ast_private *ast = i2c->dev->dev_private; - uint32_t val, val2, count, pass; + struct ast_crtc_state *new_ast_state, *ast_state; + struct drm_device *dev = crtc->dev; - 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)); + if (drm_WARN_ON(dev, !crtc->state)) + return NULL; - return val & 1 ? 1 : 0; -} + 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); -static int get_data(void *i2c_priv) -{ - struct ast_i2c_chan *i2c = i2c_priv; - struct ast_private *ast = i2c->dev->dev_private; - uint32_t val, val2, count, pass; + ast_state = to_ast_crtc_state(crtc->state); - 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)); + new_ast_state->format = ast_state->format; + new_ast_state->std_table = ast_state->std_table; + new_ast_state->vmode = ast_state->vmode; - return val & 1 ? 1 : 0; + return &new_ast_state->base; } -static void set_clock(void *i2c_priv, int clock) +static void ast_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) { - struct ast_i2c_chan *i2c = i2c_priv; - struct ast_private *ast = i2c->dev->dev_private; - 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; - } -} + struct ast_crtc_state *ast_state = to_ast_crtc_state(state); -static void set_data(void *i2c_priv, int data) -{ - struct ast_i2c_chan *i2c = i2c_priv; - struct ast_private *ast = i2c->dev->dev_private; - 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; - } + __drm_atomic_helper_crtc_destroy_state(&ast_state->base); + kfree(ast_state); } -static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev) +static const struct drm_crtc_funcs ast_crtc_funcs = { + .reset = ast_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = ast_crtc_atomic_duplicate_state, + .atomic_destroy_state = ast_crtc_atomic_destroy_state, +}; + +static int ast_crtc_init(struct ast_device *ast) { - struct ast_i2c_chan *i2c; + struct drm_device *dev = &ast->base; + struct drm_crtc *crtc = &ast->crtc; int ret; - i2c = kzalloc(sizeof(struct ast_i2c_chan), GFP_KERNEL); - if (!i2c) - return NULL; + ret = drm_crtc_init_with_planes(dev, crtc, &ast->primary_plane.base, + &ast->cursor_plane.base.base, &ast_crtc_funcs, + NULL); + if (ret) + return ret; - 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_ERROR("Failed to register bit i2c\n"); - goto out_free; - } + drm_mode_crtc_set_gamma_size(crtc, AST_LUT_SIZE); + drm_crtc_enable_color_mgmt(crtc, 0, false, AST_LUT_SIZE); - return i2c; -out_free: - kfree(i2c); - return NULL; -} + drm_crtc_helper_add(crtc, &ast_crtc_helper_funcs); -static void ast_i2c_destroy(struct ast_i2c_chan *i2c) -{ - if (!i2c) - return; - i2c_del_adapter(&i2c->adapter); - kfree(i2c); + return 0; } -static void ast_show_cursor(struct drm_crtc *crtc) -{ - struct ast_private *ast = crtc->dev->dev_private; - u8 jreg; +/* + * Mode config + */ - jreg = 0x2; - /* enable ARGB cursor */ - jreg |= 1; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg); +static void ast_mode_config_helper_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct ast_device *ast = to_ast_device(state->dev); + + /* + * Concurrent operations could possibly trigger a call to + * drm_connector_helper_funcs.get_modes by reading the display + * modes. Protect access to registers by acquiring the modeset + * lock. + */ + mutex_lock(&ast->modeset_lock); + drm_atomic_helper_commit_tail(state); + mutex_unlock(&ast->modeset_lock); } -static void ast_hide_cursor(struct drm_crtc *crtc) +static const struct drm_mode_config_helper_funcs ast_mode_config_helper_funcs = { + .atomic_commit_tail = ast_mode_config_helper_atomic_commit_tail, +}; + +static enum drm_mode_status ast_mode_config_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) { - struct ast_private *ast = crtc->dev->dev_private; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00); + const struct drm_format_info *info = drm_format_info(DRM_FORMAT_XRGB8888); + struct ast_device *ast = to_ast_device(dev); + unsigned long max_fb_size = ast_fb_vram_size(ast); + u64 pitch; + + if (drm_WARN_ON_ONCE(dev, !info)) + return MODE_ERROR; /* driver bug */ + + pitch = drm_format_info_min_pitch(info, 0, mode->hdisplay); + if (!pitch) + return MODE_BAD_WIDTH; + if (pitch > AST_PRIMARY_PLANE_MAX_OFFSET) + return MODE_BAD_WIDTH; /* maximum programmable pitch */ + if (pitch > max_fb_size / mode->vdisplay) + return MODE_MEM; + + return MODE_OK; } -static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height) +static const struct drm_mode_config_funcs ast_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .mode_valid = ast_mode_config_mode_valid, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int ast_mode_config_init(struct ast_device *ast) { - 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 *srcxor, *dstxor; - 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; + struct drm_device *dev = &ast->base; + int ret; - } + ret = drmm_mutex_init(dev, &ast->modeset_lock); + if (ret) + return ret; - 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; + ret = drmm_mode_config_init(dev); + if (ret) + return ret; - dstxor += 2; - srcxor += 4; - } - dstxor += last_alpha_dst_delta; - } - return csum; -} + 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; -static int ast_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, - uint32_t height) -{ - struct ast_private *ast = crtc->dev->dev_private; - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - struct drm_gem_object *obj; - struct ast_bo *bo; - uint64_t gpu_addr; - u32 csum; - int ret; - struct ttm_bo_kmap_obj uobj_map; - u8 *src, *dst; - bool src_isiomem, dst_isiomem; - if (!handle) { - ast_hide_cursor(crtc); - return 0; + if (ast->support_fullhd) { + dev->mode_config.max_width = 1920; + dev->mode_config.max_height = 2048; + } else { + dev->mode_config.max_width = 1600; + dev->mode_config.max_height = 1200; } - if (width > AST_MAX_HWC_WIDTH || height > AST_MAX_HWC_HEIGHT) - return -EINVAL; - - obj = drm_gem_object_lookup(file_priv, handle); - if (!obj) { - DRM_ERROR("Cannot find cursor object %x for crtc\n", handle); - return -ENOENT; - } - bo = gem_to_ast_bo(obj); + dev->mode_config.helper_private = &ast_mode_config_helper_funcs; - ret = ast_bo_reserve(bo, false); + ret = ast_primary_plane_init(ast); if (ret) - goto fail; - - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map); - - src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem); - dst = ttm_kmap_obj_virtual(&ast->cache_kmap, &dst_isiomem); - - if (src_isiomem == true) - DRM_ERROR("src cursor bo should be in main memory\n"); - if (dst_isiomem == false) - DRM_ERROR("dst bo should be in VRAM\n"); - - dst += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor; - - /* do data transfer to cursor cache */ - csum = copy_cursor_image(src, dst, width, height); - - /* write checksum + signature */ - ttm_bo_kunmap(&uobj_map); - ast_bo_unreserve(bo); - { - u8 *dst = (u8 *)ast->cache_kmap.virtual + (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + 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); - - /* set pattern offset */ - gpu_addr = ast->cursor_cache_gpu_addr; - gpu_addr += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor; - gpu_addr >>= 3; - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, gpu_addr & 0xff); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, (gpu_addr >> 8) & 0xff); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, (gpu_addr >> 16) & 0xff); - } - ast_crtc->cursor_width = width; - ast_crtc->cursor_height = height; - ast_crtc->offset_x = AST_MAX_HWC_WIDTH - width; - ast_crtc->offset_y = AST_MAX_HWC_WIDTH - height; - - ast->next_cursor = (ast->next_cursor + 1) % AST_DEFAULT_HWC_NUM; + return ret; - ast_show_cursor(crtc); + ret = ast_cursor_plane_init(ast); + if (ret) + return ret; - drm_gem_object_put_unlocked(obj); - return 0; -fail: - drm_gem_object_put_unlocked(obj); - return ret; -} + ret = ast_crtc_init(ast); + if (ret) + return ret; -static int ast_cursor_move(struct drm_crtc *crtc, - int x, int y) -{ - struct ast_crtc *ast_crtc = to_ast_crtc(crtc); - struct ast_private *ast = crtc->dev->dev_private; - int x_offset, y_offset; - u8 *sig; - - sig = (u8 *)ast->cache_kmap.virtual + (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE; - writel(x, sig + AST_HWC_SIGNATURE_X); - writel(y, sig + AST_HWC_SIGNATURE_Y); - - x_offset = ast_crtc->offset_x; - y_offset = ast_crtc->offset_y; - if (x < 0) { - x_offset = (-x) + ast_crtc->offset_x; - x = 0; + switch (ast->tx_chip) { + case AST_TX_NONE: + ret = ast_vga_output_init(ast); + break; + case AST_TX_SIL164: + ret = ast_sil164_output_init(ast); + break; + case AST_TX_DP501: + ret = ast_dp501_output_init(ast); + break; + case AST_TX_ASTDP: + ret = ast_astdp_output_init(ast); + break; } + if (ret) + return ret; - if (y < 0) { - y_offset = (-y) + ast_crtc->offset_y; - y = 0; - } - 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, (x & 0xff)); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc5, ((x >> 8) & 0x0f)); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc6, (y & 0xff)); - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07)); - - /* dummy write to fire HWC */ - ast_show_cursor(crtc); + drm_mode_config_reset(dev); + drmm_kms_helper_poll_init(dev); return 0; } diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index f7d421359d56..b72914dbed38 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -26,427 +26,84 @@ * Authors: Dave Airlie <airlied@redhat.com> */ -#include <drm/drmP.h> -#include "ast_drv.h" - -#include "ast_dram_tables.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 = dev->dev_private; - - 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 = dev->dev_private; - - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04); -} - - -bool ast_is_vga_enabled(struct drm_device *dev) -{ - struct ast_private *ast = dev->dev_private; - u8 ch; - - if (ast->chip == AST1180) { - /* TODO 1180 */ - } else { - ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT); - return !!(ch & 0x01); - } - return false; -} - -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 = dev->dev_private; - 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); */ +#include <linux/delay.h> +#include <linux/pci.h> - /* 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); +#include <drm/drm_print.h> - /* 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); -} +#include "ast_drv.h" +#include "ast_post.h" -u32 ast_mindwm(struct ast_private *ast, u32 r) +u32 __ast_mindwm(void __iomem *regs, u32 r) { - uint32_t data; + u32 data; - ast_write32(ast, 0xf004, r & 0xffff0000); - ast_write32(ast, 0xf000, 0x1); + __ast_write32(regs, 0xf004, r & 0xffff0000); + __ast_write32(regs, 0xf000, 0x1); do { - data = ast_read32(ast, 0xf004) & 0xffff0000; + data = __ast_read32(regs, 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); + return __ast_read32(regs, 0x10000 + (r & 0x0000ffff)); } -/* - * 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) +void __ast_moutdwm(void __iomem *regs, u32 r, u32 v) { - 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; -} + u32 data; -#if 0 /* unused in DDX driver - here for completeness */ -static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen) -{ - u32 data, timeout; + __ast_write32(regs, 0xf004, r & 0xffff0000); + __ast_write32(regs, 0xf000, 0x1); - 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; + data = __ast_read32(regs, 0xf004) & 0xffff0000; + } while (data != (r & 0xffff0000)); - for (i = 0; i < 8; i++) - if (mmctestburst2_ast2150(ast, i)) - return 0; - return 1; + __ast_write32(regs, 0x10000 + (r & 0x0000ffff), v); } -static int cbrscan_ast2150(struct ast_private *ast, int busw) +u32 ast_mindwm(struct ast_device *ast, u32 r) { - 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; + return __ast_mindwm(ast->regs, r); } - -static void cbrdlli_ast2150(struct ast_private *ast, int busw) +void ast_moutdwm(struct ast_device *ast, u32 r, u32 v) { - 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)); + __ast_moutdwm(ast->regs, r, v); } - - -static void ast_init_dram_reg(struct drm_device *dev) +int ast_post_gpu(struct ast_device *ast) { - struct ast_private *ast = dev->dev_private; - 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 == 2200) - 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++; - } + int ret; - /* 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; - } + if (AST_GEN(ast) >= 7) { + ret = ast_2600_post(ast); + if (ret) + return ret; + } else if (AST_GEN(ast) >= 6) { + ret = ast_2500_post(ast); + if (ret) + return ret; + } else if (AST_GEN(ast) >= 4) { + ret = ast_2300_post(ast); + if (ret) + return ret; + } else if (AST_GEN(ast) >= 2) { + ret = ast_2100_post(ast); + if (ret) + return ret; + } else { + ret = ast_2000_post(ast); + if (ret) + return ret; } - /* 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) -{ - u32 reg; - struct ast_private *ast = dev->dev_private; - - pci_read_config_dword(ast->dev->pdev, 0x04, ®); - reg |= 0x3; - pci_write_config_dword(ast->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 */ - } + return 0; } -/* 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) +bool mmc_test(struct ast_device *ast, u32 datagen, u8 test_ctl) { u32 data, timeout; @@ -466,1616 +123,7 @@ static bool mmc_test(struct ast_private *ast, u32 datagen, u8 test_ctl) 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) +bool mmc_test_burst(struct ast_device *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 = dev->dev_private; - 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 = dev->dev_private; - 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_ERROR("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_post.h b/drivers/gpu/drm/ast/ast_post.h new file mode 100644 index 000000000000..aa5d247bebe8 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_post.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef AST_POST_H +#define AST_POST_H + +#include <linux/limits.h> +#include <linux/types.h> + +struct ast_device; + +/* DRAM timing tables */ +struct ast_dramstruct { + u16 index; + u32 data; +}; + +/* hardware fields */ +#define __AST_DRAMSTRUCT_DRAM_TYPE 0x0004 + +/* control commands */ +#define __AST_DRAMSTRUCT_UDELAY 0xff00 +#define __AST_DRAMSTRUCT_INVALID 0xffff + +#define __AST_DRAMSTRUCT_INDEX(_name) \ + (__AST_DRAMSTRUCT_ ## _name) + +#define AST_DRAMSTRUCT_INIT(_name, _value) \ + { __AST_DRAMSTRUCT_INDEX(_name), (_value) } + +#define AST_DRAMSTRUCT_UDELAY(_usecs) \ + AST_DRAMSTRUCT_INIT(UDELAY, _usecs) +#define AST_DRAMSTRUCT_INVALID \ + AST_DRAMSTRUCT_INIT(INVALID, U32_MAX) + +#define AST_DRAMSTRUCT_IS(_entry, _name) \ + ((_entry)->index == __AST_DRAMSTRUCT_INDEX(_name)) + +u32 __ast_mindwm(void __iomem *regs, u32 r); +void __ast_moutdwm(void __iomem *regs, u32 r, u32 v); + +bool mmc_test(struct ast_device *ast, u32 datagen, u8 test_ctl); +bool mmc_test_burst(struct ast_device *ast, u32 datagen); + +/* ast_2000.c */ +void ast_2000_set_def_ext_reg(struct ast_device *ast); + +/* ast_2300.c */ +void ast_2300_set_def_ext_reg(struct ast_device *ast); + +#endif diff --git a/drivers/gpu/drm/ast/ast_reg.h b/drivers/gpu/drm/ast/ast_reg.h new file mode 100644 index 000000000000..30578e3b07e4 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_reg.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef __AST_REG_H__ +#define __AST_REG_H__ + +#include <linux/bits.h> + +/* + * Modesetting + */ + +#define AST_IO_MM_OFFSET (0x380) +#define AST_IO_MM_LENGTH (128) + +#define AST_IO_VGAARI_W (0x40) + +#define AST_IO_VGAMR_W (0x42) +#define AST_IO_VGAMR_R (0x4c) +#define AST_IO_VGAMR_IOSEL BIT(0) + +#define AST_IO_VGAER (0x43) +#define AST_IO_VGAER_VGA_ENABLE BIT(0) + +#define AST_IO_VGASRI (0x44) +#define AST_IO_VGASR1_SD BIT(5) +#define AST_IO_VGADRR (0x47) +#define AST_IO_VGADWR (0x48) +#define AST_IO_VGAPDR (0x49) +#define AST_IO_VGAGRI (0x4E) + +#define AST_IO_VGACRI (0x54) +#define AST_IO_VGACR17_SYNC_ENABLE BIT(7) /* called "Hardware reset" in docs */ +#define AST_IO_VGACR80_PASSWORD (0xa8) +#define AST_IO_VGACR99_VGAMEM_RSRV_MASK GENMASK(1, 0) +#define AST_IO_VGACRA1_VGAIO_DISABLED BIT(1) +#define AST_IO_VGACRA1_MMIO_ENABLED BIT(2) +#define AST_IO_VGACRA3_DVO_ENABLED BIT(7) +#define AST_IO_VGACRAA_VGAMEM_SIZE_MASK GENMASK(1, 0) +#define AST_IO_VGACRB6_HSYNC_OFF BIT(0) +#define AST_IO_VGACRB6_VSYNC_OFF BIT(1) +#define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */ +#define AST_IO_VGACRCB_HWC_ENABLED BIT(1) + +/* mirrors SCU100[7:0] */ +#define AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK GENMASK(7, 6) +#define AST_IO_VGACRD0_VRAM_INIT_BY_BMC BIT(7) +#define AST_IO_VGACRD0_VRAM_INIT_READY BIT(6) +#define AST_IO_VGACRD0_IKVM_WIDESCREEN BIT(0) + +#define AST_IO_VGACRD1_MCU_FW_EXECUTING BIT(5) +/* Display Transmitter Type */ +#define AST_IO_VGACRD1_TX_TYPE_MASK GENMASK(3, 1) +#define AST_IO_VGACRD1_NO_TX 0x00 +#define AST_IO_VGACRD1_TX_ITE66121_VBIOS 0x02 +#define AST_IO_VGACRD1_TX_SIL164_VBIOS 0x04 +#define AST_IO_VGACRD1_TX_CH7003_VBIOS 0x06 +#define AST_IO_VGACRD1_TX_DP501_VBIOS 0x08 +#define AST_IO_VGACRD1_TX_ANX9807_VBIOS 0x0a +#define AST_IO_VGACRD1_TX_FW_EMBEDDED_FW 0x0c /* special case of DP501 */ +#define AST_IO_VGACRD1_TX_ASTDP 0x0e +#define AST_IO_VGACRD1_SUPPORTS_WUXGA BIT(0) + +/* + * AST DisplayPort + */ +#define AST_IO_VGACRD7_EDID_VALID_FLAG BIT(0) +#define AST_IO_VGACRDC_LINK_SUCCESS BIT(0) +#define AST_IO_VGACRDF_HPD BIT(0) +#define AST_IO_VGACRDF_DP_VIDEO_ENABLE BIT(4) /* mirrors AST_IO_VGACRE3_DP_VIDEO_ENABLE */ +#define AST_IO_VGACRE0_24BPP BIT(5) /* 18 bpp, if unset */ +#define AST_IO_VGACRE3_DP_VIDEO_ENABLE BIT(0) +#define AST_IO_VGACRE3_DP_PHY_SLEEP BIT(4) +#define AST_IO_VGACRE5_EDID_READ_DONE BIT(0) + +#define AST_IO_VGAIR1_R (0x5A) +#define AST_IO_VGAIR1_VREFRESH BIT(3) + +#endif diff --git a/drivers/gpu/drm/ast/ast_sil164.c b/drivers/gpu/drm/ast/ast_sil164.c new file mode 100644 index 000000000000..be01254dd48a --- /dev/null +++ b/drivers/gpu/drm/ast/ast_sil164.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include "ast_ddc.h" +#include "ast_drv.h" + +/* + * Encoder + */ + +static const struct drm_encoder_funcs ast_sil164_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +/* + * Connector + */ + +static int ast_sil164_connector_helper_get_modes(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + int count; + + if (ast_connector->physical_status == connector_status_connected) { + count = drm_connector_helper_get_modes(connector); + } else { + drm_edid_connector_update(connector, NULL); + + /* + * There's no EDID data without a connected monitor. Set BMC- + * compatible modes in this case. The XGA default resolution + * should work well for all BMCs. + */ + count = drm_add_modes_noedid(connector, 4096, 4096); + if (count) + drm_set_preferred_mode(connector, 1024, 768); + } + + return count; +} + +static int ast_sil164_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + enum drm_connector_status status; + + status = drm_connector_helper_detect_from_ddc(connector, ctx, force); + + if (status != ast_connector->physical_status) + ++connector->epoch_counter; + ast_connector->physical_status = status; + + return connector_status_connected; +} + +static const struct drm_connector_helper_funcs ast_sil164_connector_helper_funcs = { + .get_modes = ast_sil164_connector_helper_get_modes, + .detect_ctx = ast_sil164_connector_helper_detect_ctx, +}; + +static const struct drm_connector_funcs ast_sil164_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/* + * Output + */ + +int ast_sil164_output_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct drm_crtc *crtc = &ast->crtc; + struct i2c_adapter *ddc; + struct drm_encoder *encoder; + struct ast_connector *ast_connector; + struct drm_connector *connector; + int ret; + + /* DDC */ + + ddc = ast_ddc_create(ast); + if (IS_ERR(ddc)) + return PTR_ERR(ddc); + + /* encoder */ + + encoder = &ast->output.sil164.encoder; + ret = drm_encoder_init(dev, encoder, &ast_sil164_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + return ret; + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* connector */ + + ast_connector = &ast->output.sil164.connector; + connector = &ast_connector->base; + ret = drm_connector_init_with_ddc(dev, connector, &ast_sil164_connector_funcs, + DRM_MODE_CONNECTOR_DVII, ddc); + if (ret) + return ret; + drm_connector_helper_add(connector, &ast_sil164_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + + ast_connector->physical_status = connector->status; + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h index d665dd5af5dd..7da5b5c60f41 100644 --- a/drivers/gpu/drm/ast/ast_tables.h +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -24,6 +24,8 @@ #ifndef AST_TABLES_H #define AST_TABLES_H +#include "ast_drv.h" + /* Std. Table Index Definition */ #define TextModeIndex 0 #define EGAModeIndex 1 @@ -31,114 +33,6 @@ #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 */ { @@ -212,134 +106,4 @@ static const struct ast_vbios_stdtable vbios_stdtable[] = { }, }; -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 diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c deleted file mode 100644 index c168d62fe8f9..000000000000 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * 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 <drm/drmP.h> -#include <drm/ttm/ttm_page_alloc.h> - -#include "ast_drv.h" - -static inline struct ast_private * -ast_bdev(struct ttm_bo_device *bd) -{ - return container_of(bd, struct ast_private, ttm.bdev); -} - -static void ast_bo_ttm_destroy(struct ttm_buffer_object *tbo) -{ - struct ast_bo *bo; - - bo = container_of(tbo, struct ast_bo, bo); - - drm_gem_object_release(&bo->gem); - kfree(bo); -} - -static bool ast_ttm_bo_is_ast_bo(struct ttm_buffer_object *bo) -{ - if (bo->destroy == &ast_bo_ttm_destroy) - return true; - return false; -} - -static int -ast_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, - struct ttm_mem_type_manager *man) -{ - switch (type) { - case TTM_PL_SYSTEM: - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; - man->available_caching = TTM_PL_MASK_CACHING; - man->default_caching = TTM_PL_FLAG_CACHED; - break; - case TTM_PL_VRAM: - man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; - man->available_caching = TTM_PL_FLAG_UNCACHED | - TTM_PL_FLAG_WC; - man->default_caching = TTM_PL_FLAG_WC; - break; - default: - DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); - return -EINVAL; - } - return 0; -} - -static void -ast_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) -{ - struct ast_bo *astbo = ast_bo(bo); - - if (!ast_ttm_bo_is_ast_bo(bo)) - return; - - ast_ttm_placement(astbo, TTM_PL_FLAG_SYSTEM); - *pl = astbo->placement; -} - -static int ast_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) -{ - struct ast_bo *astbo = ast_bo(bo); - - return drm_vma_node_verify_access(&astbo->gem.vma_node, - filp->private_data); -} - -static int ast_ttm_io_mem_reserve(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) -{ - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - struct ast_private *ast = ast_bdev(bdev); - - mem->bus.addr = NULL; - mem->bus.offset = 0; - mem->bus.size = mem->num_pages << PAGE_SHIFT; - mem->bus.base = 0; - mem->bus.is_iomem = false; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; - switch (mem->mem_type) { - case TTM_PL_SYSTEM: - /* system memory */ - return 0; - case TTM_PL_VRAM: - mem->bus.offset = mem->start << PAGE_SHIFT; - mem->bus.base = pci_resource_start(ast->dev->pdev, 0); - mem->bus.is_iomem = true; - break; - default: - return -EINVAL; - break; - } - return 0; -} - -static void ast_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - -static void ast_ttm_backend_destroy(struct ttm_tt *tt) -{ - ttm_tt_fini(tt); - kfree(tt); -} - -static struct ttm_backend_func ast_tt_backend_func = { - .destroy = &ast_ttm_backend_destroy, -}; - - -static struct ttm_tt *ast_ttm_tt_create(struct ttm_buffer_object *bo, - uint32_t page_flags) -{ - struct ttm_tt *tt; - - tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL); - if (tt == NULL) - return NULL; - tt->func = &ast_tt_backend_func; - if (ttm_tt_init(tt, bo, page_flags)) { - kfree(tt); - return NULL; - } - return tt; -} - -struct ttm_bo_driver ast_bo_driver = { - .ttm_tt_create = ast_ttm_tt_create, - .init_mem_type = ast_bo_init_mem_type, - .eviction_valuable = ttm_bo_eviction_valuable, - .evict_flags = ast_bo_evict_flags, - .move = NULL, - .verify_access = ast_bo_verify_access, - .io_mem_reserve = &ast_ttm_io_mem_reserve, - .io_mem_free = &ast_ttm_io_mem_free, -}; - -int ast_mm_init(struct ast_private *ast) -{ - int ret; - struct drm_device *dev = ast->dev; - struct ttm_bo_device *bdev = &ast->ttm.bdev; - - ret = ttm_bo_device_init(&ast->ttm.bdev, - &ast_bo_driver, - dev->anon_inode->i_mapping, - DRM_FILE_PAGE_OFFSET, - true); - if (ret) { - DRM_ERROR("Error initialising bo driver; %d\n", ret); - return ret; - } - - ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, - ast->vram_size >> PAGE_SHIFT); - if (ret) { - DRM_ERROR("Failed ttm VRAM init: %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 0; -} - -void ast_mm_fini(struct ast_private *ast) -{ - struct drm_device *dev = ast->dev; - - ttm_bo_device_release(&ast->ttm.bdev); - - arch_phys_wc_del(ast->fb_mtrr); - arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0)); -} - -void ast_ttm_placement(struct ast_bo *bo, int domain) -{ - u32 c = 0; - unsigned i; - - bo->placement.placement = bo->placements; - bo->placement.busy_placement = bo->placements; - if (domain & TTM_PL_FLAG_VRAM) - bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; - if (domain & TTM_PL_FLAG_SYSTEM) - bo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; - if (!c) - bo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM; - bo->placement.num_placement = c; - bo->placement.num_busy_placement = c; - for (i = 0; i < c; ++i) { - bo->placements[i].fpfn = 0; - bo->placements[i].lpfn = 0; - } -} - -int ast_bo_create(struct drm_device *dev, int size, int align, - uint32_t flags, struct ast_bo **pastbo) -{ - struct ast_private *ast = dev->dev_private; - struct ast_bo *astbo; - size_t acc_size; - int ret; - - astbo = kzalloc(sizeof(struct ast_bo), GFP_KERNEL); - if (!astbo) - return -ENOMEM; - - ret = drm_gem_object_init(dev, &astbo->gem, size); - if (ret) - goto error; - - astbo->bo.bdev = &ast->ttm.bdev; - - ast_ttm_placement(astbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); - - acc_size = ttm_bo_dma_acc_size(&ast->ttm.bdev, size, - sizeof(struct ast_bo)); - - ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size, - ttm_bo_type_device, &astbo->placement, - align >> PAGE_SHIFT, false, acc_size, - NULL, NULL, ast_bo_ttm_destroy); - if (ret) - goto error; - - *pastbo = astbo; - return 0; -error: - kfree(astbo); - return ret; -} - -static inline u64 ast_bo_gpu_offset(struct ast_bo *bo) -{ - return bo->bo.offset; -} - -int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr) -{ - struct ttm_operation_ctx ctx = { false, false }; - int i, ret; - - if (bo->pin_count) { - bo->pin_count++; - if (gpu_addr) - *gpu_addr = ast_bo_gpu_offset(bo); - } - - ast_ttm_placement(bo, pl_flag); - for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; - ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx); - if (ret) - return ret; - - bo->pin_count = 1; - if (gpu_addr) - *gpu_addr = ast_bo_gpu_offset(bo); - return 0; -} - -int ast_bo_unpin(struct ast_bo *bo) -{ - struct ttm_operation_ctx ctx = { false, false }; - int i; - if (!bo->pin_count) { - DRM_ERROR("unpin bad %p\n", bo); - return 0; - } - bo->pin_count--; - if (bo->pin_count) - return 0; - - for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; - return ttm_bo_validate(&bo->bo, &bo->placement, &ctx); -} - -int ast_bo_push_sysram(struct ast_bo *bo) -{ - struct ttm_operation_ctx ctx = { false, false }; - int i, ret; - if (!bo->pin_count) { - DRM_ERROR("unpin bad %p\n", bo); - return 0; - } - bo->pin_count--; - if (bo->pin_count) - return 0; - - if (bo->kmap.virtual) - ttm_bo_kunmap(&bo->kmap); - - ast_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); - for (i = 0; i < bo->placement.num_placement ; i++) - bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; - - ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx); - if (ret) { - DRM_ERROR("pushing to VRAM failed\n"); - return ret; - } - return 0; -} - -int ast_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct drm_file *file_priv; - struct ast_private *ast; - - if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) - return -EINVAL; - - file_priv = filp->private_data; - ast = file_priv->minor->dev->dev_private; - return ttm_bo_mmap(filp, vma, &ast->ttm.bdev); -} diff --git a/drivers/gpu/drm/ast/ast_vbios.c b/drivers/gpu/drm/ast/ast_vbios.c new file mode 100644 index 000000000000..0953e6dd3976 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_vbios.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2005 ASPEED Technology Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the authors not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. The authors makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ast_drv.h" +#include "ast_vbios.h" + +/* 4:3 */ + +static const struct ast_vbios_enhtable res_640x480[] = { + { 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60 Hz */ + (SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2e }, + { 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72 Hz */ + (SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2e }, + { 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75 Hz */ + (SyncNN | Charx8Dot), 75, 3, 0x2e }, + { 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85 Hz */ + (SyncNN | Charx8Dot), 85, 4, 0x2e }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_800x600[] = { + { 1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56 Hz */ + (SyncPP | Charx8Dot), 56, 1, 0x30 }, + { 1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60 Hz */ + (SyncPP | Charx8Dot), 60, 2, 0x30 }, + { 1040, 800, 56, 120, 666, 600, 37, 6, VCLK50, /* 72 Hz */ + (SyncPP | Charx8Dot), 72, 3, 0x30 }, + { 1056, 800, 16, 80, 625, 600, 1, 3, VCLK49_5, /* 75 Hz */ + (SyncPP | Charx8Dot), 75, 4, 0x30 }, + { 1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* 85 Hz */ + (SyncPP | Charx8Dot), 84, 5, 0x30 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1024x768[] = { + { 1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60 Hz */ + (SyncNN | Charx8Dot), 60, 1, 0x31 }, + { 1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70 Hz */ + (SyncNN | Charx8Dot), 70, 2, 0x31 }, + { 1312, 1024, 16, 96, 800, 768, 1, 3, VCLK78_75, /* 75 Hz */ + (SyncPP | Charx8Dot), 75, 3, 0x31 }, + { 1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* 85 Hz */ + (SyncPP | Charx8Dot), 84, 4, 0x31 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1152x864[] = { + { 1600, 1152, 64, 128, 900, 864, 1, 3, VCLK108, /* 75 Hz */ + (SyncPP | Charx8Dot | NewModeInfo), 75, 1, 0x3b }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1280x1024[] = { + { 1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60 Hz */ + (SyncPP | Charx8Dot), 60, 1, 0x32 }, + { 1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75 Hz */ + (SyncPP | Charx8Dot), 75, 2, 0x32 }, + { 1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* 85 Hz */ + (SyncPP | Charx8Dot), 85, 3, 0x32 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1600x1200[] = { + { 2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60 Hz */ + (SyncPP | Charx8Dot), 60, 1, 0x33 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +/* 16:9 */ + +static const struct ast_vbios_enhtable res_1360x768[] = { + { 1792, 1360, 64, 112, 795, 768, 3, 6, VCLK85_5, /* 60 Hz */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1600x900[] = { + { 1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60 Hz CVT RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x3a }, + { 2112, 1600, 88, 168, 934, 900, 3, 5, VCLK118_25, /* 60 Hz CVT */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3a }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1920x1080[] = { + { 2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60 Hz */ + (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x38 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +/* 16:10 */ + +static const struct ast_vbios_enhtable res_1280x800[] = { + { 1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60 Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x35 }, + { 1680, 1280, 72, 128, 831, 800, 3, 6, VCLK83_5, /* 60 Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1440x900[] = { + { 1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60 Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x36 }, + { 1904, 1440, 80, 152, 934, 900, 3, 6, VCLK106_5, /* 60 Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1680x1050[] = { + { 1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60 Hz RB */ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x37 }, + { 2240, 1680, 104, 176, 1089, 1050, 3, 6, VCLK146_25, /* 60 Hz */ + (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +static const struct ast_vbios_enhtable res_1920x1200[] = { + { 2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60 Hz RB*/ + (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo | + AST2500PreCatchCRT), 60, 1, 0x34 }, + AST_VBIOS_INVALID_MODE, /* end */ +}; + +/* + * VBIOS mode tables + */ + +static const struct ast_vbios_enhtable *res_table_wuxga[] = { + &res_1920x1200[0], + NULL, +}; + +static const struct ast_vbios_enhtable *res_table_fullhd[] = { + &res_1920x1080[0], + NULL, +}; + +static const struct ast_vbios_enhtable *res_table_wsxga_p[] = { + &res_1280x800[0], + &res_1360x768[0], + &res_1440x900[0], + &res_1600x900[0], + &res_1680x1050[0], + NULL, +}; + +static const struct ast_vbios_enhtable *res_table[] = { + &res_640x480[0], + &res_800x600[0], + &res_1024x768[0], + &res_1152x864[0], + &res_1280x1024[0], + &res_1600x1200[0], + NULL, +}; + +static const struct ast_vbios_enhtable * +__ast_vbios_find_mode_table(const struct ast_vbios_enhtable **vmode_tables, + unsigned int hdisplay, + unsigned int vdisplay) +{ + while (*vmode_tables) { + if ((*vmode_tables)->hde == hdisplay && (*vmode_tables)->vde == vdisplay) + return *vmode_tables; + ++vmode_tables; + } + + return NULL; +} + +static const struct ast_vbios_enhtable *ast_vbios_find_mode_table(const struct ast_device *ast, + unsigned int hdisplay, + unsigned int vdisplay) +{ + const struct ast_vbios_enhtable *vmode_table = NULL; + + if (ast->support_wuxga) + vmode_table = __ast_vbios_find_mode_table(res_table_wuxga, hdisplay, vdisplay); + if (!vmode_table && ast->support_fullhd) + vmode_table = __ast_vbios_find_mode_table(res_table_fullhd, hdisplay, vdisplay); + if (!vmode_table && ast->support_wsxga_p) + vmode_table = __ast_vbios_find_mode_table(res_table_wsxga_p, hdisplay, vdisplay); + if (!vmode_table) + vmode_table = __ast_vbios_find_mode_table(res_table, hdisplay, vdisplay); + + return vmode_table; +} + +const struct ast_vbios_enhtable *ast_vbios_find_mode(const struct ast_device *ast, + const struct drm_display_mode *mode) +{ + const struct ast_vbios_enhtable *best_vmode = NULL; + const struct ast_vbios_enhtable *vmode_table; + const struct ast_vbios_enhtable *vmode; + u32 refresh_rate; + + vmode_table = ast_vbios_find_mode_table(ast, mode->hdisplay, mode->vdisplay); + if (!vmode_table) + return NULL; + + refresh_rate = drm_mode_vrefresh(mode); + + for (vmode = vmode_table; ast_vbios_mode_is_valid(vmode); ++vmode) { + if (((mode->flags & DRM_MODE_FLAG_NVSYNC) && (vmode->flags & PVSync)) || + ((mode->flags & DRM_MODE_FLAG_PVSYNC) && (vmode->flags & NVSync)) || + ((mode->flags & DRM_MODE_FLAG_NHSYNC) && (vmode->flags & PHSync)) || + ((mode->flags & DRM_MODE_FLAG_PHSYNC) && (vmode->flags & NHSync))) { + continue; + } + if (vmode->refresh_rate <= refresh_rate && + (!best_vmode || vmode->refresh_rate > best_vmode->refresh_rate)) + best_vmode = vmode; + } + + return best_vmode; +} diff --git a/drivers/gpu/drm/ast/ast_vbios.h b/drivers/gpu/drm/ast/ast_vbios.h new file mode 100644 index 000000000000..8cf025010594 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_vbios.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2005 ASPEED Technology Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the authors not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. The authors makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* Ported from xf86-video-ast driver */ + +#ifndef AST_VBIOS_H +#define AST_VBIOS_H + +#include <linux/types.h> + +struct ast_device; +struct drm_display_mode; + +#define Charx8Dot 0x00000001 +#define HalfDCLK 0x00000002 +#define DoubleScanMode 0x00000004 +#define LineCompareOff 0x00000008 +#define HBorder 0x00000020 +#define VBorder 0x00000010 +#define WideScreenMode 0x00000100 +#define NewModeInfo 0x00000200 +#define NHSync 0x00000400 +#define PHSync 0x00000800 +#define NVSync 0x00001000 +#define PVSync 0x00002000 +#define SyncPP (PVSync | PHSync) +#define SyncPN (PVSync | NHSync) +#define SyncNP (NVSync | PHSync) +#define SyncNN (NVSync | NHSync) +#define AST2500PreCatchCRT 0x00004000 + +/* DCLK Index */ +#define VCLK25_175 0x00 +#define VCLK28_322 0x01 +#define VCLK31_5 0x02 +#define VCLK36 0x03 +#define VCLK40 0x04 +#define VCLK49_5 0x05 +#define VCLK50 0x06 +#define VCLK56_25 0x07 +#define VCLK65 0x08 +#define VCLK75 0x09 +#define VCLK78_75 0x0a +#define VCLK94_5 0x0b +#define VCLK108 0x0c +#define VCLK135 0x0d +#define VCLK157_5 0x0e +#define VCLK162 0x0f +/* #define VCLK193_25 0x10 */ +#define VCLK154 0x10 +#define VCLK83_5 0x11 +#define VCLK106_5 0x12 +#define VCLK146_25 0x13 +#define VCLK148_5 0x14 +#define VCLK71 0x15 +#define VCLK88_75 0x16 +#define VCLK119 0x17 +#define VCLK85_5 0x18 +#define VCLK97_75 0x19 +#define VCLK118_25 0x1a + +struct ast_vbios_enhtable { + u32 ht; + u32 hde; + u32 hfp; + u32 hsync; + u32 vt; + u32 vde; + u32 vfp; + u32 vsync; + u32 dclk_index; + u32 flags; + u32 refresh_rate; + u32 refresh_rate_index; + u32 mode_id; +}; + +#define AST_VBIOS_INVALID_MODE \ + {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u} + +static inline bool ast_vbios_mode_is_valid(const struct ast_vbios_enhtable *vmode) +{ + return vmode->ht && vmode->vt && vmode->refresh_rate; +} + +const struct ast_vbios_enhtable *ast_vbios_find_mode(const struct ast_device *ast, + const struct drm_display_mode *mode); + +#endif diff --git a/drivers/gpu/drm/ast/ast_vga.c b/drivers/gpu/drm/ast/ast_vga.c new file mode 100644 index 000000000000..abe0fff8485c --- /dev/null +++ b/drivers/gpu/drm/ast/ast_vga.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include "ast_ddc.h" +#include "ast_drv.h" + +/* + * Encoder + */ + +static const struct drm_encoder_funcs ast_vga_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +/* + * Connector + */ + +static int ast_vga_connector_helper_get_modes(struct drm_connector *connector) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + int count; + + if (ast_connector->physical_status == connector_status_connected) { + count = drm_connector_helper_get_modes(connector); + } else { + drm_edid_connector_update(connector, NULL); + + /* + * There's no EDID data without a connected monitor. Set BMC- + * compatible modes in this case. The XGA default resolution + * should work well for all BMCs. + */ + count = drm_add_modes_noedid(connector, 4096, 4096); + if (count) + drm_set_preferred_mode(connector, 1024, 768); + } + + return count; +} + +static int ast_vga_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_connector *ast_connector = to_ast_connector(connector); + enum drm_connector_status status; + + status = drm_connector_helper_detect_from_ddc(connector, ctx, force); + + if (status != ast_connector->physical_status) + ++connector->epoch_counter; + ast_connector->physical_status = status; + + return connector_status_connected; +} + +static const struct drm_connector_helper_funcs ast_vga_connector_helper_funcs = { + .get_modes = ast_vga_connector_helper_get_modes, + .detect_ctx = ast_vga_connector_helper_detect_ctx, +}; + +static const struct drm_connector_funcs ast_vga_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/* + * Output + */ + +int ast_vga_output_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct drm_crtc *crtc = &ast->crtc; + struct i2c_adapter *ddc; + struct drm_encoder *encoder; + struct ast_connector *ast_connector; + struct drm_connector *connector; + int ret; + + /* DDC */ + + ddc = ast_ddc_create(ast); + if (IS_ERR(ddc)) + return PTR_ERR(ddc); + + /* encoder */ + + encoder = &ast->output.vga.encoder; + ret = drm_encoder_init(dev, encoder, &ast_vga_encoder_funcs, + DRM_MODE_ENCODER_DAC, NULL); + if (ret) + return ret; + encoder->possible_crtcs = drm_crtc_mask(crtc); + + /* connector */ + + ast_connector = &ast->output.vga.connector; + connector = &ast_connector->base; + ret = drm_connector_init_with_ddc(dev, connector, &ast_vga_connector_funcs, + DRM_MODE_CONNECTOR_VGA, ddc); + if (ret) + return ret; + drm_connector_helper_add(connector, &ast_vga_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + + ast_connector->physical_status = connector->status; + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; +} |
