Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+16
View File
@@ -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.
+26
View File
@@ -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
+252
View File
@@ -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;
}
+482
View File
@@ -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;
}
@@ -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;
}
File diff suppressed because it is too large Load Diff
+100
View File
@@ -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;
}
+678
View File
@@ -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;
}
+116
View File
@@ -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;
}
@@ -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;
}
+187
View File
@@ -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;
}
+11
View File
@@ -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
+576
View File
@@ -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;
}
+601
View File
@@ -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;
}
+616
View File
@@ -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");
+566
View File
@@ -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
+97
View File
@@ -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;
}
File diff suppressed because it is too large Load Diff
@@ -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);
}
@@ -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
+265
View File
@@ -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
@@ -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;
}
@@ -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
+241
View File
@@ -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;
}
+108
View File
@@ -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
+126
View File
@@ -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;
}