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