Commit 312fec14 authored by Dave Airlie's avatar Dave Airlie

drm: Initial KMS driver for AST (ASpeed Technologies) 2000 series (v2)

This is the initial driver for the Aspeed Technologies chips found in
servers. This driver supports the AST 2000, 2100, 2200, 2150 and 2300. It
doesn't support the AST11xx due to lack of hw to test it on, and them requiring
different codepaths.

This driver is intended to be used with xf86-video-modesetting in userspace.

This driver has a slightly different design than other KMS drivers, but
future server chips will probably share similiar setup. As these GPUs commonly
have low video RAM, it doesn't make sense to put the kms console in VRAM
always. This driver places the kms console into system RAM, and does dirty
updates to a copy in video RAM. When userspace sets a new scanout buffer,
it forcefully evicts the video RAM console, and X can create a framebuffer
that can use all of of video RAM.

This driver uses TTM but in a very simple fashion to control the eviction
to system RAM of the console, and multiple servers.

v2: add s/r support, fix Kconfig.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent db2e034d
......@@ -186,3 +186,6 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"
source "drivers/gpu/drm/udl/Kconfig"
source "drivers/gpu/drm/ast/Kconfig"
......@@ -42,4 +42,5 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/
obj-y += i2c/
config DRM_AST
tristate "AST server chips"
depends on DRM && PCI && EXPERIMENTAL
select DRM_TTM
select FB_SYS_COPYAREA
select FB_SYS_FILLRECT
select FB_SYS_IMAGEBLIT
select DRM_KMS_HELPER
help
Say yes for experimental AST GPU driver. Do not enable
this driver without having a working -modesetting,
and a version of AST that knows to fail if KMS
is bound to the driver. These GPUs are commonly found
in server chipsets.
#
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm
ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o
obj-$(CONFIG_DRM_AST) := ast.o
\ No newline at end of file
#ifndef AST_DRAM_TABLES_H
#define AST_DRAM_TABLES_H
/* DRAM timing tables */
struct ast_dramstruct {
u16 index;
u32 data;
};
static const struct ast_dramstruct ast2000_dram_table_data[] = {
{ 0x0108, 0x00000000 },
{ 0x0120, 0x00004a21 },
{ 0xFF00, 0x00000043 },
{ 0x0000, 0xFFFFFFFF },
{ 0x0004, 0x00000089 },
{ 0x0008, 0x22331353 },
{ 0x000C, 0x0d07000b },
{ 0x0010, 0x11113333 },
{ 0x0020, 0x00110350 },
{ 0x0028, 0x1e0828f0 },
{ 0x0024, 0x00000001 },
{ 0x001C, 0x00000000 },
{ 0x0014, 0x00000003 },
{ 0xFF00, 0x00000043 },
{ 0x0018, 0x00000131 },
{ 0x0014, 0x00000001 },
{ 0xFF00, 0x00000043 },
{ 0x0018, 0x00000031 },
{ 0x0014, 0x00000001 },
{ 0xFF00, 0x00000043 },
{ 0x0028, 0x1e0828f1 },
{ 0x0024, 0x00000003 },
{ 0x002C, 0x1f0f28fb },
{ 0x0030, 0xFFFFFE01 },
{ 0xFFFF, 0xFFFFFFFF }
};
static const struct ast_dramstruct ast1100_dram_table_data[] = {
{ 0x2000, 0x1688a8a8 },
{ 0x2020, 0x000041f0 },
{ 0xFF00, 0x00000043 },
{ 0x0000, 0xfc600309 },
{ 0x006C, 0x00909090 },
{ 0x0064, 0x00050000 },
{ 0x0004, 0x00000585 },
{ 0x0008, 0x0011030f },
{ 0x0010, 0x22201724 },
{ 0x0018, 0x1e29011a },
{ 0x0020, 0x00c82222 },
{ 0x0014, 0x01001523 },
{ 0x001C, 0x1024010d },
{ 0x0024, 0x00cb2522 },
{ 0x0038, 0xffffff82 },
{ 0x003C, 0x00000000 },
{ 0x0040, 0x00000000 },
{ 0x0044, 0x00000000 },
{ 0x0048, 0x00000000 },
{ 0x004C, 0x00000000 },
{ 0x0050, 0x00000000 },
{ 0x0054, 0x00000000 },
{ 0x0058, 0x00000000 },
{ 0x005C, 0x00000000 },
{ 0x0060, 0x032aa02a },
{ 0x0064, 0x002d3000 },
{ 0x0068, 0x00000000 },
{ 0x0070, 0x00000000 },
{ 0x0074, 0x00000000 },
{ 0x0078, 0x00000000 },
{ 0x007C, 0x00000000 },
{ 0x0034, 0x00000001 },
{ 0xFF00, 0x00000043 },
{ 0x002C, 0x00000732 },
{ 0x0030, 0x00000040 },
{ 0x0028, 0x00000005 },
{ 0x0028, 0x00000007 },
{ 0x0028, 0x00000003 },
{ 0x0028, 0x00000001 },
{ 0x000C, 0x00005a08 },
{ 0x002C, 0x00000632 },
{ 0x0028, 0x00000001 },
{ 0x0030, 0x000003c0 },
{ 0x0028, 0x00000003 },
{ 0x0030, 0x00000040 },
{ 0x0028, 0x00000003 },
{ 0x000C, 0x00005a21 },
{ 0x0034, 0x00007c03 },
{ 0x0120, 0x00004c41 },
{ 0xffff, 0xffffffff },
};
static const struct ast_dramstruct ast2100_dram_table_data[] = {
{ 0x2000, 0x1688a8a8 },
{ 0x2020, 0x00004120 },
{ 0xFF00, 0x00000043 },
{ 0x0000, 0xfc600309 },
{ 0x006C, 0x00909090 },
{ 0x0064, 0x00070000 },
{ 0x0004, 0x00000489 },
{ 0x0008, 0x0011030f },
{ 0x0010, 0x32302926 },
{ 0x0018, 0x274c0122 },
{ 0x0020, 0x00ce2222 },
{ 0x0014, 0x01001523 },
{ 0x001C, 0x1024010d },
{ 0x0024, 0x00cb2522 },
{ 0x0038, 0xffffff82 },
{ 0x003C, 0x00000000 },
{ 0x0040, 0x00000000 },
{ 0x0044, 0x00000000 },
{ 0x0048, 0x00000000 },
{ 0x004C, 0x00000000 },
{ 0x0050, 0x00000000 },
{ 0x0054, 0x00000000 },
{ 0x0058, 0x00000000 },
{ 0x005C, 0x00000000 },
{ 0x0060, 0x0f2aa02a },
{ 0x0064, 0x003f3005 },
{ 0x0068, 0x02020202 },
{ 0x0070, 0x00000000 },
{ 0x0074, 0x00000000 },
{ 0x0078, 0x00000000 },
{ 0x007C, 0x00000000 },
{ 0x0034, 0x00000001 },
{ 0xFF00, 0x00000043 },
{ 0x002C, 0x00000942 },
{ 0x0030, 0x00000040 },
{ 0x0028, 0x00000005 },
{ 0x0028, 0x00000007 },
{ 0x0028, 0x00000003 },
{ 0x0028, 0x00000001 },
{ 0x000C, 0x00005a08 },
{ 0x002C, 0x00000842 },
{ 0x0028, 0x00000001 },
{ 0x0030, 0x000003c0 },
{ 0x0028, 0x00000003 },
{ 0x0030, 0x00000040 },
{ 0x0028, 0x00000003 },
{ 0x000C, 0x00005a21 },
{ 0x0034, 0x00007c03 },
{ 0x0120, 0x00005061 },
{ 0xffff, 0xffffffff },
};
#endif
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*/
/*
* Authors: Dave Airlie <airlied@redhat.com>
*/
#include <linux/module.h>
#include <linux/console.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc_helper.h"
#include "ast_drv.h"
int ast_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, ast_modeset, int, 0400);
#define PCI_VENDOR_ASPEED 0x1a03
static struct drm_driver driver;
#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 DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL),
AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL),
/* AST_VGA_DEVICE(PCI_CHIP_AST1180, NULL), - don't bind to 1180 for now */
{0, 0, 0},
};
MODULE_DEVICE_TABLE(pci, pciidlist);
static int __devinit
ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
return drm_get_pci_dev(pdev, ent, &driver);
}
static void
ast_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_put_dev(dev);
}
static int ast_drm_freeze(struct drm_device *dev)
{
drm_kms_helper_poll_disable(dev);
pci_save_state(dev->pdev);
console_lock();
ast_fbdev_set_suspend(dev, 1);
console_unlock();
return 0;
}
static int ast_drm_thaw(struct drm_device *dev)
{
int error = 0;
ast_post_gpu(dev);
drm_mode_config_reset(dev);
mutex_lock(&dev->mode_config.mutex);
drm_helper_resume_force_mode(dev);
mutex_unlock(&dev->mode_config.mutex);
console_lock();
ast_fbdev_set_suspend(dev, 0);
console_unlock();
return error;
}
static int ast_drm_resume(struct drm_device *dev)
{
int ret;
if (pci_enable_device(dev->pdev))
return -EIO;
ret = ast_drm_thaw(dev);
if (ret)
return ret;
drm_kms_helper_poll_enable(dev);
return 0;
}
static int ast_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *ddev = pci_get_drvdata(pdev);
int error;
error = ast_drm_freeze(ddev);
if (error)
return error;
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int ast_pm_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *ddev = pci_get_drvdata(pdev);
return ast_drm_resume(ddev);
}
static int ast_pm_freeze(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *ddev = pci_get_drvdata(pdev);
if (!ddev || !ddev->dev_private)
return -ENODEV;
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 = pciidlist,
.probe = ast_pci_probe,
.remove = ast_pci_remove,
.driver.pm = &ast_pm_ops,
};
static const struct file_operations ast_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.mmap = ast_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
.read = drm_read,
};
static struct drm_driver driver = {
.driver_features = DRIVER_USE_MTRR | DRIVER_MODESET | DRIVER_GEM,
.dev_priv_size = 0,
.load = ast_driver_load,
.unload = ast_driver_unload,
.fops = &ast_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
.gem_init_object = ast_gem_init_object,
.gem_free_object = ast_gem_free_object,
.dumb_create = ast_dumb_create,
.dumb_map_offset = ast_dumb_mmap_offset,
.dumb_destroy = ast_dumb_destroy,
};
static int __init ast_init(void)
{
if (vgacon_text_force() && ast_modeset == -1)
return -EINVAL;
if (ast_modeset == 0)
return -EINVAL;
return drm_pci_init(&driver, &ast_pci_driver);
}
static void __exit ast_exit(void)
{
drm_pci_exit(&driver, &ast_pci_driver);
}
module_init(ast_init);
module_exit(ast_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");
/*
* 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 "drm_fb_helper.h"
#include "ttm/ttm_bo_api.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_placement.h"
#include "ttm/ttm_memory.h"
#include "ttm/ttm_module.h"
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#define DRIVER_AUTHOR "Dave Airlie"
#define DRIVER_NAME "ast"
#define DRIVER_DESC "AST"
#define DRIVER_DATE "20120228"
#define DRIVER_MAJOR 0
#define DRIVER_MINOR 1
#define DRIVER_PATCHLEVEL 0
#define PCI_CHIP_AST2000 0x2000
#define PCI_CHIP_AST2100 0x2010
#define PCI_CHIP_AST1180 0x1180
enum ast_chip {
AST2000,
AST2100,
AST1100,
AST2200,
AST2150,
AST2300,
AST1180,
};
#define AST_DRAM_512Mx16 0
#define AST_DRAM_1Gx16 1
#define AST_DRAM_512Mx32 2
#define AST_DRAM_1Gx32 3
#define AST_DRAM_2Gx16 6
#define AST_DRAM_4Gx16 7
struct ast_fbdev;
struct ast_private {
struct drm_device *dev;
void __iomem *regs;
void __iomem *ioregs;
enum ast_chip chip;
bool vga2_clone;
uint32_t dram_bus_width;
uint32_t dram_type;
uint32_t mclk;
uint32_t vram_size;
struct ast_fbdev *fbdev;
int fb_mtrr;
struct {
struct drm_global_reference mem_global_ref;
struct ttm_bo_global_ref bo_global_ref;
struct ttm_bo_device bdev;
atomic_t validate_sequence;
} ttm;
struct drm_gem_object *cursor_cache;
uint64_t cursor_cache_gpu_addr;
struct ttm_bo_kmap_obj cache_kmap;
int next_cursor;
};
int ast_driver_load(struct drm_device *dev, unsigned long flags);
int ast_driver_unload(struct drm_device *dev);
struct ast_gem_object;
#define AST_IO_AR_PORT_WRITE (0x40)
#define AST_IO_MISC_PORT_WRITE (0x42)
#define AST_IO_SEQ_PORT (0x44)
#define AST_DAC_INDEX_READ (0x3c7)
#define AST_IO_DAC_INDEX_WRITE (0x48)
#define AST_IO_DAC_DATA (0x49)
#define AST_IO_GR_PORT (0x4E)
#define AST_IO_CRTC_PORT (0x54)
#define AST_IO_INPUT_STATUS1_READ (0x5A)
#define AST_IO_MISC_PORT_READ (0x4C)
#define __ast_read(x) \
static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \
u##x val = 0;\
val = ioread##x(ast->regs + reg); \
return val;\
}
__ast_read(8);
__ast_read(16);
__ast_read(32)
#define __ast_io_read(x) \
static inline u##x ast_io_read##x(struct ast_private *ast, u32 reg) { \
u##x val = 0;\
val = ioread##x(ast->ioregs + reg); \
return val;\
}
__ast_io_read(8);
__ast_io_read(16);
__ast_io_read(32);
#define __ast_write(x) \
static inline void ast_write##x(struct ast_private *ast, u32 reg, u##x val) {\
iowrite##x(val, ast->regs + reg);\
}
__ast_write(8);
__ast_write(16);
__ast_write(32);
#define __ast_io_write(x) \
static inline void ast_io_write##x(struct ast_private *ast, u32 reg, u##x val) {\
iowrite##x(val, ast->ioregs + reg);\
}
__ast_io_write(8);
__ast_io_write(16);
#undef __ast_io_write
static inline void ast_set_index_reg(struct ast_private *ast,
uint32_t base, uint8_t index,
uint8_t val)
{
ast_io_write16(ast, base, ((u16)val << 8) | index);
}
void ast_set_index_reg_mask(struct ast_private *ast,
uint32_t base, uint8_t index,
uint8_t mask, uint8_t val);
uint8_t ast_get_index_reg(struct ast_private *ast,
uint32_t base, uint8_t index);
uint8_t ast_get_index_reg_mask(struct ast_private *ast,
uint32_t base, uint8_t index, uint8_t mask);
static inline void ast_open_key(struct ast_private *ast)
{
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xA1, 0xFF, 0x04);
}
#define AST_VIDMEM_SIZE_8M 0x00800000
#define AST_VIDMEM_SIZE_16M 0x01000000
#define AST_VIDMEM_SIZE_32M 0x02000000
#define AST_VIDMEM_SIZE_64M 0x04000000
#define AST_VIDMEM_SIZE_128M 0x08000000
#define AST_VIDMEM_DEFAULT_SIZE AST_VIDMEM_SIZE_8M
#define AST_MAX_HWC_WIDTH 64
#define AST_MAX_HWC_HEIGHT 64
#define AST_HWC_SIZE (AST_MAX_HWC_WIDTH*AST_MAX_HWC_HEIGHT*2)
#define AST_HWC_SIGNATURE_SIZE 32
#define AST_DEFAULT_HWC_NUM 2
/* define for signature structure */
#define AST_HWC_SIGNATURE_CHECKSUM 0x00
#define AST_HWC_SIGNATURE_SizeX 0x04
#define AST_HWC_SIGNATURE_SizeY 0x08
#define AST_HWC_SIGNATURE_X 0x0C
#define AST_HWC_SIGNATURE_Y 0x10
#define AST_HWC_SIGNATURE_HOTSPOTX 0x14
#define AST_HWC_SIGNATURE_HOTSPOTY 0x18
struct ast_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
struct i2c_algo_bit_data bit;
};
struct ast_connector {
struct drm_connector base;
struct ast_i2c_chan *i2c;
};
struct ast_crtc {
struct drm_crtc base;
u8 lut_r[256], lut_g[256], lut_b[256];
struct drm_gem_object *cursor_bo;
uint64_t cursor_addr;
int cursor_width, cursor_height;
u8 offset_x, offset_y;
};
struct ast_encoder {
struct drm_encoder base;
};
struct ast_framebuffer {
struct drm_framebuffer base;
struct drm_gem_object *obj;
};
struct ast_fbdev {
struct drm_fb_helper helper;
struct ast_framebuffer afb;
struct list_head fbdev_list;
void *sysram;
int size;
struct ttm_bo_kmap_obj mapping;
};
#define to_ast_crtc(x) container_of(x, struct ast_crtc, base)
#define to_ast_connector(x) container_of(x, struct ast_connector, base)
#define to_ast_encoder(x) container_of(x, struct ast_encoder, base)
#define to_ast_framebuffer(x) container_of(x, struct ast_framebuffer, base)
struct ast_vbios_stdtable {
u8 misc;
u8 seq[4];
u8 crtc[25];
u8 ar[20];
u8 gr[9];
};
struct ast_vbios_enhtable {
u32 ht;
u32 hde;
u32 hfp;
u32 hsync;
u32 vt;
u32 vde;
u32 vfp;
u32 vsync;
u32 dclk_index;
u32 flags;
u32 refresh_rate;
u32 refresh_rate_index;
u32 mode_id;
};
struct ast_vbios_dclk_info {
u8 param1;
u8 param2;
u8 param3;
};
struct ast_vbios_mode_info {
struct ast_vbios_stdtable *std_table;
struct ast_vbios_enhtable *enh_table;
};
extern int ast_mode_init(struct drm_device *dev);
extern void ast_mode_fini(struct drm_device *dev);
int ast_framebuffer_init(struct drm_device *dev,
struct ast_framebuffer *ast_fb,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj);
int ast_fbdev_init(struct drm_device *dev);
void ast_fbdev_fini(struct drm_device *dev);
void ast_fbdev_set_suspend(struct drm_device *dev, int state);
struct ast_bo {
struct ttm_buffer_object bo;
struct ttm_placement placement;
struct ttm_bo_kmap_obj kmap;
struct drm_gem_object gem;
u32 placements[3];
int pin_count;
};
#define gem_to_ast_bo(gobj) container_of((gobj), struct ast_bo, gem)
static inline struct ast_bo *
ast_bo(struct ttm_buffer_object *bo)
{
return container_of(bo, struct ast_bo, bo);
}
#define to_ast_obj(x) container_of(x, struct ast_gem_object, base)
#define AST_MM_ALIGN_SHIFT 4
#define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1)
extern int ast_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args);
extern int ast_dumb_destroy(struct drm_file *file,
struct drm_device *dev,
uint32_t handle);
extern int ast_gem_init_object(struct drm_gem_object *obj);
extern void ast_gem_free_object(struct drm_gem_object *obj);
extern int ast_dumb_mmap_offset(struct drm_file *file,
struct drm_device *dev,
uint32_t handle,
uint64_t *offset);
#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
int ast_mm_init(struct ast_private *ast);
void ast_mm_fini(struct ast_private *ast);
int ast_bo_create(struct drm_device *dev, int size, int align,
uint32_t flags, struct ast_bo **pastbo);
int ast_gem_create(struct drm_device *dev,
u32 size, bool iskernel,
struct drm_gem_object **obj);
int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr);
int ast_bo_unpin(struct ast_bo *bo);
int ast_bo_reserve(struct ast_bo *bo, bool no_wait);
void ast_bo_unreserve(struct ast_bo *bo);
void ast_ttm_placement(struct ast_bo *bo, int domain);
int ast_bo_push_sysram(struct ast_bo *bo);
int ast_mmap(struct file *filp, struct vm_area_struct *vma);
/* ast post */
void ast_post_gpu(struct drm_device *dev);
#endif
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*/
/*
* Authors: Dave Airlie <airlied@redhat.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_fb_helper.h"
#include "ast_drv.h"
static void ast_dirty_update(struct ast_fbdev *afbdev,
int x, int y, int width, int height)
{
int i;
struct drm_gem_object *obj;
struct ast_bo *bo;
int src_offset, dst_offset;
int bpp = (afbdev->afb.base.bits_per_pixel + 7)/8;
int ret;
bool unmap = false;
obj = afbdev->afb.obj;
bo = gem_to_ast_bo(obj);
ret = ast_bo_reserve(bo, true);
if (ret) {
DRM_ERROR("failed to reserve fb bo\n");
return;
}
if (!bo->kmap.virtual) {
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
if (ret) {
DRM_ERROR("failed to kmap fb updates\n");
ast_bo_unreserve(bo);
return;
}
unmap = true;
}
for (i = y; i < y + height; i++) {
/* assume equal stride for now */
src_offset = dst_offset = i * afbdev->afb.base.pitches[0] + (x * bpp);
memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
}
if (unmap)
ttm_bo_kunmap(&bo->kmap);
ast_bo_unreserve(bo);
}
static void ast_fillrect(struct fb_info *info,
const struct fb_fillrect *rect)
{
struct ast_fbdev *afbdev = info->par;
sys_fillrect(info, rect);
ast_dirty_update(afbdev, rect->dx, rect->dy, rect->width,
rect->height);
}
static void ast_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{
struct ast_fbdev *afbdev = info->par;
sys_copyarea(info, area);
ast_dirty_update(afbdev, area->dx, area->dy, area->width,
area->height);
}
static void ast_imageblit(struct fb_info *info,
const struct fb_image *image)
{
struct ast_fbdev *afbdev = info->par;
sys_imageblit(info, image);
ast_dirty_update(afbdev, image->dx, image->dy, image->width,
image->height);
}
static struct fb_ops astfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_fillrect = ast_fillrect,
.fb_copyarea = ast_copyarea,
.fb_imageblit = ast_imageblit,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_blank = drm_fb_helper_blank,
.fb_setcmap = drm_fb_helper_setcmap,
.fb_debug_enter = drm_fb_helper_debug_enter,
.fb_debug_leave = drm_fb_helper_debug_leave,
};
static int astfb_create_object(struct ast_fbdev *afbdev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object **gobj_p)
{
struct drm_device *dev = afbdev->helper.dev;
u32 bpp, depth;
u32 size;
struct drm_gem_object *gobj;
int ret = 0;
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
size = mode_cmd->pitches[0] * mode_cmd->height;
ret = ast_gem_create(dev, size, true, &gobj);
if (ret)
return ret;
*gobj_p = gobj;
return ret;
}
static int astfb_create(struct ast_fbdev *afbdev,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_device *dev = afbdev->helper.dev;
struct drm_mode_fb_cmd2 mode_cmd;
struct drm_framebuffer *fb;
struct fb_info *info;
int size, ret;
struct device *device = &dev->pdev->dev;
void *sysram;
struct drm_gem_object *gobj = NULL;
struct ast_bo *bo = NULL;
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7)/8);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
size = mode_cmd.pitches[0] * mode_cmd.height;
ret = astfb_create_object(afbdev, &mode_cmd, &gobj);
if (ret) {
DRM_ERROR("failed to create fbcon backing object %d\n", ret);
return ret;
}
bo = gem_to_ast_bo(gobj);
sysram = vmalloc(size);
if (!sysram)
return -ENOMEM;
info = framebuffer_alloc(0, device);
if (!info) {
ret = -ENOMEM;
goto out;
}
info->par = afbdev;
ret = ast_framebuffer_init(dev, &afbdev->afb, &mode_cmd, gobj);
if (ret)
goto out;
afbdev->sysram = sysram;
afbdev->size = size;
fb = &afbdev->afb.base;
afbdev->helper.fb = fb;
afbdev->helper.fbdev = info;
strcpy(info->fix.id, "astdrmfb");
info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &astfb_ops;
ret = fb_alloc_cmap(&info->cmap, 256, 0);
if (ret) {
ret = -ENOMEM;
goto out;
}
info->apertures = alloc_apertures(1);
if (!info->apertures) {
ret = -ENOMEM;
goto out;
}
info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0);
info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0);
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(info, &afbdev->helper, sizes->fb_width, sizes->fb_height);
info->screen_base = sysram;
info->screen_size = size;
info->pixmap.flags = FB_PIXMAP_SYSTEM;
DRM_DEBUG_KMS("allocated %dx%d\n",
fb->width, fb->height);
return 0;
out:
return ret;
}
static void ast_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno)
{
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
ast_crtc->lut_r[regno] = red >> 8;
ast_crtc->lut_g[regno] = green >> 8;
ast_crtc->lut_b[regno] = blue >> 8;
}
static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno)
{
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
*red = ast_crtc->lut_r[regno] << 8;
*green = ast_crtc->lut_g[regno] << 8;
*blue = ast_crtc->lut_b[regno] << 8;
}
static int ast_find_or_create_single(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct ast_fbdev *afbdev = (struct ast_fbdev *)helper;
int new_fb = 0;
int ret;
if (!helper->fb) {
ret = astfb_create(afbdev, sizes);
if (ret)
return ret;
new_fb = 1;
}
return new_fb;
}
static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
.gamma_set = ast_fb_gamma_set,
.gamma_get = ast_fb_gamma_get,
.fb_probe = ast_find_or_create_single,
};
static void ast_fbdev_destroy(struct drm_device *dev,
struct ast_fbdev *afbdev)
{
struct fb_info *info;
struct ast_framebuffer *afb = &afbdev->afb;
if (afbdev->helper.fbdev) {
info = afbdev->helper.fbdev;
unregister_framebuffer(info);
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
if (afb->obj) {
drm_gem_object_unreference_unlocked(afb->obj);
afb->obj = NULL;
}
drm_fb_helper_fini(&afbdev->helper);
vfree(afbdev->sysram);
drm_framebuffer_cleanup(&afb->base);
}
int ast_fbdev_init(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
struct ast_fbdev *afbdev;
int ret;
afbdev = kzalloc(sizeof(struct ast_fbdev), GFP_KERNEL);
if (!afbdev)
return -ENOMEM;
ast->fbdev = afbdev;
afbdev->helper.funcs = &ast_fb_helper_funcs;
ret = drm_fb_helper_init(dev, &afbdev->helper,
1, 1);
if (ret) {
kfree(afbdev);
return ret;
}
drm_fb_helper_single_add_all_connectors(&afbdev->helper);
drm_fb_helper_initial_config(&afbdev->helper, 32);
return 0;
}
void ast_fbdev_fini(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
if (!ast->fbdev)
return;
ast_fbdev_destroy(dev, ast->fbdev);
kfree(ast->fbdev);
ast->fbdev = NULL;
}
void ast_fbdev_set_suspend(struct drm_device *dev, int state)
{
struct ast_private *ast = dev->dev_private;
if (!ast->fbdev)
return;
fb_set_suspend(ast->fbdev->helper.fbdev, state);
}
/*
* 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 "drmP.h"
#include "ast_drv.h"
#include "drm_fb_helper.h"
#include "drm_crtc_helper.h"
#include "ast_dram_tables.h"
void ast_set_index_reg_mask(struct ast_private *ast,
uint32_t base, uint8_t index,
uint8_t mask, uint8_t val)
{
u8 tmp;
ast_io_write8(ast, base, index);
tmp = (ast_io_read8(ast, base + 1) & mask) | val;
ast_set_index_reg(ast, base, index, tmp);
}
uint8_t ast_get_index_reg(struct ast_private *ast,
uint32_t base, uint8_t index)
{
uint8_t ret;
ast_io_write8(ast, base, index);
ret = ast_io_read8(ast, base + 1);
return ret;
}
uint8_t ast_get_index_reg_mask(struct ast_private *ast,
uint32_t base, uint8_t index, uint8_t mask)
{
uint8_t ret;
ast_io_write8(ast, base, index);
ret = ast_io_read8(ast, base + 1) & mask;
return ret;
}
static int ast_detect_chip(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
if (dev->pdev->device == PCI_CHIP_AST1180) {
ast->chip = AST1100;
DRM_INFO("AST 1180 detected\n");
} else {
if (dev->pdev->revision >= 0x20) {
ast->chip = AST2300;
DRM_INFO("AST 2300 detected\n");
} else if (dev->pdev->revision >= 0x10) {
uint32_t data;
ast_write32(ast, 0xf004, 0x1e6e0000);
ast_write32(ast, 0xf000, 0x1);
data = ast_read32(ast, 0x1207c);
switch (data & 0x0300) {
case 0x0200:
ast->chip = AST1100;
DRM_INFO("AST 1100 detected\n");
break;
case 0x0100:
ast->chip = AST2200;
DRM_INFO("AST 2200 detected\n");
break;
case 0x0000:
ast->chip = AST2150;
DRM_INFO("AST 2150 detected\n");
break;
default:
ast->chip = AST2100;
DRM_INFO("AST 2100 detected\n");
break;
}
ast->vga2_clone = false;
} else {
ast->chip = 2000;
DRM_INFO("AST 2000 detected\n");
}
}
return 0;
}
static int ast_get_dram_info(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
uint32_t data, data2;
uint32_t denum, num, div, ref_pll;
ast_write32(ast, 0xf004, 0x1e6e0000);
ast_write32(ast, 0xf000, 0x1);
ast_write32(ast, 0x10000, 0xfc600309);
do {
;
} while (ast_read32(ast, 0x10000) != 0x01);
data = ast_read32(ast, 0x10004);
if (data & 0x400)
ast->dram_bus_width = 16;
else
ast->dram_bus_width = 32;
if (ast->chip == AST2300) {
switch (data & 0x03) {
case 0:
ast->dram_type = AST_DRAM_512Mx16;
break;
default:
case 1:
ast->dram_type = AST_DRAM_1Gx16;
break;
case 2:
ast->dram_type = AST_DRAM_2Gx16;
break;
case 3:
ast->dram_type = AST_DRAM_4Gx16;
break;
}
} else {
switch (data & 0x0c) {
case 0:
case 4:
ast->dram_type = AST_DRAM_512Mx16;
break;
case 8:
if (data & 0x40)
ast->dram_type = AST_DRAM_1Gx16;
else
ast->dram_type = AST_DRAM_512Mx32;
break;
case 0xc:
ast->dram_type = AST_DRAM_1Gx32;
break;
}
}
data = ast_read32(ast, 0x10120);
data2 = ast_read32(ast, 0x10170);
if (data2 & 0x2000)
ref_pll = 14318;
else
ref_pll = 12000;
denum = data & 0x1f;
num = (data & 0x3fe0) >> 5;
data = (data & 0xc000) >> 14;
switch (data) {
case 3:
div = 0x4;
break;
case 2:
case 1:
div = 0x2;
break;
default:
div = 0x1;
break;
}
ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000);
return 0;
}
uint32_t ast_get_max_dclk(struct drm_device *dev, int bpp)
{
struct ast_private *ast = dev->dev_private;
uint32_t dclk, jreg;
uint32_t dram_bus_width, mclk, dram_bandwidth, actual_dram_bandwidth, dram_efficency = 500;
dram_bus_width = ast->dram_bus_width;
mclk = ast->mclk;
if (ast->chip == AST2100 ||
ast->chip == AST1100 ||
ast->chip == AST2200 ||
ast->chip == AST2150 ||
ast->dram_bus_width == 16)
dram_efficency = 600;
else if (ast->chip == AST2300)
dram_efficency = 400;
dram_bandwidth = mclk * dram_bus_width * 2 / 8;
actual_dram_bandwidth = dram_bandwidth * dram_efficency / 1000;
if (ast->chip == AST1180)
dclk = actual_dram_bandwidth / ((bpp + 1) / 8);
else {
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
if ((jreg & 0x08) && (ast->chip == AST2000))
dclk = actual_dram_bandwidth / ((bpp + 1 + 16) / 8);
else if ((jreg & 0x08) && (bpp == 8))
dclk = actual_dram_bandwidth / ((bpp + 1 + 24) / 8);
else
dclk = actual_dram_bandwidth / ((bpp + 1) / 8);
}
if (ast->chip == AST2100 ||
ast->chip == AST2200 ||
ast->chip == AST2300 ||
ast->chip == AST1180) {
if (dclk > 200)
dclk = 200;
} else {
if (dclk > 165)
dclk = 165;
}
return dclk;
}
static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
struct ast_framebuffer *ast_fb = to_ast_framebuffer(fb);
if (ast_fb->obj)
drm_gem_object_unreference_unlocked(ast_fb->obj);
drm_framebuffer_cleanup(fb);
kfree(fb);
}
static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb,
struct drm_file *file,
unsigned int *handle)
{
return -EINVAL;
}
static const struct drm_framebuffer_funcs ast_fb_funcs = {
.destroy = ast_user_framebuffer_destroy,
.create_handle = ast_user_framebuffer_create_handle,
};
int ast_framebuffer_init(struct drm_device *dev,
struct ast_framebuffer *ast_fb,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj)
{
int ret;
ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs);
if (ret) {
DRM_ERROR("framebuffer init failed %d\n", ret);
return ret;
}
drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd);
ast_fb->obj = obj;
return 0;
}
static struct drm_framebuffer *
ast_user_framebuffer_create(struct drm_device *dev,
struct drm_file *filp,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_gem_object *obj;
struct ast_framebuffer *ast_fb;
int ret;
obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]);
if (obj == NULL)
return ERR_PTR(-ENOENT);
ast_fb = kzalloc(sizeof(*ast_fb), GFP_KERNEL);
if (!ast_fb) {
drm_gem_object_unreference_unlocked(obj);
return ERR_PTR(-ENOMEM);
}
ret = ast_framebuffer_init(dev, ast_fb, mode_cmd, obj);
if (ret) {
drm_gem_object_unreference_unlocked(obj);
kfree(ast_fb);
return ERR_PTR(ret);
}
return &ast_fb->base;
}
static const struct drm_mode_config_funcs ast_mode_funcs = {
.fb_create = ast_user_framebuffer_create,
};
static u32 ast_get_vram_info(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 jreg;
ast_open_key(ast);
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);
switch (jreg & 3) {
case 0: return AST_VIDMEM_SIZE_8M;
case 1: return AST_VIDMEM_SIZE_16M;
case 2: return AST_VIDMEM_SIZE_32M;
case 3: return AST_VIDMEM_SIZE_64M;
}
return AST_VIDMEM_DEFAULT_SIZE;
}
int ast_driver_load(struct drm_device *dev, unsigned long flags)
{
struct ast_private *ast;
int ret = 0;
ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL);
if (!ast)
return -ENOMEM;
dev->dev_private = ast;
ast->dev = dev;
ast->regs = pci_iomap(dev->pdev, 1, 0);
if (!ast->regs) {
ret = -EIO;
goto out_free;
}
ast->ioregs = pci_iomap(dev->pdev, 2, 0);
if (!ast->ioregs) {
ret = -EIO;
goto out_free;
}
ast_detect_chip(dev);
if (ast->chip != AST1180) {
ast_get_dram_info(dev);
ast->vram_size = ast_get_vram_info(dev);
DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
}
ret = ast_mm_init(ast);
if (ret)
goto out_free;
drm_mode_config_init(dev);
dev->mode_config.funcs = (void *)&ast_mode_funcs;
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.preferred_depth = 24;
dev->mode_config.prefer_shadow = 1;
if (ast->chip == AST2100 ||
ast->chip == AST2200 ||
ast->chip == AST2300 ||
ast->chip == AST1180) {
dev->mode_config.max_width = 1920;
dev->mode_config.max_height = 2048;
} else {
dev->mode_config.max_width = 1600;
dev->mode_config.max_height = 1200;
}
ret = ast_mode_init(dev);
if (ret)
goto out_free;
ret = ast_fbdev_init(dev);
if (ret)
goto out_free;
return 0;
out_free:
kfree(ast);
dev->dev_private = NULL;
return ret;
}
int ast_driver_unload(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
ast_mode_fini(dev);
ast_fbdev_fini(dev);
drm_mode_config_cleanup(dev);
ast_mm_fini(ast);
pci_iounmap(dev->pdev, ast->ioregs);
pci_iounmap(dev->pdev, ast->regs);
kfree(ast);
return 0;
}
int ast_gem_create(struct drm_device *dev,
u32 size, bool iskernel,
struct drm_gem_object **obj)
{
struct ast_bo *astbo;
int ret;
*obj = NULL;
size = roundup(size, PAGE_SIZE);
if (size == 0)
return -EINVAL;
ret = ast_bo_create(dev, size, 0, 0, &astbo);
if (ret) {
if (ret != -ERESTARTSYS)
DRM_ERROR("failed to allocate GEM object\n");
return ret;
}
*obj = &astbo->gem;
return 0;
}
int ast_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
int ret;
struct drm_gem_object *gobj;
u32 handle;
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
ret = ast_gem_create(dev, args->size, false,
&gobj);
if (ret)
return ret;
ret = drm_gem_handle_create(file, gobj, &handle);
drm_gem_object_unreference_unlocked(gobj);
if (ret)
return ret;
args->handle = handle;
return 0;
}
int ast_dumb_destroy(struct drm_file *file,
struct drm_device *dev,
uint32_t handle)
{
return drm_gem_handle_delete(file, handle);
}
int ast_gem_init_object(struct drm_gem_object *obj)
{
BUG();
return 0;
}
void ast_bo_unref(struct ast_bo **bo)
{
struct ttm_buffer_object *tbo;
if ((*bo) == NULL)
return;
tbo = &((*bo)->bo);
ttm_bo_unref(&tbo);
if (tbo == NULL)
*bo = NULL;
}
void ast_gem_free_object(struct drm_gem_object *obj)
{
struct ast_bo *ast_bo = gem_to_ast_bo(obj);
if (!ast_bo)
return;
ast_bo_unref(&ast_bo);
}
static inline u64 ast_bo_mmap_offset(struct ast_bo *bo)
{
return bo->bo.addr_space_offset;
}
int
ast_dumb_mmap_offset(struct drm_file *file,
struct drm_device *dev,
uint32_t handle,
uint64_t *offset)
{
struct drm_gem_object *obj;
int ret;
struct ast_bo *bo;
mutex_lock(&dev->struct_mutex);
obj = drm_gem_object_lookup(dev, file, handle);
if (obj == NULL) {
ret = -ENOENT;
goto out_unlock;
}
bo = gem_to_ast_bo(obj);
*offset = ast_bo_mmap_offset(bo);
drm_gem_object_unreference(obj);
ret = 0;
out_unlock:
mutex_unlock(&dev->struct_mutex);
return ret;
}
/*
* Copyright 2012 Red Hat Inc.
* Parts based on xf86-video-ast
* Copyright (c) 2005 ASPEED Technology Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*/
/*
* Authors: Dave Airlie <airlied@redhat.com>
*/
#include <linux/export.h>
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "ast_drv.h"
#include "ast_tables.h"
static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev);
static void ast_i2c_destroy(struct ast_i2c_chan *i2c);
static int ast_cursor_set(struct drm_crtc *crtc,
struct drm_file *file_priv,
uint32_t handle,
uint32_t width,
uint32_t height);
static int ast_cursor_move(struct drm_crtc *crtc,
int x, int y);
static inline void ast_load_palette_index(struct ast_private *ast,
u8 index, u8 red, u8 green,
u8 blue)
{
ast_io_write8(ast, AST_IO_DAC_INDEX_WRITE, index);
ast_io_read8(ast, AST_IO_SEQ_PORT);
ast_io_write8(ast, AST_IO_DAC_DATA, red);
ast_io_read8(ast, AST_IO_SEQ_PORT);
ast_io_write8(ast, AST_IO_DAC_DATA, green);
ast_io_read8(ast, AST_IO_SEQ_PORT);
ast_io_write8(ast, AST_IO_DAC_DATA, blue);
ast_io_read8(ast, AST_IO_SEQ_PORT);
}
static void ast_crtc_load_lut(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
int i;
if (!crtc->enabled)
return;
for (i = 0; i < 256; i++)
ast_load_palette_index(ast, i, ast_crtc->lut_r[i],
ast_crtc->lut_g[i], ast_crtc->lut_b[i]);
}
static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
struct ast_vbios_mode_info *vbios_mode)
{
struct ast_private *ast = crtc->dev->dev_private;
u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate;
u32 hborder, vborder;
switch (crtc->fb->bits_per_pixel) {
case 8:
vbios_mode->std_table = &vbios_stdtable[VGAModeIndex];
color_index = VGAModeIndex - 1;
break;
case 16:
vbios_mode->std_table = &vbios_stdtable[HiCModeIndex];
color_index = HiCModeIndex;
break;
case 24:
case 32:
vbios_mode->std_table = &vbios_stdtable[TrueCModeIndex];
color_index = TrueCModeIndex;
break;
default:
return false;
}
switch (crtc->mode.crtc_hdisplay) {
case 640:
vbios_mode->enh_table = &res_640x480[refresh_rate_index];
break;
case 800:
vbios_mode->enh_table = &res_800x600[refresh_rate_index];
break;
case 1024:
vbios_mode->enh_table = &res_1024x768[refresh_rate_index];
break;
case 1280:
if (crtc->mode.crtc_vdisplay == 800)
vbios_mode->enh_table = &res_1280x800[refresh_rate_index];
else
vbios_mode->enh_table = &res_1280x1024[refresh_rate_index];
break;
case 1440:
vbios_mode->enh_table = &res_1440x900[refresh_rate_index];
break;
case 1600:
vbios_mode->enh_table = &res_1600x1200[refresh_rate_index];
break;
case 1680:
vbios_mode->enh_table = &res_1680x1050[refresh_rate_index];
break;
case 1920:
if (crtc->mode.crtc_vdisplay == 1080)
vbios_mode->enh_table = &res_1920x1080[refresh_rate_index];
else
vbios_mode->enh_table = &res_1920x1200[refresh_rate_index];
break;
default:
return false;
}
refresh_rate = drm_mode_vrefresh(mode);
while (vbios_mode->enh_table->refresh_rate < refresh_rate) {
vbios_mode->enh_table++;
if ((vbios_mode->enh_table->refresh_rate > refresh_rate) ||
(vbios_mode->enh_table->refresh_rate == 0xff)) {
vbios_mode->enh_table--;
break;
}
}
hborder = (vbios_mode->enh_table->flags & HBorder) ? 8 : 0;
vborder = (vbios_mode->enh_table->flags & VBorder) ? 8 : 0;
adjusted_mode->crtc_htotal = vbios_mode->enh_table->ht;
adjusted_mode->crtc_hblank_start = vbios_mode->enh_table->hde + hborder;
adjusted_mode->crtc_hblank_end = vbios_mode->enh_table->ht - hborder;
adjusted_mode->crtc_hsync_start = vbios_mode->enh_table->hde + hborder +
vbios_mode->enh_table->hfp;
adjusted_mode->crtc_hsync_end = (vbios_mode->enh_table->hde + hborder +
vbios_mode->enh_table->hfp +
vbios_mode->enh_table->hsync);
adjusted_mode->crtc_vtotal = vbios_mode->enh_table->vt;
adjusted_mode->crtc_vblank_start = vbios_mode->enh_table->vde + vborder;
adjusted_mode->crtc_vblank_end = vbios_mode->enh_table->vt - vborder;
adjusted_mode->crtc_vsync_start = vbios_mode->enh_table->vde + vborder +
vbios_mode->enh_table->vfp;
adjusted_mode->crtc_vsync_end = (vbios_mode->enh_table->vde + vborder +
vbios_mode->enh_table->vfp +
vbios_mode->enh_table->vsync);
refresh_rate_index = vbios_mode->enh_table->refresh_rate_index;
mode_id = vbios_mode->enh_table->mode_id;
if (ast->chip == AST1180) {
/* TODO 1180 */
} else {
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8c, (u8)((color_index & 0xf) << 4));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8d, refresh_rate_index & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->fb->bits_per_pixel);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8);
}
return true;
}
static void ast_set_std_reg(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
struct ast_private *ast = crtc->dev->dev_private;
struct ast_vbios_stdtable *stdtable;
u32 i;
u8 jreg;
stdtable = vbios_mode->std_table;
jreg = stdtable->misc;
ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg);
/* Set SEQ */
ast_set_index_reg(ast, AST_IO_SEQ_PORT, 0x00, 0x03);
for (i = 0; i < 4; i++) {
jreg = stdtable->seq[i];
if (!i)
jreg |= 0x20;
ast_set_index_reg(ast, AST_IO_SEQ_PORT, (i + 1) , jreg);
}
/* Set CRTC */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00);
for (i = 0; i < 25; i++)
ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, stdtable->crtc[i]);
/* set AR */
jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ);
for (i = 0; i < 20; i++) {
jreg = stdtable->ar[i];
ast_io_write8(ast, AST_IO_AR_PORT_WRITE, (u8)i);
ast_io_write8(ast, AST_IO_AR_PORT_WRITE, jreg);
}
ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x14);
ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x00);
jreg = ast_io_read8(ast, AST_IO_INPUT_STATUS1_READ);
ast_io_write8(ast, AST_IO_AR_PORT_WRITE, 0x20);
/* Set GR */
for (i = 0; i < 9; i++)
ast_set_index_reg(ast, AST_IO_GR_PORT, i, stdtable->gr[i]);
}
static void ast_set_crtc_reg(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
struct ast_private *ast = crtc->dev->dev_private;
u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0;
u16 temp;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x00);
temp = (mode->crtc_htotal >> 3) - 5;
if (temp & 0x100)
jregAC |= 0x01; /* HT D[8] */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x00, 0x00, temp);
temp = (mode->crtc_hdisplay >> 3) - 1;
if (temp & 0x100)
jregAC |= 0x04; /* HDE D[8] */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x01, 0x00, temp);
temp = (mode->crtc_hblank_start >> 3) - 1;
if (temp & 0x100)
jregAC |= 0x10; /* HBS D[8] */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x02, 0x00, temp);
temp = ((mode->crtc_hblank_end >> 3) - 1) & 0x7f;
if (temp & 0x20)
jreg05 |= 0x80; /* HBE D[5] */
if (temp & 0x40)
jregAD |= 0x01; /* HBE D[5] */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x03, 0xE0, (temp & 0x1f));
temp = (mode->crtc_hsync_start >> 3) - 1;
if (temp & 0x100)
jregAC |= 0x40; /* HRS D[5] */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x04, 0x00, temp);
temp = ((mode->crtc_hsync_end >> 3) - 1) & 0x3f;
if (temp & 0x20)
jregAD |= 0x04; /* HRE D[5] */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05));
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAC, 0x00, jregAC);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAD, 0x00, jregAD);
/* vert timings */
temp = (mode->crtc_vtotal) - 2;
if (temp & 0x100)
jreg07 |= 0x01;
if (temp & 0x200)
jreg07 |= 0x20;
if (temp & 0x400)
jregAE |= 0x01;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x06, 0x00, temp);
temp = (mode->crtc_vsync_start) - 1;
if (temp & 0x100)
jreg07 |= 0x04;
if (temp & 0x200)
jreg07 |= 0x80;
if (temp & 0x400)
jregAE |= 0x08;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x10, 0x00, temp);
temp = (mode->crtc_vsync_end - 1) & 0x3f;
if (temp & 0x10)
jregAE |= 0x20;
if (temp & 0x20)
jregAE |= 0x40;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x70, temp & 0xf);
temp = mode->crtc_vdisplay - 1;
if (temp & 0x100)
jreg07 |= 0x02;
if (temp & 0x200)
jreg07 |= 0x40;
if (temp & 0x400)
jregAE |= 0x02;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x12, 0x00, temp);
temp = mode->crtc_vblank_start - 1;
if (temp & 0x100)
jreg07 |= 0x08;
if (temp & 0x200)
jreg09 |= 0x20;
if (temp & 0x400)
jregAE |= 0x04;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x15, 0x00, temp);
temp = mode->crtc_vblank_end - 1;
if (temp & 0x100)
jregAE |= 0x10;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x16, 0x00, temp);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x07, 0x00, jreg07);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x09, 0xdf, jreg09);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xAE, 0x00, (jregAE | 0x80));
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x11, 0x7f, 0x80);
}
static void ast_set_offset_reg(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
u16 offset;
offset = crtc->fb->pitches[0] >> 3;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x13, (offset & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xb0, (offset >> 8) & 0x3f);
}
static void ast_set_dclk_reg(struct drm_device *dev, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
struct ast_private *ast = dev->dev_private;
struct ast_vbios_dclk_info *clk_info;
clk_info = &dclk_table[vbios_mode->enh_table->dclk_index];
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc0, 0x00, clk_info->param1);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xc1, 0x00, clk_info->param2);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xbb, 0x0f,
(clk_info->param3 & 0x80) | ((clk_info->param3 & 0x3) << 4));
}
static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
struct ast_private *ast = crtc->dev->dev_private;
u8 jregA0 = 0, jregA3 = 0, jregA8 = 0;
switch (crtc->fb->bits_per_pixel) {
case 8:
jregA0 = 0x70;
jregA3 = 0x01;
jregA8 = 0x00;
break;
case 15:
case 16:
jregA0 = 0x70;
jregA3 = 0x04;
jregA8 = 0x02;
break;
case 32:
jregA0 = 0x70;
jregA3 = 0x08;
jregA8 = 0x02;
break;
}
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa0, 0x8f, jregA0);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xf0, jregA3);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8);
/* Set Threshold */
if (ast->chip == AST2300) {
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60);
} else if (ast->chip == AST2100 ||
ast->chip == AST1100 ||
ast->chip == AST2200 ||
ast->chip == AST2150) {
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x3f);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x2f);
} else {
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x2f);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x1f);
}
}
void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
struct ast_private *ast = dev->dev_private;
u8 jreg;
jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ);
jreg |= (vbios_mode->enh_table->flags & SyncNN);
ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg);
}
bool ast_set_dac_reg(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct ast_vbios_mode_info *vbios_mode)
{
switch (crtc->fb->bits_per_pixel) {
case 8:
break;
default:
return false;
}
return true;
}
void ast_set_start_address_crt1(struct drm_crtc *crtc, unsigned offset)
{
struct ast_private *ast = crtc->dev->dev_private;
u32 addr;
addr = offset >> 2;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x0d, (u8)(addr & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x0c, (u8)((addr >> 8) & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xaf, (u8)((addr >> 16) & 0xff));
}
static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct ast_private *ast = crtc->dev->dev_private;
if (ast->chip == AST1180)
return;
switch (mode) {
case DRM_MODE_DPMS_ON:
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0);
ast_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_OFF:
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20);
break;
}
}
static bool ast_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
/* ast is different - we will force move buffers out of VRAM */
static int ast_crtc_do_set_base(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int x, int y, int atomic)
{
struct ast_private *ast = crtc->dev->dev_private;
struct drm_gem_object *obj;
struct ast_framebuffer *ast_fb;
struct ast_bo *bo;
int ret;
u64 gpu_addr;
/* push the previous fb to system ram */
if (!atomic && fb) {
ast_fb = to_ast_framebuffer(fb);
obj = ast_fb->obj;
bo = gem_to_ast_bo(obj);
ret = ast_bo_reserve(bo, false);
if (ret)
return ret;
ast_bo_push_sysram(bo);
ast_bo_unreserve(bo);
}
ast_fb = to_ast_framebuffer(crtc->fb);
obj = ast_fb->obj;
bo = gem_to_ast_bo(obj);
ret = ast_bo_reserve(bo, false);
if (ret)
return ret;
ret = ast_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
if (ret) {
ast_bo_unreserve(bo);
return ret;
}
if (&ast->fbdev->afb == ast_fb) {
/* if pushing console in kmap it */
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
if (ret)
DRM_ERROR("failed to kmap fbcon\n");
}
ast_bo_unreserve(bo);
ast_set_start_address_crt1(crtc, (u32)gpu_addr);
return 0;
}
static int ast_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
return ast_crtc_do_set_base(crtc, old_fb, x, y, 0);
}
static int ast_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct ast_private *ast = crtc->dev->dev_private;
struct ast_vbios_mode_info vbios_mode;
bool ret;
if (ast->chip == AST1180) {
DRM_ERROR("AST 1180 modesetting not supported\n");
return -EINVAL;
}
ret = ast_get_vbios_mode_info(crtc, mode, adjusted_mode, &vbios_mode);
if (ret == false)
return -EINVAL;
ast_open_key(ast);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04);
ast_set_std_reg(crtc, adjusted_mode, &vbios_mode);
ast_set_crtc_reg(crtc, adjusted_mode, &vbios_mode);
ast_set_offset_reg(crtc);
ast_set_dclk_reg(dev, adjusted_mode, &vbios_mode);
ast_set_ext_reg(crtc, adjusted_mode, &vbios_mode);
ast_set_sync_reg(dev, adjusted_mode, &vbios_mode);
ast_set_dac_reg(crtc, adjusted_mode, &vbios_mode);
ast_crtc_mode_set_base(crtc, x, y, old_fb);
return 0;
}
static void ast_crtc_disable(struct drm_crtc *crtc)
{
}
static void ast_crtc_prepare(struct drm_crtc *crtc)
{
}
static void ast_crtc_commit(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0);
}
static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
.dpms = ast_crtc_dpms,
.mode_fixup = ast_crtc_mode_fixup,
.mode_set = ast_crtc_mode_set,
.mode_set_base = ast_crtc_mode_set_base,
.disable = ast_crtc_disable,
.load_lut = ast_crtc_load_lut,
.disable = ast_crtc_disable,
.prepare = ast_crtc_prepare,
.commit = ast_crtc_commit,
};
static void ast_crtc_reset(struct drm_crtc *crtc)
{
}
static void ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, uint32_t start, uint32_t size)
{
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
int end = (start + size > 256) ? 256 : start + size, i;
/* userspace palettes are always correct as is */
for (i = start; i < end; i++) {
ast_crtc->lut_r[i] = red[i] >> 8;
ast_crtc->lut_g[i] = green[i] >> 8;
ast_crtc->lut_b[i] = blue[i] >> 8;
}
ast_crtc_load_lut(crtc);
}
static void ast_crtc_destroy(struct drm_crtc *crtc)
{
drm_crtc_cleanup(crtc);
kfree(crtc);
}
static const struct drm_crtc_funcs ast_crtc_funcs = {
.cursor_set = ast_cursor_set,
.cursor_move = ast_cursor_move,
.reset = ast_crtc_reset,
.set_config = drm_crtc_helper_set_config,
.gamma_set = ast_crtc_gamma_set,
.destroy = ast_crtc_destroy,
};
int ast_crtc_init(struct drm_device *dev)
{
struct ast_crtc *crtc;
int i;
crtc = kzalloc(sizeof(struct ast_crtc), GFP_KERNEL);
if (!crtc)
return -ENOMEM;
drm_crtc_init(dev, &crtc->base, &ast_crtc_funcs);
drm_mode_crtc_set_gamma_size(&crtc->base, 256);
drm_crtc_helper_add(&crtc->base, &ast_crtc_helper_funcs);
for (i = 0; i < 256; i++) {
crtc->lut_r[i] = i;
crtc->lut_g[i] = i;
crtc->lut_b[i] = i;
}
return 0;
}
static void ast_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
kfree(encoder);
}
static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
struct drm_mode_object *obj;
struct drm_encoder *encoder;
/* pick the encoder ids */
if (enc_id) {
obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
if (!obj)
return NULL;
encoder = obj_to_encoder(obj);
return encoder;
}
return NULL;
}
static const struct drm_encoder_funcs ast_enc_funcs = {
.destroy = ast_encoder_destroy,
};
static void ast_encoder_dpms(struct drm_encoder *encoder, int mode)
{
}
static bool ast_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void ast_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
}
static void ast_encoder_prepare(struct drm_encoder *encoder)
{
}
static void ast_encoder_commit(struct drm_encoder *encoder)
{
}
static const struct drm_encoder_helper_funcs ast_enc_helper_funcs = {
.dpms = ast_encoder_dpms,
.mode_fixup = ast_mode_fixup,
.prepare = ast_encoder_prepare,
.commit = ast_encoder_commit,
.mode_set = ast_encoder_mode_set,
};
int ast_encoder_init(struct drm_device *dev)
{
struct ast_encoder *ast_encoder;
ast_encoder = kzalloc(sizeof(struct ast_encoder), GFP_KERNEL);
if (!ast_encoder)
return -ENOMEM;
drm_encoder_init(dev, &ast_encoder->base, &ast_enc_funcs,
DRM_MODE_ENCODER_DAC);
drm_encoder_helper_add(&ast_encoder->base, &ast_enc_helper_funcs);
ast_encoder->base.possible_crtcs = 1;
return 0;
}
static int ast_get_modes(struct drm_connector *connector)
{
struct ast_connector *ast_connector = to_ast_connector(connector);
struct edid *edid;
int ret;
edid = drm_get_edid(connector, &ast_connector->i2c->adapter);
if (edid) {
drm_mode_connector_update_edid_property(&ast_connector->base, edid);
ret = drm_add_edid_modes(connector, edid);
return ret;
} else
drm_mode_connector_update_edid_property(&ast_connector->base, NULL);
return 0;
}
static int ast_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return MODE_OK;
}
static void ast_connector_destroy(struct drm_connector *connector)
{
struct ast_connector *ast_connector = to_ast_connector(connector);
ast_i2c_destroy(ast_connector->i2c);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
static enum drm_connector_status
ast_connector_detect(struct drm_connector *connector, bool force)
{
return connector_status_connected;
}
static const struct drm_connector_helper_funcs ast_connector_helper_funcs = {
.mode_valid = ast_mode_valid,
.get_modes = ast_get_modes,
.best_encoder = ast_best_single_encoder,
};
static const struct drm_connector_funcs ast_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = ast_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = ast_connector_destroy,
};
int ast_connector_init(struct drm_device *dev)
{
struct ast_connector *ast_connector;
struct drm_connector *connector;
struct drm_encoder *encoder;
ast_connector = kzalloc(sizeof(struct ast_connector), GFP_KERNEL);
if (!ast_connector)
return -ENOMEM;
connector = &ast_connector->base;
drm_connector_init(dev, connector, &ast_connector_funcs, DRM_MODE_CONNECTOR_VGA);
drm_connector_helper_add(connector, &ast_connector_helper_funcs);
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
drm_sysfs_connector_add(connector);
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
encoder = list_first_entry(&dev->mode_config.encoder_list, struct drm_encoder, head);
drm_mode_connector_attach_encoder(connector, encoder);
ast_connector->i2c = ast_i2c_create(dev);
if (!ast_connector->i2c)
DRM_ERROR("failed to add ddc bus for connector\n");
return 0;
}
/* allocate cursor cache and pin at start of VRAM */
int ast_cursor_init(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
int size;
int ret;
struct drm_gem_object *obj;
struct ast_bo *bo;
uint64_t gpu_addr;
size = (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE) * AST_DEFAULT_HWC_NUM;
ret = ast_gem_create(dev, size, true, &obj);
if (ret)
return ret;
bo = gem_to_ast_bo(obj);
ret = ast_bo_reserve(bo, false);
if (unlikely(ret != 0))
goto fail;
ret = ast_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
ast_bo_unreserve(bo);
if (ret)
goto fail;
/* kmap the object */
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &ast->cache_kmap);
if (ret)
goto fail;
ast->cursor_cache = obj;
ast->cursor_cache_gpu_addr = gpu_addr;
DRM_ERROR("pinned cursor cache at %llx\n", ast->cursor_cache_gpu_addr);
return 0;
fail:
return ret;
}
void ast_cursor_fini(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
ttm_bo_kunmap(&ast->cache_kmap);
drm_gem_object_unreference_unlocked(ast->cursor_cache);
}
int ast_mode_init(struct drm_device *dev)
{
ast_cursor_init(dev);
ast_crtc_init(dev);
ast_encoder_init(dev);
ast_connector_init(dev);
return 0;
}
void ast_mode_fini(struct drm_device *dev)
{
ast_cursor_fini(dev);
}
static int get_clock(void *i2c_priv)
{
struct ast_i2c_chan *i2c = i2c_priv;
struct ast_private *ast = i2c->dev->dev_private;
uint32_t val;
val = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x10) >> 4;
return val & 1 ? 1 : 0;
}
static int get_data(void *i2c_priv)
{
struct ast_i2c_chan *i2c = i2c_priv;
struct ast_private *ast = i2c->dev->dev_private;
uint32_t val;
val = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x20) >> 5;
return val & 1 ? 1 : 0;
}
static void set_clock(void *i2c_priv, int clock)
{
struct ast_i2c_chan *i2c = i2c_priv;
struct ast_private *ast = i2c->dev->dev_private;
int i;
u8 ujcrb7, jtemp;
for (i = 0; i < 0x10000; i++) {
ujcrb7 = ((clock & 0x01) ? 0 : 1);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xfe, ujcrb7);
jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x01);
if (ujcrb7 == jtemp)
break;
}
}
static void set_data(void *i2c_priv, int data)
{
struct ast_i2c_chan *i2c = i2c_priv;
struct ast_private *ast = i2c->dev->dev_private;
int i;
u8 ujcrb7, jtemp;
for (i = 0; i < 0x10000; i++) {
ujcrb7 = ((data & 0x01) ? 0 : 1) << 2;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0xfb, ujcrb7);
jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x04);
if (ujcrb7 == jtemp)
break;
}
}
static struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev)
{
struct ast_i2c_chan *i2c;
int ret;
i2c = kzalloc(sizeof(struct ast_i2c_chan), GFP_KERNEL);
if (!i2c)
return NULL;
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.class = I2C_CLASS_DDC;
i2c->adapter.dev.parent = &dev->pdev->dev;
i2c->dev = dev;
i2c_set_adapdata(&i2c->adapter, i2c);
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
"AST i2c bit bus");
i2c->adapter.algo_data = &i2c->bit;
i2c->bit.udelay = 20;
i2c->bit.timeout = 2;
i2c->bit.data = i2c;
i2c->bit.setsda = set_data;
i2c->bit.setscl = set_clock;
i2c->bit.getsda = get_data;
i2c->bit.getscl = get_clock;
ret = i2c_bit_add_bus(&i2c->adapter);
if (ret) {
DRM_ERROR("Failed to register bit i2c\n");
goto out_free;
}
return i2c;
out_free:
kfree(i2c);
return NULL;
}
static void ast_i2c_destroy(struct ast_i2c_chan *i2c)
{
if (!i2c)
return;
i2c_del_adapter(&i2c->adapter);
kfree(i2c);
}
void ast_show_cursor(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
u8 jreg;
jreg = 0x2;
/* enable ARGB cursor */
jreg |= 1;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, jreg);
}
void ast_hide_cursor(struct drm_crtc *crtc)
{
struct ast_private *ast = crtc->dev->dev_private;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xcb, 0xfc, 0x00);
}
static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height)
{
union {
u32 ul;
u8 b[4];
} srcdata32[2], data32;
union {
u16 us;
u8 b[2];
} data16;
u32 csum = 0;
s32 alpha_dst_delta, last_alpha_dst_delta;
u8 *srcxor, *dstxor;
int i, j;
u32 per_pixel_copy, two_pixel_copy;
alpha_dst_delta = AST_MAX_HWC_WIDTH << 1;
last_alpha_dst_delta = alpha_dst_delta - (width << 1);
srcxor = src;
dstxor = (u8 *)dst + last_alpha_dst_delta + (AST_MAX_HWC_HEIGHT - height) * alpha_dst_delta;
per_pixel_copy = width & 1;
two_pixel_copy = width >> 1;
for (j = 0; j < height; j++) {
for (i = 0; i < two_pixel_copy; i++) {
srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0;
srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0;
data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4);
data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4);
data32.b[2] = srcdata32[0].b[1] | (srcdata32[1].b[0] >> 4);
data32.b[3] = srcdata32[0].b[3] | (srcdata32[1].b[2] >> 4);
writel(data32.ul, dstxor);
csum += data32.ul;
dstxor += 4;
srcxor += 8;
}
for (i = 0; i < per_pixel_copy; i++) {
srcdata32[0].ul = *((u32 *)srcxor) & 0xf0f0f0f0;
data16.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4);
data16.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4);
writew(data16.us, dstxor);
csum += (u32)data16.us;
dstxor += 2;
srcxor += 4;
}
dstxor += last_alpha_dst_delta;
}
return csum;
}
static int ast_cursor_set(struct drm_crtc *crtc,
struct drm_file *file_priv,
uint32_t handle,
uint32_t width,
uint32_t height)
{
struct ast_private *ast = crtc->dev->dev_private;
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
struct drm_gem_object *obj;
struct ast_bo *bo;
uint64_t gpu_addr;
u32 csum;
int ret;
struct ttm_bo_kmap_obj uobj_map;
u8 *src, *dst;
bool src_isiomem, dst_isiomem;
if (!handle) {
ast_hide_cursor(crtc);
return 0;
}
if (width > AST_MAX_HWC_WIDTH || height > AST_MAX_HWC_HEIGHT)
return -EINVAL;
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
if (!obj) {
DRM_ERROR("Cannot find cursor object %x for crtc\n", handle);
return -ENOENT;
}
bo = gem_to_ast_bo(obj);
ret = ast_bo_reserve(bo, false);
if (ret)
goto fail;
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map);
src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem);
dst = ttm_kmap_obj_virtual(&ast->cache_kmap, &dst_isiomem);
if (src_isiomem == true)
DRM_ERROR("src cursor bo should be in main memory\n");
if (dst_isiomem == false)
DRM_ERROR("dst bo should be in VRAM\n");
dst += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor;
/* do data transfer to cursor cache */
csum = copy_cursor_image(src, dst, width, height);
/* write checksum + signature */
ttm_bo_kunmap(&uobj_map);
ast_bo_unreserve(bo);
{
u8 *dst = (u8 *)ast->cache_kmap.virtual + (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE;
writel(csum, dst);
writel(width, dst + AST_HWC_SIGNATURE_SizeX);
writel(height, dst + AST_HWC_SIGNATURE_SizeY);
writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTX);
writel(0, dst + AST_HWC_SIGNATURE_HOTSPOTY);
/* set pattern offset */
gpu_addr = ast->cursor_cache_gpu_addr;
gpu_addr += (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor;
gpu_addr >>= 3;
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc8, gpu_addr & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc9, (gpu_addr >> 8) & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xca, (gpu_addr >> 16) & 0xff);
}
ast_crtc->cursor_width = width;
ast_crtc->cursor_height = height;
ast_crtc->offset_x = AST_MAX_HWC_WIDTH - width;
ast_crtc->offset_y = AST_MAX_HWC_WIDTH - height;
ast->next_cursor = (ast->next_cursor + 1) % AST_DEFAULT_HWC_NUM;
ast_show_cursor(crtc);
drm_gem_object_unreference_unlocked(obj);
return 0;
fail:
drm_gem_object_unreference_unlocked(obj);
return ret;
}
static int ast_cursor_move(struct drm_crtc *crtc,
int x, int y)
{
struct ast_crtc *ast_crtc = to_ast_crtc(crtc);
struct ast_private *ast = crtc->dev->dev_private;
int x_offset, y_offset;
u8 *sig;
sig = (u8 *)ast->cache_kmap.virtual + (AST_HWC_SIZE + AST_HWC_SIGNATURE_SIZE)*ast->next_cursor + AST_HWC_SIZE;
writel(x, sig + AST_HWC_SIGNATURE_X);
writel(y, sig + AST_HWC_SIGNATURE_Y);
x_offset = ast_crtc->offset_x;
y_offset = ast_crtc->offset_y;
if (x < 0) {
x_offset = (-x) + ast_crtc->offset_x;
x = 0;
}
if (y < 0) {
y_offset = (-y) + ast_crtc->offset_y;
y = 0;
}
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc2, x_offset);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc3, y_offset);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc4, (x & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc5, ((x >> 8) & 0x0f));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc6, (y & 0xff));
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07));
/* dummy write to fire HWC */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xCB, 0xFF, 0x00);
return 0;
}
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*/
/*
* Authors: Dave Airlie <airlied@redhat.com>
*/
#include "drmP.h"
#include "ast_drv.h"
#include "ast_dram_tables.h"
static void ast_init_dram_2300(struct drm_device *dev);
static void
ast_enable_vga(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
ast_io_write8(ast, 0x43, 0x01);
ast_io_write8(ast, 0x42, 0x01);
}
#if 0 /* will use later */
static bool
ast_is_vga_enabled(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 ch;
if (ast->chip == AST1180) {
/* TODO 1180 */
} else {
ch = ast_io_read8(ast, 0x43);
if (ch) {
ast_open_key(ast);
ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff);
return ch & 0x04;
}
}
return 0;
}
#endif
static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff };
static const u8 extreginfo_ast2300[] = { 0x0f, 0x04, 0x1f, 0xff };
static void
ast_set_def_ext_reg(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 i, index, reg;
const u8 *ext_reg_info;
/* reset scratch */
for (i = 0x81; i <= 0x8f; i++)
ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00);
if (ast->chip == AST2300) {
if (dev->pdev->revision >= 0x20)
ext_reg_info = extreginfo_ast2300;
else
ext_reg_info = extreginfo_ast2300a0;
} else
ext_reg_info = extreginfo;
index = 0xa0;
while (*ext_reg_info != 0xff) {
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, index, 0x00, *ext_reg_info);
index++;
ext_reg_info++;
}
/* disable standard IO/MEM decode if secondary */
/* ast_set_index_reg-mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x3); */
/* Set Ext. Default */
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x8c, 0x00, 0x01);
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb7, 0x00, 0x00);
/* Enable RAMDAC for A1 */
reg = 0x04;
if (ast->chip == AST2300)
reg |= 0x20;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg);
}
static inline u32 mindwm(struct ast_private *ast, u32 r)
{
ast_write32(ast, 0xf004, r & 0xffff0000);
ast_write32(ast, 0xf000, 0x1);
return ast_read32(ast, 0x10000 + (r & 0x0000ffff));
}
static inline void moutdwm(struct ast_private *ast, u32 r, u32 v)
{
ast_write32(ast, 0xf004, r & 0xffff0000);
ast_write32(ast, 0xf000, 0x1);
ast_write32(ast, 0x10000 + (r & 0x0000ffff), v);
}
/*
* AST2100/2150 DLL CBR Setting
*/
#define CBR_SIZE_AST2150 ((16 << 10) - 1)
#define CBR_PASSNUM_AST2150 5
#define CBR_THRESHOLD_AST2150 10
#define CBR_THRESHOLD2_AST2150 10
#define TIMEOUT_AST2150 5000000
#define CBR_PATNUM_AST2150 8
static const u32 pattern_AST2150[14] = {
0xFF00FF00,
0xCC33CC33,
0xAA55AA55,
0xFFFE0001,
0x683501FE,
0x0F1929B0,
0x2D0B4346,
0x60767F02,
0x6FBE36A6,
0x3A253035,
0x3019686D,
0x41C6167E,
0x620152BF,
0x20F050E0
};
static u32 mmctestburst2_ast2150(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0xffffffff;
}
} while (!data);
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0xffffffff;
}
} while (!data);
data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
moutdwm(ast, 0x1e6e0070, 0x00000000);
return data;
}
#if 0 /* unused in DDX driver - here for completeness */
static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0xffffffff;
}
} while (!data);
data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
moutdwm(ast, 0x1e6e0070, 0x00000000);
return data;
}
#endif
static int cbrtest_ast2150(struct ast_private *ast)
{
int i;
for (i = 0; i < 8; i++)
if (mmctestburst2_ast2150(ast, i))
return 0;
return 1;
}
static int cbrscan_ast2150(struct ast_private *ast, int busw)
{
u32 patcnt, loop;
for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
if (cbrtest_ast2150(ast))
break;
}
if (loop == CBR_PASSNUM_AST2150)
return 0;
}
return 1;
}
static void cbrdlli_ast2150(struct ast_private *ast, int busw)
{
u32 dll_min[4], dll_max[4], dlli, data, passcnt;
cbr_start:
dll_min[0] = dll_min[1] = dll_min[2] = dll_min[3] = 0xff;
dll_max[0] = dll_max[1] = dll_max[2] = dll_max[3] = 0x0;
passcnt = 0;
for (dlli = 0; dlli < 100; dlli++) {
moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
data = cbrscan_ast2150(ast, busw);
if (data != 0) {
if (data & 0x1) {
if (dll_min[0] > dlli)
dll_min[0] = dlli;
if (dll_max[0] < dlli)
dll_max[0] = dlli;
}
passcnt++;
} else if (passcnt >= CBR_THRESHOLD_AST2150)
goto cbr_start;
}
if (dll_max[0] == 0 || (dll_max[0]-dll_min[0]) < CBR_THRESHOLD_AST2150)
goto cbr_start;
dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
}
static void ast_init_dram_reg(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 j;
u32 data, temp, i;
const struct ast_dramstruct *dram_reg_info;
j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
if ((j & 0x80) == 0) { /* VGA only */
if (ast->chip == AST2000) {
dram_reg_info = ast2000_dram_table_data;
ast_write32(ast, 0xf004, 0x1e6e0000);
ast_write32(ast, 0xf000, 0x1);
ast_write32(ast, 0x10100, 0xa8);
do {
;
} while (ast_read32(ast, 0x10100) != 0xa8);
} else {/* AST2100/1100 */
if (ast->chip == AST2100 || ast->chip == 2200)
dram_reg_info = ast2100_dram_table_data;
else
dram_reg_info = ast1100_dram_table_data;
ast_write32(ast, 0xf004, 0x1e6e0000);
ast_write32(ast, 0xf000, 0x1);
ast_write32(ast, 0x12000, 0x1688A8A8);
do {
;
} while (ast_read32(ast, 0x12000) != 0x01);
ast_write32(ast, 0x10000, 0xfc600309);
do {
;
} while (ast_read32(ast, 0x10000) != 0x01);
}
while (dram_reg_info->index != 0xffff) {
if (dram_reg_info->index == 0xff00) {/* delay fn */
for (i = 0; i < 15; i++)
udelay(dram_reg_info->data);
} else if (dram_reg_info->index == 0x4 && ast->chip != AST2000) {
data = dram_reg_info->data;
if (ast->dram_type == AST_DRAM_1Gx16)
data = 0x00000d89;
else if (ast->dram_type == AST_DRAM_1Gx32)
data = 0x00000c8d;
temp = ast_read32(ast, 0x12070);
temp &= 0xc;
temp <<= 2;
ast_write32(ast, 0x10000 + dram_reg_info->index, data | temp);
} else
ast_write32(ast, 0x10000 + dram_reg_info->index, dram_reg_info->data);
dram_reg_info++;
}
/* AST 2100/2150 DRAM calibration */
data = ast_read32(ast, 0x10120);
if (data == 0x5061) { /* 266Mhz */
data = ast_read32(ast, 0x10004);
if (data & 0x40)
cbrdlli_ast2150(ast, 16); /* 16 bits */
else
cbrdlli_ast2150(ast, 32); /* 32 bits */
}
switch (ast->chip) {
case AST2000:
temp = ast_read32(ast, 0x10140);
ast_write32(ast, 0x10140, temp | 0x40);
break;
case AST1100:
case AST2100:
case AST2200:
case AST2150:
temp = ast_read32(ast, 0x1200c);
ast_write32(ast, 0x1200c, temp & 0xfffffffd);
temp = ast_read32(ast, 0x12040);
ast_write32(ast, 0x12040, temp | 0x40);
break;
default:
break;
}
}
/* wait ready */
do {
j = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
} while ((j & 0x40) == 0);
}
void ast_post_gpu(struct drm_device *dev)
{
u32 reg;
struct ast_private *ast = dev->dev_private;
pci_read_config_dword(ast->dev->pdev, 0x04, &reg);
reg |= 0x3;
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
ast_enable_vga(dev);
ast_open_key(ast);
ast_set_def_ext_reg(dev);
if (ast->chip == AST2300)
ast_init_dram_2300(dev);
else
ast_init_dram_reg(dev);
}
/* AST 2300 DRAM settings */
#define AST_DDR3 0
#define AST_DDR2 1
struct ast2300_dram_param {
u32 dram_type;
u32 dram_chipid;
u32 dram_freq;
u32 vram_size;
u32 odt;
u32 wodt;
u32 rodt;
u32 dram_config;
u32 reg_PERIOD;
u32 reg_MADJ;
u32 reg_SADJ;
u32 reg_MRS;
u32 reg_EMRS;
u32 reg_AC1;
u32 reg_AC2;
u32 reg_DQSIC;
u32 reg_DRV;
u32 reg_IOZ;
u32 reg_DQIDLY;
u32 reg_FREQ;
u32 madj_max;
u32 dll2_finetune_step;
};
/*
* DQSI DLL CBR Setting
*/
#define CBR_SIZE1 ((4 << 10) - 1)
#define CBR_SIZE2 ((64 << 10) - 1)
#define CBR_PASSNUM 5
#define CBR_PASSNUM2 5
#define CBR_THRESHOLD 10
#define CBR_THRESHOLD2 10
#define TIMEOUT 5000000
#define CBR_PATNUM 8
static const u32 pattern[8] = {
0xFF00FF00,
0xCC33CC33,
0xAA55AA55,
0x88778877,
0x92CC4D6E,
0x543D3CDE,
0xF1E843C7,
0x7C61D253
};
#if 0 /* unused in DDX, included for completeness */
static int mmc_test_burst(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x3000;
if (data & 0x2000) {
return 0;
}
if (++timeout > TIMEOUT) {
moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0;
}
} while (!data);
moutdwm(ast, 0x1e6e0070, 0x00000000);
return 1;
}
#endif
static int mmc_test_burst2(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x1000;
if (++timeout > TIMEOUT) {
moutdwm(ast, 0x1e6e0070, 0x0);
return -1;
}
} while (!data);
data = mindwm(ast, 0x1e6e0078);
data = (data | (data >> 16)) & 0xffff;
moutdwm(ast, 0x1e6e0070, 0x0);
return data;
}
#if 0 /* Unused in DDX here for completeness */
static int mmc_test_single(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x3000;
if (data & 0x2000)
return 0;
if (++timeout > TIMEOUT) {
moutdwm(ast, 0x1e6e0070, 0x0);
return 0;
}
} while (!data);
moutdwm(ast, 0x1e6e0070, 0x0);
return 1;
}
#endif
static int mmc_test_single2(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
moutdwm(ast, 0x1e6e0070, 0x00000000);
moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
timeout = 0;
do {
data = mindwm(ast, 0x1e6e0070) & 0x1000;
if (++timeout > TIMEOUT) {
moutdwm(ast, 0x1e6e0070, 0x0);
return -1;
}
} while (!data);
data = mindwm(ast, 0x1e6e0078);
data = (data | (data >> 16)) & 0xffff;
moutdwm(ast, 0x1e6e0070, 0x0);
return data;
}
static int cbr_test(struct ast_private *ast)
{
u32 data;
int i;
data = mmc_test_single2(ast, 0);
if ((data & 0xff) && (data & 0xff00))
return 0;
for (i = 0; i < 8; i++) {
data = mmc_test_burst2(ast, i);
if ((data & 0xff) && (data & 0xff00))
return 0;
}
if (!data)
return 3;
else if (data & 0xff)
return 2;
return 1;
}
static int cbr_scan(struct ast_private *ast)
{
u32 data, data2, patcnt, loop;
data2 = 3;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
if ((data = cbr_test(ast)) != 0) {
data2 &= data;
if (!data2)
return 0;
break;
}
}
if (loop == CBR_PASSNUM2)
return 0;
}
return data2;
}
static u32 cbr_test2(struct ast_private *ast)
{
u32 data;
data = mmc_test_burst2(ast, 0);
if (data == 0xffff)
return 0;
data |= mmc_test_single2(ast, 0);
if (data == 0xffff)
return 0;
return ~data & 0xffff;
}
static u32 cbr_scan2(struct ast_private *ast)
{
u32 data, data2, patcnt, loop;
data2 = 0xffff;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
if ((data = cbr_test2(ast)) != 0) {
data2 &= data;
if (!data)
return 0;
break;
}
}
if (loop == CBR_PASSNUM2)
return 0;
}
return data2;
}
#if 0 /* unused in DDX - added for completeness */
static void finetuneDQI(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt;
gold_sadj[0] = (mindwm(ast, 0x1E6E0024) >> 16) & 0xffff;
gold_sadj[1] = gold_sadj[0] >> 8;
gold_sadj[0] = gold_sadj[0] & 0xff;
gold_sadj[0] = (gold_sadj[0] + gold_sadj[1]) >> 1;
gold_sadj[1] = gold_sadj[0];
for (cnt = 0; cnt < 16; cnt++) {
dllmin[cnt] = 0xff;
dllmax[cnt] = 0x0;
}
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
/* Wait DQSI latch phase calibration */
moutdwm(ast, 0x1E6E0074, 0x00000010);
moutdwm(ast, 0x1E6E0070, 0x00000003);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
data = cbr_scan2(ast);
if (data != 0) {
mask = 0x00010001;
for (cnt = 0; cnt < 16; cnt++) {
if (data & mask) {
if (dllmin[cnt] > dlli) {
dllmin[cnt] = dlli;
}
if (dllmax[cnt] < dlli) {
dllmax[cnt] = dlli;
}
}
mask <<= 1;
}
passcnt++;
} else if (passcnt >= CBR_THRESHOLD) {
break;
}
}
data = 0;
for (cnt = 0; cnt < 8; cnt++) {
data >>= 3;
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) {
dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
if (gold_sadj[0] >= dlli) {
dlli = (gold_sadj[0] - dlli) >> 1;
if (dlli > 3) {
dlli = 3;
}
} else {
dlli = (dlli - gold_sadj[0]) >> 1;
if (dlli > 4) {
dlli = 4;
}
dlli = (8 - dlli) & 0x7;
}
data |= dlli << 21;
}
}
moutdwm(ast, 0x1E6E0080, data);
data = 0;
for (cnt = 8; cnt < 16; cnt++) {
data >>= 3;
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) {
dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
if (gold_sadj[1] >= dlli) {
dlli = (gold_sadj[1] - dlli) >> 1;
if (dlli > 3) {
dlli = 3;
} else {
dlli = (dlli - 1) & 0x7;
}
} else {
dlli = (dlli - gold_sadj[1]) >> 1;
dlli += 1;
if (dlli > 4) {
dlli = 4;
}
dlli = (8 - dlli) & 0x7;
}
data |= dlli << 21;
}
}
moutdwm(ast, 0x1E6E0084, data);
} /* finetuneDQI */
#endif
static void finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt;
FINETUNE_START:
for (cnt = 0; cnt < 16; cnt++) {
dllmin[cnt] = 0xff;
dllmax[cnt] = 0x0;
}
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
/* Wait DQSI latch phase calibration */
moutdwm(ast, 0x1E6E0074, 0x00000010);
moutdwm(ast, 0x1E6E0070, 0x00000003);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
data = cbr_scan2(ast);
if (data != 0) {
mask = 0x00010001;
for (cnt = 0; cnt < 16; cnt++) {
if (data & mask) {
if (dllmin[cnt] > dlli) {
dllmin[cnt] = dlli;
}
if (dllmax[cnt] < dlli) {
dllmax[cnt] = dlli;
}
}
mask <<= 1;
}
passcnt++;
} else if (passcnt >= CBR_THRESHOLD2) {
break;
}
}
gold_sadj[0] = 0x0;
passcnt = 0;
for (cnt = 0; cnt < 16; cnt++) {
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
gold_sadj[0] += dllmin[cnt];
passcnt++;
}
}
if (passcnt != 16) {
goto FINETUNE_START;
}
gold_sadj[0] = gold_sadj[0] >> 4;
gold_sadj[1] = gold_sadj[0];
data = 0;
for (cnt = 0; cnt < 8; cnt++) {
data >>= 3;
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
dlli = dllmin[cnt];
if (gold_sadj[0] >= dlli) {
dlli = ((gold_sadj[0] - dlli) * 19) >> 5;
if (dlli > 3) {
dlli = 3;
}
} else {
dlli = ((dlli - gold_sadj[0]) * 19) >> 5;
if (dlli > 4) {
dlli = 4;
}
dlli = (8 - dlli) & 0x7;
}
data |= dlli << 21;
}
}
moutdwm(ast, 0x1E6E0080, data);
data = 0;
for (cnt = 8; cnt < 16; cnt++) {
data >>= 3;
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
dlli = dllmin[cnt];
if (gold_sadj[1] >= dlli) {
dlli = ((gold_sadj[1] - dlli) * 19) >> 5;
if (dlli > 3) {
dlli = 3;
} else {
dlli = (dlli - 1) & 0x7;
}
} else {
dlli = ((dlli - gold_sadj[1]) * 19) >> 5;
dlli += 1;
if (dlli > 4) {
dlli = 4;
}
dlli = (8 - dlli) & 0x7;
}
data |= dlli << 21;
}
}
moutdwm(ast, 0x1E6E0084, data);
} /* finetuneDQI_L */
static void finetuneDQI_L2(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, data2;
for (cnt = 0; cnt < 16; cnt++) {
dllmin[cnt] = 0xff;
dllmax[cnt] = 0x0;
}
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
/* Wait DQSI latch phase calibration */
moutdwm(ast, 0x1E6E0074, 0x00000010);
moutdwm(ast, 0x1E6E0070, 0x00000003);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
data = cbr_scan2(ast);
if (data != 0) {
mask = 0x00010001;
for (cnt = 0; cnt < 16; cnt++) {
if (data & mask) {
if (dllmin[cnt] > dlli) {
dllmin[cnt] = dlli;
}
if (dllmax[cnt] < dlli) {
dllmax[cnt] = dlli;
}
}
mask <<= 1;
}
passcnt++;
} else if (passcnt >= CBR_THRESHOLD2) {
break;
}
}
gold_sadj[0] = 0x0;
gold_sadj[1] = 0xFF;
for (cnt = 0; cnt < 8; cnt++) {
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
if (gold_sadj[0] < dllmin[cnt]) {
gold_sadj[0] = dllmin[cnt];
}
if (gold_sadj[1] > dllmax[cnt]) {
gold_sadj[1] = dllmax[cnt];
}
}
}
gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1;
gold_sadj[1] = mindwm(ast, 0x1E6E0080);
data = 0;
for (cnt = 0; cnt < 8; cnt++) {
data >>= 3;
data2 = gold_sadj[1] & 0x7;
gold_sadj[1] >>= 3;
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
if (gold_sadj[0] >= dlli) {
dlli = (gold_sadj[0] - dlli) >> 1;
if (dlli > 0) {
dlli = 1;
}
if (data2 != 3) {
data2 = (data2 + dlli) & 0x7;
}
} else {
dlli = (dlli - gold_sadj[0]) >> 1;
if (dlli > 0) {
dlli = 1;
}
if (data2 != 4) {
data2 = (data2 - dlli) & 0x7;
}
}
}
data |= data2 << 21;
}
moutdwm(ast, 0x1E6E0080, data);
gold_sadj[0] = 0x0;
gold_sadj[1] = 0xFF;
for (cnt = 8; cnt < 16; cnt++) {
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
if (gold_sadj[0] < dllmin[cnt]) {
gold_sadj[0] = dllmin[cnt];
}
if (gold_sadj[1] > dllmax[cnt]) {
gold_sadj[1] = dllmax[cnt];
}
}
}
gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1;
gold_sadj[1] = mindwm(ast, 0x1E6E0084);
data = 0;
for (cnt = 8; cnt < 16; cnt++) {
data >>= 3;
data2 = gold_sadj[1] & 0x7;
gold_sadj[1] >>= 3;
if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
if (gold_sadj[0] >= dlli) {
dlli = (gold_sadj[0] - dlli) >> 1;
if (dlli > 0) {
dlli = 1;
}
if (data2 != 3) {
data2 = (data2 + dlli) & 0x7;
}
} else {
dlli = (dlli - gold_sadj[0]) >> 1;
if (dlli > 0) {
dlli = 1;
}
if (data2 != 4) {
data2 = (data2 - dlli) & 0x7;
}
}
}
data |= data2 << 21;
}
moutdwm(ast, 0x1E6E0084, data);
} /* finetuneDQI_L2 */
static void cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 dllmin[2], dllmax[2], dlli, data, data2, passcnt;
finetuneDQI_L(ast, param);
finetuneDQI_L2(ast, param);
CBR_START2:
dllmin[0] = dllmin[1] = 0xff;
dllmax[0] = dllmax[1] = 0x0;
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
/* Wait DQSI latch phase calibration */
moutdwm(ast, 0x1E6E0074, 0x00000010);
moutdwm(ast, 0x1E6E0070, 0x00000003);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
data = cbr_scan(ast);
if (data != 0) {
if (data & 0x1) {
if (dllmin[0] > dlli) {
dllmin[0] = dlli;
}
if (dllmax[0] < dlli) {
dllmax[0] = dlli;
}
}
if (data & 0x2) {
if (dllmin[1] > dlli) {
dllmin[1] = dlli;
}
if (dllmax[1] < dlli) {
dllmax[1] = dlli;
}
}
passcnt++;
} else if (passcnt >= CBR_THRESHOLD) {
break;
}
}
if (dllmax[0] == 0 || (dllmax[0]-dllmin[0]) < CBR_THRESHOLD) {
goto CBR_START2;
}
if (dllmax[1] == 0 || (dllmax[1]-dllmin[1]) < CBR_THRESHOLD) {
goto CBR_START2;
}
dlli = (dllmin[1] + dllmax[1]) >> 1;
dlli <<= 8;
dlli += (dllmin[0] + dllmax[0]) >> 1;
moutdwm(ast, 0x1E6E0068, (mindwm(ast, 0x1E6E0068) & 0xFFFF) | (dlli << 16));
data = (mindwm(ast, 0x1E6E0080) >> 24) & 0x1F;
data2 = (mindwm(ast, 0x1E6E0018) & 0xff80ffff) | (data << 16);
moutdwm(ast, 0x1E6E0018, data2);
moutdwm(ast, 0x1E6E0024, 0x8001 | (data << 1) | (param->dll2_finetune_step << 8));
/* Wait DQSI latch phase calibration */
moutdwm(ast, 0x1E6E0074, 0x00000010);
moutdwm(ast, 0x1E6E0070, 0x00000003);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0070, 0x00000003);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
} /* CBRDLL2 */
static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 trap, trap_AC2, trap_MRS;
moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
/* Ger trap info */
trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
trap_AC2 = 0x00020000 + (trap << 16);
trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19);
trap_MRS = 0x00000010 + (trap << 4);
trap_MRS |= ((trap & 0x2) << 18);
param->reg_MADJ = 0x00034C4C;
param->reg_SADJ = 0x00001800;
param->reg_DRV = 0x000000F0;
param->reg_PERIOD = param->dram_freq;
param->rodt = 0;
switch (param->dram_freq) {
case 336:
moutdwm(ast, 0x1E6E2020, 0x0190);
param->wodt = 0;
param->reg_AC1 = 0x22202725;
param->reg_AC2 = 0xAA007613 | trap_AC2;
param->reg_DQSIC = 0x000000BA;
param->reg_MRS = 0x04001400 | trap_MRS;
param->reg_EMRS = 0x00000000;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000074;
param->reg_FREQ = 0x00004DC0;
param->madj_max = 96;
param->dll2_finetune_step = 3;
break;
default:
case 396:
moutdwm(ast, 0x1E6E2020, 0x03F1);
param->wodt = 1;
param->reg_AC1 = 0x33302825;
param->reg_AC2 = 0xCC009617 | trap_AC2;
param->reg_DQSIC = 0x000000E2;
param->reg_MRS = 0x04001600 | trap_MRS;
param->reg_EMRS = 0x00000000;
param->reg_IOZ = 0x00000034;
param->reg_DRV = 0x000000FA;
param->reg_DQIDLY = 0x00000089;
param->reg_FREQ = 0x000050C0;
param->madj_max = 96;
param->dll2_finetune_step = 4;
switch (param->dram_chipid) {
default:
case AST_DRAM_512Mx16:
case AST_DRAM_1Gx16:
param->reg_AC2 = 0xCC009617 | trap_AC2;
break;
case AST_DRAM_2Gx16:
param->reg_AC2 = 0xCC009622 | trap_AC2;
break;
case AST_DRAM_4Gx16:
param->reg_AC2 = 0xCC00963F | trap_AC2;
break;
}
break;
case 408:
moutdwm(ast, 0x1E6E2020, 0x01F0);
param->wodt = 1;
param->reg_AC1 = 0x33302825;
param->reg_AC2 = 0xCC009617 | trap_AC2;
param->reg_DQSIC = 0x000000E2;
param->reg_MRS = 0x04001600 | trap_MRS;
param->reg_EMRS = 0x00000000;
param->reg_IOZ = 0x00000034;
param->reg_DRV = 0x000000FA;
param->reg_DQIDLY = 0x00000089;
param->reg_FREQ = 0x000050C0;
param->madj_max = 96;
param->dll2_finetune_step = 4;
switch (param->dram_chipid) {
default:
case AST_DRAM_512Mx16:
case AST_DRAM_1Gx16:
param->reg_AC2 = 0xCC009617 | trap_AC2;
break;
case AST_DRAM_2Gx16:
param->reg_AC2 = 0xCC009622 | trap_AC2;
break;
case AST_DRAM_4Gx16:
param->reg_AC2 = 0xCC00963F | trap_AC2;
break;
}
break;
case 456:
moutdwm(ast, 0x1E6E2020, 0x0230);
param->wodt = 0;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xCD44961A;
param->reg_DQSIC = 0x000000FC;
param->reg_MRS = 0x00081830;
param->reg_EMRS = 0x00000000;
param->reg_IOZ = 0x00000045;
param->reg_DQIDLY = 0x00000097;
param->reg_FREQ = 0x000052C0;
param->madj_max = 88;
param->dll2_finetune_step = 4;
break;
case 504:
moutdwm(ast, 0x1E6E2020, 0x0270);
param->wodt = 1;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xDE44A61D;
param->reg_DQSIC = 0x00000117;
param->reg_MRS = 0x00081A30;
param->reg_EMRS = 0x00000000;
param->reg_IOZ = 0x070000BB;
param->reg_DQIDLY = 0x000000A0;
param->reg_FREQ = 0x000054C0;
param->madj_max = 79;
param->dll2_finetune_step = 4;
break;
case 528:
moutdwm(ast, 0x1E6E2020, 0x0290);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xEF44B61E;
param->reg_DQSIC = 0x00000125;
param->reg_MRS = 0x00081A30;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x000000F5;
param->reg_IOZ = 0x00000023;
param->reg_DQIDLY = 0x00000088;
param->reg_FREQ = 0x000055C0;
param->madj_max = 76;
param->dll2_finetune_step = 3;
break;
case 576:
moutdwm(ast, 0x1E6E2020, 0x0140);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302A37;
param->reg_AC2 = 0xEF56B61E;
param->reg_DQSIC = 0x0000013F;
param->reg_MRS = 0x00101A50;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x000000FA;
param->reg_IOZ = 0x00000023;
param->reg_DQIDLY = 0x00000078;
param->reg_FREQ = 0x000057C0;
param->madj_max = 136;
param->dll2_finetune_step = 3;
break;
case 600:
moutdwm(ast, 0x1E6E2020, 0x02E1);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x32302A37;
param->reg_AC2 = 0xDF56B61F;
param->reg_DQSIC = 0x0000014D;
param->reg_MRS = 0x00101A50;
param->reg_EMRS = 0x00000004;
param->reg_DRV = 0x000000F5;
param->reg_IOZ = 0x00000023;
param->reg_DQIDLY = 0x00000078;
param->reg_FREQ = 0x000058C0;
param->madj_max = 132;
param->dll2_finetune_step = 3;
break;
case 624:
moutdwm(ast, 0x1E6E2020, 0x0160);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x32302A37;
param->reg_AC2 = 0xEF56B621;
param->reg_DQSIC = 0x0000015A;
param->reg_MRS = 0x02101A50;
param->reg_EMRS = 0x00000004;
param->reg_DRV = 0x000000F5;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000078;
param->reg_FREQ = 0x000059C0;
param->madj_max = 128;
param->dll2_finetune_step = 3;
break;
} /* switch freq */
switch (param->dram_chipid) {
case AST_DRAM_512Mx16:
param->dram_config = 0x130;
break;
default:
case AST_DRAM_1Gx16:
param->dram_config = 0x131;
break;
case AST_DRAM_2Gx16:
param->dram_config = 0x132;
break;
case AST_DRAM_4Gx16:
param->dram_config = 0x133;
break;
}; /* switch size */
switch (param->vram_size) {
default:
case AST_VIDMEM_SIZE_8M:
param->dram_config |= 0x00;
break;
case AST_VIDMEM_SIZE_16M:
param->dram_config |= 0x04;
break;
case AST_VIDMEM_SIZE_32M:
param->dram_config |= 0x08;
break;
case AST_VIDMEM_SIZE_64M:
param->dram_config |= 0x0c;
break;
}
}
static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 data, data2;
moutdwm(ast, 0x1E6E0000, 0xFC600309);
moutdwm(ast, 0x1E6E0018, 0x00000100);
moutdwm(ast, 0x1E6E0024, 0x00000000);
moutdwm(ast, 0x1E6E0034, 0x00000000);
udelay(10);
moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
udelay(10);
moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
udelay(10);
moutdwm(ast, 0x1E6E0004, param->dram_config);
moutdwm(ast, 0x1E6E0008, 0x90040f);
moutdwm(ast, 0x1E6E0010, param->reg_AC1);
moutdwm(ast, 0x1E6E0014, param->reg_AC2);
moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
moutdwm(ast, 0x1E6E0080, 0x00000000);
moutdwm(ast, 0x1E6E0084, 0x00000000);
moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
moutdwm(ast, 0x1E6E0018, 0x4040A170);
moutdwm(ast, 0x1E6E0018, 0x20402370);
moutdwm(ast, 0x1E6E0038, 0x00000000);
moutdwm(ast, 0x1E6E0040, 0xFF444444);
moutdwm(ast, 0x1E6E0044, 0x22222222);
moutdwm(ast, 0x1E6E0048, 0x22222222);
moutdwm(ast, 0x1E6E004C, 0x00000002);
moutdwm(ast, 0x1E6E0050, 0x80000000);
moutdwm(ast, 0x1E6E0050, 0x00000000);
moutdwm(ast, 0x1E6E0054, 0);
moutdwm(ast, 0x1E6E0060, param->reg_DRV);
moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0074, 0x00000000);
moutdwm(ast, 0x1E6E0078, 0x00000000);
moutdwm(ast, 0x1E6E007C, 0x00000000);
/* Wait MCLK2X lock to MCLK */
do {
data = mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
moutdwm(ast, 0x1E6E0034, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00005C04);
udelay(10);
moutdwm(ast, 0x1E6E000C, 0x00000000);
moutdwm(ast, 0x1E6E0034, 0x00000000);
data = mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
if ((data2 & 0xff) > param->madj_max) {
break;
}
moutdwm(ast, 0x1E6E0064, data2);
if (data2 & 0x00100000) {
data2 = ((data2 & 0xff) >> 3) + 3;
} else {
data2 = ((data2 & 0xff) >> 2) + 5;
}
data = mindwm(ast, 0x1E6E0068) & 0xffff00ff;
data2 += data & 0xff;
data = data | (data2 << 8);
moutdwm(ast, 0x1E6E0068, data);
udelay(10);
moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000);
udelay(10);
data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
moutdwm(ast, 0x1E6E0018, data);
data = data | 0x200;
moutdwm(ast, 0x1E6E0018, data);
do {
data = mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
moutdwm(ast, 0x1E6E0034, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00005C04);
udelay(10);
moutdwm(ast, 0x1E6E000C, 0x00000000);
moutdwm(ast, 0x1E6E0034, 0x00000000);
data = mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
}
data = mindwm(ast, 0x1E6E0018) | 0xC00;
moutdwm(ast, 0x1E6E0018, data);
moutdwm(ast, 0x1E6E0034, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00000040);
udelay(50);
/* Mode Register Setting */
moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
moutdwm(ast, 0x1E6E0028, 0x00000005);
moutdwm(ast, 0x1E6E0028, 0x00000007);
moutdwm(ast, 0x1E6E0028, 0x00000003);
moutdwm(ast, 0x1E6E0028, 0x00000001);
moutdwm(ast, 0x1E6E002C, param->reg_MRS);
moutdwm(ast, 0x1E6E000C, 0x00005C08);
moutdwm(ast, 0x1E6E0028, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
data = 0;
if (param->wodt) {
data = 0x300;
}
if (param->rodt) {
data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
}
moutdwm(ast, 0x1E6E0034, data | 0x3);
/* Wait DQI delay lock */
do {
data = mindwm(ast, 0x1E6E0080);
} while (!(data & 0x40000000));
/* Wait DQSI delay lock */
do {
data = mindwm(ast, 0x1E6E0020);
} while (!(data & 0x00000800));
/* Calibrate the DQSI delay */
cbr_dll2(ast, param);
moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
/* ECC Memory Initialization */
#ifdef ECC
moutdwm(ast, 0x1E6E007C, 0x00000000);
moutdwm(ast, 0x1E6E0070, 0x221);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0050, 0x80000000);
moutdwm(ast, 0x1E6E0050, 0x00000000);
#endif
}
static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 trap, trap_AC2, trap_MRS;
moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
/* Ger trap info */
trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
trap_AC2 = (trap << 20) | (trap << 16);
trap_AC2 += 0x00110000;
trap_MRS = 0x00000040 | (trap << 4);
param->reg_MADJ = 0x00034C4C;
param->reg_SADJ = 0x00001800;
param->reg_DRV = 0x000000F0;
param->reg_PERIOD = param->dram_freq;
param->rodt = 0;
switch (param->dram_freq) {
case 264:
moutdwm(ast, 0x1E6E2020, 0x0130);
param->wodt = 0;
param->reg_AC1 = 0x11101513;
param->reg_AC2 = 0x78117011;
param->reg_DQSIC = 0x00000092;
param->reg_MRS = 0x00000842;
param->reg_EMRS = 0x00000000;
param->reg_DRV = 0x000000F0;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x0000005A;
param->reg_FREQ = 0x00004AC0;
param->madj_max = 138;
param->dll2_finetune_step = 3;
break;
case 336:
moutdwm(ast, 0x1E6E2020, 0x0190);
param->wodt = 1;
param->reg_AC1 = 0x22202613;
param->reg_AC2 = 0xAA009016 | trap_AC2;
param->reg_DQSIC = 0x000000BA;
param->reg_MRS = 0x00000A02 | trap_MRS;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x000000FA;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000074;
param->reg_FREQ = 0x00004DC0;
param->madj_max = 96;
param->dll2_finetune_step = 3;
break;
default:
case 396:
moutdwm(ast, 0x1E6E2020, 0x03F1);
param->wodt = 1;
param->rodt = 0;
param->reg_AC1 = 0x33302714;
param->reg_AC2 = 0xCC00B01B | trap_AC2;
param->reg_DQSIC = 0x000000E2;
param->reg_MRS = 0x00000C02 | trap_MRS;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x000000FA;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000089;
param->reg_FREQ = 0x000050C0;
param->madj_max = 96;
param->dll2_finetune_step = 4;
switch (param->dram_chipid) {
case AST_DRAM_512Mx16:
param->reg_AC2 = 0xCC00B016 | trap_AC2;
break;
default:
case AST_DRAM_1Gx16:
param->reg_AC2 = 0xCC00B01B | trap_AC2;
break;
case AST_DRAM_2Gx16:
param->reg_AC2 = 0xCC00B02B | trap_AC2;
break;
case AST_DRAM_4Gx16:
param->reg_AC2 = 0xCC00B03F | trap_AC2;
break;
}
break;
case 408:
moutdwm(ast, 0x1E6E2020, 0x01F0);
param->wodt = 1;
param->rodt = 0;
param->reg_AC1 = 0x33302714;
param->reg_AC2 = 0xCC00B01B | trap_AC2;
param->reg_DQSIC = 0x000000E2;
param->reg_MRS = 0x00000C02 | trap_MRS;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x000000FA;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000089;
param->reg_FREQ = 0x000050C0;
param->madj_max = 96;
param->dll2_finetune_step = 4;
switch (param->dram_chipid) {
case AST_DRAM_512Mx16:
param->reg_AC2 = 0xCC00B016 | trap_AC2;
break;
default:
case AST_DRAM_1Gx16:
param->reg_AC2 = 0xCC00B01B | trap_AC2;
break;
case AST_DRAM_2Gx16:
param->reg_AC2 = 0xCC00B02B | trap_AC2;
break;
case AST_DRAM_4Gx16:
param->reg_AC2 = 0xCC00B03F | trap_AC2;
break;
}
break;
case 456:
moutdwm(ast, 0x1E6E2020, 0x0230);
param->wodt = 0;
param->reg_AC1 = 0x33302815;
param->reg_AC2 = 0xCD44B01E;
param->reg_DQSIC = 0x000000FC;
param->reg_MRS = 0x00000E72;
param->reg_EMRS = 0x00000000;
param->reg_DRV = 0x00000000;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000097;
param->reg_FREQ = 0x000052C0;
param->madj_max = 88;
param->dll2_finetune_step = 3;
break;
case 504:
moutdwm(ast, 0x1E6E2020, 0x0261);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302815;
param->reg_AC2 = 0xDE44C022;
param->reg_DQSIC = 0x00000117;
param->reg_MRS = 0x00000E72;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x0000000A;
param->reg_IOZ = 0x00000045;
param->reg_DQIDLY = 0x000000A0;
param->reg_FREQ = 0x000054C0;
param->madj_max = 79;
param->dll2_finetune_step = 3;
break;
case 528:
moutdwm(ast, 0x1E6E2020, 0x0120);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302815;
param->reg_AC2 = 0xEF44D024;
param->reg_DQSIC = 0x00000125;
param->reg_MRS = 0x00000E72;
param->reg_EMRS = 0x00000004;
param->reg_DRV = 0x000000F9;
param->reg_IOZ = 0x00000045;
param->reg_DQIDLY = 0x000000A7;
param->reg_FREQ = 0x000055C0;
param->madj_max = 76;
param->dll2_finetune_step = 3;
break;
case 552:
moutdwm(ast, 0x1E6E2020, 0x02A1);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x43402915;
param->reg_AC2 = 0xFF44E025;
param->reg_DQSIC = 0x00000132;
param->reg_MRS = 0x00000E72;
param->reg_EMRS = 0x00000040;
param->reg_DRV = 0x0000000A;
param->reg_IOZ = 0x00000045;
param->reg_DQIDLY = 0x000000AD;
param->reg_FREQ = 0x000056C0;
param->madj_max = 76;
param->dll2_finetune_step = 3;
break;
case 576:
moutdwm(ast, 0x1E6E2020, 0x0140);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x43402915;
param->reg_AC2 = 0xFF44E027;
param->reg_DQSIC = 0x0000013F;
param->reg_MRS = 0x00000E72;
param->reg_EMRS = 0x00000004;
param->reg_DRV = 0x000000F5;
param->reg_IOZ = 0x00000045;
param->reg_DQIDLY = 0x000000B3;
param->reg_FREQ = 0x000057C0;
param->madj_max = 76;
param->dll2_finetune_step = 3;
break;
}
switch (param->dram_chipid) {
case AST_DRAM_512Mx16:
param->dram_config = 0x100;
break;
default:
case AST_DRAM_1Gx16:
param->dram_config = 0x121;
break;
case AST_DRAM_2Gx16:
param->dram_config = 0x122;
break;
case AST_DRAM_4Gx16:
param->dram_config = 0x123;
break;
}; /* switch size */
switch (param->vram_size) {
default:
case AST_VIDMEM_SIZE_8M:
param->dram_config |= 0x00;
break;
case AST_VIDMEM_SIZE_16M:
param->dram_config |= 0x04;
break;
case AST_VIDMEM_SIZE_32M:
param->dram_config |= 0x08;
break;
case AST_VIDMEM_SIZE_64M:
param->dram_config |= 0x0c;
break;
}
}
static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 data, data2;
moutdwm(ast, 0x1E6E0000, 0xFC600309);
moutdwm(ast, 0x1E6E0018, 0x00000100);
moutdwm(ast, 0x1E6E0024, 0x00000000);
moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
udelay(10);
moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
udelay(10);
moutdwm(ast, 0x1E6E0004, param->dram_config);
moutdwm(ast, 0x1E6E0008, 0x90040f);
moutdwm(ast, 0x1E6E0010, param->reg_AC1);
moutdwm(ast, 0x1E6E0014, param->reg_AC2);
moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
moutdwm(ast, 0x1E6E0080, 0x00000000);
moutdwm(ast, 0x1E6E0084, 0x00000000);
moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
moutdwm(ast, 0x1E6E0018, 0x4040A130);
moutdwm(ast, 0x1E6E0018, 0x20402330);
moutdwm(ast, 0x1E6E0038, 0x00000000);
moutdwm(ast, 0x1E6E0040, 0xFF808000);
moutdwm(ast, 0x1E6E0044, 0x88848466);
moutdwm(ast, 0x1E6E0048, 0x44440008);
moutdwm(ast, 0x1E6E004C, 0x00000000);
moutdwm(ast, 0x1E6E0050, 0x80000000);
moutdwm(ast, 0x1E6E0050, 0x00000000);
moutdwm(ast, 0x1E6E0054, 0);
moutdwm(ast, 0x1E6E0060, param->reg_DRV);
moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0074, 0x00000000);
moutdwm(ast, 0x1E6E0078, 0x00000000);
moutdwm(ast, 0x1E6E007C, 0x00000000);
/* Wait MCLK2X lock to MCLK */
do {
data = mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
moutdwm(ast, 0x1E6E0034, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00005C04);
udelay(10);
moutdwm(ast, 0x1E6E000C, 0x00000000);
moutdwm(ast, 0x1E6E0034, 0x00000000);
data = mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
if ((data2 & 0xff) > param->madj_max) {
break;
}
moutdwm(ast, 0x1E6E0064, data2);
if (data2 & 0x00100000) {
data2 = ((data2 & 0xff) >> 3) + 3;
} else {
data2 = ((data2 & 0xff) >> 2) + 5;
}
data = mindwm(ast, 0x1E6E0068) & 0xffff00ff;
data2 += data & 0xff;
data = data | (data2 << 8);
moutdwm(ast, 0x1E6E0068, data);
udelay(10);
moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000);
udelay(10);
data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
moutdwm(ast, 0x1E6E0018, data);
data = data | 0x200;
moutdwm(ast, 0x1E6E0018, data);
do {
data = mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
moutdwm(ast, 0x1E6E0034, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00005C04);
udelay(10);
moutdwm(ast, 0x1E6E000C, 0x00000000);
moutdwm(ast, 0x1E6E0034, 0x00000000);
data = mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
}
data = mindwm(ast, 0x1E6E0018) | 0xC00;
moutdwm(ast, 0x1E6E0018, data);
moutdwm(ast, 0x1E6E0034, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00000000);
udelay(50);
/* Mode Register Setting */
moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
moutdwm(ast, 0x1E6E0028, 0x00000005);
moutdwm(ast, 0x1E6E0028, 0x00000007);
moutdwm(ast, 0x1E6E0028, 0x00000003);
moutdwm(ast, 0x1E6E0028, 0x00000001);
moutdwm(ast, 0x1E6E000C, 0x00005C08);
moutdwm(ast, 0x1E6E002C, param->reg_MRS);
moutdwm(ast, 0x1E6E0028, 0x00000001);
moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
moutdwm(ast, 0x1E6E0028, 0x00000003);
moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
moutdwm(ast, 0x1E6E0028, 0x00000003);
moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
data = 0;
if (param->wodt) {
data = 0x500;
}
if (param->rodt) {
data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
}
moutdwm(ast, 0x1E6E0034, data | 0x3);
moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
/* Wait DQI delay lock */
do {
data = mindwm(ast, 0x1E6E0080);
} while (!(data & 0x40000000));
/* Wait DQSI delay lock */
do {
data = mindwm(ast, 0x1E6E0020);
} while (!(data & 0x00000800));
/* Calibrate the DQSI delay */
cbr_dll2(ast, param);
/* ECC Memory Initialization */
#ifdef ECC
moutdwm(ast, 0x1E6E007C, 0x00000000);
moutdwm(ast, 0x1E6E0070, 0x221);
do {
data = mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
moutdwm(ast, 0x1E6E0070, 0x00000000);
moutdwm(ast, 0x1E6E0050, 0x80000000);
moutdwm(ast, 0x1E6E0050, 0x00000000);
#endif
}
static void ast_init_dram_2300(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
struct ast2300_dram_param param;
u32 temp;
u8 reg;
reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
if ((reg & 0x80) == 0) {/* vga only */
ast_write32(ast, 0xf004, 0x1e6e0000);
ast_write32(ast, 0xf000, 0x1);
ast_write32(ast, 0x12000, 0x1688a8a8);
do {
;
} while (ast_read32(ast, 0x12000) != 0x1);
ast_write32(ast, 0x10000, 0xfc600309);
do {
;
} while (ast_read32(ast, 0x10000) != 0x1);
/* Slow down CPU/AHB CLK in VGA only mode */
temp = ast_read32(ast, 0x12008);
temp |= 0x73;
ast_write32(ast, 0x12008, temp);
param.dram_type = AST_DDR3;
if (temp & 0x01000000)
param.dram_type = AST_DDR2;
param.dram_chipid = ast->dram_type;
param.dram_freq = ast->mclk;
param.vram_size = ast->vram_size;
if (param.dram_type == AST_DDR3) {
get_ddr3_info(ast, &param);
ddr3_init(ast, &param);
} else {
get_ddr2_info(ast, &param);
ddr2_init(ast, &param);
}
temp = mindwm(ast, 0x1e6e2040);
moutdwm(ast, 0x1e6e2040, temp | 0x40);
}
/* wait ready */
do {
reg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
} while ((reg & 0x40) == 0);
}
/*
* Copyright (c) 2005 ASPEED Technology Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the authors not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. The authors makes no representations
* about the suitability of this software for any purpose. It is provided
* "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* Ported from xf86-video-ast driver */
#ifndef AST_TABLES_H
#define AST_TABLES_H
/* Std. Table Index Definition */
#define TextModeIndex 0
#define EGAModeIndex 1
#define VGAModeIndex 2
#define HiCModeIndex 3
#define TrueCModeIndex 4
#define Charx8Dot 0x00000001
#define HalfDCLK 0x00000002
#define DoubleScanMode 0x00000004
#define LineCompareOff 0x00000008
#define SyncPP 0x00000000
#define SyncPN 0x00000040
#define SyncNP 0x00000080
#define SyncNN 0x000000C0
#define HBorder 0x00000020
#define VBorder 0x00000010
#define WideScreenMode 0x00000100
/* 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
static struct ast_vbios_dclk_info dclk_table[] = {
{0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */
{0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
{0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
{0x76, 0x63, 0x01}, /* 03: VCLK36 */
{0xEE, 0x67, 0x01}, /* 04: VCLK40 */
{0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
{0xC6, 0x64, 0x01}, /* 06: VCLK50 */
{0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
{0x80, 0x64, 0x00}, /* 08: VCLK65 */
{0x7B, 0x63, 0x00}, /* 09: VCLK75 */
{0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */
{0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */
{0x8E, 0x62, 0x00}, /* 0C: VCLK108 */
{0x85, 0x24, 0x00}, /* 0D: VCLK135 */
{0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */
{0x6A, 0x22, 0x00}, /* 0F: VCLK162 */
{0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
{0xa7, 0x78, 0x80}, /* 11: VCLK83.5 */
{0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
{0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
{0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
};
static struct ast_vbios_stdtable vbios_stdtable[] = {
/* MD_2_3_400 */
{
0x67,
{0x00,0x03,0x00,0x02},
{0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f,
0x00,0x4f,0x0d,0x0e,0x00,0x00,0x00,0x00,
0x9c,0x8e,0x8f,0x28,0x1f,0x96,0xb9,0xa3,
0xff},
{0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x0c,0x00,0x0f,0x08},
{0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x00,
0xff}
},
/* Mode12/ExtEGATable */
{
0xe3,
{0x01,0x0f,0x00,0x06},
{0x5f,0x4f,0x50,0x82,0x55,0x81,0x0b,0x3e,
0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0xe9,0x8b,0xdf,0x28,0x00,0xe7,0x04,0xe3,
0xff},
{0x00,0x01,0x02,0x03,0x04,0x05,0x14,0x07,
0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x01,0x00,0x0f,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
0xff}
},
/* ExtVGATable */
{
0x2f,
{0x01,0x0f,0x00,0x0e},
{0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
0xff},
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x01,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
0xff}
},
/* ExtHiCTable */
{
0x2f,
{0x01,0x0f,0x00,0x0e},
{0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
0xff},
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x01,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
0xff}
},
/* ExtTrueCTable */
{
0x2f,
{0x01,0x0f,0x00,0x0e},
{0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
0xff},
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x01,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x0f,
0xff}
},
};
static struct ast_vbios_enhtable res_640x480[] = {
{ 800, 640, 8, 96, 525, 480, 2, 2, VCLK25_175, /* 60Hz */
(SyncNN | HBorder | VBorder | Charx8Dot), 60, 1, 0x2E },
{ 832, 640, 16, 40, 520, 480, 1, 3, VCLK31_5, /* 72Hz */
(SyncNN | HBorder | VBorder | Charx8Dot), 72, 2, 0x2E },
{ 840, 640, 16, 64, 500, 480, 1, 3, VCLK31_5, /* 75Hz */
(SyncNN | Charx8Dot) , 75, 3, 0x2E },
{ 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* 85Hz */
(SyncNN | Charx8Dot) , 85, 4, 0x2E },
{ 832, 640, 56, 56, 509, 480, 1, 3, VCLK36, /* end */
(SyncNN | Charx8Dot) , 0xFF, 4, 0x2E },
};
static struct ast_vbios_enhtable res_800x600[] = {
{1024, 800, 24, 72, 625, 600, 1, 2, VCLK36, /* 56Hz */
(SyncPP | Charx8Dot), 56, 1, 0x30 },
{1056, 800, 40, 128, 628, 600, 1, 4, VCLK40, /* 60Hz */
(SyncPP | Charx8Dot), 60, 2, 0x30 },
{1040, 800, 56, 120, 666, 600, 37, 6, VCLK50, /* 72Hz */
(SyncPP | Charx8Dot), 72, 3, 0x30 },
{1056, 800, 16, 80, 625, 600, 1, 3, VCLK49_5, /* 75Hz */
(SyncPP | Charx8Dot), 75, 4, 0x30 },
{1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* 85Hz */
(SyncPP | Charx8Dot), 84, 5, 0x30 },
{1048, 800, 32, 64, 631, 600, 1, 3, VCLK56_25, /* end */
(SyncPP | Charx8Dot), 0xFF, 5, 0x30 },
};
static struct ast_vbios_enhtable res_1024x768[] = {
{1344, 1024, 24, 136, 806, 768, 3, 6, VCLK65, /* 60Hz */
(SyncNN | Charx8Dot), 60, 1, 0x31 },
{1328, 1024, 24, 136, 806, 768, 3, 6, VCLK75, /* 70Hz */
(SyncNN | Charx8Dot), 70, 2, 0x31 },
{1312, 1024, 16, 96, 800, 768, 1, 3, VCLK78_75, /* 75Hz */
(SyncPP | Charx8Dot), 75, 3, 0x31 },
{1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* 85Hz */
(SyncPP | Charx8Dot), 84, 4, 0x31 },
{1376, 1024, 48, 96, 808, 768, 1, 3, VCLK94_5, /* end */
(SyncPP | Charx8Dot), 0xFF, 4, 0x31 },
};
static struct ast_vbios_enhtable res_1280x1024[] = {
{1688, 1280, 48, 112, 1066, 1024, 1, 3, VCLK108, /* 60Hz */
(SyncPP | Charx8Dot), 60, 1, 0x32 },
{1688, 1280, 16, 144, 1066, 1024, 1, 3, VCLK135, /* 75Hz */
(SyncPP | Charx8Dot), 75, 2, 0x32 },
{1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* 85Hz */
(SyncPP | Charx8Dot), 85, 3, 0x32 },
{1728, 1280, 64, 160, 1072, 1024, 1, 3, VCLK157_5, /* end */
(SyncPP | Charx8Dot), 0xFF, 3, 0x32 },
};
static struct ast_vbios_enhtable res_1600x1200[] = {
{2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* 60Hz */
(SyncPP | Charx8Dot), 60, 1, 0x33 },
{2160, 1600, 64, 192, 1250, 1200, 1, 3, VCLK162, /* end */
(SyncPP | Charx8Dot), 0xFF, 1, 0x33 },
};
static struct ast_vbios_enhtable res_1920x1200[] = {
{2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
(SyncNP | Charx8Dot), 60, 1, 0x34 },
{2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
(SyncNP | Charx8Dot), 0xFF, 1, 0x34 },
};
/* 16:10 */
static struct ast_vbios_enhtable res_1280x800[] = {
{1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
(SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x35 },
{1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
(SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x35 },
};
static struct ast_vbios_enhtable res_1440x900[] = {
{1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
(SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x36 },
{1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
(SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x36 },
};
static struct ast_vbios_enhtable res_1680x1050[] = {
{2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
(SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x37 },
{2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
(SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x37 },
};
/* HDTV */
static struct ast_vbios_enhtable res_1920x1080[] = {
{2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x38 },
{2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x38 },
};
#endif
/*
* 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 "drmP.h"
#include "ast_drv.h"
#include <ttm/ttm_page_alloc.h>
static inline struct ast_private *
ast_bdev(struct ttm_bo_device *bd)
{
return container_of(bd, struct ast_private, ttm.bdev);
}
static int
ast_ttm_mem_global_init(struct drm_global_reference *ref)
{
return ttm_mem_global_init(ref->object);
}
static void
ast_ttm_mem_global_release(struct drm_global_reference *ref)
{
ttm_mem_global_release(ref->object);
}
static int ast_ttm_global_init(struct ast_private *ast)
{
struct drm_global_reference *global_ref;
int r;
global_ref = &ast->ttm.mem_global_ref;
global_ref->global_type = DRM_GLOBAL_TTM_MEM;
global_ref->size = sizeof(struct ttm_mem_global);
global_ref->init = &ast_ttm_mem_global_init;
global_ref->release = &ast_ttm_mem_global_release;
r = drm_global_item_ref(global_ref);
if (r != 0) {
DRM_ERROR("Failed setting up TTM memory accounting "
"subsystem.\n");
return r;
}
ast->ttm.bo_global_ref.mem_glob =
ast->ttm.mem_global_ref.object;
global_ref = &ast->ttm.bo_global_ref.ref;
global_ref->global_type = DRM_GLOBAL_TTM_BO;
global_ref->size = sizeof(struct ttm_bo_global);
global_ref->init = &ttm_bo_global_init;
global_ref->release = &ttm_bo_global_release;
r = drm_global_item_ref(global_ref);
if (r != 0) {
DRM_ERROR("Failed setting up TTM BO subsystem.\n");
drm_global_item_unref(&ast->ttm.mem_global_ref);
return r;
}
return 0;
}
void
ast_ttm_global_release(struct ast_private *ast)
{
if (ast->ttm.mem_global_ref.release == NULL)
return;
drm_global_item_unref(&ast->ttm.bo_global_ref.ref);
drm_global_item_unref(&ast->ttm.mem_global_ref);
ast->ttm.mem_global_ref.release = NULL;
}
static void ast_bo_ttm_destroy(struct ttm_buffer_object *tbo)
{
struct ast_bo *bo;
bo = container_of(tbo, struct ast_bo, bo);
drm_gem_object_release(&bo->gem);
kfree(bo);
}
bool ast_ttm_bo_is_ast_bo(struct ttm_buffer_object *bo)
{
if (bo->destroy == &ast_bo_ttm_destroy)
return true;
return false;
}
static int
ast_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
switch (type) {
case TTM_PL_SYSTEM:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
man->func = &ttm_bo_manager_func;
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
break;
default:
DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
return -EINVAL;
}
return 0;
}
static void
ast_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
{
struct ast_bo *astbo = ast_bo(bo);
if (!ast_ttm_bo_is_ast_bo(bo))
return;
ast_ttm_placement(astbo, TTM_PL_FLAG_SYSTEM);
*pl = astbo->placement;
}
static int ast_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
return 0;
}
static int ast_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
struct ttm_mem_reg *mem)
{
struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
struct ast_private *ast = ast_bdev(bdev);
mem->bus.addr = NULL;
mem->bus.offset = 0;
mem->bus.size = mem->num_pages << PAGE_SHIFT;
mem->bus.base = 0;
mem->bus.is_iomem = false;
if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
return -EINVAL;
switch (mem->mem_type) {
case TTM_PL_SYSTEM:
/* system memory */
return 0;
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = pci_resource_start(ast->dev->pdev, 0);
mem->bus.is_iomem = true;
break;
default:
return -EINVAL;
break;
}
return 0;
}
static void ast_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
{
}
static int ast_bo_move(struct ttm_buffer_object *bo,
bool evict, bool interruptible,
bool no_wait_reserve, bool no_wait_gpu,
struct ttm_mem_reg *new_mem)
{
int r;
r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem);
return r;
}
static void ast_ttm_backend_destroy(struct ttm_tt *tt)
{
ttm_tt_fini(tt);
kfree(tt);
}
static struct ttm_backend_func ast_tt_backend_func = {
.destroy = &ast_ttm_backend_destroy,
};
struct ttm_tt *ast_ttm_tt_create(struct ttm_bo_device *bdev,
unsigned long size, uint32_t page_flags,
struct page *dummy_read_page)
{
struct ttm_tt *tt;
tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL);
if (tt == NULL)
return NULL;
tt->func = &ast_tt_backend_func;
if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) {
kfree(tt);
return NULL;
}
return tt;
}
static int ast_ttm_tt_populate(struct ttm_tt *ttm)
{
return ttm_pool_populate(ttm);
}
static void ast_ttm_tt_unpopulate(struct ttm_tt *ttm)
{
ttm_pool_unpopulate(ttm);
}
struct ttm_bo_driver ast_bo_driver = {
.ttm_tt_create = ast_ttm_tt_create,
.ttm_tt_populate = ast_ttm_tt_populate,
.ttm_tt_unpopulate = ast_ttm_tt_unpopulate,
.init_mem_type = ast_bo_init_mem_type,
.evict_flags = ast_bo_evict_flags,
.move = ast_bo_move,
.verify_access = ast_bo_verify_access,
.io_mem_reserve = &ast_ttm_io_mem_reserve,
.io_mem_free = &ast_ttm_io_mem_free,
};
int ast_mm_init(struct ast_private *ast)
{
int ret;
struct drm_device *dev = ast->dev;
struct ttm_bo_device *bdev = &ast->ttm.bdev;
ret = ast_ttm_global_init(ast);
if (ret)
return ret;
ret = ttm_bo_device_init(&ast->ttm.bdev,
ast->ttm.bo_global_ref.ref.object,
&ast_bo_driver, DRM_FILE_PAGE_OFFSET,
true);
if (ret) {
DRM_ERROR("Error initialising bo driver; %d\n", ret);
return ret;
}
ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
ast->vram_size >> PAGE_SHIFT);
if (ret) {
DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
return ret;
}
ast->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0),
pci_resource_len(dev->pdev, 0),
DRM_MTRR_WC);
return 0;
}
void ast_mm_fini(struct ast_private *ast)
{
struct drm_device *dev = ast->dev;
ttm_bo_device_release(&ast->ttm.bdev);
ast_ttm_global_release(ast);
if (ast->fb_mtrr >= 0) {
drm_mtrr_del(ast->fb_mtrr,
pci_resource_start(dev->pdev, 0),
pci_resource_len(dev->pdev, 0), DRM_MTRR_WC);
ast->fb_mtrr = -1;
}
}
void ast_ttm_placement(struct ast_bo *bo, int domain)
{
u32 c = 0;
bo->placement.fpfn = 0;
bo->placement.lpfn = 0;
bo->placement.placement = bo->placements;
bo->placement.busy_placement = bo->placements;
if (domain & TTM_PL_FLAG_VRAM)
bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
if (domain & TTM_PL_FLAG_SYSTEM)
bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
if (!c)
bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
bo->placement.num_placement = c;
bo->placement.num_busy_placement = c;
}
int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
{
int ret;
ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
if (ret) {
if (ret != -ERESTARTSYS)
DRM_ERROR("reserve failed %p\n", bo);
return ret;
}
return 0;
}
void ast_bo_unreserve(struct ast_bo *bo)
{
ttm_bo_unreserve(&bo->bo);
}
int ast_bo_create(struct drm_device *dev, int size, int align,
uint32_t flags, struct ast_bo **pastbo)
{
struct ast_private *ast = dev->dev_private;
struct ast_bo *astbo;
size_t acc_size;
int ret;
astbo = kzalloc(sizeof(struct ast_bo), GFP_KERNEL);
if (!astbo)
return -ENOMEM;
ret = drm_gem_object_init(dev, &astbo->gem, size);
if (ret) {
kfree(astbo);
return ret;
}
astbo->gem.driver_private = NULL;
astbo->bo.bdev = &ast->ttm.bdev;
ast_ttm_placement(astbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
acc_size = ttm_bo_dma_acc_size(&ast->ttm.bdev, size,
sizeof(struct ast_bo));
ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size,
ttm_bo_type_device, &astbo->placement,
align >> PAGE_SHIFT, 0, false, NULL, acc_size,
ast_bo_ttm_destroy);
if (ret)
return ret;
*pastbo = astbo;
return 0;
}
static inline u64 ast_bo_gpu_offset(struct ast_bo *bo)
{
return bo->bo.offset;
}
int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr)
{
int i, ret;
if (bo->pin_count) {
bo->pin_count++;
if (gpu_addr)
*gpu_addr = ast_bo_gpu_offset(bo);
}
ast_ttm_placement(bo, pl_flag);
for (i = 0; i < bo->placement.num_placement; i++)
bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false);
if (ret)
return ret;
bo->pin_count = 1;
if (gpu_addr)
*gpu_addr = ast_bo_gpu_offset(bo);
return 0;
}
int ast_bo_unpin(struct ast_bo *bo)
{
int i, ret;
if (!bo->pin_count) {
DRM_ERROR("unpin bad %p\n", bo);
return 0;
}
bo->pin_count--;
if (bo->pin_count)
return 0;
for (i = 0; i < bo->placement.num_placement ; i++)
bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false);
if (ret)
return ret;
return 0;
}
int ast_bo_push_sysram(struct ast_bo *bo)
{
int i, ret;
if (!bo->pin_count) {
DRM_ERROR("unpin bad %p\n", bo);
return 0;
}
bo->pin_count--;
if (bo->pin_count)
return 0;
if (bo->kmap.virtual)
ttm_bo_kunmap(&bo->kmap);
ast_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
for (i = 0; i < bo->placement.num_placement ; i++)
bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false);
if (ret) {
DRM_ERROR("pushing to VRAM failed\n");
return ret;
}
return 0;
}
int ast_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv;
struct ast_private *ast;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
return drm_mmap(filp, vma);
file_priv = filp->private_data;
ast = file_priv->minor->dev->dev_private;
return ttm_bo_mmap(filp, vma, &ast->ttm.bdev);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment