diff options
32 files changed, 15170 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 2520db0b776e..699af5a0f4a6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -311,6 +311,8 @@ source "drivers/gpu/drm/vmwgfx/Kconfig" source "drivers/gpu/drm/gma500/Kconfig" +source "drivers/gpu/drm/via/Kconfig" + source "drivers/gpu/drm/udl/Kconfig" source "drivers/gpu/drm/ast/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 104b42df2e95..0782f388b6e9 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -146,6 +146,7 @@ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ obj-$(CONFIG_DRM_GMA500) += gma500/ +obj-$(CONFIG_DRM_VIA) += via/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_ARMADA) += armada/ diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c new file mode 100644 index 000000000000..bbd9bbd5e5a0 --- /dev/null +++ b/drivers/gpu/drm/drm_hashtab.c @@ -0,0 +1,205 @@ +/************************************************************************** + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA. + * All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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. + * + * + **************************************************************************/ +/* + * Simple open hash tab implementation. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +#include <linux/hash.h> +#include <linux/mm.h> +#include <linux/rculist.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include <drm/drm_print.h> + +#include "drm_legacy.h" + +int drm_ht_create(struct drm_open_hash *ht, unsigned int order) +{ + unsigned int size = 1 << order; + + ht->order = order; + ht->table = NULL; + if (size <= PAGE_SIZE / sizeof(*ht->table)) + ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL); + else + ht->table = vzalloc(array_size(size, sizeof(*ht->table))); + if (!ht->table) { + DRM_ERROR("Out of memory for hash table\n"); + return -ENOMEM; + } + return 0; +} + +void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key) +{ + struct drm_hash_item *entry; + struct hlist_head *h_list; + unsigned int hashed_key; + int count = 0; + + hashed_key = hash_long(key, ht->order); + DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); + h_list = &ht->table[hashed_key]; + hlist_for_each_entry(entry, h_list, head) + DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); +} +EXPORT_SYMBOL(drm_ht_verbose_list); + +static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, + unsigned long key) +{ + struct drm_hash_item *entry; + struct hlist_head *h_list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each_entry(entry, h_list, head) { + if (entry->key == key) + return &entry->head; + if (entry->key > key) + break; + } + return NULL; +} + +static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht, + unsigned long key) +{ + struct drm_hash_item *entry; + struct hlist_head *h_list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each_entry_rcu(entry, h_list, head) { + if (entry->key == key) + return &entry->head; + if (entry->key > key) + break; + } + return NULL; +} + +int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) +{ + struct drm_hash_item *entry; + struct hlist_head *h_list; + struct hlist_node *parent; + unsigned int hashed_key; + unsigned long key = item->key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + parent = NULL; + hlist_for_each_entry(entry, h_list, head) { + if (entry->key == key) + return -EINVAL; + if (entry->key > key) + break; + parent = &entry->head; + } + if (parent) { + hlist_add_behind_rcu(&item->head, parent); + } else { + hlist_add_head_rcu(&item->head, h_list); + } + return 0; +} + +/* + * Just insert an item and return any "bits" bit key that hasn't been + * used before. + */ +int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item, + unsigned long seed, int bits, int shift, + unsigned long add) +{ + int ret; + unsigned long mask = (1UL << bits) - 1; + unsigned long first, unshifted_key; + + unshifted_key = hash_long(seed, bits); + first = unshifted_key; + do { + item->key = (unshifted_key << shift) + add; + ret = drm_ht_insert_item(ht, item); + if (ret) + unshifted_key = (unshifted_key + 1) & mask; + } while(ret && (unshifted_key != first)); + + if (ret) { + DRM_ERROR("Available key bit space exhausted\n"); + return -EINVAL; + } + return 0; +} + +int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key, + struct drm_hash_item **item) +{ + struct hlist_node *list; + + list = drm_ht_find_key_rcu(ht, key); + if (!list) + return -EINVAL; + + *item = hlist_entry(list, struct drm_hash_item, head); + return 0; +} + +int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key) +{ + struct hlist_node *list; + + list = drm_ht_find_key(ht, key); + if (list) { + hlist_del_init_rcu(list); + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL(drm_ht_remove_key); + +int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item) +{ + hlist_del_init_rcu(&item->head); + return 0; +} + +void drm_ht_remove(struct drm_open_hash *ht) +{ + if (ht->table) { + kvfree(ht->table); + ht->table = NULL; + } +} diff --git a/drivers/gpu/drm/via/Kconfig b/drivers/gpu/drm/via/Kconfig new file mode 100644 index 000000000000..fa6ade047a37 --- /dev/null +++ b/drivers/gpu/drm/via/Kconfig @@ -0,0 +1,9 @@ +config DRM_VIA + tristate "OpenChrome (VIA Technologies Chrome)" + depends on DRM && PCI && X86 + select DRM_KMS_HELPER + select DRM_TTM + help + Choose this option if you have VIA Technologies UniChrome or + Chrome9 integrated graphics. If M is selected the module will + be called via. diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile new file mode 100644 index 000000000000..7acc04ebe995 --- /dev/null +++ b/drivers/gpu/drm/via/Makefile @@ -0,0 +1,27 @@ +# +# 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 +via-y := via_connector.o \ + via_crtc.o \ + via_crtc_hw.o \ + via_cursor.o \ + via_dac.o \ + via_drv.o \ + via_encoder.o \ + via_hdmi.o \ + via_i2c.o \ + via_init.o \ + via_ioctl.o \ + via_lvds.o \ + via_object.o \ + via_pll.o \ + via_pm.o \ + via_sii164.o \ + via_tmds.o \ + via_ttm.o \ + via_tx.o \ + via_vt1632.o + +obj-$(CONFIG_DRM_VIA) += via.o diff --git a/drivers/gpu/drm/via/via_3d_reg.h b/drivers/gpu/drm/via/via_3d_reg.h new file mode 100644 index 000000000000..3ed53c2fa8ca --- /dev/null +++ b/drivers/gpu/drm/via/via_3d_reg.h @@ -0,0 +1,1845 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 1998-2011 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2011 S3 Graphics, Inc. All Rights Reserved. + */ + +#ifndef VIA_3D_REG_H +#define VIA_3D_REG_H +#define HC_REG_BASE 0x0400 + +#define HC_REG_TRANS_SPACE 0x0040 + +#define HC_ParaN_MASK 0xffffffff +#define HC_Para_MASK 0x00ffffff +#define HC_SubA_MASK 0xff000000 +#define HC_SubA_SHIFT 24 +/* Transmission Setting + */ +#define HC_REG_TRANS_SET 0x003c +#define HC_ParaSubType_MASK 0xff000000 +#define HC_ParaType_MASK 0x00ff0000 +#define HC_ParaOS_MASK 0x0000ff00 +#define HC_ParaAdr_MASK 0x000000ff +#define HC_ParaSubType_SHIFT 24 +#define HC_ParaType_SHIFT 16 +#define HC_ParaOS_SHIFT 8 +#define HC_ParaAdr_SHIFT 0 + +#define HC_ParaType_CmdVdata 0x0000 +#define HC_ParaType_NotTex 0x0001 +#define HC_ParaType_Tex 0x0002 +#define HC_ParaType_Palette 0x0003 +#define HC_ParaType_PreCR 0x0010 +#define HC_ParaType_Auto 0x00fe +#define INV_ParaType_Dummy 0x00300000 + +/* Transmission Space + */ +#define HC_REG_Hpara0 0x0040 +#define HC_REG_HpataAF 0x02fc + +/* Read + */ +#define HC_REG_HREngSt 0x0000 +#define HC_REG_HRFIFOempty 0x0004 +#define HC_REG_HRFIFOfull 0x0008 +#define HC_REG_HRErr 0x000c +#define HC_REG_FIFOstatus 0x0010 +/* HC_REG_HREngSt 0x0000 + */ +#define HC_HDASZC_MASK 0x00010000 +#define HC_HSGEMI_MASK 0x0000f000 +#define HC_HLGEMISt_MASK 0x00000f00 +#define HC_HCRSt_MASK 0x00000080 +#define HC_HSE0St_MASK 0x00000040 +#define HC_HSE1St_MASK 0x00000020 +#define HC_HPESt_MASK 0x00000010 +#define HC_HXESt_MASK 0x00000008 +#define HC_HBESt_MASK 0x00000004 +#define HC_HE2St_MASK 0x00000002 +#define HC_HE3St_MASK 0x00000001 +/* HC_REG_HRFIFOempty 0x0004 + */ +#define HC_HRZDempty_MASK 0x00000010 +#define HC_HRTXAempty_MASK 0x00000008 +#define HC_HRTXDempty_MASK 0x00000004 +#define HC_HWZDempty_MASK 0x00000002 +#define HC_HWCDempty_MASK 0x00000001 +/* HC_REG_HRFIFOfull 0x0008 + */ +#define HC_HRZDfull_MASK 0x00000010 +#define HC_HRTXAfull_MASK 0x00000008 +#define HC_HRTXDfull_MASK 0x00000004 +#define HC_HWZDfull_MASK 0x00000002 +#define HC_HWCDfull_MASK 0x00000001 +/* HC_REG_HRErr 0x000c + */ +#define HC_HAGPCMErr_MASK 0x80000000 +#define HC_HAGPCMErrC_MASK 0x70000000 +/* HC_REG_FIFOstatus 0x0010 + */ +#define HC_HRFIFOATall_MASK 0x80000000 +#define HC_HRFIFOATbusy_MASK 0x40000000 +#define HC_HRATFGMDo_MASK 0x00000100 +#define HC_HRATFGMDi_MASK 0x00000080 +#define HC_HRATFRZD_MASK 0x00000040 +#define HC_HRATFRTXA_MASK 0x00000020 +#define HC_HRATFRTXD_MASK 0x00000010 +#define HC_HRATFWZD_MASK 0x00000008 +#define HC_HRATFWCD_MASK 0x00000004 +#define HC_HRATTXTAG_MASK 0x00000002 +#define HC_HRATTXCH_MASK 0x00000001 + +/* AGP Command Setting + */ +#define HC_SubA_HAGPBstL 0x0060 +#define HC_SubA_HAGPBendL 0x0061 +#define HC_SubA_HAGPCMNT 0x0062 +#define HC_SubA_HAGPBpL 0x0063 +#define HC_SubA_HAGPBpH 0x0064 +/* HC_SubA_HAGPCMNT 0x0062 + */ +#define HC_HAGPCMNT_MASK 0x00800000 +#define HC_HCmdErrClr_MASK 0x00400000 +#define HC_HAGPBendH_MASK 0x0000ff00 +#define HC_HAGPBstH_MASK 0x000000ff +#define HC_HAGPBendH_SHIFT 8 +#define HC_HAGPBstH_SHIFT 0 +/* HC_SubA_HAGPBpL 0x0063 + */ +#define HC_HAGPBpL_MASK 0x00fffffc +#define HC_HAGPBpID_MASK 0x00000003 +#define HC_HAGPBpID_PAUSE 0x00000000 +#define HC_HAGPBpID_JUMP 0x00000001 +#define HC_HAGPBpID_STOP 0x00000002 +/* HC_SubA_HAGPBpH 0x0064 + */ +#define HC_HAGPBpH_MASK 0x00ffffff + +/* Miscellaneous Settings + */ +#define HC_SubA_HClipTB 0x0070 +#define HC_SubA_HClipLR 0x0071 +#define HC_SubA_HFPClipTL 0x0072 +#define HC_SubA_HFPClipBL 0x0073 +#define HC_SubA_HFPClipLL 0x0074 +#define HC_SubA_HFPClipRL 0x0075 +#define HC_SubA_HFPClipTBH 0x0076 +#define HC_SubA_HFPClipLRH 0x0077 +#define HC_SubA_HLP 0x0078 +#define HC_SubA_HLPRF 0x0079 +#define HC_SubA_HSolidCL 0x007a +#define HC_SubA_HPixGC 0x007b +#define HC_SubA_HSPXYOS 0x007c +#define HC_SubA_HVertexCNT 0x007d + +#define HC_HClipT_MASK 0x00fff000 +#define HC_HClipT_SHIFT 12 +#define HC_HClipB_MASK 0x00000fff +#define HC_HClipB_SHIFT 0 +#define HC_HClipL_MASK 0x00fff000 +#define HC_HClipL_SHIFT 12 +#define HC_HClipR_MASK 0x00000fff +#define HC_HClipR_SHIFT 0 +#define HC_HFPClipBH_MASK 0x0000ff00 +#define HC_HFPClipBH_SHIFT 8 +#define HC_HFPClipTH_MASK 0x000000ff +#define HC_HFPClipTH_SHIFT 0 +#define HC_HFPClipRH_MASK 0x0000ff00 +#define HC_HFPClipRH_SHIFT 8 +#define HC_HFPClipLH_MASK 0x000000ff +#define HC_HFPClipLH_SHIFT 0 +#define HC_HSolidCH_MASK 0x000000ff +#define HC_HPixGC_MASK 0x00800000 +#define HC_HSPXOS_MASK 0x00fff000 +#define HC_HSPXOS_SHIFT 12 +#define HC_HSPYOS_MASK 0x00000fff + +/* + * Command A + */ +#define HC_HCmdHeader_MASK 0xfe000000 /*0xffe00000 */ +#define HC_HE3Fire_MASK 0x00100000 +#define HC_HPMType_MASK 0x000f0000 +#define HC_HEFlag_MASK 0x0000e000 +#define HC_HShading_MASK 0x00001c00 +#define HC_HPMValidN_MASK 0x00000200 +#define HC_HPLEND_MASK 0x00000100 +#define HC_HVCycle_MASK 0x000000ff +#define HC_HVCycle_Style_MASK 0x000000c0 +#define HC_HVCycle_ChgA_MASK 0x00000030 +#define HC_HVCycle_ChgB_MASK 0x0000000c +#define HC_HVCycle_ChgC_MASK 0x00000003 +#define HC_HPMType_Point 0x00000000 +#define HC_HPMType_Line 0x00010000 +#define HC_HPMType_Tri 0x00020000 +#define HC_HPMType_TriWF 0x00040000 +#define HC_HEFlag_NoAA 0x00000000 +#define HC_HEFlag_ab 0x00008000 +#define HC_HEFlag_bc 0x00004000 +#define HC_HEFlag_ca 0x00002000 +#define HC_HShading_Solid 0x00000000 +#define HC_HShading_FlatA 0x00000400 +#define HC_HShading_FlatB 0x00000800 +#define HC_HShading_FlatC 0x00000c00 +#define HC_HShading_Gouraud 0x00001000 +#define HC_HVCycle_Full 0x00000000 +#define HC_HVCycle_AFP 0x00000040 +#define HC_HVCycle_One 0x000000c0 +#define HC_HVCycle_NewA 0x00000000 +#define HC_HVCycle_AA 0x00000010 +#define HC_HVCycle_AB 0x00000020 +#define HC_HVCycle_AC 0x00000030 +#define HC_HVCycle_NewB 0x00000000 +#define HC_HVCycle_BA 0x00000004 +#define HC_HVCycle_BB 0x00000008 +#define HC_HVCycle_BC 0x0000000c +#define HC_HVCycle_NewC 0x00000000 +#define HC_HVCycle_CA 0x00000001 +#define HC_HVCycle_CB 0x00000002 +#define HC_HVCycle_CC 0x00000003 + +/* Command B + */ +#define HC_HLPrst_MASK 0x00010000 +#define HC_HLLastP_MASK 0x00008000 +#define HC_HVPMSK_MASK 0x00007f80 +#define HC_HBFace_MASK 0x00000040 +#define HC_H2nd1VT_MASK 0x0000003f +#define HC_HVPMSK_X 0x00004000 +#define HC_HVPMSK_Y 0x00002000 +#define HC_HVPMSK_Z 0x00001000 +#define HC_HVPMSK_W 0x00000800 +#define HC_HVPMSK_Cd 0x00000400 +#define HC_HVPMSK_Cs 0x00000200 +#define HC_HVPMSK_S 0x00000100 +#define HC_HVPMSK_T 0x00000080 + +/* Enable Setting + */ +#define HC_SubA_HEnable 0x0000 +#define HC_HenForce1P_MASK 0x00800000 /* [Force 1 Pipe] */ +#define HC_HenZDCheck_MASK 0x00400000 /* [Z dirty bit settings] */ +#define HC_HenTXEnvMap_MASK 0x00200000 +#define HC_HenVertexCNT_MASK 0x00100000 +#define HC_HenCPUDAZ_MASK 0x00080000 +#define HC_HenDASZWC_MASK 0x00040000 +#define HC_HenFBCull_MASK 0x00020000 +#define HC_HenCW_MASK 0x00010000 +#define HC_HenAA_MASK 0x00008000 +#define HC_HenST_MASK 0x00004000 +#define HC_HenZT_MASK 0x00002000 +#define HC_HenZW_MASK 0x00001000 +#define HC_HenAT_MASK 0x00000800 +#define HC_HenAW_MASK 0x00000400 +#define HC_HenSP_MASK 0x00000200 +#define HC_HenLP_MASK 0x00000100 +#define HC_HenTXCH_MASK 0x00000080 +#define HC_HenTXMP_MASK 0x00000040 +#define HC_HenTXPP_MASK 0x00000020 +#define HC_HenTXTR_MASK 0x00000010 +#define HC_HenCS_MASK 0x00000008 +#define HC_HenFOG_MASK 0x00000004 +#define HC_HenABL_MASK 0x00000002 +#define HC_HenDT_MASK 0x00000001 + +/* Z Setting + */ +#define HC_SubA_HZWBBasL 0x0010 +#define HC_SubA_HZWBBasH 0x0011 +#define HC_SubA_HZWBType 0x0012 +#define HC_SubA_HZBiasL 0x0013 +#define HC_SubA_HZWBend 0x0014 +#define HC_SubA_HZWTMD 0x0015 +#define HC_SubA_HZWCDL 0x0016 +#define HC_SubA_HZWCTAGnum 0x0017 +#define HC_SubA_HZCYNum 0x0018 +#define HC_SubA_HZWCFire 0x0019 +/* HC_SubA_HZWBType + */ +#define HC_HZWBType_MASK 0x00800000 +#define HC_HZBiasedWB_MASK 0x00400000 +#define HC_HZONEasFF_MASK 0x00200000 +#define HC_HZOONEasFF_MASK 0x00100000 +#define HC_HZWBFM_MASK 0x00030000 +#define HC_HZWBLoc_MASK 0x0000c000 +#define HC_HZWBPit_MASK 0x00003fff +#define HC_HZWBFM_16 0x00000000 +#define HC_HZWBFM_32 0x00020000 +#define HC_HZWBFM_24 0x00030000 +#define HC_HZWBLoc_Local 0x00000000 +#define HC_HZWBLoc_SyS 0x00004000 +/* HC_SubA_HZWBend + */ +#define HC_HZWBend_MASK 0x00ffe000 +#define HC_HZBiasH_MASK 0x000000ff +#define HC_HZWBend_SHIFT 10 +/* HC_SubA_HZWTMD + */ +#define HC_HZWTMD_MASK 0x00070000 +#define HC_HEBEBias_MASK 0x00007f00 +#define HC_HZNF_MASK 0x000000ff +#define HC_HZWTMD_NeverPass 0x00000000 +#define HC_HZWTMD_LT 0x00010000 +#define HC_HZWTMD_EQ 0x00020000 +#define HC_HZWTMD_LE 0x00030000 +#define HC_HZWTMD_GT 0x00040000 +#define HC_HZWTMD_NE 0x00050000 +#define HC_HZWTMD_GE 0x00060000 +#define HC_HZWTMD_AllPass 0x00070000 +#define HC_HEBEBias_SHIFT 8 +/* HC_SubA_HZWCDL 0x0016 + */ +#define HC_HZWCDL_MASK 0x00ffffff +/* HC_SubA_HZWCTAGnum 0x0017 + */ +#define HC_HZWCTAGnum_MASK 0x00ff0000 +#define HC_HZWCTAGnum_SHIFT 16 +#define HC_HZWCDH_MASK 0x000000ff +#define HC_HZWCDH_SHIFT 0 +/* HC_SubA_HZCYNum 0x0018 + */ +#define HC_HZCYNum_MASK 0x00030000 +#define HC_HZCYNum_SHIFT 16 +#define HC_HZWCQWnum_MASK 0x00003fff +#define HC_HZWCQWnum_SHIFT 0 +/* HC_SubA_HZWCFire 0x0019 + */ +#define HC_ZWCFire_MASK 0x00010000 +#define HC_HZWCQWnumLast_MASK 0x00003fff +#define HC_HZWCQWnumLast_SHIFT 0 + +/* Stencil Setting + */ +#define HC_SubA_HSTREF 0x0023 +#define HC_SubA_HSTMD 0x0024 +/* HC_SubA_HSBFM + */ +#define HC_HSBFM_MASK 0x00030000 +#define HC_HSBLoc_MASK 0x0000c000 +#define HC_HSBPit_MASK 0x00003fff +/* HC_SubA_HSTREF + */ +#define HC_HSTREF_MASK 0x00ff0000 +#define HC_HSTOPMSK_MASK 0x0000ff00 +#define HC_HSTBMSK_MASK 0x000000ff +#define HC_HSTREF_SHIFT 16 +#define HC_HSTOPMSK_SHIFT 8 +/* HC_SubA_HSTMD + */ +#define HC_HSTMD_MASK 0x00070000 +#define HC_HSTOPSF_MASK 0x000001c0 +#define HC_HSTOPSPZF_MASK 0x00000038 +#define HC_HSTOPSPZP_MASK 0x00000007 +#define HC_HSTMD_NeverPass 0x00000000 +#define HC_HSTMD_LT 0x00010000 +#define HC_HSTMD_EQ 0x00020000 +#define HC_HSTMD_LE 0x00030000 +#define HC_HSTMD_GT 0x00040000 +#define HC_HSTMD_NE 0x00050000 +#define HC_HSTMD_GE 0x00060000 +#define HC_HSTMD_AllPass 0x00070000 +#define HC_HSTOPSF_KEEP 0x00000000 +#define HC_HSTOPSF_ZERO 0x00000040 +#define HC_HSTOPSF_REPLACE 0x00000080 +#define HC_HSTOPSF_INCRSAT 0x000000c0 +#define HC_HSTOPSF_DECRSAT 0x00000100 +#define HC_HSTOPSF_INVERT 0x00000140 +#define HC_HSTOPSF_INCR 0x00000180 +#define HC_HSTOPSF_DECR 0x000001c0 +#define HC_HSTOPSPZF_KEEP 0x00000000 +#define HC_HSTOPSPZF_ZERO 0x00000008 +#define HC_HSTOPSPZF_REPLACE 0x00000010 +#define HC_HSTOPSPZF_INCRSAT 0x00000018 +#define HC_HSTOPSPZF_DECRSAT 0x00000020 +#define HC_HSTOPSPZF_INVERT 0x00000028 +#define HC_HSTOPSPZF_INCR 0x00000030 +#define HC_HSTOPSPZF_DECR 0x00000038 +#define HC_HSTOPSPZP_KEEP 0x00000000 +#define HC_HSTOPSPZP_ZERO 0x00000001 +#define HC_HSTOPSPZP_REPLACE 0x00000002 +#define HC_HSTOPSPZP_INCRSAT 0x00000003 +#define HC_HSTOPSPZP_DECRSAT 0x00000004 +#define HC_HSTOPSPZP_INVERT 0x00000005 +#define HC_HSTOPSPZP_INCR 0x00000006 +#define HC_HSTOPSPZP_DECR 0x00000007 + +/* Alpha Setting + */ +#define HC_SubA_HABBasL 0x0030 +#define HC_SubA_HABBasH 0x0031 +#define HC_SubA_HABFM 0x0032 +#define HC_SubA_HATMD 0x0033 +#define HC_SubA_HABLCsat 0x0034 +#define HC_SubA_HABLCop 0x0035 +#define HC_SubA_HABLAsat 0x0036 +#define HC_SubA_HABLAop 0x0037 +#define HC_SubA_HABLRCa 0x0038 +#define HC_SubA_HABLRFCa 0x0039 +#define HC_SubA_HABLRCbias 0x003a +#define HC_SubA_HABLRCb 0x003b +#define HC_SubA_HABLRFCb 0x003c +#define HC_SubA_HABLRAa 0x003d +#define HC_SubA_HABLRAb 0x003e +/* HC_SubA_HABFM + */ +#define HC_HABFM_MASK 0x00030000 +#define HC_HABLoc_MASK 0x0000c000 +#define HC_HABPit_MASK 0x000007ff +/* HC_SubA_HATMD + */ +#define HC_HATMD_MASK 0x00000700 +#define HC_HATREF_MASK 0x000000ff +#define HC_HATMD_NeverPass 0x00000000 +#define HC_HATMD_LT 0x00000100 +#define HC_HATMD_EQ 0x00000200 +#define HC_HATMD_LE 0x00000300 +#define HC_HATMD_GT 0x00000400 +#define HC_HATMD_NE 0x00000500 +#define HC_HATMD_GE 0x00000600 +#define HC_HATMD_AllPass 0x00000700 +/* HC_SubA_HABLCsat + */ +#define HC_HABLCsat_MASK 0x00010000 +#define HC_HABLCa_MASK 0x0000fc00 +#define HC_HABLCa_C_MASK 0x0000c000 +#define HC_HABLCa_OPC_MASK 0x00003c00 +#define HC_HABLFCa_MASK 0x000003f0 +#define HC_HABLFCa_C_MASK 0x00000300 +#define HC_HABLFCa_OPC_MASK 0x000000f0 +#define HC_HABLCbias_MASK 0x0000000f +#define HC_HABLCbias_C_MASK 0x00000008 +#define HC_HABLCbias_OPC_MASK 0x00000007 +/*-- Define the input color. + */ +#define HC_XC_Csrc 0x00000000 +#define HC_XC_Cdst 0x00000001 +#define HC_XC_Asrc 0x00000002 +#define HC_XC_Adst 0x00000003 +#define HC_XC_Fog 0x00000004 +#define HC_XC_HABLRC 0x00000005 +#define HC_XC_minSrcDst 0x00000006 +#define HC_XC_maxSrcDst 0x00000007 +#define HC_XC_mimAsrcInvAdst 0x00000008 +#define HC_XC_OPC 0x00000000 +#define HC_XC_InvOPC 0x00000010 +#define HC_XC_OPCp5 0x00000020 +/*-- Define the input Alpha + */ +#define HC_XA_OPA 0x00000000 +#define HC_XA_InvOPA 0x00000010 +#define HC_XA_OPAp5 0x00000020 +#define HC_XA_0 0x00000000 +#define HC_XA_Asrc 0x00000001 +#define HC_XA_Adst 0x00000002 +#define HC_XA_Fog 0x00000003 +#define HC_XA_minAsrcFog 0x00000004 +#define HC_XA_minAsrcAdst 0x00000005 +#define HC_XA_maxAsrcFog 0x00000006 +#define HC_XA_maxAsrcAdst 0x00000007 +#define HC_XA_HABLRA 0x00000008 +#define HC_XA_minAsrcInvAdst 0x00000008 +#define HC_XA_HABLFRA 0x00000009 +/*-- + */ +#define HC_HABLCa_OPC (HC_XC_OPC << 10) +#define HC_HABLCa_InvOPC (HC_XC_InvOPC << 10) +#define HC_HABLCa_OPCp5 (HC_XC_OPCp5 << 10) +#define HC_HABLCa_Csrc (HC_XC_Csrc << 10) +#define HC_HABLCa_Cdst (HC_XC_Cdst << 10) +#define HC_HABLCa_Asrc (HC_XC_Asrc << 10) +#define HC_HABLCa_Adst (HC_XC_Adst << 10) +#define HC_HABLCa_Fog (HC_XC_Fog << 10) +#define HC_HABLCa_HABLRCa (HC_XC_HABLRC << 10) +#define HC_HABLCa_minSrcDst (HC_XC_minSrcDst << 10) +#define HC_HABLCa_maxSrcDst (HC_XC_maxSrcDst << 10) +#define HC_HABLFCa_OPC (HC_XC_OPC << 4) +#define HC_HABLFCa_InvOPC (HC_XC_InvOPC << 4) +#define HC_HABLFCa_OPCp5 (HC_XC_OPCp5 << 4) +#define HC_HABLFCa_Csrc (HC_XC_Csrc << 4) +#define HC_HABLFCa_Cdst (HC_XC_Cdst << 4) +#define HC_HABLFCa_Asrc (HC_XC_Asrc << 4) +#define HC_HABLFCa_Adst (HC_XC_Adst << 4) +#define HC_HABLFCa_Fog (HC_XC_Fog << 4) +#define HC_HABLFCa_HABLRCa (HC_XC_HABLRC << 4) +#define HC_HABLFCa_minSrcDst (HC_XC_minSrcDst << 4) +#define HC_HABLFCa_maxSrcDst (HC_XC_maxSrcDst << 4) +#define HC_HABLFCa_mimAsrcInvAdst (HC_XC_mimAsrcInvAdst << 4) +#define HC_HABLCbias_HABLRCbias 0x00000000 +#define HC_HABLCbias_Asrc 0x00000001 +#define HC_HABLCbias_Adst 0x00000002 +#define HC_HABLCbias_Fog 0x00000003 +#define HC_HABLCbias_Cin 0x00000004 +/* HC_SubA_HABLCop 0x0035 + */ +#define HC_HABLdot_MASK 0x00010000 +#define HC_HABLCop_MASK 0x00004000 +#define HC_HABLCb_MASK 0x00003f00 +#define HC_HABLCb_C_MASK 0x00003000 +#define HC_HABLCb_OPC_MASK 0x00000f00 +#define HC_HABLFCb_MASK 0x000000fc +#define HC_HABLFCb_C_MASK 0x000000c0 +#define HC_HABLFCb_OPC_MASK 0x0000003c +#define HC_HABLCshift_MASK 0x00000003 +#define HC_HABLCb_OPC (HC_XC_OPC << 8) +#define HC_HABLCb_InvOPC (HC_XC_InvOPC << 8) +#define HC_HABLCb_OPCp5 (HC_XC_OPCp5 << 8) +#define HC_HABLCb_Csrc (HC_XC_Csrc << 8) +#define HC_HABLCb_Cdst (HC_XC_Cdst << 8) +#define HC_HABLCb_Asrc (HC_XC_Asrc << 8) +#define HC_HABLCb_Adst (HC_XC_Adst << 8) +#define HC_HABLCb_Fog (HC_XC_Fog << 8) +#define HC_HABLCb_HABLRCa (HC_XC_HABLRC << 8) +#define HC_HABLCb_minSrcDst (HC_XC_minSrcDst << 8) +#define HC_HABLCb_maxSrcDst (HC_XC_maxSrcDst << 8) +#define HC_HABLFCb_OPC (HC_XC_OPC << 2) +#define HC_HABLFCb_InvOPC (HC_XC_InvOPC << 2) +#define HC_HABLFCb_OPCp5 (HC_XC_OPCp5 << 2) +#define HC_HABLFCb_Csrc (HC_XC_Csrc << 2) +#define HC_HABLFCb_Cdst (HC_XC_Cdst << 2) +#define HC_HABLFCb_Asrc (HC_XC_Asrc << 2) +#define HC_HABLFCb_Adst (HC_XC_Adst << 2) +#define HC_HABLFCb_Fog (HC_XC_Fog << 2) +#define HC_HABLFCb_HABLRCb (HC_XC_HABLRC << 2) +#define HC_HABLFCb_minSrcDst (HC_XC_minSrcDst << 2) +#define HC_HABLFCb_maxSrcDst (HC_XC_maxSrcDst << 2) +#define HC_HABLFCb_mimAsrcInvAdst (HC_XC_mimAsrcInvAdst << 2) +/* HC_SubA_HABLAsat 0x0036 + */ +#define HC_HABLAsat_MASK 0x00010000 +#define HC_HABLAa_MASK 0x0000fc00 +#define HC_HABLAa_A_MASK 0x0000c000 +#define HC_HABLAa_OPA_MASK 0x00003c00 +#define HC_HABLFAa_MASK 0x000003f0 +#define HC_HABLFAa_A_MASK 0x00000300 +#define HC_HABLFAa_OPA_MASK 0x000000f0 +#define HC_HABLAbias_MASK 0x0000000f +#define HC_HABLAbias_A_MASK 0x00000008 +#define HC_HABLAbias_OPA_MASK 0x00000007 +#define HC_HABLAa_OPA (HC_XA_OPA << 10) +#define HC_HABLAa_InvOPA (HC_XA_InvOPA << 10) +#define HC_HABLAa_OPAp5 (HC_XA_OPAp5 << 10) +#define HC_HABLAa_0 (HC_XA_0 << 10) +#define HC_HABLAa_Asrc (HC_XA_Asrc << 10) +#define HC_HABLAa_Adst (HC_XA_Adst << 10) +#define HC_HABLAa_Fog (HC_XA_Fog << 10) +#define HC_HABLAa_minAsrcFog (HC_XA_minAsrcFog << 10) +#define HC_HABLAa_minAsrcAdst (HC_XA_minAsrcAdst << 10) +#define HC_HABLAa_maxAsrcFog (HC_XA_maxAsrcFog << 10) +#define HC_HABLAa_maxAsrcAdst (HC_XA_maxAsrcAdst << 10) +#define HC_HABLAa_HABLRA (HC_XA_HABLRA << 10) +#define HC_HABLFAa_OPA (HC_XA_OPA << 4) +#define HC_HABLFAa_InvOPA (HC_XA_InvOPA << 4) +#define HC_HABLFAa_OPAp5 (HC_XA_OPAp5 << 4) +#define HC_HABLFAa_0 (HC_XA_0 << 4) +#define HC_HABLFAa_Asrc (HC_XA_Asrc << 4) +#define HC_HABLFAa_Adst (HC_XA_Adst << 4) +#define HC_HABLFAa_Fog (HC_XA_Fog << 4) +#define HC_HABLFAa_minAsrcFog (HC_XA_minAsrcFog << 4) +#define HC_HABLFAa_minAsrcAdst (HC_XA_minAsrcAdst << 4) +#define HC_HABLFAa_maxAsrcFog (HC_XA_maxAsrcFog << 4) +#define HC_HABLFAa_maxAsrcAdst (HC_XA_maxAsrcAdst << 4) +#define HC_HABLFAa_minAsrcInvAdst (HC_XA_minAsrcInvAdst << 4) +#define HC_HABLFAa_HABLFRA (HC_XA_HABLFRA << 4) +#define HC_HABLAbias_HABLRAbias 0x00000000 +#define HC_HABLAbias_Asrc 0x00000001 +#define HC_HABLAbias_Adst 0x00000002 +#define HC_HABLAbias_Fog 0x00000003 +#define HC_HABLAbias_Aaa 0x00000004 +/* HC_SubA_HABLAop 0x0037 + */ +#define HC_HABLAop_MASK 0x00004000 +#define HC_HABLAb_MASK 0x00003f00 +#define HC_HABLAb_OPA_MASK 0x00000f00 +#define HC_HABLFAb_MASK 0x000000fc +#define HC_HABLFAb_OPA_MASK 0x0000003c +#define HC_HABLAshift_MASK 0x00000003 +#define HC_HABLAb_OPA (HC_XA_OPA << 8) +#define HC_HABLAb_InvOPA (HC_XA_InvOPA << 8) +#define HC_HABLAb_OPAp5 (HC_XA_OPAp5 << 8) +#define HC_HABLAb_0 (HC_XA_0 << 8) +#define HC_HABLAb_Asrc (HC_XA_Asrc << 8) +#define HC_HABLAb_Adst (HC_XA_Adst << 8) +#define HC_HABLAb_Fog (HC_XA_Fog << 8) +#define HC_HABLAb_minAsrcFog (HC_XA_minAsrcFog << 8) +#define HC_HABLAb_minAsrcAdst (HC_XA_minAsrcAdst << 8) +#define HC_HABLAb_maxAsrcFog (HC_XA_maxAsrcFog << 8) +#define HC_HABLAb_maxAsrcAdst (HC_XA_maxAsrcAdst << 8) +#define HC_HABLAb_HABLRA (HC_XA_HABLRA << 8) +#define HC_HABLFAb_OPA (HC_XA_OPA << 2) +#define HC_HABLFAb_InvOPA (HC_XA_InvOPA << 2) +#define HC_HABLFAb_OPAp5 (HC_XA_OPAp5 << 2) +#define HC_HABLFAb_0 (HC_XA_0 << 2) +#define HC_HABLFAb_Asrc (HC_XA_Asrc << 2) +#define HC_HABLFAb_Adst (HC_XA_Adst << 2) +#define HC_HABLFAb_Fog (HC_XA_Fog << 2) +#define HC_HABLFAb_minAsrcFog (HC_XA_minAsrcFog << 2) +#define HC_HABLFAb_minAsrcAdst (HC_XA_minAsrcAdst << 2) +#define HC_HABLFAb_maxAsrcFog (HC_XA_maxAsrcFog << 2) +#define HC_HABLFAb_maxAsrcAdst (HC_XA_maxAsrcAdst << 2) +#define HC_HABLFAb_minAsrcInvAdst (HC_XA_minAsrcInvAdst << 2) +#define HC_HABLFAb_HABLFRA (HC_XA_HABLFRA << 2) +/* HC_SubA_HABLRAa 0x003d + */ +#define HC_HABLRAa_MASK 0x00ff0000 +#define HC_HABLRFAa_MASK 0x0000ff00 +#define HC_HABLRAbias_MASK 0x000000ff +#define HC_HABLRAa_SHIFT 16 +#define HC_HABLRFAa_SHIFT 8 +/* HC_SubA_HABLRAb 0x003e + */ +#define HC_HABLRAb_MASK 0x0000ff00 +#define HC_HABLRFAb_MASK 0x000000ff +#define HC_HABLRAb_SHIFT 8 + +/* Destination Setting + */ +#define HC_SubA_HDBBasL 0x0040 +#define HC_SubA_HDBBasH 0x0041 +#define HC_SubA_HDBFM 0x0042 +#define HC_SubA_HFBBMSKL 0x0043 +#define HC_SubA_HROP 0x0044 +/* HC_SubA_HDBFM 0x0042 + */ +#define HC_HDBFM_MASK 0x001f0000 +#define HC_HDBLoc_MASK 0x0000c000 +#define HC_HDBPit_MASK 0x00003fff +#define HC_HDBFM_RGB555 0x00000000 +#define HC_HDBFM_RGB565 0x00010000 +#define HC_HDBFM_ARGB4444 0x00020000 +#define HC_HDBFM_ARGB1555 0x00030000 +#define HC_HDBFM_BGR555 0x00040000 +#define HC_HDBFM_BGR565 0x00050000 +#define HC_HDBFM_ABGR4444 0x00060000 +#define HC_HDBFM_ABGR1555 0x00070000 +#define HC_HDBFM_ARGB0888 0x00080000 +#define HC_HDBFM_ARGB8888 0x00090000 +#define HC_HDBFM_ABGR0888 0x000a0000 +#define HC_HDBFM_ABGR8888 0x000b0000 +#define HC_HDBLoc_Local 0x00000000 +#define HC_HDBLoc_Sys 0x00004000 +/* HC_SubA_HROP 0x0044 + */ +#define HC_HROP_MASK 0x00000f00 +#define HC_HFBBMSKH_MASK 0x000000ff +#define HC_HROP_BLACK 0x00000000 +#define HC_HROP_DPon 0x00000100 +#define HC_HROP_DPna 0x00000200 +#define HC_HROP_Pn 0x00000300 +#define HC_HROP_PDna 0x00000400 +#define HC_HROP_Dn 0x00000500 +#define HC_HROP_DPx 0x00000600 +#define HC_HROP_DPan 0x00000700 +#define HC_HROP_DPa 0x00000800 +#define HC_HROP_DPxn 0x00000900 +#define HC_HROP_D 0x00000a00 +#define HC_HROP_DPno 0x00000b00 +#define HC_HROP_P 0x00000c00 +#define HC_HROP_PDno 0x00000d00 +#define HC_HROP_DPo 0x00000e00 +#define HC_HROP_WHITE 0x00000f00 + +/* Fog Setting + */ +#define HC_SubA_HFogLF 0x0050 +#define HC_SubA_HFogCL 0x0051 +#define HC_SubA_HFogCH 0x0052 +#define HC_SubA_HFogStL 0x0053 +#define HC_SubA_HFogStH 0x0054 +#define HC_SubA_HFogOOdMF 0x0055 +#define HC_SubA_HFogOOdEF 0x0056 +#define HC_SubA_HFogEndL 0x0057 +#define HC_SubA_HFogDenst 0x0058 +/* HC_SubA_FogLF 0x0050 + */ +#define HC_FogLF_MASK 0x00000010 +#define HC_FogEq_MASK 0x00000008 +#define HC_FogMD_MASK 0x00000007 +#define HC_FogMD_LocalFog 0x00000000 +#define HC_FogMD_LinearFog 0x00000002 +#define HC_FogMD_ExponentialFog 0x00000004 +#define HC_FogMD_Exponential2Fog 0x00000005 +/* #define HC_FogMD_FogTable 0x00000003 */ + +/* HC_SubA_HFogDenst 0x0058 + */ +#define HC_FogDenst_MASK 0x001fff00 +#define HC_FogEndL_MASK 0x000000ff + +/* Texture subtype definitions + */ +#define HC_SubType_Samp0 0x00000020 +#define HC_SubType_Samp1 0x00000021 + + +/* Texture subtype definitions + */ +#define HC_SubType_Tex0 0x00000000 +#define HC_SubType_Tex1 0x00000001 +#define HC_SubType_TexGeneral 0x000000fe + +/* Attribute of texture n + */ +#define HC_SubA_HTXnL0BasL 0x0000 +#define HC_SubA_HTXnL1BasL 0x0001 +#define HC_SubA_HTXnL2BasL 0x0002 +#define HC_SubA_HTXnL3BasL 0x0003 +#define HC_SubA_HTXnL4BasL 0x0004 +#define HC_SubA_HTXnL5BasL 0x0005 +#define HC_SubA_HTXnL6BasL 0x0006 +#define HC_SubA_HTXnL7BasL 0x0007 +#define HC_SubA_HTXnL8BasL 0x0008 +#define HC_SubA_HTXnL9BasL 0x0009 +#define HC_SubA_HTXnLaBasL 0x000a +#define HC_SubA_HTXnLbBasL 0x000b +#define HC_SubA_HTXnLcBasL 0x000c +#define HC_SubA_HTXnLdBasL 0x000d +#define HC_SubA_HTXnLeBasL 0x000e +#define HC_SubA_HTXnLfBasL 0x000f +#define HC_SubA_HTXnL10BasL 0x0010 +#define HC_SubA_HTXnL11BasL 0x0011 +#define HC_SubA_HTXnL012BasH 0x0020 +#define HC_SubA_HTXnL345BasH 0x0021 +#define HC_SubA_HTXnL678BasH 0x0022 +#define HC_SubA_HTXnL9abBasH 0x0023 +#define HC_SubA_HTXnLcdeBasH 0x0024 +#define HC_SubA_HTXnLf1011BasH 0x0025 +#define HC_SubA_HTXnL0Pit 0x002b +#define HC_SubA_HTXnL1Pit 0x002c +#define HC_SubA_HTXnL2Pit 0x002d +#define HC_SubA_HTXnL3Pit 0x002e +#define HC_SubA_HTXnL4Pit 0x002f +#define HC_SubA_HTXnL5Pit 0x0030 +#define HC_SubA_HTXnL6Pit 0x0031 +#define HC_SubA_HTXnL7Pit 0x0032 +#define HC_SubA_HTXnL8Pit 0x0033 +#define HC_SubA_HTXnL9Pit 0x0034 +#define HC_SubA_HTXnLaPit 0x0035 +#define HC_SubA_HTXnLbPit 0x0036 +#define HC_SubA_HTXnLcPit 0x0037 +#define HC_SubA_HTXnLdPit 0x0038 +#define HC_SubA_HTXnLePit 0x0039 +#define HC_SubA_HTXnLfPit 0x003a +#define HC_SubA_HTXnL10Pit 0x003b +#define HC_SubA_HTXnL11Pit 0x003c +#define HC_SubA_HTXnL0_5WE 0x004b +#define HC_SubA_HTXnL6_bWE 0x004c +#define HC_SubA_HTXnLc_11WE 0x004d +#define HC_SubA_HTXnL0_5HE 0x0051 +#define HC_SubA_HTXnL6_bHE 0x0052 +#define HC_SubA_HTXnLc_11HE 0x0053 +#define HC_SubA_HTXnL0OS 0x0077 +#define HC_SubA_HTXnTB 0x0078 +#define HC_SubA_HTXnMPMD 0x0079 +#define HC_SubA_HTXnCLODu 0x007a +#define HC_SubA_HTXnFM 0x007b +#define HC_SubA_HTXnTRCH 0x007c +#define HC_SubA_HTXnTRCL 0x007d +#define HC_SubA_HTXnTBC 0x007e +#define HC_SubA_HTXnTRAH 0x007f +#define HC_SubA_HTXnTBLCsat 0x0080 +#define HC_SubA_HTXnTBLCop 0x0081 +#define HC_SubA_HTXnTBLMPfog 0x0082 +#define HC_SubA_HTXnTBLAsat 0x0083 +#define HC_SubA_HTXnTBLRCa 0x0085 +#define HC_SubA_HTXnTBLRCb 0x0086 +#define HC_SubA_HTXnTBLRCc 0x0087 +#define HC_SubA_HTXnTBLRCbias 0x0088 +#define HC_SubA_HTXnTBLRAa 0x0089 +#define HC_SubA_HTXnTBLRFog 0x008a +#define HC_SubA_HTXnBumpM00 0x0090 +#define HC_SubA_HTXnBumpM01 0x0091 +#define HC_SubA_HTXnBumpM10 0x0092 +#define HC_SubA_HTXnBumpM11 0x0093 +#define HC_SubA_HTXnLScale 0x0094 + +#define HC_SubA_HTXSMD 0x0000 +#define HC_SubA_HTXYUV2RGB1 0x0001 +#define HC_SubA_HTXYUV2RGB2 0x0002 +#define HC_SubA_HTXYUV2RGB3 0x0003 +#define HTXYUV2RGB4BT601 (1<<23) +#define HTXYUV2RGB4BT709 (1<<22) +/* HC_SubA_HTXnL012BasH 0x0020 + */ +#define HC_HTXnL0BasH_MASK 0x000000ff +#define HC_HTXnL1BasH_MASK 0x0000ff00 +#define HC_HTXnL2BasH_MASK 0x00ff0000 +#define HC_HTXnL1BasH_SHIFT 8 +#define HC_HTXnL2BasH_SHIFT 16 +/* HC_SubA_HTXnL345BasH 0x0021 + */ +#define HC_HTXnL3BasH_MASK 0x000000ff +#define HC_HTXnL4BasH_MASK 0x0000ff00 +#define HC_HTXnL5BasH_MASK 0x00ff0000 +#define HC_HTXnL4BasH_SHIFT 8 +#define HC_HTXnL5BasH_SHIFT 16 +/* HC_SubA_HTXnL678BasH 0x0022 + */ +#define HC_HTXnL6BasH_MASK 0x000000ff +#define HC_HTXnL7BasH_MASK 0x0000ff00 +#define HC_HTXnL8BasH_MASK 0x00ff0000 +#define HC_HTXnL7BasH_SHIFT 8 +#define HC_HTXnL8BasH_SHIFT 16 +/* HC_SubA_HTXnL9abBasH 0x0023 + */ +#define HC_HTXnL9BasH_MASK 0x000000ff +#define HC_HTXnLaBasH_MASK 0x0000ff00 +#define HC_HTXnLbBasH_MASK 0x00ff0000 +#define HC_HTXnLaBasH_SHIFT 8 +#define HC_HTXnLbBasH_SHIFT 16 +/* HC_SubA_HTXnLcdeBasH 0x0024 + */ +#define HC_HTXnLcBasH_MASK 0x000000ff +#define HC_HTXnLdBasH_MASK 0x0000ff00 +#define HC_HTXnLeBasH_MASK 0x00ff0000 +#define HC_HTXnLdBasH_SHIFT 8 +#define HC_HTXnLeBasH_SHIFT 16 +/* HC_SubA_HTXnLcdeBasH 0x0025 + */ +#define HC_HTXnLfBasH_MASK 0x000000ff +#define HC_HTXnL10BasH_MASK 0x0000ff00 +#define HC_HTXnL11BasH_MASK 0x00ff0000 +#define HC_HTXnL10BasH_SHIFT 8 +#define HC_HTXnL11BasH_SHIFT 16 +/* HC_SubA_HTXnL0Pit 0x002b + */ +#define HC_HTXnLnPit_MASK 0x00003fff +#define HC_HTXnEnPit_MASK 0x00080000 +#define HC_HTXnLnPitE_MASK 0x00f00000 +#define HC_HTXnLnPitE_SHIFT 20 +/* HC_SubA_HTXnL0_5WE 0x004b + */ +#define HC_HTXnL0WE_MASK 0x0000000f +#define HC_HTXnL1WE_MASK 0x000000f0 +#define HC_HTXnL2WE_MASK 0x00000f00 +#define HC_HTXnL3WE_MASK 0x0000f000 +#define HC_HTXnL4WE_MASK 0x000f0000 +#define HC_HTXnL5WE_MASK 0x00f00000 +#define HC_HTXnL1WE_SHIFT 4 +#define HC_HTXnL2WE_SHIFT 8 +#define HC_HTXnL3WE_SHIFT 12 +#define HC_HTXnL4WE_SHIFT 16 +#define HC_HTXnL5WE_SHIFT 20 +/* HC_SubA_HTXnL6_bWE 0x004c + */ +#define HC_HTXnL6WE_MASK 0x0000000f +#define HC_HTXnL7WE_MASK 0x000000f0 +#define HC_HTXnL8WE_MASK 0x00000f00 +#define HC_HTXnL9WE_MASK 0x0000f000 +#define HC_HTXnLaWE_MASK 0x000f0000 +#define HC_HTXnLbWE_MASK 0x00f00000 +#define HC_HTXnL7WE_SHIFT 4 +#define HC_HTXnL8WE_SHIFT 8 +#define HC_HTXnL9WE_SHIFT 12 +#define HC_HTXnLaWE_SHIFT 16 +#define HC_HTXnLbWE_SHIFT 20 +/* HC_SubA_HTXnLc_11WE 0x004d + */ +#define HC_HTXnLcWE_MASK 0x0000000f +#define HC_HTXnLdWE_MASK 0x000000f0 +#define HC_HTXnLeWE_MASK 0x00000f00 +#define HC_HTXnLfWE_MASK 0x0000f000 +#define HC_HTXnL10WE_MASK 0x000f0000 +#define HC_HTXnL11WE_MASK 0x00f00000 +#define HC_HTXnLdWE_SHIFT 4 +#define HC_HTXnLeWE_SHIFT 8 +#define HC_HTXnLfWE_SHIFT 12 +#define HC_HTXnL10WE_SHIFT 16 +#define HC_HTXnL11WE_SHIFT 20 +/* HC_SubA_HTXnL0_5HE 0x0051 + */ +#define HC_HTXnL0HE_MASK 0x0000000f +#define HC_HTXnL1HE_MASK 0x000000f0 +#define HC_HTXnL2HE_MASK 0x00000f00 +#define HC_HTXnL3HE_MASK 0x0000f000 +#define HC_HTXnL4HE_MASK 0x000f0000 +#define HC_HTXnL5HE_MASK 0x00f00000 +#define HC_HTXnL1HE_SHIFT 4 +#define HC_HTXnL2HE_SHIFT 8 +#define HC_HTXnL3HE_SHIFT 12 +#define HC_HTXnL4HE_SHIFT 16 +#define HC_HTXnL5HE_SHIFT 20 +/* HC_SubA_HTXnL6_bHE 0x0052 + */ +#define HC_HTXnL6HE_MASK 0x0000000f +#define HC_HTXnL7HE_MASK 0x000000f0 +#define HC_HTXnL8HE_MASK 0x00000f00 +#define HC_HTXnL9HE_MASK 0x0000f000 +#define HC_HTXnLaHE_MASK 0x000f0000 +#define HC_HTXnLbHE_MASK 0x00f00000 +#define HC_HTXnL7HE_SHIFT 4 +#define HC_HTXnL8HE_SHIFT 8 +#define HC_HTXnL9HE_SHIFT 12 +#define HC_HTXnLaHE_SHIFT 16 +#define HC_HTXnLbHE_SHIFT 20 +/* HC_SubA_HTXnLc_11HE 0x0053 + */ +#define HC_HTXnLcHE_MASK 0x0000000f +#define HC_HTXnLdHE_MASK 0x000000f0 +#define HC_HTXnLeHE_MASK 0x00000f00 +#define HC_HTXnLfHE_MASK 0x0000f000 +#define HC_HTXnL10HE_MASK 0x000f0000 +#define HC_HTXnL11HE_MASK 0x00f00000 +#define HC_HTXnLdHE_SHIFT 4 +#define HC_HTXnLeHE_SHIFT 8 +#define HC_HTXnLfHE_SHIFT 12 +#define HC_HTXnL10HE_SHIFT 16 +#define HC_HTXnL11HE_SHIFT 20 +/* HC_SubA_HTXnL0OS 0x0077 + */ +#define HC_HTXnL0OS_MASK 0x003ff000 +#define HC_HTXnLVmax_MASK 0x00000fc0 +#define HC_HTXnLVmin_MASK 0x0000003f +#define HC_HTXnL0OS_SHIFT 12 +#define HC_HTXnLVmax_SHIFT 6 +/* HC_SubA_HTXnTB 0x0078 + */ +#define HC_HTXnTB_MASK 0x00f00000 +#define HC_HTXnFLSe_MASK 0x0000e000 +#define HC_HTXnFLSs_MASK 0x00001c00 +#define HC_HTXnFLTe_MASK 0x00000380 +#define HC_HTXnFLTs_MASK 0x00000070 +#define HC_HTXnFLDs_MASK 0x0000000f +#define HC_HTXnTB_NoTB 0x00000000 +#define HC_HTXnTB_TBC_S 0x00100000 +#define HC_HTXnTB_TBC_T 0x00200000 +#define HC_HTXnTB_TB_S 0x00400000 +#define HC_HTXnTB_TB_T 0x00800000 +#define HC_HTXnFLSe_Nearest 0x00000000 +#define HC_HTXnFLSe_Linear 0x00002000 +#define HC_HTXnFLSe_NonLinear 0x00004000 +#define HC_HTXnFLSe_Sharp 0x00008000 +#define HC_HTXnFLSe_Flat_Gaussian_Cubic 0x0000c000 +#define HC_HTXnFLSs_Nearest 0x00000000 +#define HC_HTXnFLSs_Linear 0x00000400 +#define HC_HTXnFLSs_NonLinear 0x00000800 +#define HC_HTXnFLSs_Flat_Gaussian_Cubic 0x00001800 +#define HC_HTXnFLTe_Nearest 0x00000000 +#define HC_HTXnFLTe_Linear 0x00000080 +#define HC_HTXnFLTe_NonLinear 0x00000100 +#define HC_HTXnFLTe_Sharp 0x00000180 +#define HC_HTXnFLTe_Flat_Gaussian_Cubic 0x00000300 +#define HC_HTXnFLTs_Nearest 0x00000000 +#define HC_HTXnFLTs_Linear 0x00000010 +#define HC_HTXnFLTs_NonLinear 0x00000020 +#define HC_HTXnFLTs_Flat_Gaussian_Cubic 0x00000060 +#define HC_HTXnFLDs_Tex0 0x00000000 +#define HC_HTXnFLDs_Nearest 0x00000001 +#define HC_HTXnFLDs_Linear 0x00000002 +#define HC_HTXnFLDs_NonLinear 0x00000003 +#define HC_HTXnFLDs_Dither 0x00000004 +#define HC_HTXnFLDs_ConstLOD 0x00000005 +#define HC_HTXnFLDs_Ani 0x00000006 +#define HC_HTXnFLDs_AniDither 0x00000007 +/* HC_SubA_HTXnMPMD 0x0079 + */ +#define HC_HTXnMPMD_SMASK 0x00070000 +#define HC_HTXnMPMD_TMASK 0x00380000 +#define HC_HTXnLODDTf_MASK 0x00000007 +#define HC_HTXnXY2ST_MASK 0x00000008 +#define HC_HTXnMPMD_Tsingle 0x00000000 +#define HC_HTXnMPMD_Tclamp 0x00080000 +#define HC_HTXnMPMD_Trepeat 0x00100000 +#define HC_HTXnMPMD_Tmirror 0x00180000 +#define HC_HTXnMPMD_Twrap 0x00200000 +#define HC_HTXnMPMD_Ssingle 0x00000000 +#define HC_HTXnMPMD_Sclamp 0x00010000 +#define HC_HTXnMPMD_Srepeat 0x00020000 +#define HC_HTXnMPMD_Smirror 0x00030000 +#define HC_HTXnMPMD_Swrap 0x00040000 +/* HC_SubA_HTXnCLODu 0x007a + */ +#define HC_HTXnCLODu_MASK 0x000ffc00 +#define HC_HTXnCLODd_MASK 0x000003ff +#define HC_HTXnCLODu_SHIFT 10 +/* HC_SubA_HTXnFM 0x007b + */ +#define HC_HTXnFM_MASK 0x00ff0000 +#define HC_HTXnLoc_MASK 0x00000003 +#define HC_HTXnFM_INDEX 0x00000000 +#define HC_HTXnFM_Intensity 0x00080000 +#define HC_HTXnFM_Lum 0x00100000 +#define HC_HTXnFM_Alpha 0x00180000 +#define HC_HTXnFM_DX 0x00280000 +#define HC_HTXnFM_YUV 0x00300000 +#define HC_HTXnFM_ARGB16 0x00880000 +#define HC_HTXnFM_ARGB32 0x00980000 +#define HC_HTXnFM_ABGR16 0x00a80000 +#define HC_HTXnFM_ABGR32 0x00b80000 +#define HC_HTXnFM_RGBA16 0x00c80000 +#define HC_HTXnFM_RGBA32 0x00d80000 +#define HC_HTXnFM_BGRA16 0x00e80000 +#define HC_HTXnFM_BGRA32 0x00f80000 +#define HC_HTXnFM_BUMPMAP 0x00380000 +#define HC_HTXnFM_Index1 (HC_HTXnFM_INDEX | 0x00000000) +#define HC_HTXnFM_Index2 (HC_HTXnFM_INDEX | 0x00010000) +#define HC_HTXnFM_Index4 (HC_HTXnFM_INDEX | 0x00020000) +#define HC_HTXnFM_Index8 (HC_HTXnFM_INDEX | 0x00030000) +#define HC_HTXnFM_T1 (HC_HTXnFM_Intensity | 0x00000000) +#define HC_HTXnFM_T2 (HC_HTXnFM_Intensity | 0x00010000) +#define HC_HTXnFM_T4 (HC_HTXnFM_Intensity | 0x00020000) +#define HC_HTXnFM_T8 (HC_HTXnFM_Intensity | 0x00030000) +#define HC_HTXnFM_L1 (HC_HTXnFM_Lum | 0x00000000) +#define HC_HTXnFM_L2 (HC_HTXnFM_Lum | 0x00010000) +#define HC_HTXnFM_L4 (HC_HTXnFM_Lum | 0x00020000) +#define HC_HTXnFM_L8 (HC_HTXnFM_Lum | 0x00030000) +#define HC_HTXnFM_AL44 (HC_HTXnFM_Lum | 0x00040000) +#define HC_HTXnFM_AL88 (HC_HTXnFM_Lum | 0x00050000) +#define HC_HTXnFM_A1 (HC_HTXnFM_Alpha | 0x00000000) +#define HC_HTXnFM_A2 (HC_HTXnFM_Alpha | 0x00010000) +#define HC_HTXnFM_A4 (HC_HTXnFM_Alpha | 0x00020000) +#define HC_HTXnFM_A8 (HC_HTXnFM_Alpha | 0x00030000) +#define HC_HTXnFM_DX1 (HC_HTXnFM_DX | 0x00010000) +#define HC_HTXnFM_DX23 (HC_HTXnFM_DX | 0x00020000) +#define HC_HTXnFM_DX45 (HC_HTXnFM_DX | 0x00030000) +/* YUV package mode */ +#define HC_HTXnFM_YUY2 (HC_HTXnFM_YUV | 0x00000000) +/* YUV planner mode */ +#define HC_HTXnFM_YV12 (HC_HTXnFM_YUV | 0x00040000) +/* YUV planner mode */ +#define HC_HTXnFM_IYUV (HC_HTXnFM_YUV | 0x00040000) +#define HC_HTXnFM_RGB555 (HC_HTXnFM_ARGB16 | 0x00000000) +#define HC_HTXnFM_RGB565 (HC_HTXnFM_ARGB16 | 0x00010000) +#define HC_HTXnFM_ARGB1555 (HC_HTXnFM_ARGB16 | 0x00020000) +#define HC_HTXnFM_ARGB4444 (HC_HTXnFM_ARGB16 | 0x00030000) +#define HC_HTXnFM_ARGB0888 (HC_HTXnFM_ARGB32 | 0x00000000) +#define HC_HTXnFM_ARGB8888 (HC_HTXnFM_ARGB32 | 0x00010000) +#define HC_HTXnFM_BGR555 (HC_HTXnFM_ABGR16 | 0x00000000) +#define HC_HTXnFM_BGR565 (HC_HTXnFM_ABGR16 | 0x00010000) +#define HC_HTXnFM_ABGR1555 (HC_HTXnFM_ABGR16 | 0x00020000) +#define HC_HTXnFM_ABGR4444 (HC_HTXnFM_ABGR16 | 0x00030000) +#define HC_HTXnFM_ABGR0888 (HC_HTXnFM_ABGR32 | 0x00000000) +#define HC_HTXnFM_ABGR8888 (HC_HTXnFM_ABGR32 | 0x00010000) +#define HC_HTXnFM_RGBA5550 (HC_HTXnFM_RGBA16 | 0x00000000) +#define HC_HTXnFM_RGBA5551 (HC_HTXnFM_RGBA16 | 0x00020000) +#define HC_HTXnFM_RGBA4444 (HC_HTXnFM_RGBA16 | 0x00030000) +#define HC_HTXnFM_RGBA8880 (HC_HTXnFM_RGBA32 | 0x00000000) +#define HC_HTXnFM_RGBA8888 (HC_HTXnFM_RGBA32 | 0x00010000) +#define HC_HTXnFM_BGRA5550 (HC_HTXnFM_BGRA16 | 0x00000000) +#define HC_HTXnFM_BGRA5551 (HC_HTXnFM_BGRA16 | 0x00020000) +#define HC_HTXnFM_BGRA4444 (HC_HTXnFM_BGRA16 | 0x00030000) +#define HC_HTXnFM_BGRA8880 (HC_HTXnFM_BGRA32 | 0x00000000) +#define HC_HTXnFM_BGRA8888 (HC_HTXnFM_BGRA32 | 0x00010000) +#define HC_HTXnFM_VU88 (HC_HTXnFM_BUMPMAP | 0x00000000) +#define HC_HTXnFM_LVU655 (HC_HTXnFM_BUMPMAP | 0x00010000) +#define HC_HTXnFM_LVU888 (HC_HTXnFM_BUMPMAP | 0x00020000) +#define HC_HTXnLoc_Local 0x00000000 +#define HC_HTXnLoc_Sys 0x00000002 +#define HC_HTXnLoc_AGP 0x00000003 + +/* Video Texture */ +#define HC_HTXnYUV2RGBMode_RGB 0x00000000 +#define HC_HTXnYUV2RGBMode_SDTV 0x00000001 +#define HC_HTXnYUV2RGBMode_HDTV 0x00000002 +#define HC_HTXnYUV2RGBMode_TABLE 0x00000003 + +/* HC_SubA_HTXnTRAH 0x007f + */ +#define HC_HTXnTRAH_MASK 0x00ff0000 +#define HC_HTXnTRAL_MASK 0x0000ff00 +#define HC_HTXnTBA_MASK 0x000000ff +#define HC_HTXnTRAH_SHIFT 16 +#define HC_HTXnTRAL_SHIFT 8 +/* HC_SubA_HTXnTBLCsat 0x0080 + *-- Define the input texture. + */ +#define HC_XTC_TOPC 0x00000000 +#define HC_XTC_InvTOPC 0x00000010 +#define HC_XTC_TOPCp5 0x00000020 +#define HC_XTC_Cbias 0x00000000 +#define HC_XTC_InvCbias 0x00000010 +#define HC_XTC_0 0x00000000 +#define HC_XTC_Dif 0x00000001 +#define HC_XTC_Spec 0x00000002 +#define HC_XTC_Tex 0x00000003 +#define HC_XTC_Cur 0x00000004 +#define HC_XTC_Adif 0x00000005 +#define HC_XTC_Fog 0x00000006 +#define HC_XTC_Atex 0x00000007 +#define HC_XTC_Acur 0x00000008 +#define HC_XTC_HTXnTBLRC 0x00000009 +#define HC_XTC_Ctexnext 0x0000000a +/*-- + */ +#define HC_HTXnTBLCsat_MASK 0x00800000 +#define HC_HTXnTBLCa_MASK 0x000fc000 +#define HC_HTXnTBLCb_MASK 0x00001f80 +#define HC_HTXnTBLCc_MASK 0x0000003f +#define HC_HTXnTBLCa_TOPC (HC_XTC_TOPC << 14) +#define HC_HTXnTBLCa_InvTOPC (HC_XTC_InvTOPC << 14) +#define HC_HTXnTBLCa_TOPCp5 (HC_XTC_TOPCp5 << 14) +#define HC_HTXnTBLCa_0 (HC_XTC_0 << 14) +#define HC_HTXnTBLCa_Dif (HC_XTC_Dif << 14) +#define HC_HTXnTBLCa_Spec (HC_XTC_Spec << 14) +#define HC_HTXnTBLCa_Tex (HC_XTC_Tex << 14) +#define HC_HTXnTBLCa_Cur (HC_XTC_Cur << 14) +#define HC_HTXnTBLCa_Adif (HC_XTC_Adif << 14) +#define HC_HTXnTBLCa_Fog (HC_XTC_Fog << 14) +#define HC_HTXnTBLCa_Atex (HC_XTC_Atex << 14) +#define HC_HTXnTBLCa_Acur (HC_XTC_Acur << 14) +#define HC_HTXnTBLCa_HTXnTBLRC (HC_XTC_HTXnTBLRC << 14) +#define HC_HTXnTBLCa_Ctexnext (HC_XTC_Ctexnext << 14) +#define HC_HTXnTBLCb_TOPC (HC_XTC_TOPC << 7) +#define HC_HTXnTBLCb_InvTOPC (HC_XTC_InvTOPC << 7) +#define HC_HTXnTBLCb_TOPCp5 (HC_XTC_TOPCp5 << 7) +#define HC_HTXnTBLCb_0 (HC_XTC_0 << 7) +#define HC_HTXnTBLCb_Dif (HC_XTC_Dif << 7) +#define HC_HTXnTBLCb_Spec (HC_XTC_Spec << 7) +#define HC_HTXnTBLCb_Tex (HC_XTC_Tex << 7) +#define HC_HTXnTBLCb_Cur (HC_XTC_Cur << 7) +#define HC_HTXnTBLCb_Adif (HC_XTC_Adif << 7) +#define HC_HTXnTBLCb_Fog (HC_XTC_Fog << 7) +#define HC_HTXnTBLCb_Atex (HC_XTC_Atex << 7) +#define HC_HTXnTBLCb_Acur (HC_XTC_Acur << 7) +#define HC_HTXnTBLCb_HTXnTBLRC (HC_XTC_HTXnTBLRC << 7) +#define HC_HTXnTBLCb_Ctexnext (HC_XTC_Ctexnext << 7) +#define HC_HTXnTBLCc_TOPC (HC_XTC_TOPC << 0) +#define HC_HTXnTBLCc_InvTOPC (HC_XTC_InvTOPC << 0) +#define HC_HTXnTBLCc_TOPCp5 (HC_XTC_TOPCp5 << 0) +#define HC_HTXnTBLCc_0 (HC_XTC_0 << 0) +#define HC_HTXnTBLCc_Dif (HC_XTC_Dif << 0) +#define HC_HTXnTBLCc_Spec (HC_XTC_Spec << 0) +#define HC_HTXnTBLCc_Tex (HC_XTC_Tex << 0) +#define HC_HTXnTBLCc_Cur (HC_XTC_Cur << 0) +#define HC_HTXnTBLCc_Adif (HC_XTC_Adif << 0) +#define HC_HTXnTBLCc_Fog (HC_XTC_Fog << 0) +#define HC_HTXnTBLCc_Atex (HC_XTC_Atex << 0) +#define HC_HTXnTBLCc_Acur (HC_XTC_Acur << 0) +#define HC_HTXnTBLCc_HTXnTBLRC (HC_XTC_HTXnTBLRC << 0) +#define HC_HTXnTBLCc_Ctexnext (HC_XTC_Ctexnext << 0) +/* HC_SubA_HTXnTBLCop 0x0081 + */ +#define HC_HTXnTBLdot_MASK 0x00c00000 +#define HC_HTXnTBLCop_MASK 0x00380000 +#define HC_HTXnTBLCbias_MASK 0x0007c000 +#define HC_HTXnTBLCshift_MASK 0x00001800 +#define HC_HTXnTBLAop_MASK 0x00000380 +#define HC_HTXnTBLAbias_MASK 0x00000078 +#define HC_HTXnTBLAshift_MASK 0x00000003 +#define HC_HTXnTBLCop_Add 0x00000000 +#define HC_HTXnTBLCop_Sub 0x00080000 +#define HC_HTXnTBLCop_Min 0x00100000 +#define HC_HTXnTBLCop_Max 0x00180000 +#define HC_HTXnTBLCop_Mask 0x00200000 +#define HC_HTXnTBLCbias_Cbias (HC_XTC_Cbias << 14) +#define HC_HTXnTBLCbias_InvCbias (HC_XTC_InvCbias << 14) +#define HC_HTXnTBLCbias_0 (HC_XTC_0 << 14) +#define HC_HTXnTBLCbias_Dif (HC_XTC_Dif << 14) +#define HC_HTXnTBLCbias_Spec (HC_XTC_Spec << 14) +#define HC_HTXnTBLCbias_Tex (HC_XTC_Tex << 14) +#define HC_HTXnTBLCbias_Cur (HC_XTC_Cur << 14) +#define HC_HTXnTBLCbias_Adif (HC_XTC_Adif << 14) +#define HC_HTXnTBLCbias_Fog (HC_XTC_Fog << 14) +#define HC_HTXnTBLCbias_Atex (HC_XTC_Atex << 14) +#define HC_HTXnTBLCbias_Acur (HC_XTC_Acur << 14) +#define HC_HTXnTBLCbias_HTXnTBLRC (HC_XTC_HTXnTBLRC << 14) +#define HC_HTXnTBLCshift_1 0x00000000 +#define HC_HTXnTBLCshift_2 0x00000800 +#define HC_HTXnTBLCshift_No 0x00001000 +#define HC_HTXnTBLCshift_DotP 0x00001800 +/*=* John Sheng [2003.7.18] texture combine *=*/ +#define HC_HTXnTBLDOT3 0x00080000 +#define HC_HTXnTBLDOT4 0x000C0000 + +#define HC_HTXnTBLAop_Add 0x00000000 +#define HC_HTXnTBLAop_Sub 0x00000080 +#define HC_HTXnTBLAop_Min 0x00000100 +#define HC_HTXnTBLAop_Max 0x00000180 +#define HC_HTXnTBLAop_Mask 0x00000200 +#define HC_HTXnTBLAbias_Inv 0x00000040 +#define HC_HTXnTBLAbias_Adif 0x00000000 +#define HC_HTXnTBLAbias_Fog 0x00000008 +#define HC_HTXnTBLAbias_Acur 0x00000010 +#define HC_HTXnTBLAbias_HTXnTBLRAbias 0x00000018 +#define HC_HTXnTBLAbias_Atex 0x00000020 +#define HC_HTXnTBLAshift_1 0x00000000 +#define HC_HTXnTBLAshift_2 0x00000001 +#define HC_HTXnTBLAshift_No 0x00000002 +/* #define HC_HTXnTBLAshift_DotP 0x00000003 */ +/* HC_SubA_HTXnTBLMPFog 0x0082 + */ +#define HC_HTXnTBLMPfog_MASK 0x00e00000 +#define HC_HTXnTBLMPfog_0 0x00000000 +#define HC_HTXnTBLMPfog_Adif 0x00200000 +#define HC_HTXnTBLMPfog_Fog 0x00400000 +#define HC_HTXnTBLMPfog_Atex 0x00600000 +#define HC_HTXnTBLMPfog_Acur 0x00800000 +#define HC_HTXnTBLMPfog_GHTXnTBLRFog 0x00a00000 +/* HC_SubA_HTXnTBLAsat 0x0083 + *-- Define the texture alpha input. + */ +#define HC_XTA_TOPA 0x00000000 +#define HC_XTA_InvTOPA 0x00000008 +#define HC_XTA_TOPAp5 0x00000010 +#define HC_XTA_Adif 0x00000000 +#define HC_XTA_Fog 0x00000001 +#define HC_XTA_Acur 0x00000002 +#define HC_XTA_HTXnTBLRA 0x00000003 +#define HC_XTA_Atex 0x00000004 +#define HC_XTA_Atexnext 0x00000005 +/*-- + */ +#define HC_HTXnTBLAsat_MASK 0x00800000 +#define HC_HTXnTBLAMB_MASK 0x00700000 +#define HC_HTXnTBLAa_MASK 0x0007c000 +#define HC_HTXnTBLAb_MASK 0x00000f80 +#define HC_HTXnTBLAc_MASK 0x0000001f +#define HC_HTXnTBLAMB_SHIFT 20 +#define HC_HTXnTBLAa_TOPA (HC_XTA_TOPA << 14) +#define HC_HTXnTBLAa_InvTOPA (HC_XTA_InvTOPA << 14) +#define HC_HTXnTBLAa_TOPAp5 (HC_XTA_TOPAp5 << 14) +#define HC_HTXnTBLAa_Adif (HC_XTA_Adif << 14) +#define HC_HTXnTBLAa_Fog (HC_XTA_Fog << 14) +#define HC_HTXnTBLAa_Acur (HC_XTA_Acur << 14) +#define HC_HTXnTBLAa_HTXnTBLRA (HC_XTA_HTXnTBLRA << 14) +#define HC_HTXnTBLAa_Atex (HC_XTA_Atex << 14) +#define HC_HTXnTBLAa_Atexnext (HC_XTA_Atexnext << 14) +#define HC_HTXnTBLAb_TOPA (HC_XTA_TOPA << 7) +#define HC_HTXnTBLAb_InvTOPA (HC_XTA_InvTOPA << 7) +#define HC_HTXnTBLAb_TOPAp5 (HC_XTA_TOPAp5 << 7) +#define HC_HTXnTBLAb_Adif (HC_XTA_Adif << 7) +#define HC_HTXnTBLAb_Fog (HC_XTA_Fog << 7) +#define HC_HTXnTBLAb_Acur (HC_XTA_Acur << 7) +#define HC_HTXnTBLAb_HTXnTBLRA (HC_XTA_HTXnTBLRA << 7) +#define HC_HTXnTBLAb_Atex (HC_XTA_Atex << 7) +#define HC_HTXnTBLAb_Atexnext (HC_XTA_Atexnext << 7) +#define HC_HTXnTBLAc_TOPA (HC_XTA_TOPA << 0) +#define HC_HTXnTBLAc_InvTOPA (HC_XTA_InvTOPA << 0) +#define HC_HTXnTBLAc_TOPAp5 (HC_XTA_TOPAp5 << 0) +#define HC_HTXnTBLAc_Adif (HC_XTA_Adif << 0) +#define HC_HTXnTBLAc_Fog (HC_XTA_Fog << 0) +#define HC_HTXnTBLAc_Acur (HC_XTA_Acur << 0) +#define HC_HTXnTBLAc_HTXnTBLRA (HC_XTA_HTXnTBLRA << 0) +#define HC_HTXnTBLAc_Atex (HC_XTA_Atex << 0) +#define HC_HTXnTBLAc_Atexnext (HC_XTA_Atexnext << 0) +/* HC_SubA_HTXnTBLRAa 0x0089 + */ +#define HC_HTXnTBLRAa_MASK 0x00ff0000 +#define HC_HTXnTBLRAb_MASK 0x0000ff00 +#define HC_HTXnTBLRAc_MASK 0x000000ff +#define HC_HTXnTBLRAa_SHIFT 16 +#define HC_HTXnTBLRAb_SHIFT 8 +#define HC_HTXnTBLRAc_SHIFT 0 +/* HC_SubA_HTXnTBLRFog 0x008a + */ +#define HC_HTXnTBLRFog_MASK 0x0000ff00 +#define HC_HTXnTBLRAbias_MASK 0x000000ff +#define HC_HTXnTBLRFog_SHIFT 8 +#define HC_HTXnTBLRAbias_SHIFT 0 +/* HC_SubA_HTXnLScale 0x0094 + */ +#define HC_HTXnLScale_MASK 0x0007fc00 +#define HC_HTXnLOff_MASK 0x000001ff +#define HC_HTXnLScale_SHIFT 10 +/* HC_SubA_HTXSMD 0x0000 + */ +#define HC_HTXSMD_MASK 0x00000080 +#define HC_HTXTMD_MASK 0x00000040 +#define HC_HTXNum_MASK 0x00000038 +#define HC_HTXTRMD_MASK 0x00000006 +#define HC_HTXCHCLR_MASK 0x00000001 +#define HC_HTXNum_SHIFT 3 + +/* Texture Palette n + */ +#define HC_SubType_TexPalette0 0x00000000 +#define HC_SubType_TexPalette1 0x00000001 +#define HC_SubType_FogTable 0x00000010 +#define HC_SubType_Stipple 0x00000014 +/* HC_SubA_TexPalette0 0x0000 + */ +#define HC_HTPnA_MASK 0xff000000 +#define HC_HTPnR_MASK 0x00ff0000 +#define HC_HTPnG_MASK 0x0000ff00 +#define HC_HTPnB_MASK 0x000000ff +/* HC_SubA_FogTable 0x0010 + */ +#define HC_HFPn3_MASK 0xff000000 +#define HC_HFPn2_MASK 0x00ff0000 +#define HC_HFPn1_MASK 0x0000ff00 +#define HC_HFPn_MASK 0x000000ff +#define HC_HFPn3_SHIFT 24 +#define HC_HFPn2_SHIFT 16 +#define HC_HFPn1_SHIFT 8 + +/* Auto Testing & Security + */ +#define HC_SubA_HenFIFOAT 0x0000 +#define HC_SubA_HFBDrawFirst 0x0004 +#define HC_SubA_HFBBasL 0x0005 +#define HC_SubA_HFBDst 0x0006 +/* HC_SubA_HenFIFOAT 0x0000 + */ +#define HC_HenFIFOAT_MASK 0x00000020 +#define HC_HenGEMILock_MASK 0x00000010 +#define HC_HenFBASwap_MASK 0x00000008 +#define HC_HenOT_MASK 0x00000004 +#define HC_HenCMDQ_MASK 0x00000002 +#define HC_HenTXCTSU_MASK 0x00000001 +/* HC_SubA_HFBDrawFirst 0x0004 + */ +#define HC_HFBDrawFirst_MASK 0x00000800 +#define HC_HFBQueue_MASK 0x00000400 +#define HC_HFBLock_MASK 0x00000200 +#define HC_HEOF_MASK 0x00000100 +#define HC_HFBBasH_MASK 0x000000ff + +/* GEMI Setting + */ +#define HC_SubA_HTArbRCM 0x0008 +#define HC_SubA_HTArbRZ 0x000a +#define HC_SubA_HTArbWZ 0x000b +#define HC_SubA_HTArbRTX 0x000c +#define HC_SubA_HTArbRCW 0x000d +#define HC_SubA_HTArbE2 0x000e +#define HC_SubA_HArbRQCM 0x0010 +#define HC_SubA_HArbWQCM 0x0011 +#define HC_SubA_HGEMITout 0x0020 +#define HC_SubA_HFthRTXD 0x0040 +#define HC_SubA_HFthRTXA 0x0044 +#define HC_SubA_HCMDQstL 0x0050 +#define HC_SubA_HCMDQendL 0x0051 +#define HC_SubA_HCMDQLen 0x0052 +/* HC_SubA_HTArbRCM 0x0008 + */ +#define HC_HTArbRCM_MASK 0x0000ffff +/* HC_SubA_HTArbRZ 0x000a + */ +#define HC_HTArbRZ_MASK 0x0000ffff +/* HC_SubA_HTArbWZ 0x000b + */ +#define HC_HTArbWZ_MASK 0x0000ffff +/* HC_SubA_HTArbRTX 0x000c + */ +#define HC_HTArbRTX_MASK 0x0000ffff +/* HC_SubA_HTArbRCW 0x000d + */ +#define HC_HTArbRCW_MASK 0x0000ffff +/* HC_SubA_HTArbE2 0x000e + */ +#define HC_HTArbE2_MASK 0x0000ffff +/* HC_SubA_HArbRQCM 0x0010 + */ +#define HC_HTArbRQCM_MASK 0x0000ffff +/* HC_SubA_HArbWQCM 0x0011 + */ +#define HC_HArbWQCM_MASK 0x0000ffff +/* HC_SubA_HGEMITout 0x0020 + */ +#define HC_HGEMITout_MASK 0x000f0000 +#define HC_HNPArbZC_MASK 0x0000ffff +#define HC_HGEMITout_SHIFT 16 +/* HC_SubA_HFthRTXD 0x0040 + */ +#define HC_HFthRTXD_MASK 0x00ff0000 +#define HC_HFthRZD_MASK 0x0000ff00 +#define HC_HFthWZD_MASK 0x000000ff +#define HC_HFthRTXD_SHIFT 16 +#define HC_HFthRZD_SHIFT 8 +/* HC_SubA_HFthRTXA 0x0044 + */ +#define HC_HFthRTXA_MASK 0x000000ff + +/**************************************************************************** + * Define the Halcyon Internal register access constants. For simulator only. + ***************************************************************************/ +#define HC_SIMA_HAGPBstL 0x0000 +#define HC_SIMA_HAGPBendL 0x0001 +#define HC_SIMA_HAGPCMNT 0x0002 +#define HC_SIMA_HAGPBpL 0x0003 +#define HC_SIMA_HAGPBpH 0x0004 +#define HC_SIMA_HClipTB 0x0005 +#define HC_SIMA_HClipLR 0x0006 +#define HC_SIMA_HFPClipTL 0x0007 +#define HC_SIMA_HFPClipBL 0x0008 +#define HC_SIMA_HFPClipLL 0x0009 +#define HC_SIMA_HFPClipRL 0x000a +#define HC_SIMA_HFPClipTBH 0x000b +#define HC_SIMA_HFPClipLRH 0x000c +#define HC_SIMA_HLP 0x000d +#define HC_SIMA_HLPRF 0x000e +#define HC_SIMA_HSolidCL 0x000f +#define HC_SIMA_HPixGC 0x0010 +#define HC_SIMA_HSPXYOS 0x0011 +#define HC_SIMA_HCmdA 0x0012 +#define HC_SIMA_HCmdB 0x0013 +#define HC_SIMA_HEnable 0x0014 +#define HC_SIMA_HZWBBasL 0x0015 +#define HC_SIMA_HZWBBasH 0x0016 +#define HC_SIMA_HZWBType 0x0017 +#define HC_SIMA_HZBiasL 0x0018 +#define HC_SIMA_HZWBend 0x0019 +#define HC_SIMA_HZWTMD 0x001a +#define HC_SIMA_HZWCDL 0x001b +#define HC_SIMA_HZWCTAGnum 0x001c +#define HC_SIMA_HZCYNum 0x001d +#define HC_SIMA_HZWCFire 0x001e +/* #define HC_SIMA_HSBBasL 0x001d */ +/* #define HC_SIMA_HSBBasH 0x001e */ +/* #define HC_SIMA_HSBFM 0x001f */ +#define HC_SIMA_HSTREF 0x0020 +#define HC_SIMA_HSTMD 0x0021 +#define HC_SIMA_HABBasL 0x0022 +#define HC_SIMA_HABBasH 0x0023 +#define HC_SIMA_HABFM 0x0024 +#define HC_SIMA_HATMD 0x0025 +#define HC_SIMA_HABLCsat 0x0026 +#define HC_SIMA_HABLCop 0x0027 +#define HC_SIMA_HABLAsat 0x0028 +#define HC_SIMA_HABLAop 0x0029 +#define HC_SIMA_HABLRCa 0x002a +#define HC_SIMA_HABLRFCa 0x002b +#define HC_SIMA_HABLRCbias 0x002c +#define HC_SIMA_HABLRCb 0x002d +#define HC_SIMA_HABLRFCb 0x002e +#define HC_SIMA_HABLRAa 0x002f +#define HC_SIMA_HABLRAb 0x0030 +#define HC_SIMA_HDBBasL 0x0031 +#define HC_SIMA_HDBBasH 0x0032 +#define HC_SIMA_HDBFM 0x0033 +#define HC_SIMA_HFBBMSKL 0x0034 +#define HC_SIMA_HROP 0x0035 +#define HC_SIMA_HFogLF 0x0036 +#define HC_SIMA_HFogCL 0x0037 +#define HC_SIMA_HFogCH 0x0038 +#define HC_SIMA_HFogStL 0x0039 +#define HC_SIMA_HFogStH 0x003a +#define HC_SIMA_HFogOOdMF 0x003b +#define HC_SIMA_HFogOOdEF 0x003c +#define HC_SIMA_HFogEndL 0x003d +#define HC_SIMA_HFogDenst 0x003e +/*---- start of texture 0 setting ---- + */ +#define HC_SIMA_HTX0L0BasL 0x0040 +#define HC_SIMA_HTX0L1BasL 0x0041 +#define HC_SIMA_HTX0L2BasL 0x0042 +#define HC_SIMA_HTX0L3BasL 0x0043 +#define HC_SIMA_HTX0L4BasL 0x0044 +#define HC_SIMA_HTX0L5BasL 0x0045 +#define HC_SIMA_HTX0L6BasL 0x0046 +#define HC_SIMA_HTX0L7BasL 0x0047 +#define HC_SIMA_HTX0L8BasL 0x0048 +#define HC_SIMA_HTX0L9BasL 0x0049 +#define HC_SIMA_HTX0LaBasL 0x004a +#define HC_SIMA_HTX0LbBasL 0x004b +#define HC_SIMA_HTX0LcBasL 0x004c +#define HC_SIMA_HTX0LdBasL 0x004d +#define HC_SIMA_HTX0LeBasL 0x004e +#define HC_SIMA_HTX0LfBasL 0x004f +#define HC_SIMA_HTX0L10BasL 0x0050 +#define HC_SIMA_HTX0L11BasL 0x0051 +#define HC_SIMA_HTX0L012BasH 0x0052 +#define HC_SIMA_HTX0L345BasH 0x0053 +#define HC_SIMA_HTX0L678BasH 0x0054 +#define HC_SIMA_HTX0L9abBasH 0x0055 +#define HC_SIMA_HTX0LcdeBasH 0x0056 +#define HC_SIMA_HTX0Lf1011BasH 0x0057 +#define HC_SIMA_HTX0L0Pit 0x0058 +#define HC_SIMA_HTX0L1Pit 0x0059 +#define HC_SIMA_HTX0L2Pit 0x005a +#define HC_SIMA_HTX0L3Pit 0x005b +#define HC_SIMA_HTX0L4Pit 0x005c +#define HC_SIMA_HTX0L5Pit 0x005d +#define HC_SIMA_HTX0L6Pit 0x005e +#define HC_SIMA_HTX0L7Pit 0x005f +#define HC_SIMA_HTX0L8Pit 0x0060 +#define HC_SIMA_HTX0L9Pit 0x0061 +#define HC_SIMA_HTX0LaPit 0x0062 +#define HC_SIMA_HTX0LbPit 0x0063 +#define HC_SIMA_HTX0LcPit 0x0064 +#define HC_SIMA_HTX0LdPit 0x0065 +#define HC_SIMA_HTX0LePit 0x0066 +#define HC_SIMA_HTX0LfPit 0x0067 +#define HC_SIMA_HTX0L10Pit 0x0068 +#define HC_SIMA_HTX0L11Pit 0x0069 +#define HC_SIMA_HTX0L0_5WE 0x006a +#define HC_SIMA_HTX0L6_bWE 0x006b +#define HC_SIMA_HTX0Lc_11WE 0x006c +#define HC_SIMA_HTX0L0_5HE 0x006d +#define HC_SIMA_HTX0L6_bHE 0x006e +#define HC_SIMA_HTX0Lc_11HE 0x006f +#define HC_SIMA_HTX0L0OS 0x0070 +#define HC_SIMA_HTX0TB 0x0071 +#define HC_SIMA_HTX0MPMD 0x0072 +#define HC_SIMA_HTX0CLODu 0x0073 +#define HC_SIMA_HTX0FM 0x0074 +#define HC_SIMA_HTX0TRCH 0x0075 +#define HC_SIMA_HTX0TRCL 0x0076 +#define HC_SIMA_HTX0TBC 0x0077 +#define HC_SIMA_HTX0TRAH 0x0078 +#define HC_SIMA_HTX0TBLCsat 0x0079 +#define HC_SIMA_HTX0TBLCop 0x007a +#define HC_SIMA_HTX0TBLMPfog 0x007b +#define HC_SIMA_HTX0TBLAsat 0x007c +#define HC_SIMA_HTX0TBLRCa 0x007d +#define HC_SIMA_HTX0TBLRCb 0x007e +#define HC_SIMA_HTX0TBLRCc 0x007f +#define HC_SIMA_HTX0TBLRCbias 0x0080 +#define HC_SIMA_HTX0TBLRAa 0x0081 +#define HC_SIMA_HTX0TBLRFog 0x0082 +#define HC_SIMA_HTX0BumpM00 0x0083 +#define HC_SIMA_HTX0BumpM01 0x0084 +#define HC_SIMA_HTX0BumpM10 0x0085 +#define HC_SIMA_HTX0BumpM11 0x0086 +#define HC_SIMA_HTX0LScale 0x0087 +/*---- end of texture 0 setting ---- 0x008f + */ +#define HC_SIMA_TX0TX1_OFF 0x0050 +/*---- start of texture 1 setting ---- + */ +#define HC_SIMA_HTX1L0BasL \ + (HC_SIMA_HTX0L0BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L1BasL \ + (HC_SIMA_HTX0L1BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L2BasL \ + (HC_SIMA_HTX0L2BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L3BasL \ + (HC_SIMA_HTX0L3BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L4BasL (\ + HC_SIMA_HTX0L4BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L5BasL \ + (HC_SIMA_HTX0L5BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6BasL \ + (HC_SIMA_HTX0L6BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L7BasL \ + (HC_SIMA_HTX0L7BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L8BasL \ + (HC_SIMA_HTX0L8BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9BasL \ + (HC_SIMA_HTX0L9BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LaBasL \ + (HC_SIMA_HTX0LaBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LbBasL \ + (HC_SIMA_HTX0LbBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcBasL \ + (HC_SIMA_HTX0LcBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LdBasL \ + (HC_SIMA_HTX0LdBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LeBasL \ + (HC_SIMA_HTX0LeBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LfBasL \ + (HC_SIMA_HTX0LfBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L10BasL \ + (HC_SIMA_HTX0L10BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L11BasL \ + (HC_SIMA_HTX0L11BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L012BasH \ + (HC_SIMA_HTX0L012BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L345BasH \ + (HC_SIMA_HTX0L345BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L678BasH \ + (HC_SIMA_HTX0L678BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9abBasH \ + (HC_SIMA_HTX0L9abBasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcdeBasH \ + (HC_SIMA_HTX0LcdeBasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lf1011BasH \ + (HC_SIMA_HTX0Lf1011BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0Pit \ + (HC_SIMA_HTX0L0Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L1Pit \ + (HC_SIMA_HTX0L1Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L2Pit \ + (HC_SIMA_HTX0L2Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L3Pit \ + (HC_SIMA_HTX0L3Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L4Pit \ + (HC_SIMA_HTX0L4Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L5Pit \ + (HC_SIMA_HTX0L5Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6Pit \ + (HC_SIMA_HTX0L6Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L7Pit \ + (HC_SIMA_HTX0L7Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L8Pit \ + (HC_SIMA_HTX0L8Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9Pit \ + (HC_SIMA_HTX0L9Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LaPit \ + (HC_SIMA_HTX0LaPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LbPit \ + (HC_SIMA_HTX0LbPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcPit \ + (HC_SIMA_HTX0LcPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LdPit \ + (HC_SIMA_HTX0LdPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LePit \ + (HC_SIMA_HTX0LePit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LfPit \ + (HC_SIMA_HTX0LfPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L10Pit \ + (HC_SIMA_HTX0L10Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L11Pit \ + (HC_SIMA_HTX0L11Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0_5WE \ + (HC_SIMA_HTX0L0_5WE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6_bWE \ + (HC_SIMA_HTX0L6_bWE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lc_11WE \ + (HC_SIMA_HTX0Lc_11WE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0_5HE \ + (HC_SIMA_HTX0L0_5HE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6_bHE \ + (HC_SIMA_HTX0L6_bHE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lc_11HE \ + (HC_SIMA_HTX0Lc_11HE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0OS \ + (HC_SIMA_HTX0L0OS + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TB \ + (HC_SIMA_HTX0TB + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1MPMD \ + (HC_SIMA_HTX0MPMD + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1CLODu \ + (HC_SIMA_HTX0CLODu + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1FM \ + (HC_SIMA_HTX0FM + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRCH \ + (HC_SIMA_HTX0TRCH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRCL \ + (HC_SIMA_HTX0TRCL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBC \ + (HC_SIMA_HTX0TBC + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRAH \ + (HC_SIMA_HTX0TRAH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LTC \ + (HC_SIMA_HTX0LTC + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LTA \ + (HC_SIMA_HTX0LTA + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLCsat \ + (HC_SIMA_HTX0TBLCsat + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLCop \ + (HC_SIMA_HTX0TBLCop + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLMPfog \ + (HC_SIMA_HTX0TBLMPfog + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLAsat \ + (HC_SIMA_HTX0TBLAsat + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCa \ + (HC_SIMA_HTX0TBLRCa + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCb \ + (HC_SIMA_HTX0TBLRCb + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCc \ + (HC_SIMA_HTX0TBLRCc + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCbias \ + (HC_SIMA_HTX0TBLRCbias + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRAa \ + (HC_SIMA_HTX0TBLRAa + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRFog \ + (HC_SIMA_HTX0TBLRFog + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM00 \ + (HC_SIMA_HTX0BumpM00 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM01 \ + (HC_SIMA_HTX0BumpM01 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM10 \ + (HC_SIMA_HTX0BumpM10 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM11 \ + (HC_SIMA_HTX0BumpM11 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LScale \ + (HC_SIMA_HTX0LScale + HC_SIMA_TX0TX1_OFF) +/*---- end of texture 1 setting ---- 0xaf + */ +#define HC_SIMA_HTXSMD 0x00b0 +#define HC_SIMA_HenFIFOAT 0x00b1 +#define HC_SIMA_HFBDrawFirst 0x00b2 +#define HC_SIMA_HFBBasL 0x00b3 +#define HC_SIMA_HTArbRCM 0x00b4 +#define HC_SIMA_HTArbRZ 0x00b5 +#define HC_SIMA_HTArbWZ 0x00b6 +#define HC_SIMA_HTArbRTX 0x00b7 +#define HC_SIMA_HTArbRCW 0x00b8 +#define HC_SIMA_HTArbE2 0x00b9 +#define HC_SIMA_HGEMITout 0x00ba +#define HC_SIMA_HFthRTXD 0x00bb +#define HC_SIMA_HFthRTXA 0x00bc +/* Define the texture palette 0 + */ +#define HC_SIMA_HTP0 0x0100 +#define HC_SIMA_HTP1 0x0200 +#define HC_SIMA_FOGTABLE 0x0300 +#define HC_SIMA_STIPPLE 0x0400 +#define HC_SIMA_HE3Fire 0x0440 +#define HC_SIMA_TRANS_SET 0x0441 +#define HC_SIMA_HREngSt 0x0442 +#define HC_SIMA_HRFIFOempty 0x0443 +#define HC_SIMA_HRFIFOfull 0x0444 +#define HC_SIMA_HRErr 0x0445 +#define HC_SIMA_FIFOstatus 0x0446 + +/**************************************************************************** + * Define the AGP command header. + ***************************************************************************/ +#define HC_ACMD_MASK 0xfe000000 +#define HC_ACMD_SUB_MASK 0x0c000000 +#define HC_ACMD_HCmdA 0xee000000 +#define HC_ACMD_HCmdB 0xec000000 +#define HC_ACMD_HCmdC 0xea000000 +#define HC_ACMD_H1 0xf0000000 +#define HC_ACMD_H2 0xf2000000 +#define HC_ACMD_H3 0xf4000000 +#define HC_ACMD_H4 0xf6000000 + +#define HC_ACMD_H1IO_MASK 0x000001ff +#define HC_ACMD_H2IO1_MASK 0x001ff000 +#define HC_ACMD_H2IO2_MASK 0x000001ff +#define HC_ACMD_H2IO1_SHIFT 12 +#define HC_ACMD_H2IO2_SHIFT 0 +#define HC_ACMD_H3IO_MASK 0x000001ff +#define HC_ACMD_H3COUNT_MASK 0x01fff000 +#define HC_ACMD_H3COUNT_SHIFT 12 +#define HC_ACMD_H4ID_MASK 0x000001ff +#define HC_ACMD_H4COUNT_MASK 0x01fffe00 +#define HC_ACMD_H4COUNT_SHIFT 9 + +/***************************************************************************** + * Define Header + ****************************************************************************/ +#define HC_HEADER2 0xF210F110 + +/***************************************************************************** + * Define Dummy Value + ****************************************************************************/ +#define HC_DUMMY 0xCCCCCCCC +/***************************************************************************** + * Define for DMA use + ****************************************************************************/ +#define HALCYON_HEADER2 0XF210F110 +#define HALCYON_FIRECMD 0XEE100000 +#define HALCYON_FIREMASK 0XFFF00000 +#define HALCYON_CMDB 0XEC000000 +#define HALCYON_CMDBMASK 0XFFFE0000 +#define HALCYON_SUB_ADDR0 0X00000000 +#define HALCYON_HEADER1MASK 0XFFFFFC00 +#define HALCYON_HEADER1 0XF0000000 +#define HC_SubA_HAGPBstL 0x0060 +#define HC_SubA_HAGPBendL 0x0061 +#define HC_SubA_HAGPCMNT 0x0062 +#define HC_SubA_HAGPBpL 0x0063 +#define HC_SubA_HAGPBpH 0x0064 +#define HC_HAGPCMNT_MASK 0x00800000 +#define HC_HCmdErrClr_MASK 0x00400000 +#define HC_HAGPBendH_MASK 0x0000ff00 +#define HC_HAGPBstH_MASK 0x000000ff +#define HC_HAGPBendH_SHIFT 8 +#define HC_HAGPBstH_SHIFT 0 +#define HC_HAGPBpL_MASK 0x00fffffc +#define HC_HAGPBpID_MASK 0x00000003 +#define HC_HAGPBpID_PAUSE 0x00000000 +#define HC_HAGPBpID_JUMP 0x00000001 +#define HC_HAGPBpID_STOP 0x00000002 +#define HC_HAGPBpH_MASK 0x00ffffff + + +#define VIA_VIDEO_HEADER5 0xFE040000 +#define VIA_VIDEO_HEADER6 0xFE050000 +#define VIA_VIDEO_HEADER7 0xFE060000 +#define VIA_VIDEOMASK 0xFFFF0000 + +/***************************************************************************** + * Define for H5 DMA use + ****************************************************************************/ +#define H5_HC_DUMMY 0xCC000000 + +/* Command Header Type */ +#define INV_DUMMY_MASK 0xFF000000 +#define INV_AGPHeader0 0xFE000000 +#define INV_AGPHeader1 0xFE010000 +#define INV_AGPHeader2 0xFE020000 +#define INV_AGPHeader3 0xFE030000 +#define INV_AGPHeader4 0xFE040000 +#define INV_AGPHeader5 0xFE050000 +#define INV_AGPHeader6 0xFE060000 +#define INV_AGPHeader7 0xFE070000 +#define INV_AGPHeader9 0xFE090000 +#define INV_AGPHeaderA 0xFE0A0000 +#define INV_AGPHeader40 0xFE400000 +#define INV_AGPHeader41 0xFE410000 +#define INV_AGPHeader43 0xFE430000 +#define INV_AGPHeader45 0xFE450000 +#define INV_AGPHeader47 0xFE470000 +#define INV_AGPHeader4A 0xFE4A0000 +#define INV_AGPHeader82 0xFE820000 +#define INV_AGPHeader83 0xFE830000 +#define INV_AGPHeader_MASK 0xFFFF0000 +#define INV_AGPHeader2A 0xFE2A0000 +#define INV_AGPHeader25 0xFE250000 +#define INV_AGPHeader20 0xFE200000 +#define INV_AGPHeader23 0xFE230000 +#define INV_AGPHeaderE2 0xFEE20000 +#define INV_AGPHeaderE3 0xFEE30000 + +/*Transmission IO Space*/ +#define INV_REG_CR_TRANS 0x041C +#define INV_REG_CR_BEGIN 0x0420 +#define INV_REG_CR_END 0x0438 + +#define INV_REG_3D_TRANS 0x043C +#define INV_REG_3D_BEGIN 0x0440 +#define INV_REG_3D_END 0x06FC + +#define INV_ParaType_CmdVdata 0x0000 + +/* H5 Enable Setting + */ +#define INV_HC_SubA_HEnable1 0x00 + +#define INV_HC_HenAT4ALLRT_MASK 0x00100000 +#define INV_HC_HenATMRT3_MASK 0x00080000 +#define INV_HC_HenATMRT2_MASK 0x00040000 +#define INV_HC_HenATMRT1_MASK 0x00020000 +#define INV_HC_HenATMRT0_MASK 0x00010000 +#define INV_HC_HenSCMRT3_MASK 0x00008000 +#define INV_HC_HenSCMRT2_MASK 0x00004000 +#define INV_HC_HenSCMRT1_MASK 0x00002000 +#define INV_HC_HenSCMRT0_MASK 0x00001000 +#define INV_HC_HenFOGMRT3_MASK 0x00000800 +#define INV_HC_HenFOGMRT2_MASK 0x00000400 +#define INV_HC_HenFOGMRT1_MASK 0x00000200 +#define INV_HC_HenFOGMRT0_MASK 0x00000100 +#define INV_HC_HenABLMRT3_MASK 0x00000080 +#define INV_HC_HenABLMRT2_MASK 0x00000040 +#define INV_HC_HenABLMRT1_MASK 0x00000020 +#define INV_HC_HenABLMRT0_MASK 0x00000010 +#define INV_HC_HenDTMRT3_MASK 0x00000008 +#define INV_HC_HenDTMRT2_MASK 0x00000004 +#define INV_HC_HenDTMRT1_MASK 0x00000002 +#define INV_HC_HenDTMRT0_MASK 0x00000001 + +#define INV_HC_SubA_HEnable2 0x01 + +#define INV_HC_HenLUL2DR_MASK 0x00800000 +#define INV_HC_HenLDIAMOND_MASK 0x00400000 +#define INV_HC_HenPSPRITE_MASK 0x00200000 +#define INV_HC_HenC2S_MASK 0x00100000 +#define INV_HC_HenFOGPP_MASK 0x00080000 +#define INV_HC_HenSCPP_MASK 0x00040000 +#define INV_HC_HenCPP_MASK 0x00020000 +#define INV_HC_HenCZ_MASK 0x00002000 +#define INV_HC_HenVC_MASK 0x00001000 +#define INV_HC_HenCL_MASK 0x00000800 +#define INV_HC_HenPS_MASK 0x00000400 +#define INV_HC_HenWCZ_MASK 0x00000200 +#define INV_HC_HenTXCH_MASK 0x00000100 +#define INV_HC_HenBFCULL_MASK 0x00000080 +#define INV_HC_HenCW_MASK 0x00000040 +#define INV_HC_HenAA_MASK 0x00000020 +#define INV_HC_HenST_MASK 0x00000010 +#define INV_HC_HenZT_MASK 0x00000008 +#define INV_HC_HenZW_MASK 0x00000004 +#define INV_HC_HenSP_MASK 0x00000002 +#define INV_HC_HenLP_MASK 0x00000001 + +/* H5 Miscellaneous Settings + */ +#define INV_HC_SubA_HCClipTL 0x0080 +#define INV_HC_SubA_HCClipBL 0x0081 +#define INV_HC_SubA_HSClipTL 0x0082 +#define INV_HC_SubA_HSClipBL 0x0083 +#define INV_HC_SubA_HSolidCL 0x0086 +#define INV_HC_SubA_HSolidCH 0x0087 +#define INV_HC_SubA_HGBClipGL 0x0088 +#define INV_HC_SubA_HGBClipGR 0x0089 + + +#define INV_HC_ParaType_Vetex 0x00040000 + +#endif diff --git a/drivers/gpu/drm/via/via_connector.c b/drivers/gpu/drm/via/via_connector.c new file mode 100644 index 000000000000..3ebd2783b76f --- /dev/null +++ b/drivers/gpu/drm/via/via_connector.c @@ -0,0 +1,46 @@ +/* + * Copyright © 2017-2020 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#include <drm/drm_connector.h> + +#include "via_drv.h" + +void via_connector_destroy(struct drm_connector *connector) +{ + struct via_connector *con = container_of(connector, struct via_connector, base); + struct drm_property *property, *tmp; + + list_for_each_entry_safe(property, tmp, &con->props, head) + drm_property_destroy(connector->dev, property); + list_del(&con->props); + + drm_connector_update_edid_property(connector, NULL); + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + diff --git a/drivers/gpu/drm/via/via_crtc.c b/drivers/gpu/drm/via/via_crtc.c new file mode 100644 index 000000000000..74537c31e0f0 --- /dev/null +++ b/drivers/gpu/drm/via/via_crtc.c @@ -0,0 +1,2335 @@ +/* + * Copyright © 2019-2020 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> +#include <drm/drm_mode.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h> + +#include <drm/ttm/ttm_bo.h> + +#include "via_drv.h" +#include "via_disp_reg.h" + + +static struct vga_regset vpit_table[] = { + {VGA_SEQ_I, 0x01, 0xFF, 0x01 }, + {VGA_SEQ_I, 0x02, 0xFF, 0x0F }, + {VGA_SEQ_I, 0x03, 0xFF, 0x00 }, + {VGA_SEQ_I, 0x04, 0xFF, 0x0E }, + {VGA_GFX_I, 0x00, 0xFF, 0x00 }, + {VGA_GFX_I, 0x01, 0xFF, 0x00 }, + {VGA_GFX_I, 0x02, 0xFF, 0x00 }, + {VGA_GFX_I, 0x03, 0xFF, 0x00 }, + {VGA_GFX_I, 0x04, 0xFF, 0x00 }, + {VGA_GFX_I, 0x05, 0xFF, 0x00 }, + {VGA_GFX_I, 0x06, 0xFF, 0x05 }, + {VGA_GFX_I, 0x07, 0xFF, 0x0F }, + {VGA_GFX_I, 0x08, 0xFF, 0xFF } +}; + +static void via_iga_common_init(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Be careful with 3C5.15[5] - Wrap Around Disable. + * It must be set to 1 for proper operation. */ + /* 3C5.15[5] - Wrap Around Disable + * 0: Disable (For Mode 0-13) + * 1: Enable + * 3C5.15[1] - Extended Display Mode Enable + * 0: Disable + * 1: Enable */ + svga_wseq_mask(VGABASE, 0x15, BIT(5) | BIT(1), BIT(5) | BIT(1)); + + /* + * It was observed on NeoWare CA10 thin client with DVI that not + * resetting CR55[7] to 0 causes the screen driven by IGA2 to get + * distorted. + */ + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + svga_wcrt_mask(VGABASE, 0x55, 0x00, BIT(7)); + } + + /* + * Disable simultaneous display. + * Turning this on causes IGA1 to have a display issue. + */ + /* + * 3X5.6B[3] - Simultaneous Display Enable + * 0: Disable + * 1: Enable + */ + svga_wcrt_mask(VGABASE, 0x6B, 0x00, BIT(3)); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_iga1_set_color_depth(struct drm_device *dev, + u8 cpp, u8 depth) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 data; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + data = 0x00; + + /* Set the color depth for IGA1. */ + switch (cpp) { + case 1: + data |= BIT(4); + break; + case 2: + data = (depth == 15) ? BIT(2) : BIT(4) | BIT(2); + break; + case 4: + data = (depth == 30) ? BIT(3) : BIT(3) | BIT(2); + data |= BIT(4); + break; + default: + break; + } + + /* + * 3C5.15[4] - Hi Color Mode Select + * 0: 555 + * 1: 565 + * 3C5.15[3:2] - Display Color Depth Select + * 00: 8bpp + * 01: 16bpp + * 10: 30bpp + * 11: 32bpp + */ + svga_wseq_mask(VGABASE, 0x15, data, BIT(4) | BIT(3) | BIT(2)); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_iga2_set_color_depth(struct drm_device *dev, + u8 cpp, u8 depth) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 data; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + data = 0x00; + + /* Set the color depth for IGA2. */ + switch (cpp) { + case 1: + break; + case 2: + data = BIT(6); + break; + case 4: + data = (depth == 30) ? BIT(7) : BIT(7) | BIT(6); + break; + default: + break; + } + + /* + * 3X5.67[7:6] - Display Color Depth Select + * 00: 8bpp + * 01: 16bpp + * 10: 30bpp + * 11: 32bpp + */ + svga_wcrt_mask(VGABASE, 0x67, data, BIT(7) | BIT(6)); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static int via_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + int end = (size > 256) ? 256 : size, i; + u8 val = 0; + int ret = 0; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if ((!crtc->enabled) || (!crtc->primary->fb)) { + ret = -EINVAL; + goto exit; + } + + if (!iga->index) { + /* + * Access IGA1's pallette LUT. + */ + svga_wseq_mask(VGABASE, 0x1A, 0x00, BIT(0)); + + /* + * Is it an 8-bit color mode? + */ + if (crtc->primary->fb->format->cpp[0] == 1) { + /* Change to Primary Display's LUT */ + val = vga_rseq(VGABASE, 0x1B); + vga_wseq(VGABASE, 0x1B, val); + val = vga_rcrt(VGABASE, 0x67); + vga_wcrt(VGABASE, 0x67, val); + + /* Fill in IGA1's LUT */ + for (i = 0; i < end; i++) { + /* Bit mask of palette */ + vga_w(VGABASE, VGA_PEL_MSK, 0xFF); + vga_w(VGABASE, VGA_PEL_IW, i); + vga_w(VGABASE, VGA_PEL_D, r[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, g[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, b[i] >> 8); + } + /* enable LUT */ + svga_wseq_mask(VGABASE, 0x1B, 0x00, BIT(0)); + /* + * Disable gamma in case it was enabled + * previously + */ + svga_wcrt_mask(VGABASE, 0x33, 0x00, BIT(7)); + } else { + /* Enable Gamma */ + svga_wcrt_mask(VGABASE, 0x33, BIT(7), BIT(7)); + + /* Fill in IGA1's gamma */ + for (i = 0; i < end; i++) { + /* bit mask of palette */ + vga_w(VGABASE, VGA_PEL_MSK, 0xFF); + vga_w(VGABASE, VGA_PEL_IW, i); + vga_w(VGABASE, VGA_PEL_D, r[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, g[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, b[i] >> 8); + } + } + } else { + /* + * Access IGA2's pallette LUT. + */ + svga_wseq_mask(VGABASE, 0x1A, BIT(0), BIT(0)); + + /* + * Is it an 8-bit color mode? + */ + if (crtc->primary->fb->format->cpp[0] == 1) { + /* Enable Secondary Display Engine */ + svga_wseq_mask(VGABASE, 0x1B, BIT(7), BIT(7)); + /* Second Display Color Depth, 8bpp */ + svga_wcrt_mask(VGABASE, 0x67, 0x3F, 0x3F); + + /* + * Enable second display channel just in case. + */ + if (!(vga_rcrt(VGABASE, 0x6A) & BIT(7))) + svga_wcrt_mask(VGABASE, 0x6A, + BIT(7), BIT(7)); + + /* Fill in IGA2's LUT */ + for (i = 0; i < end; i++) { + /* Bit mask of palette */ + vga_w(VGABASE, VGA_PEL_MSK, 0xFF); + vga_w(VGABASE, VGA_PEL_IW, i); + vga_w(VGABASE, VGA_PEL_D, r[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, g[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, b[i] >> 8); + } + /* + * Disable gamma in case it was enabled + * previously + */ + svga_wcrt_mask(VGABASE, 0x6A, 0x00, BIT(1)); + } else { + u8 reg_bits = BIT(1); + + /* Bit 1 enables gamma */ + svga_wcrt_mask(VGABASE, 0x6A, BIT(1), BIT(1)); + + /* Old platforms LUT are 6 bits in size. + * Newer it is 8 bits. */ + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + case PCI_DEVICE_ID_VIA_KM400_GFX: + case PCI_DEVICE_ID_VIA_K8M800_GFX: + case PCI_DEVICE_ID_VIA_PM800_GFX: + break; + + default: + reg_bits |= BIT(5); + break; + } + svga_wcrt_mask(VGABASE, 0x6A, reg_bits, + reg_bits); + + /* + * Before we fill the second LUT, we have to + * enable second display channel. If it's + * enabled before, we don't need to do that, + * or else the secondary display will be dark + * for about 1 sec and then be turned on + * again. + */ + if (!(vga_rcrt(VGABASE, 0x6A) & BIT(7))) + svga_wcrt_mask(VGABASE, 0x6A, + BIT(7), BIT(7)); + + /* Fill in IGA2's gamma */ + for (i = 0; i < end; i++) { + /* bit mask of palette */ + vga_w(VGABASE, VGA_PEL_MSK, 0xFF); + vga_w(VGABASE, VGA_PEL_IW, i); + vga_w(VGABASE, VGA_PEL_D, r[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, g[i] >> 8); + vga_w(VGABASE, VGA_PEL_D, b[i] >> 8); + } + } + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_crtc_destroy(struct drm_crtc *crtc) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + + drm_crtc_cleanup(&iga->base); + kfree(iga); +} + +static const struct drm_crtc_funcs via_drm_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .gamma_set = via_gamma_set, + .set_config = drm_atomic_helper_set_config, + .destroy = via_crtc_destroy, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static void via_load_vpit_regs(struct via_drm_priv *dev_priv) +{ + u8 ar[] = {0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, + 0x01, 0x00, 0x0F, 0x00}; + struct vga_registers vpit_regs; + unsigned int i = 0; + u8 reg_value = 0; + + /* Enable changing the palette registers */ + reg_value = vga_r(VGABASE, VGA_IS1_RC); + vga_w(VGABASE, VGA_ATT_W, 0x00); + + /* Write Misc register */ + vga_w(VGABASE, VGA_MIS_W, 0xCF); + + /* Fill VPIT registers */ + vpit_regs.count = ARRAY_SIZE(vpit_table); + vpit_regs.regs = vpit_table; + load_register_tables(VGABASE, &vpit_regs); + + /* Write Attribute Controller */ + for (i = 0; i < 0x14; i++) { + reg_value = vga_r(VGABASE, VGA_IS1_RC); + vga_w(VGABASE, VGA_ATT_W, i); + vga_w(VGABASE, VGA_ATT_W, ar[i]); + } + + /* Disable changing the palette registers */ + reg_value = vga_r(VGABASE, VGA_IS1_RC); + vga_w(VGABASE, VGA_ATT_W, BIT(5)); +} + +static int via_iga1_display_fifo_regs(struct drm_device *dev, + struct via_crtc *iga, + struct drm_display_mode *mode, + struct drm_framebuffer *fb) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u32 reg_value; + unsigned int fifo_max_depth = 0; + unsigned int fifo_threshold = 0; + unsigned int fifo_high_threshold = 0; + unsigned int display_queue_expire_num = 0; + bool enable_extended_display_fifo = false; + int ret = 0; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + if (dev_priv->revision == CLE266_REVISION_AX) { + if (mode->hdisplay > 1024) { + /* SR17[6:0] */ + fifo_max_depth = 96; + + /* SR16[5:0] */ + fifo_threshold = 92; + + /* SR18[5:0] */ + fifo_high_threshold = 92; + + enable_extended_display_fifo = true; + } else { + /* SR17[6:0] */ + fifo_max_depth = 64; + + /* SR16[5:0] */ + fifo_threshold = 32; + + /* SR18[5:0] */ + fifo_high_threshold = 56; + + enable_extended_display_fifo = false; + } + + if (dev_priv->vram_type <= VIA_MEM_DDR_200) { + if (fb->format->depth == 24) { + if (mode->hdisplay > 1024) { + if (mode->vdisplay > 768) { + /* SR22[4:0] */ + display_queue_expire_num = 16; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 12; + } + } else if (mode->hdisplay > 640) { + /* SR22[4:0] */ + display_queue_expire_num = 40; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 124; + } + } else if (fb->format->depth == 16){ + if (mode->hdisplay > 1400) { + /* SR22[4:0] */ + display_queue_expire_num = 16; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 12; + } + } else { + /* SR22[4:0] */ + display_queue_expire_num = 124; + } + } else { + if (mode->hdisplay > 1280) { + /* SR22[4:0] */ + display_queue_expire_num = 16; + } else if (mode->hdisplay > 1024) { + /* SR22[4:0] */ + display_queue_expire_num = 12; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 124; + } + } + /* dev_priv->revision == CLE266_REVISION_CX */ + } else { + if (mode->hdisplay >= 1024) { + /* SR17[6:0] */ + fifo_max_depth = 128; + + /* SR16[5:0] */ + fifo_threshold = 112; + + /* SR18[5:0] */ + fifo_high_threshold = 92; + + enable_extended_display_fifo = false; + } else { + /* SR17[6:0] */ + fifo_max_depth = 64; + + /* SR16[5:0] */ + fifo_threshold = 32; + + /* SR18[5:0] */ + fifo_high_threshold = 56; + + enable_extended_display_fifo = false; + } + + if (dev_priv->vram_type <= VIA_MEM_DDR_200) { + if (mode->hdisplay > 1024) { + if (mode->vdisplay > 768) { + /* SR22[4:0] */ + display_queue_expire_num = 16; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 12; + } + } else if (mode->hdisplay > 640) { + /* SR22[4:0] */ + display_queue_expire_num = 40; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 124; + } + } else { + if (mode->hdisplay >= 1280) { + /* SR22[4:0] */ + display_queue_expire_num = 16; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 124; + } + } + } + break; + + case PCI_DEVICE_ID_VIA_KM400_GFX: + if ((mode->hdisplay >= 1600) && + (dev_priv->vram_type <= VIA_MEM_DDR_200)) { + /* SR17[6:0] */ + fifo_max_depth = 58; + + /* SR16[5:0] */ + fifo_threshold = 24; + + /* SR18[5:0] */ + fifo_high_threshold = 92; + } else { + /* SR17[6:0] */ + fifo_max_depth = 128; + + /* SR16[5:0] */ + fifo_threshold = 112; + + /* SR18[5:0] */ + fifo_high_threshold = 92; + } + + enable_extended_display_fifo = false; + + if (dev_priv->vram_type <= VIA_MEM_DDR_200) { + if (mode->hdisplay >= 1600) { + /* SR22[4:0] */ + display_queue_expire_num = 16; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 8; + } + } else { + if (mode->hdisplay >= 1600) { + /* SR22[4:0] */ + display_queue_expire_num = 40; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 36; + } + } + + break; + case PCI_DEVICE_ID_VIA_K8M800_GFX: + /* SR17[7:0] */ + fifo_max_depth = 384; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = 328; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = 296; + + if ((fb->format->depth == 24) && + (mode->hdisplay >= 1400)) { + /* SR22[4:0] */ + display_queue_expire_num = 64; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 128; + } + + break; + case PCI_DEVICE_ID_VIA_PM800_GFX: + /* SR17[7:0] */ + fifo_max_depth = 192; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = 128; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = 64; + + if ((fb->format->depth == 24) && + (mode->hdisplay >= 1400)) { + /* SR22[4:0] */ + display_queue_expire_num = 64; + } else { + /* SR22[4:0] */ + display_queue_expire_num = 124; + } + + break; + case PCI_DEVICE_ID_VIA_P4M800_PRO_GFX: + /* SR17[7:0] */ + fifo_max_depth = CN700_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = CN700_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = CN700_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = CN700_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* CX700 */ + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + /* SR17[7:0] */ + fifo_max_depth = CX700_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = CX700_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = CX700_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = CX700_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + + /* K8M890 */ + case PCI_DEVICE_ID_VIA_CHROME9: + /* SR17[7:0] */ + fifo_max_depth = K8M890_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = K8M890_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = K8M890_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = K8M890_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* P4M890 */ + case PCI_DEVICE_ID_VIA_P4M890_GFX: + /* SR17[7:0] */ + fifo_max_depth = P4M890_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = P4M890_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = P4M890_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = P4M890_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* P4M900 */ + case PCI_DEVICE_ID_VIA_CHROME9_HC: + /* SR17[7:0] */ + fifo_max_depth = P4M900_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = P4M900_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = P4M900_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = P4M900_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* VX800 */ + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + /* SR17[7:0] */ + fifo_max_depth = VX800_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = VX800_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = VX800_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = VX800_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* VX855 */ + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + /* SR17[7:0] */ + fifo_max_depth = VX855_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = VX855_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = VX855_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = VX855_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* VX900 */ + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* SR17[7:0] */ + fifo_max_depth = VX900_IGA1_FIFO_MAX_DEPTH; + + /* SR16[7], SR16[5:0] */ + fifo_threshold = VX900_IGA1_FIFO_THRESHOLD; + + /* SR18[7], SR18[5:0] */ + fifo_high_threshold = VX900_IGA1_FIFO_HIGH_THRESHOLD; + + /* SR22[4:0] */ + display_queue_expire_num = VX900_IGA1_DISPLAY_QUEUE_EXPIRE_NUM; + break; + default: + ret = -EINVAL; + break; + } + + if (ret) { + goto exit; + } + + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_K8M800_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_PM800_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_P4M800_PRO_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_UNICHROME_PRO_II)) { + /* Force PREQ to be always higher than TREQ. */ + svga_wseq_mask(VGABASE, 0x18, BIT(6), BIT(6)); + } else { + svga_wseq_mask(VGABASE, 0x18, 0x00, BIT(6)); + } + + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + if (enable_extended_display_fifo) { + reg_value = VIA_READ(0x0298); + VIA_WRITE(0x0298, reg_value | 0x20000000); + + /* Turn on IGA1 extended display FIFO. */ + reg_value = VIA_READ(0x0230); + VIA_WRITE(0x0230, reg_value | 0x00200000); + + reg_value = VIA_READ(0x0298); + VIA_WRITE(0x0298, reg_value & (~0x20000000)); + } else { + reg_value = VIA_READ(0x0298); + VIA_WRITE(0x0298, reg_value | 0x20000000); + + /* Turn off IGA1 extended display FIFO. */ + reg_value = VIA_READ(0x0230); + VIA_WRITE(0x0230, reg_value & (~0x00200000)); + + reg_value = VIA_READ(0x0298); + VIA_WRITE(0x0298, reg_value & (~0x20000000)); + + } + } + + /* Set IGA1 Display FIFO Depth Select */ + reg_value = IGA1_FIFO_DEPTH_SELECT_FORMULA(fifo_max_depth); + load_value_to_registers(VGABASE, &iga->fifo_depth, reg_value); + + /* Set Display FIFO Threshold Select */ + reg_value = fifo_threshold / 4; + load_value_to_registers(VGABASE, &iga->threshold, reg_value); + + /* Set FIFO High Threshold Select */ + reg_value = fifo_high_threshold / 4; + load_value_to_registers(VGABASE, &iga->high_threshold, reg_value); + + /* Set Display Queue Expire Num */ + reg_value = display_queue_expire_num / 4; + load_value_to_registers(VGABASE, &iga->display_queue, reg_value); + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static int via_iga2_display_fifo_regs(struct drm_device *dev, + struct via_crtc *iga, + struct drm_display_mode *mode, + struct drm_framebuffer *fb) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u32 reg_value; + unsigned int fifo_max_depth = 0; + unsigned int fifo_threshold = 0; + unsigned int fifo_high_threshold = 0; + unsigned int display_queue_expire_num = 0; + bool enable_extended_display_fifo = false; + int ret = 0; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + if (dev_priv->revision == CLE266_REVISION_AX) { + if (((dev_priv->vram_type <= VIA_MEM_DDR_200) && + (fb->format->depth > 16) && + (mode->vdisplay > 768)) || + ((dev_priv->vram_type <= VIA_MEM_DDR_266) && + (fb->format->depth > 16) && + (mode->hdisplay > 1280))) { + /* CR68[7:4] */ + fifo_max_depth = 88; + + /* CR68[3:0] */ + fifo_threshold = 44; + + enable_extended_display_fifo = true; + } else { + /* CR68[7:4] */ + fifo_max_depth = 56; + + /* CR68[3:0] */ + fifo_threshold = 28; + + enable_extended_display_fifo = false; + } + /* dev_priv->revision == CLE266_REVISION_CX */ + } else { + if (mode->hdisplay >= 1024) { + /* CR68[7:4] */ + fifo_max_depth = 88; + + /* CR68[3:0] */ + fifo_threshold = 44; + + enable_extended_display_fifo = false; + } else { + /* CR68[7:4] */ + fifo_max_depth = 56; + + /* CR68[3:0] */ + fifo_threshold = 28; + + enable_extended_display_fifo = false; + } + } + + break; + case PCI_DEVICE_ID_VIA_KM400_GFX: + if (mode->hdisplay >= 1600) { + /* CR68[7:4] */ + fifo_max_depth = 120; + + /* CR68[3:0] */ + fifo_threshold = 44; + + enable_extended_display_fifo = true; + } else if (((mode->hdisplay > 1024) && + (fb->format->depth == 24) && + (dev_priv->vram_type <= VIA_MEM_DDR_333)) || + ((mode->hdisplay == 1024) && + (fb->format->depth == 24) && + (dev_priv->vram_type <= VIA_MEM_DDR_200))) { + /* CR68[7:4] */ + fifo_max_depth = 104; + + /* CR68[3:0] */ + fifo_threshold = 28; + + enable_extended_display_fifo = true; + } else if (((mode->hdisplay > 1280) && + (fb->format->depth == 16) && + (dev_priv->vram_type <= VIA_MEM_DDR_333)) || + ((mode->hdisplay == 1280) && + (fb->format->depth == 16) && + (dev_priv->vram_type <= VIA_MEM_DDR_200))) { + /* CR68[7:4] */ + fifo_max_depth = 88; + + /* CR68[3:0] */ + fifo_threshold = 44; + + enable_extended_display_fifo = true; + } else { + /* CR68[7:4] */ + fifo_max_depth = 56; + + /* CR68[3:0] */ + fifo_threshold = 28; + + enable_extended_display_fifo = false; + } + + break; + case PCI_DEVICE_ID_VIA_K8M800_GFX: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = 376; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = 328; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = 296; + + if ((fb->format->depth == 24) && + (mode->hdisplay >= 1400)) { + /* CR94[6:0] */ + display_queue_expire_num = 64; + } else { + /* CR94[6:0] */ + display_queue_expire_num = 128; + } + + break; + case PCI_DEVICE_ID_VIA_PM800_GFX: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = 96; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = 64; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = 32; + + if ((fb->format->depth == 24) && + (mode->hdisplay >= 1400)) { + /* CR94[6:0] */ + display_queue_expire_num = 64; + } else { + /* CR94[6:0] */ + display_queue_expire_num = 128; + } + + break; + case PCI_DEVICE_ID_VIA_P4M800_PRO_GFX: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = CN700_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = CN700_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = CN700_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = CN700_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* CX700 */ + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = CX700_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = CX700_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = CX700_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = CX700_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + + /* K8M890 */ + case PCI_DEVICE_ID_VIA_CHROME9: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = K8M890_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = K8M890_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = K8M890_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = K8M890_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* P4M890 */ + case PCI_DEVICE_ID_VIA_P4M890_GFX: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = P4M890_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = P4M890_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = P4M890_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = P4M890_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* P4M900 */ + case PCI_DEVICE_ID_VIA_CHROME9_HC: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = P4M900_IGA2_FIFO_MAX_DEPTH; + + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_threshold = P4M900_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = P4M900_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = P4M900_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* VX800 */ + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = VX800_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = VX800_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = VX800_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = VX800_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* VX855 */ + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = VX855_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = VX855_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = VX855_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = VX855_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + /* VX900 */ + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* CR95[7], CR94[7], CR68[7:4] */ + fifo_max_depth = VX900_IGA2_FIFO_MAX_DEPTH; + + /* CR95[6:4], CR68[3:0] */ + fifo_threshold = VX900_IGA2_FIFO_THRESHOLD; + + /* CR95[2:0], CR92[3:0] */ + fifo_high_threshold = VX900_IGA2_FIFO_HIGH_THRESHOLD; + + /* CR94[6:0] */ + display_queue_expire_num = VX900_IGA2_DISPLAY_QUEUE_EXPIRE_NUM; + break; + default: + ret = -EINVAL; + break; + } + + if (ret) { + goto exit; + } + + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + if (enable_extended_display_fifo) { + /* Enable IGA2 extended display FIFO. */ + svga_wcrt_mask(VGABASE, 0x6a, BIT(5), BIT(5)); + } else { + /* Disable IGA2 extended display FIFO. */ + svga_wcrt_mask(VGABASE, 0x6a, 0x00, BIT(5)); + } + } + + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + /* Set IGA2 Display FIFO Depth Select */ + reg_value = IGA2_FIFO_DEPTH_SELECT_FORMULA(fifo_max_depth); + load_value_to_registers(VGABASE, &iga->fifo_depth, reg_value); + + /* Set Display FIFO Threshold Select */ + reg_value = fifo_threshold / 4; + load_value_to_registers(VGABASE, &iga->threshold, reg_value); + } else { + /* Set IGA2 Display FIFO Depth Select */ + reg_value = IGA2_FIFO_DEPTH_SELECT_FORMULA(fifo_max_depth); + load_value_to_registers(VGABASE, &iga->fifo_depth, reg_value); + + /* Set Display FIFO Threshold Select */ + reg_value = fifo_threshold / 4; + load_value_to_registers(VGABASE, &iga->threshold, reg_value); + + /* Set FIFO High Threshold Select */ + reg_value = fifo_high_threshold / 4; + load_value_to_registers(VGABASE, &iga->high_threshold, reg_value); + + /* Set Display Queue Expire Num */ + reg_value = display_queue_expire_num / 4; + load_value_to_registers(VGABASE, &iga->display_queue, reg_value); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +/* Load CRTC Pixel Timing registers */ +void via_load_crtc_pixel_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u32 reg_value = 0; + + reg_value = IGA1_PIXELTIMING_HOR_TOTAL_FORMULA(mode->crtc_htotal); + load_value_to_registers(VGABASE, &iga->pixel_timings.htotal, + reg_value); + + reg_value = IGA1_PIXELTIMING_HOR_ADDR_FORMULA(mode->crtc_hdisplay) << 16; + load_value_to_registers(VGABASE, &iga->pixel_timings.hdisplay, + reg_value); + + reg_value = IGA1_PIXELTIMING_HOR_BLANK_START_FORMULA( + mode->crtc_hblank_start); + load_value_to_registers(VGABASE, &iga->pixel_timings.hblank_start, + reg_value); + + reg_value = IGA1_PIXELTIMING_HOR_BLANK_END_FORMULA(mode->crtc_hblank_end) << 16; + load_value_to_registers(VGABASE, &iga->pixel_timings.hblank_end, reg_value); + + reg_value = IGA1_PIXELTIMING_HOR_SYNC_START_FORMULA(mode->crtc_hsync_start); + load_value_to_registers(VGABASE, &iga->pixel_timings.hsync_start, + reg_value); + + reg_value = IGA1_PIXELTIMING_HOR_SYNC_END_FORMULA(mode->crtc_hsync_end) << 16; + load_value_to_registers(VGABASE, &iga->pixel_timings.hsync_end, reg_value); + + reg_value = IGA1_PIXELTIMING_VER_TOTAL_FORMULA(mode->crtc_vtotal); + load_value_to_registers(VGABASE, &iga->pixel_timings.vtotal, reg_value); + + reg_value = IGA1_PIXELTIMING_VER_ADDR_FORMULA(mode->crtc_vdisplay) << 16; + load_value_to_registers(VGABASE, &iga->pixel_timings.vdisplay, reg_value); + + reg_value = IGA1_PIXELTIMING_VER_BLANK_START_FORMULA( + mode->crtc_vblank_start); + load_value_to_registers(VGABASE, &iga->pixel_timings.vblank_start, reg_value); + + reg_value = IGA1_PIXELTIMING_VER_BLANK_END_FORMULA(mode->crtc_vblank_end) << 16; + load_value_to_registers(VGABASE, &iga->pixel_timings.vblank_end, reg_value); + + reg_value = IGA1_PIXELTIMING_VER_SYNC_START_FORMULA(mode->crtc_vsync_start); + load_value_to_registers(VGABASE, &iga->pixel_timings.vsync_start, reg_value); + + reg_value = IGA1_PIXELTIMING_VER_SYNC_END_FORMULA(mode->crtc_vsync_end) << 12; + load_value_to_registers(VGABASE, &iga->pixel_timings.vsync_end, reg_value); + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + reg_value = IGA1_PIXELTIMING_HVSYNC_OFFSET_END_FORMULA( + mode->crtc_htotal, mode->crtc_hsync_start); + VIA_WRITE_MASK(IGA1_PIX_HALF_LINE_REG, reg_value, + IGA1_PIX_HALF_LINE_MASK); + + svga_wcrt_mask(VGABASE, 0x32, BIT(2), BIT(2)); + /** + * According to information from HW team, + * we need to set 0xC280[1] = 1 (HDMI function enable) + * or 0xC640[0] = 1 (DP1 enable) + * to let the half line function work. + * Otherwise, the clock for interlace mode + * will not correct. + * This is a special setting for 410. + */ + VIA_WRITE_MASK(0xC280, BIT(1), BIT(1)); + } else { + VIA_WRITE_MASK(IGA1_PIX_HALF_LINE_REG, 0x0, IGA1_PIX_HALF_LINE_MASK); + svga_wcrt_mask(VGABASE, 0x32, 0x00, BIT(2)); + + } + svga_wcrt_mask(VGABASE, 0xFD, BIT(5), BIT(5)); +} + +/* Load CRTC timing registers */ +void via_load_crtc_timing(struct via_crtc *iga, struct drm_display_mode *mode) +{ + struct drm_device *dev = iga->base.dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u32 reg_value = 0; + + if (!iga->index) { + if (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HD) { + /* Disable IGA1 shadow timing */ + svga_wcrt_mask(VGABASE, 0x45, 0x00, BIT(0)); + + /* Disable IGA1 pixel timing */ + svga_wcrt_mask(VGABASE, 0xFD, 0x00, BIT(5)); + } + + reg_value = IGA1_HOR_TOTAL_FORMULA(mode->crtc_htotal); + load_value_to_registers(VGABASE, &iga->timings.htotal, reg_value); + + reg_value = IGA1_HOR_ADDR_FORMULA(mode->crtc_hdisplay); + load_value_to_registers(VGABASE, &iga->timings.hdisplay, reg_value); + + reg_value = IGA1_HOR_BLANK_START_FORMULA(mode->crtc_hblank_start); + load_value_to_registers(VGABASE, &iga->timings.hblank_start, reg_value); + + reg_value = IGA1_HOR_BLANK_END_FORMULA(mode->crtc_hblank_end); + load_value_to_registers(VGABASE, &iga->timings.hblank_end, reg_value); + + reg_value = IGA1_HOR_SYNC_START_FORMULA(mode->crtc_hsync_start); + load_value_to_registers(VGABASE, &iga->timings.hsync_start, reg_value); + + reg_value = IGA1_HOR_SYNC_END_FORMULA(mode->crtc_hsync_end); + load_value_to_registers(VGABASE, &iga->timings.hsync_end, reg_value); + + reg_value = IGA1_VER_TOTAL_FORMULA(mode->crtc_vtotal); + load_value_to_registers(VGABASE, &iga->timings.vtotal, reg_value); + + reg_value = IGA1_VER_ADDR_FORMULA(mode->crtc_vdisplay); + load_value_to_registers(VGABASE, &iga->timings.vdisplay, reg_value); + + reg_value = IGA1_VER_BLANK_START_FORMULA(mode->crtc_vblank_start); + load_value_to_registers(VGABASE, &iga->timings.vblank_start, reg_value); + + reg_value = IGA1_VER_BLANK_END_FORMULA(mode->crtc_vblank_end); + load_value_to_registers(VGABASE, &iga->timings.vblank_end, reg_value); + + reg_value = IGA1_VER_SYNC_START_FORMULA(mode->crtc_vsync_start); + load_value_to_registers(VGABASE, &iga->timings.vsync_start, reg_value); + + reg_value = IGA1_VER_SYNC_END_FORMULA(mode->crtc_vsync_end); + load_value_to_registers(VGABASE, &iga->timings.vsync_end, reg_value); + } else { + reg_value = IGA2_HOR_TOTAL_FORMULA(mode->crtc_htotal); + load_value_to_registers(VGABASE, &iga->timings.htotal, reg_value); + + reg_value = IGA2_HOR_ADDR_FORMULA(mode->crtc_hdisplay); + load_value_to_registers(VGABASE, &iga->timings.hdisplay, reg_value); + + reg_value = IGA2_HOR_BLANK_START_FORMULA(mode->crtc_hblank_start); + load_value_to_registers(VGABASE, &iga->timings.hblank_start, reg_value); + + reg_value = IGA2_HOR_BLANK_END_FORMULA(mode->crtc_hblank_end); + load_value_to_registers(VGABASE, &iga->timings.hblank_end, reg_value); + + reg_value = IGA2_HOR_SYNC_START_FORMULA(mode->crtc_hsync_start); + load_value_to_registers(VGABASE, &iga->timings.hsync_start, reg_value); + + reg_value = IGA2_HOR_SYNC_END_FORMULA(mode->crtc_hsync_end); + load_value_to_registers(VGABASE, &iga->timings.hsync_end, reg_value); + + reg_value = IGA2_VER_TOTAL_FORMULA(mode->crtc_vtotal); + load_value_to_registers(VGABASE, &iga->timings.vtotal, reg_value); + + reg_value = IGA2_VER_ADDR_FORMULA(mode->crtc_vdisplay); + load_value_to_registers(VGABASE, &iga->timings.vdisplay, reg_value); + + reg_value = IGA2_VER_BLANK_START_FORMULA(mode->crtc_vblank_start); + load_value_to_registers(VGABASE, &iga->timings.vblank_start, reg_value); + + reg_value = IGA2_VER_BLANK_END_FORMULA(mode->crtc_vblank_end); + load_value_to_registers(VGABASE, &iga->timings.vblank_end, reg_value); + + reg_value = IGA2_VER_SYNC_START_FORMULA(mode->crtc_vsync_start); + load_value_to_registers(VGABASE, &iga->timings.vsync_start, reg_value); + + reg_value = IGA2_VER_SYNC_END_FORMULA(mode->crtc_vsync_end); + load_value_to_registers(VGABASE, &iga->timings.vsync_end, reg_value); + } +} + +/* + * This function changes the destination of scaling up/down + * and CRTC timing registers + * crtc : which IGA + * scale_type : upscaling(VIA_EXPAND) or downscaling(VIA_SHRINK) + */ +void via_set_scale_path(struct drm_crtc *crtc, u32 scale_type) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 reg_cr_fd = vga_rcrt(VGABASE, 0xFD); + + if (!iga->index) + /* register reuse: select IGA1 path */ + reg_cr_fd |= BIT(7); + else + /* register reuse: select IGA2 path */ + reg_cr_fd &= ~BIT(7); + + /* only IGA1 up scaling need to clear this bit CRFD.5. */ + if (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HD) { + if (!iga->index + && ((VIA_HOR_EXPAND & scale_type) + || (VIA_VER_EXPAND & scale_type))) + reg_cr_fd &= ~BIT(5); + } + + /* CRFD.0 = 0 : common IGA2, = 1 : downscaling IGA */ + switch (scale_type) { + case VIA_NO_SCALING: + case VIA_EXPAND: + case VIA_HOR_EXPAND: + case VIA_VER_EXPAND: + /* register reuse: as common IGA2 */ + reg_cr_fd &= ~BIT(0); + break; + + case VIA_SHRINK: + /* register reuse: as downscaling IGA */ + reg_cr_fd |= BIT(0); + break; + + default: + break; + } + vga_wcrt(VGABASE, 0xFD, reg_cr_fd); +} + +/* disable IGA scaling */ +static void via_disable_iga_scaling(struct drm_crtc *crtc) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_device *dev = crtc->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + if (iga->index) { + /* IGA2 scalings disable */ + via_set_scale_path(crtc, VIA_SHRINK); + /* disable IGA down scaling and buffer sharing. */ + svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7) | BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + + /* Disable scale up as well */ + via_set_scale_path(crtc, VIA_EXPAND); + /* disable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, 0, BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + } else { + /* IGA1 scalings disable */ + via_set_scale_path(crtc, VIA_SHRINK); + /* disable IGA down scaling and buffer sharing. */ + svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7) | BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + + /* Disable scale up as well */ + via_set_scale_path(crtc, VIA_EXPAND); + /* disable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, 0, BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + } +} + +/* + * Enable IGA scale functions. + * + * input : iga_path = IGA1 or IGA2 or + * IGA1+IGA2 + * + * scale_type = VIA_HOR_EXPAND or VIA_VER_EXPAND or VIA_EXPAND or + * VIA_SHRINK or VIA_SHRINK + VIA_EXPAND + */ +bool via_set_iga_scale_function(struct drm_crtc *crtc, u32 scale_type) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_device *dev = crtc->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + if (!(scale_type & (VIA_SHRINK + VIA_EXPAND))) + return false; + + if (iga->index) { + /* IGA2 scalings enable */ + if (VIA_SHRINK & scale_type) { + via_set_scale_path(crtc, VIA_SHRINK); + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* enable IGA down scaling */ + svga_wcrt_mask(VGABASE, 0x89, BIT(0), BIT(0)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2) | BIT(1), BIT(2) | BIT(1)); + } + + if (VIA_EXPAND & scale_type) { + via_set_scale_path(crtc, VIA_EXPAND); + /* enable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, BIT(0), BIT(0)); + } + + if ((VIA_EXPAND & scale_type) == VIA_EXPAND) { + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2) | BIT(1), BIT(2) | BIT(1)); + } else if (VIA_HOR_EXPAND & scale_type) { + /* Horizontal scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7)); + /* hor scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(1), BIT(1)); + } else if (VIA_VER_EXPAND & scale_type) { + /* Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + /* ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2), BIT(2)); + } + } else { + /* IGA1 scalings enable */ + if (VIA_SHRINK & scale_type) { + via_set_scale_path(crtc, VIA_SHRINK); + + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* enable IGA down scaling */ + svga_wcrt_mask(VGABASE, 0x89, BIT(0), BIT(0)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2) | BIT(1), BIT(2) | BIT(1)); + } + + if (VIA_EXPAND & scale_type) { + via_set_scale_path(crtc, VIA_EXPAND); + /* enable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, BIT(0), BIT(0)); + } + + if ((VIA_EXPAND & scale_type) == VIA_EXPAND) { + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2) | BIT(1), BIT(2) | BIT(1)); + } else if (VIA_HOR_EXPAND & scale_type) { + /* Horizontal scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7)); + /* hor scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(1), BIT(1)); + } else if (VIA_VER_EXPAND & scale_type) { + /* Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + /* ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2), BIT(2)); + } + } + return true; +} + +/* + * 1. get scale factors from source and dest H & V size + * 2. load scale factors into registers + * 3. enable H or V scale ( set CRA2 bit7 or bit3 ) + */ +bool via_load_iga_scale_factor_regs(struct via_drm_priv *dev_priv, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + u32 scale_type, u32 is_hor_or_ver) +{ + u32 dst_hor_regs = adjusted_mode->crtc_hdisplay; + u32 dst_ver_regs = adjusted_mode->crtc_vdisplay; + u32 src_hor_regs = mode->crtc_hdisplay; + u32 src_ver_regs = mode->crtc_vdisplay; + u32 hor_factor = 0, ver_factor = 0; + struct vga_registers reg; + + if ((0 == src_hor_regs) || (0 == src_ver_regs) || (0 == dst_hor_regs) + || (0 == dst_ver_regs)) + return false; + + if (VIA_EXPAND == scale_type) { + if (HOR_SCALE & is_hor_or_ver) { + hor_factor = ((src_hor_regs - 1) * 4096) / (dst_hor_regs - 1); + reg.count = ARRAY_SIZE(lcd_hor_scaling); + reg.regs = lcd_hor_scaling; + load_value_to_registers(VGABASE, ®, hor_factor); + /* Horizontal scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7)); + } + + if (VER_SCALE & is_hor_or_ver) { + ver_factor = ((src_ver_regs - 1) * 2048) / (dst_ver_regs - 1); + reg.count = ARRAY_SIZE(lcd_ver_scaling); + reg.regs = lcd_ver_scaling; + load_value_to_registers(VGABASE, ®, ver_factor); + /* Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + } + + } else if (VIA_SHRINK == scale_type) { + + if (src_hor_regs > dst_hor_regs) + hor_factor = ((src_hor_regs - dst_hor_regs) * 4096) / dst_hor_regs; + + if (src_ver_regs > dst_ver_regs) + ver_factor = ((src_ver_regs - dst_ver_regs) * 2048) / dst_ver_regs; + + reg.count = ARRAY_SIZE(lcd_hor_scaling); + reg.regs = lcd_hor_scaling; + load_value_to_registers(VGABASE, ®, hor_factor); + + reg.count = ARRAY_SIZE(lcd_ver_scaling); + reg.regs = lcd_ver_scaling; + load_value_to_registers(VGABASE, ®, ver_factor); + + /* set buffer sharing enable bit . */ + if (hor_factor || ver_factor) { + if (dst_hor_regs > 1024) + svga_wcrt_mask(VGABASE, 0x89, BIT(7), BIT(7)); + else + svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7)); + } + + if (hor_factor) + /* CRA2[7]:1 Enable Hor scaling + CRA2[6]:1 Linear Mode */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(6), BIT(7) | BIT(6)); + else + svga_wcrt_mask(VGABASE, 0xA2, 0, BIT(7)); + + if (ver_factor) + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + else + svga_wcrt_mask(VGABASE, 0xA2, 0, BIT(3)); + } + return true; +} + +void via_set_iga2_downscale_source_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + unsigned int viewx = adjusted_mode->hdisplay, + viewy = adjusted_mode->vdisplay; + unsigned int srcx = mode->crtc_hdisplay, srcy = mode->crtc_vdisplay; + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_display_mode *src_timing; + + src_timing = drm_mode_duplicate(crtc->dev, adjusted_mode); + /* derived source timing */ + if (srcx <= viewx) { + src_timing->crtc_htotal = adjusted_mode->crtc_htotal; + src_timing->crtc_hdisplay = adjusted_mode->crtc_hdisplay; + } else { + unsigned int htotal = adjusted_mode->crtc_htotal - + adjusted_mode->crtc_hdisplay; + + src_timing->crtc_htotal = htotal + srcx; + src_timing->crtc_hdisplay = srcx; + } + src_timing->crtc_hblank_start = src_timing->crtc_hdisplay; + src_timing->crtc_hblank_end = src_timing->crtc_htotal; + src_timing->crtc_hsync_start = src_timing->crtc_hdisplay + 2; + src_timing->crtc_hsync_end = src_timing->crtc_hsync_start + 1; + + if (srcy <= viewy) { + src_timing->crtc_vtotal = adjusted_mode->crtc_vtotal; + src_timing->crtc_vdisplay = adjusted_mode->crtc_vdisplay; + } else { + unsigned int vtotal = adjusted_mode->crtc_vtotal - + adjusted_mode->crtc_vdisplay; + + src_timing->crtc_vtotal = vtotal + srcy; + src_timing->crtc_vdisplay = srcy; + } + src_timing->crtc_vblank_start = src_timing->crtc_vdisplay; + src_timing->crtc_vblank_end = src_timing->crtc_vtotal; + src_timing->crtc_vsync_start = src_timing->crtc_vdisplay + 2; + src_timing->crtc_vsync_end = src_timing->crtc_vsync_start + 1; + + via_set_scale_path(crtc, VIA_NO_SCALING); + /* load src timing */ + via_load_crtc_timing(iga, src_timing); + + /* Cleanup up source timings */ + drm_mode_destroy(crtc->dev, src_timing); +} + +void via_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct drm_display_mode *mode = &crtc->state->mode; + struct drm_display_mode *adjusted_mode = + &crtc->state->adjusted_mode; + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 reg_value = 0; + int ret; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Load standard registers */ + via_load_vpit_regs(dev_priv); + + /* + * For VX855 and VX900 chipsets, CRTC unlock register is + * CR47[4]. For all others, CR47[0]. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HCM) || + (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HD)) { + reg_value = BIT(4); + } else { + reg_value = BIT(0); + } + + /* Unlock CRTC registers. */ + svga_wcrt_mask(VGABASE, 0x11, 0x00, BIT(7)); + svga_wcrt_mask(VGABASE, 0x47, 0x00, reg_value); + + if (!iga->index) { + /* IGA1 reset */ + vga_wcrt(VGABASE, 0x09, 0x00); /* initial CR09=0 */ + svga_wcrt_mask(VGABASE, 0x11, 0x00, BIT(6)); + + /* disable IGA scales first */ + via_disable_iga_scaling(crtc); + + /* + * when not down scaling, we only need load one + * timing. + */ + via_load_crtc_timing(iga, adjusted_mode); + + switch (adjusted_mode->crtc_htotal % 8) { + case 0: + default: + break; + case 2: + reg_value = BIT(7); + break; + case 4: + reg_value = BIT(6); + break; + case 6: + reg_value = BIT(3); + break; + } + + svga_wcrt_mask(VGABASE, 0x47, + reg_value, BIT(7) | BIT(6) | BIT(3)); + } else { + /* disable IGA scales first */ + via_disable_iga_scaling(crtc); + + /* Load crtc timing and IGA scaling */ + if (iga->scaling_mode & VIA_SHRINK) { + /* + * enable IGA2 down scaling and set + * Interpolation + */ + via_set_iga_scale_function(crtc, VIA_SHRINK); + + /* load hor and ver downscaling factor */ + /* + * interlace modes scaling support(example + * 1080I): we should use mode->crtc_vdisplay + * here, because crtc_vdisplay=540, + * vdisplay=1080, we need 540 here, not 1080. + */ + via_load_iga_scale_factor_regs(dev_priv, + mode, + adjusted_mode, + VIA_SHRINK, + HOR_VER_SCALE); + /* load src timing to timing registers */ + /* + * interlace modes scaling support(example + * 1080I): we should use mode->crtc_vdisplay + * here, because crtc_vdisplay=540, + * vdisplay=1080, we need 540 here, not 1080. + */ + via_set_iga2_downscale_source_timing(crtc, + mode, + adjusted_mode); + + /* Download dst timing */ + via_set_scale_path(crtc, VIA_SHRINK); + via_load_crtc_timing(iga, adjusted_mode); + /* + * very necessary to set IGA to none scaling + * status need to fix why so need. + */ + via_set_scale_path(crtc, VIA_NO_SCALING); + } else { + /* + * when not down scaling, we only need load + * one timing. + */ + via_load_crtc_timing(iga, adjusted_mode); + + /* II. up scaling */ + if (iga->scaling_mode & VIA_EXPAND) { + /* Horizontal scaling */ + if (iga->scaling_mode & + VIA_HOR_EXPAND) { + via_set_iga_scale_function( + crtc, + VIA_HOR_EXPAND); + via_load_iga_scale_factor_regs( + dev_priv, + mode, + adjusted_mode, + VIA_EXPAND, + HOR_SCALE); + } + + /* Vertical scaling */ + if (iga->scaling_mode & + VIA_VER_EXPAND) { + via_set_iga_scale_function( + crtc, + VIA_VER_EXPAND); + via_load_iga_scale_factor_regs( + dev_priv, + mode, + adjusted_mode, + VIA_EXPAND, + VER_SCALE); + } + } + } + } + + if (!iga->index) { + /* Set non-interlace / interlace mode. */ + via_iga1_set_interlace_mode(VGABASE, + adjusted_mode->flags & + DRM_MODE_FLAG_INTERLACE); + + /* No HSYNC shift. */ + via_iga1_set_hsync_shift(VGABASE, 0x05); + + /* Load display FIFO. */ + ret = via_iga1_display_fifo_regs(dev, iga, adjusted_mode, + crtc->primary->fb); + if (ret) { + goto exit; + } + + /* Set PLL */ + if (adjusted_mode->clock) { + u32 clock = adjusted_mode->clock * 1000; + u32 pll_regs; + + if (iga->scaling_mode & VIA_SHRINK) + clock *= 2; + pll_regs = via_get_clk_value(crtc->dev, clock); + via_set_vclock(crtc, pll_regs); + } + + via_iga_common_init(dev); + + /* Set palette LUT to 8-bit mode. */ + via_iga1_set_palette_lut_resolution(VGABASE, true); + } else { + /* Set non-interlace / interlace mode. */ + via_iga2_set_interlace_mode(VGABASE, + adjusted_mode->flags & + DRM_MODE_FLAG_INTERLACE); + + /* Load display FIFO. */ + ret = via_iga2_display_fifo_regs(dev, iga, adjusted_mode, + crtc->primary->fb); + if (ret) { + goto exit; + } + + /* Set PLL */ + if (adjusted_mode->clock) { + u32 clock = adjusted_mode->clock * 1000; + u32 pll_regs; + + if (iga->scaling_mode & VIA_SHRINK) + clock *= 2; + pll_regs = via_get_clk_value(crtc->dev, clock); + via_set_vclock(crtc, pll_regs); + } + + via_iga_common_init(dev); + + /* Set palette LUT to 8-bit mode. */ + via_iga2_set_palette_lut_resolution(VGABASE, true); + + svga_wcrt_mask(VGABASE, 0x6A, BIT(7), BIT(7)); + } +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_device *dev = crtc->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!iga->index) { + svga_wseq_mask(VGABASE, 0x01, 0x00, BIT(5)); + } else { + svga_wcrt_mask(VGABASE, 0x6B, 0x00, BIT(2)); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_crtc_helper_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_device *dev = crtc->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!iga->index) { + svga_wseq_mask(VGABASE, 0x01, BIT(5), BIT(5)); + } else { + svga_wcrt_mask(VGABASE, 0x6B, BIT(2), BIT(2)); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static const struct drm_crtc_helper_funcs via_drm_crtc_helper_funcs = { + .mode_set_nofb = via_mode_set_nofb, + .atomic_enable = via_crtc_helper_atomic_enable, + .atomic_disable = via_crtc_helper_atomic_disable, +}; + +static int via_primary_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *new_crtc_state; + struct drm_device *dev = plane->dev; + struct drm_framebuffer *fb = new_plane_state->fb; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + uint32_t frame_buffer_size; + int ret = 0; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if ((!new_plane_state->crtc) || (!new_plane_state->visible)) { + goto exit; + } + + frame_buffer_size = (fb->width * fb->format->cpp[0]) * + fb->height; + if (frame_buffer_size > dev_priv->vram_size) { + ret = -ENOMEM; + goto exit; + } + + if ((fb->width > dev->mode_config.max_width) || + (fb->width < dev->mode_config.min_width)) { + ret = -EINVAL; + goto exit; + } + + new_crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); + ret = drm_atomic_helper_check_plane_state( + new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_primary_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_device *dev = plane->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return; +} + +void via_primary_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *crtc = new_state->crtc; + struct drm_framebuffer *fb = new_state->fb; + uint32_t pitch = (new_state->crtc_y * fb->pitches[0]) + + (new_state->crtc_x * fb->format->cpp[0]); + uint32_t addr; + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_device *dev = crtc->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + struct via_bo *bo; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + gem = fb->obj[0]; + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + bo = to_ttm_bo(ttm_bo); + + if (!iga->index) { + via_iga1_set_color_depth(dev, + fb->format->cpp[0], + fb->format->depth); + + /* Set the framebuffer offset */ + addr = round_up((ttm_bo->resource->start << PAGE_SHIFT) + + pitch, 16) >> 1; + + vga_wcrt(VGABASE, 0x0D, addr & 0xFF); + vga_wcrt(VGABASE, 0x0C, (addr >> 8) & 0xFF); + /* Yes order of setting these registers matters on some hardware */ + svga_wcrt_mask(VGABASE, 0x48, ((addr >> 24) & 0x1F), 0x1F); + vga_wcrt(VGABASE, 0x34, (addr >> 16) & 0xFF); + + /* Load fetch count registers */ + pitch = ALIGN(crtc->mode.hdisplay * fb->format->cpp[0], 16); + load_value_to_registers(VGABASE, &iga->fetch, pitch >> 4); + + /* Set the primary pitch */ + pitch = ALIGN(fb->pitches[0], 16); + /* Spec does not say that first adapter skips 3 bits but old + * code did it and seems to be reasonable in analogy to + * second adapter */ + load_value_to_registers(VGABASE, &iga->offset, pitch >> 3); + } else { + via_iga2_set_color_depth(dev, + fb->format->cpp[0], + fb->format->depth); + + /* Set the framebuffer offset */ + addr = round_up((ttm_bo->resource->start << PAGE_SHIFT) + + pitch, 16); + /* Bits 9 to 3 of the frame buffer go into bits 7 to 1 + * of the register. Bit 0 is for setting tile mode or + * linear mode. A value of zero sets it to linear mode */ + vga_wcrt(VGABASE, 0x62, ((addr >> 3) & 0x7F) << 1); + vga_wcrt(VGABASE, 0x63, (addr >> 10) & 0xFF); + vga_wcrt(VGABASE, 0x64, (addr >> 18) & 0xFF); + svga_wcrt_mask(VGABASE, 0xA3, ((addr >> 26) & 0x07), 0x07); + + /* Load fetch count registers */ + pitch = ALIGN(crtc->mode.hdisplay * fb->format->cpp[0], 16); + load_value_to_registers(VGABASE, &iga->fetch, pitch >> 4); + + /* Set secondary pitch */ + pitch = ALIGN(fb->pitches[0], 16); + load_value_to_registers(VGABASE, &iga->offset, pitch >> 3); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static int via_primary_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_device *dev = plane->dev; + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + struct via_bo *bo; + int ret = 0; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!new_state->fb) { + goto exit; + } + + gem = new_state->fb->obj[0]; + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + bo = to_ttm_bo(ttm_bo); + + ret = ttm_bo_reserve(ttm_bo, true, false, NULL); + if (ret) { + goto exit; + } + + ret = via_bo_pin(bo, TTM_PL_VRAM); + ttm_bo_unreserve(ttm_bo); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_primary_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *dev = plane->dev; + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + struct via_bo *bo; + int ret; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!old_state->fb) { + goto exit; + } + + gem = old_state->fb->obj[0]; + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + bo = to_ttm_bo(ttm_bo); + + ret = ttm_bo_reserve(ttm_bo, true, false, NULL); + if (ret) { + goto exit; + } + + via_bo_unpin(bo); + ttm_bo_unreserve(ttm_bo); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static const struct drm_plane_helper_funcs +via_primary_drm_plane_helper_funcs = { + .prepare_fb = via_primary_prepare_fb, + .cleanup_fb = via_primary_cleanup_fb, + .atomic_check = via_primary_atomic_check, + .atomic_update = via_primary_atomic_update, + .atomic_disable = via_primary_atomic_disable, +}; + +static const struct drm_plane_funcs via_primary_drm_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = + drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_plane_destroy_state, +}; + +static void via_crtc_param_init(struct via_drm_priv *dev_priv, + struct drm_crtc *crtc, + uint32_t index) +{ + struct drm_device *dev = &dev_priv->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + + if (iga->index) { + iga->timings.htotal.count = ARRAY_SIZE(iga2_hor_total); + iga->timings.htotal.regs = iga2_hor_total; + + iga->timings.hdisplay.count = ARRAY_SIZE(iga2_hor_addr); + iga->timings.hdisplay.regs = iga2_hor_addr; + if (pdev->device != PCI_DEVICE_ID_VIA_CHROME9_HD) + iga->timings.hdisplay.count--; + + iga->timings.hblank_start.count = ARRAY_SIZE(iga2_hor_blank_start); + iga->timings.hblank_start.regs = iga2_hor_blank_start; + if (pdev->device != PCI_DEVICE_ID_VIA_CHROME9_HD) + iga->timings.hblank_start.count--; + + iga->timings.hblank_end.count = ARRAY_SIZE(iga2_hor_blank_end); + iga->timings.hblank_end.regs = iga2_hor_blank_end; + + iga->timings.hsync_start.count = ARRAY_SIZE(iga2_hor_sync_start); + iga->timings.hsync_start.regs = iga2_hor_sync_start; + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX + || pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX) + iga->timings.hsync_start.count--; + + iga->timings.hsync_end.count = ARRAY_SIZE(iga2_hor_sync_end); + iga->timings.hsync_end.regs = iga2_hor_sync_end; + + iga->timings.vtotal.count = ARRAY_SIZE(iga2_ver_total); + iga->timings.vtotal.regs = iga2_ver_total; + + iga->timings.vdisplay.count = ARRAY_SIZE(iga2_ver_addr); + iga->timings.vdisplay.regs = iga2_ver_addr; + + iga->timings.vblank_start.count = ARRAY_SIZE(iga2_ver_blank_start); + iga->timings.vblank_start.regs = iga2_ver_blank_start; + + iga->timings.vblank_end.count = ARRAY_SIZE(iga2_ver_blank_end); + iga->timings.vblank_end.regs = iga2_ver_blank_end; + + iga->timings.vsync_start.count = ARRAY_SIZE(iga2_ver_sync_start); + iga->timings.vsync_start.regs = iga2_ver_sync_start; + + iga->timings.vsync_end.count = ARRAY_SIZE(iga2_ver_sync_end); + iga->timings.vsync_end.regs = iga2_ver_sync_end; + + /* Secondary FIFO setup */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + iga->fifo_depth.count = + ARRAY_SIZE(iga2_cle266_fifo_depth_select); + iga->fifo_depth.regs = iga2_cle266_fifo_depth_select; + + iga->threshold.count = + ARRAY_SIZE(iga2_cle266_fifo_threshold_select); + iga->threshold.regs = iga2_cle266_fifo_threshold_select; + } else { + iga->fifo_depth.count = ARRAY_SIZE(iga2_k8m800_fifo_depth_select); + iga->fifo_depth.regs = iga2_k8m800_fifo_depth_select; + + iga->threshold.count = ARRAY_SIZE(iga2_k8m800_fifo_threshold_select); + iga->threshold.regs = iga2_k8m800_fifo_threshold_select; + + iga->high_threshold.count = ARRAY_SIZE(iga2_fifo_high_threshold_select); + iga->high_threshold.regs = iga2_fifo_high_threshold_select; + + iga->display_queue.count = ARRAY_SIZE(iga2_display_queue_expire_num); + iga->display_queue.regs = iga2_display_queue_expire_num; + } + + iga->fetch.count = ARRAY_SIZE(iga2_fetch_count); + iga->fetch.regs = iga2_fetch_count; + + /* Older hardware only uses 12 bits */ + iga->offset.count = ARRAY_SIZE(iga2_offset) - 1; + iga->offset.regs = iga2_offset; + } else { + iga->timings.htotal.count = ARRAY_SIZE(iga1_hor_total); + iga->timings.htotal.regs = iga1_hor_total; + + iga->timings.hdisplay.count = ARRAY_SIZE(iga1_hor_addr); + iga->timings.hdisplay.regs = iga1_hor_addr; + if (pdev->device != PCI_DEVICE_ID_VIA_CHROME9_HD) + iga->timings.hdisplay.count--; + + iga->timings.hblank_start.count = ARRAY_SIZE(iga1_hor_blank_start); + iga->timings.hblank_start.regs = iga1_hor_blank_start; + if (pdev->device != PCI_DEVICE_ID_VIA_CHROME9_HD) + iga->timings.hblank_start.count--; + + iga->timings.hblank_end.count = ARRAY_SIZE(iga1_hor_blank_end); + iga->timings.hblank_end.regs = iga1_hor_blank_end; + + iga->timings.hsync_start.count = ARRAY_SIZE(iga1_hor_sync_start); + iga->timings.hsync_start.regs = iga1_hor_sync_start; + + iga->timings.hsync_end.count = ARRAY_SIZE(iga1_hor_sync_end); + iga->timings.hsync_end.regs = iga1_hor_sync_end; + + iga->timings.vtotal.count = ARRAY_SIZE(iga1_ver_total); + iga->timings.vtotal.regs = iga1_ver_total; + + iga->timings.vdisplay.count = ARRAY_SIZE(iga1_ver_addr); + iga->timings.vdisplay.regs = iga1_ver_addr; + + iga->timings.vblank_start.count = ARRAY_SIZE(iga1_ver_blank_start); + iga->timings.vblank_start.regs = iga1_ver_blank_start; + + iga->timings.vblank_end.count = ARRAY_SIZE(iga1_ver_blank_end); + iga->timings.vblank_end.regs = iga1_ver_blank_end; + + iga->timings.vsync_start.count = ARRAY_SIZE(iga1_ver_sync_start); + iga->timings.vsync_start.regs = iga1_ver_sync_start; + + iga->timings.vsync_end.count = ARRAY_SIZE(iga1_ver_sync_end); + iga->timings.vsync_end.regs = iga1_ver_sync_end; + + /* Primary FIFO setup */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + iga->fifo_depth.count = ARRAY_SIZE(iga1_cle266_fifo_depth_select); + iga->fifo_depth.regs = iga1_cle266_fifo_depth_select; + + iga->threshold.count = ARRAY_SIZE(iga1_cle266_fifo_threshold_select); + iga->threshold.regs = iga1_cle266_fifo_threshold_select; + + iga->high_threshold.count = ARRAY_SIZE(iga1_cle266_fifo_high_threshold_select); + iga->high_threshold.regs = iga1_cle266_fifo_high_threshold_select; + + iga->display_queue.count = ARRAY_SIZE(iga1_cle266_display_queue_expire_num); + iga->display_queue.regs = iga1_cle266_display_queue_expire_num; + } else { + iga->fifo_depth.count = ARRAY_SIZE(iga1_k8m800_fifo_depth_select); + iga->fifo_depth.regs = iga1_k8m800_fifo_depth_select; + + iga->threshold.count = ARRAY_SIZE(iga1_k8m800_fifo_threshold_select); + iga->threshold.regs = iga1_k8m800_fifo_threshold_select; + + iga->high_threshold.count = ARRAY_SIZE(iga1_k8m800_fifo_high_threshold_select); + iga->high_threshold.regs = iga1_k8m800_fifo_high_threshold_select; + + iga->display_queue.count = ARRAY_SIZE(iga1_k8m800_display_queue_expire_num); + iga->display_queue.regs = iga1_k8m800_display_queue_expire_num; + } + + iga->fetch.count = ARRAY_SIZE(iga1_fetch_count); + iga->fetch.regs = iga1_fetch_count; + + iga->offset.count = ARRAY_SIZE(iga1_offset); + iga->offset.regs = iga1_offset; + } +} + +static int via_gamma_init(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + u16 *gamma; + uint32_t i; + int ret; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + ret = drm_mode_crtc_set_gamma_size(crtc, 256); + if (ret) { + drm_err(dev, "Failed to set gamma size!\n"); + goto exit; + } + + gamma = crtc->gamma_store; + for (i = 0; i < 256; i++) { + gamma[i] = i << 8 | i; + gamma[i + 256] = i << 8 | i; + gamma[i + 512] = i << 8 | i; + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static const uint32_t via_primary_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_C8, +}; + +int via_crtc_init(struct via_drm_priv *dev_priv, uint32_t index) +{ + struct drm_device *dev = &dev_priv->dev; + struct via_crtc *iga; + struct drm_plane *primary; + struct drm_plane *cursor; + uint32_t possible_crtcs; + int ret; + + possible_crtcs = 1 << index; + + primary = kzalloc(sizeof(struct drm_plane), GFP_KERNEL); + if (!primary) { + ret = -ENOMEM; + drm_err(dev, "Failed to allocate a primary plane.\n"); + goto exit; + } + + drm_plane_helper_add(primary, + &via_primary_drm_plane_helper_funcs); + ret = drm_universal_plane_init(dev, primary, possible_crtcs, + &via_primary_drm_plane_funcs, + via_primary_formats, + ARRAY_SIZE(via_primary_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + drm_err(dev, "Failed to initialize a primary " + "plane.\n"); + goto free_primary; + } + + cursor = kzalloc(sizeof(struct drm_plane), GFP_KERNEL); + if (!cursor) { + ret = -ENOMEM; + drm_err(dev, "Failed to allocate a cursor plane.\n"); + goto cleanup_primary; + } + + drm_plane_helper_add(cursor, + &via_cursor_drm_plane_helper_funcs); + ret = drm_universal_plane_init(dev, cursor, possible_crtcs, + &via_cursor_drm_plane_funcs, + via_cursor_formats, + via_cursor_formats_size, + NULL, DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + drm_err(dev, "Failed to initialize a cursor " + "plane.\n"); + goto free_cursor; + } + + iga = kzalloc(sizeof(struct via_crtc), GFP_KERNEL); + if (!iga) { + ret = -ENOMEM; + drm_err(dev, "Failed to allocate CRTC storage.\n"); + goto cleanup_cursor; + } + + drm_crtc_helper_add(&iga->base, + &via_drm_crtc_helper_funcs); + ret = drm_crtc_init_with_planes(dev, &iga->base, + primary, cursor, + &via_drm_crtc_funcs, + NULL); + if (ret) { + drm_err(dev, "Failed to initialize CRTC!\n"); + goto free_crtc; + } + + iga->index = index; + + via_crtc_param_init(dev_priv, &iga->base, index); + ret = via_gamma_init(&iga->base); + if (ret) { + goto free_crtc; + } + + goto exit; +free_crtc: + kfree(iga); +cleanup_cursor: + drm_plane_cleanup(cursor); +free_cursor: + kfree(cursor); +cleanup_primary: + drm_plane_cleanup(primary); +free_primary: + kfree(primary); +exit: + return ret; +} diff --git a/drivers/gpu/drm/via/via_crtc_hw.c b/drivers/gpu/drm/via/via_crtc_hw.c new file mode 100644 index 000000000000..f5446da52c0f --- /dev/null +++ b/drivers/gpu/drm/via/via_crtc_hw.c @@ -0,0 +1,91 @@ +/* + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * James Simmons <jsimmons@infradead.org> + */ + +#include <video/vga.h> + +#include "via_crtc_hw.h" + +/* + * load_register_table enables the ability to set entire + * tables of registers. For each register defined by the + * port and the index for that register is programmed + * with a masked value. + */ +void +load_register_tables(void __iomem *regbase, struct vga_registers *regs) +{ + u8 cr_index, orig, reg_mask, data; + unsigned int i; + u16 port; + + for (i = 0; i < regs->count; i++) { + reg_mask = regs->regs[i].start_bit; + data = regs->regs[i].end_bit; + cr_index = regs->regs[i].io_addr; + port = regs->regs[i].ioport; + + vga_w(regbase, port, cr_index); + orig = (vga_r(regbase, port + 1) & ~reg_mask); + vga_w(regbase, port + 1, ((data & reg_mask) | orig)); + } +} + +/* + * Due to the limitation of how much data you can write to a single + * register we run into data that can't be written in only one register. + * So load_value_to_register was developed to be able to define register + * tables that can load different bit ranges of the data to different + * registers. + */ +void +load_value_to_registers(void __iomem *regbase, struct vga_registers *regs, + unsigned int value) +{ + unsigned int bit_num = 0, shift_next_reg, reg_mask; + u8 start_index, end_index, cr_index, orig; + unsigned int data, i, j; + u16 get_bit, port; + + for (i = 0; i < regs->count; i++) { + start_index = regs->regs[i].start_bit; + end_index = regs->regs[i].end_bit; + cr_index = regs->regs[i].io_addr; + port = regs->regs[i].ioport; + reg_mask = data = 0; + + shift_next_reg = bit_num; + for (j = start_index; j <= end_index; j++) { + reg_mask = reg_mask | (1 << j); + get_bit = (value & (1 << bit_num)); + data |= ((get_bit >> shift_next_reg) << start_index); + bit_num++; + } + + vga_w(regbase, port, cr_index); + orig = (vga_r(regbase, port + 1) & ~reg_mask); + vga_w(regbase, port + 1, ((data & reg_mask) | orig)); + } +} diff --git a/drivers/gpu/drm/via/via_crtc_hw.h b/drivers/gpu/drm/via/via_crtc_hw.h new file mode 100644 index 000000000000..241dd6d11026 --- /dev/null +++ b/drivers/gpu/drm/via/via_crtc_hw.h @@ -0,0 +1,1002 @@ +/* + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * James Simmons <jsimmons@infradead.org> + */ + +#ifndef __CRTC_HW_H__ +#define __CRTC_HW_H__ + +#include <video/vga.h> + +#include <drm/drm_print.h> + + +/* To be used with via_dac_set_dpms_control inline function. */ +#define VIA_DAC_DPMS_ON 0x00 +#define VIA_DAC_DPMS_STANDBY 0x01 +#define VIA_DAC_DPMS_SUSPEND 0x02 +#define VIA_DAC_DPMS_OFF 0x03 + + +struct vga_regset { + u16 ioport; + u8 io_addr; + u8 start_bit; + u8 end_bit; +}; + +struct vga_registers { + unsigned int count; + struct vga_regset *regs; +}; + +/************************************************ + ***** Display Timing ***** + ************************************************/ + +struct crtc_timings { + struct vga_registers htotal; + struct vga_registers hdisplay; + struct vga_registers hblank_start; + struct vga_registers hblank_end; + struct vga_registers hsync_start; + struct vga_registers hsync_end; + struct vga_registers vtotal; + struct vga_registers vdisplay; + struct vga_registers vblank_start; + struct vga_registers vblank_end; + struct vga_registers vsync_start; + struct vga_registers vsync_end; +}; + +/* Write a value to misc register with a mask */ +static inline void svga_wmisc_mask(void __iomem *regbase, u8 data, u8 mask) +{ + vga_w(regbase, VGA_MIS_W, (data & mask) | (vga_r(regbase, VGA_MIS_R) & ~mask)); +} + +/* Write a value to a sequence register with a mask */ +static inline void svga_wseq_mask(void __iomem *regbase, u8 index, u8 data, u8 mask) +{ + vga_wseq(regbase, index, (data & mask) | (vga_rseq(regbase, index) & ~mask)); +} + +/* Write a value to a CRT register with a mask */ +static inline void svga_wcrt_mask(void __iomem *regbase, u8 index, u8 data, u8 mask) +{ + vga_wcrt(regbase, index, (data & mask) | (vga_rcrt(regbase, index) & ~mask)); +} + + +/*********************************************************************** + + VIA Technologies Chrome IGP Register Access Helper Functions + +***********************************************************************/ + +static inline void +via_iga1_set_palette_lut_resolution(void __iomem *regs, + bool palette_lut) +{ + /* Set the palette LUT resolution for IGA1. */ + /* 3C5.15[7] - IGA1 6 / 8 Bit LUT + * 0: 6-bit + * 1: 8-bit */ + svga_wseq_mask(regs, 0x15, palette_lut ? BIT(7) : 0x00, BIT(7)); +} + +static inline void +via_iga2_set_palette_lut_resolution(void __iomem *regs, + bool palette_lut) +{ + /* Set the palette LUT resolution for IGA2. */ + /* 3X5.6A[5] - IGA2 6 / 8 Bit LUT + * 0: 6-bit + * 1: 8-bit */ + svga_wcrt_mask(regs, 0x6a, palette_lut ? BIT(5) : 0x00, BIT(5)); +} + +static inline void +via_iga1_set_interlace_mode(void __iomem *regs, bool interlace_mode) +{ + svga_wcrt_mask(regs, 0x33, + interlace_mode ? BIT(6) : 0x00, BIT(6)); +} + +static inline void +via_iga2_set_interlace_mode(void __iomem *regs, bool interlace_mode) +{ + svga_wcrt_mask(regs, 0x67, + interlace_mode ? BIT(5) : 0x00, BIT(5)); +} + +/* + * Sets IGA1's HSYNC Shift value. + */ +static inline void +via_iga1_set_hsync_shift(void __iomem *regs, u8 shift_value) +{ + /* 3X5.33[2:0] - IGA1 HSYNC Shift */ + svga_wcrt_mask(regs, 0x33, shift_value, BIT(2) | BIT(1) | BIT(0)); +} + +/* + * Sets DIP0 (Digital Interface Port 0) I/O pad state. + * CLE266 chipset only. + */ +static inline void +via_dip0_set_io_pad_state(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.1E[7:6] - DIP0 Power Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x1E, io_pad_state << 6, BIT(7) | BIT(6)); +} + +/* + * Output enable of DIP0 (Digital Interface Port 0) interface. + * CLE266 chipset only. + */ +static inline void +via_dip0_set_output_enable(void __iomem *regs, bool output_enable) +{ + /* + * 3X5.6C[0] - DIP0 Output Enable + * 0: Output Disable + * 1: Output Enable + */ + svga_wcrt_mask(regs, 0x6c, output_enable ? BIT(0) : 0x00, BIT(0)); +} + +/* + * Sets the clock source of DIP0 (Digital Interface Port 0) + * interface. CLE266 chipset only. + */ +static inline void +via_dip0_set_clock_source(void __iomem *regs, bool clock_source) +{ + /* + * 3X5.6C[5] - DIP0 Clock Source + * 0: External + * 1: Internal + */ + svga_wcrt_mask(regs, 0x6c, clock_source ? BIT(5) : 0x00, BIT(5)); +} + +/* + * Sets the display source of DIP0 (Digital Interface Port 0) interface. + * CLE266 chipset only. + */ +static inline void +via_dip0_set_display_source(void __iomem *regs, u8 display_source) +{ + /* + * 3X5.6C[7] - DIP0 Data Source Selection + * 0: Primary Display + * 1: Secondary Display + */ + svga_wcrt_mask(regs, 0x6c, display_source << 7, BIT(7)); +} + +/* + * Sets DIP1 (Digital Interface Port 1) I/O pad state. + * CLE266 chipset only. + */ +static inline void +via_dip1_set_io_pad_state(void __iomem *regs, u8 io_pad_state) +{ + /* + * 3C5.1E[5:4] - DIP1 I/O Pad Control + * 00: I/O pad off + * 11: I/O pad on + */ + svga_wseq_mask(regs, 0x1e, io_pad_state << 4, BIT(5) | BIT(4)); +} + +/* + * Output enable of DIP1 (Digital Interface Port 1) interface. + * CLE266 chipset only. + */ +static inline void +via_dip1_set_output_enable(void __iomem *regs, bool output_enable) +{ + /* + * 3X5.93[0] - DIP1 Output Enable + * 0: Output Disable + * 1: Output Enable + */ + svga_wcrt_mask(regs, 0x93, output_enable ? BIT(0) : 0x00, BIT(0)); +} + +/* + * Sets the clock source of DIP1 (Digital Interface Port 1) + * interface. CLE266 chipset only. + */ +static inline void +via_dip1_set_clock_source(void __iomem *regs, bool clock_source) +{ + /* + * 3X5.93[5] - DIP1 Clock Source + * 0: External + * 1: Internal + */ + svga_wcrt_mask(regs, 0x93, clock_source ? BIT(5) : 0x00, BIT(5)); +} + +/* + * Sets the display source of DIP1 (Digital Interface Port 1) + * interface. CLE266 chipset only. + */ +static inline void +via_dip1_set_display_source(void __iomem *regs, u8 display_source) +{ + /* + * 3X5.93[7] - DIP1 Data Source Selection + * 0: IGA1 + * 1: IGA2 + */ + svga_wcrt_mask(regs, 0x93, display_source << 7, BIT(7)); +} + +/* + * Sets DVP0 (Digital Video Port 0) I/O pad state. + */ +static inline void +via_dvp0_set_io_pad_state(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.1E[7:6] - DVP0 Power Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x1E, io_pad_state << 6, BIT(7) | BIT(6)); +} + +/* + * Sets DVP0 (Digital Video Port 0) clock I/O pad drive strength. + */ +static inline void +via_dvp0_set_clock_drive_strength(void __iomem *regs, + u8 clock_drive_strength) +{ + /* 3C5.1E[2] - DVP0 Clock Drive Strength Bit [0] */ + svga_wseq_mask(regs, 0x1E, + clock_drive_strength << 2, BIT(2)); + + /* 3C5.2A[4] - DVP0 Clock Drive Strength Bit [1] */ + svga_wseq_mask(regs, 0x2A, + clock_drive_strength << 3, BIT(4)); +} + +/* + * Sets DVP0 (Digital Video Port 0) data I/O pads drive strength. + */ +static inline void +via_dvp0_set_data_drive_strength(void __iomem *regs, + u8 data_drive_strength) +{ + /* 3C5.1B[1] - DVP0 Data Drive Strength Bit [0] */ + svga_wseq_mask(regs, 0x1B, + data_drive_strength << 1, BIT(1)); + + /* 3C5.2A[5] - DVP0 Data Drive Strength Bit [1] */ + svga_wseq_mask(regs, 0x2A, + data_drive_strength << 4, BIT(5)); +} + +/* + * Sets the display source of DVP0 (Digital Video Port 0) interface. + */ +static inline void +via_dvp0_set_display_source(void __iomem *regs, u8 display_source) +{ + /* 3X5.96[4] - DVP0 Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x96, display_source << 4, BIT(4)); +} + +/* + * Sets DVP1 (Digital Video Port 1) I/O pad state. + */ +static inline void +via_dvp1_set_io_pad_state(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.1E[5:4] - DVP1 Power Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x1E, io_pad_state << 4, BIT(5) | BIT(4)); +} + +/* + * Sets DVP1 (Digital Video Port 1) clock I/O pad drive strength. + */ +static inline void +via_dvp1_set_clock_drive_strength(void __iomem *regs, + u8 clock_drive_strength) +{ + /* 3C5.65[3:2] - DVP1 Clock Pads Driving Select [1:0] + * 00: lowest + * 01: low + * 10: high + * 11: highest */ + svga_wseq_mask(regs, 0x65, + clock_drive_strength << 2, BIT(3) | BIT(2)); +} + +/* + * Sets DVP1 (Digital Video Port 1) data I/O pads drive strength. + */ +static inline void +via_dvp1_set_data_drive_strength(void __iomem *regs, + u8 data_drive_strength) +{ + /* 3C5.65[1:0] - DVP1 Data Pads Driving Select [1:0} + * 00: lowest + * 01: low + * 10: high + * 11: highest */ + svga_wseq_mask(regs, 0x65, + data_drive_strength, BIT(1) | BIT(0)); +} + +/* + * Sets the display source of DVP1 (Digital Video Port 1) interface. + */ +static inline void +via_dvp1_set_display_source(void __iomem *regs, u8 display_source) +{ + /* 3X5.9B[4] - DVP1 Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x9B, display_source << 4, BIT(4)); +} + +/* + * Sets analog (VGA) DAC power. + */ +static inline void via_dac_set_power(void __iomem *regs, bool output_state) +{ + /* 3X5.47[2] - DACOFF Backdoor Register + * 0: DAC on + * 1: DAC off */ + svga_wcrt_mask(regs, 0x47, + output_state ? 0x00 : BIT(2), BIT(2)); +} + +/* + * Sets analog (VGA) DPMS state. + */ +static inline void via_dac_set_dpms_control(void __iomem *regs, + u8 dpms_control) +{ + /* 3X5.36[5:4] - DPMS Control + * 00: On + * 01: Stand-by + * 10: Suspend + * 11: Off */ + svga_wcrt_mask(regs, 0x36, + dpms_control << 4, BIT(5) | BIT(4)); +} + +/* + * Sets analog (VGA) sync polarity. + */ +static inline void via_dac_set_sync_polarity(void __iomem *regs, + u8 sync_polarity) +{ + /* 3C2[7] - Analog Vertical Sync Polarity + * 0: Positive + * 1: Negative + * 3C2[6] - Analog Horizontal Sync Polarity + * 0: Positive + * 1: Negative */ + svga_wmisc_mask(regs, + sync_polarity << 6, (BIT(1) | BIT(0)) << 6); +} + +/* + * Sets analog (VGA) display source. + */ +static inline void via_dac_set_display_source(void __iomem *regs, + u8 display_source) +{ + /* 3C5.16[6] - CRT Display Source + * 0: Primary Display Stream (IGA1) + * 1: Secondary Display Stream (IGA2) */ + svga_wseq_mask(regs, 0x16, + display_source << 6, BIT(6)); +} + +/* + * Sets KM400 or later chipset's FP primary power sequence control + * type. + */ +static inline void via_lvds_set_primary_power_seq_type(void __iomem *regs, + bool ctrl_type) +{ + /* 3X5.91[0] - FP Primary Power Sequence Control Type + * 0: Hardware Control + * 1: Software Control */ + svga_wcrt_mask(regs, 0x91, + ctrl_type ? 0x00 : BIT(0), BIT(0)); +} + +/* + * Sets KM400 or later chipset's FP primary software controlled + * back light. + */ +static inline void via_lvds_set_primary_soft_back_light(void __iomem *regs, + bool soft_on) +{ + /* 3X5.91[1] - FP Primary Software Back Light On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0x91, + soft_on ? BIT(1) : 0x00, BIT(1)); +} + +/* + * Sets KM400 or later chipset's FP primary software controlled + * VEE. + */ +static inline void via_lvds_set_primary_soft_vee(void __iomem *regs, + bool soft_on) +{ + /* 3X5.91[2] - FP Primary Software VEE On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0x91, + soft_on ? BIT(2) : 0x00, BIT(2)); +} + +/* + * Sets KM400 or later chipset's FP primary software controlled + * data. + */ +static inline void via_lvds_set_primary_soft_data(void __iomem *regs, + bool soft_on) +{ + /* 3X5.91[3] - FP Primary Software Data On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0x91, + soft_on ? BIT(3) : 0x00, BIT(3)); +} + +/* + * Sets KM400 or later chipset's FP primary software controlled + * VDD. + */ +static inline void via_lvds_set_primary_soft_vdd(void __iomem *regs, + bool soft_on) +{ + /* 3X5.91[4] - FP Primary Software VDD On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0x91, + soft_on ? BIT(4) : 0x00, BIT(4)); +} + +/* + * Sets KM400 or later chipset's FP primary direct back + * light control. + */ +static inline void via_lvds_set_primary_direct_back_light_ctrl( + void __iomem *regs, bool direct_on) +{ + /* 3X5.91[6] - FP Primary Direct Back Light Control + * 0: On + * 1: Off */ + svga_wcrt_mask(regs, 0x91, + direct_on ? 0x00 : BIT(6), BIT(6)); +} + +/* + * Sets KM400 or later chipset's FP primary direct display + * period control. + */ +static inline void via_lvds_set_primary_direct_display_period( + void __iomem *regs, bool direct_on) +{ + /* 3X5.91[7] - FP Primary Direct Display Period Control + * 0: On + * 1: Off */ + svga_wcrt_mask(regs, 0x91, + direct_on ? 0x00 : BIT(7), BIT(7)); +} + +/* + * Sets KM400 or later chipset's FP primary hardware controlled + * power sequence. + */ +static inline void via_lvds_set_primary_hard_power(void __iomem *regs, + bool power_state) +{ + /* 3X5.6A[3] - FP Primary Hardware Controlled Power Sequence + * 0: Hardware Controlled Power Off + * 1: Hardware Controlled Power On */ + svga_wcrt_mask(regs, 0x6A, + power_state ? BIT(3) : 0x00, BIT(3)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary + * power sequence control type. + */ +static inline void via_lvds_set_secondary_power_seq_type(void __iomem *regs, + bool ctrl_type) +{ + /* 3X5.D3[0] - FP Secondary Power Sequence Control Type + * 0: Hardware Control + * 1: Software Control */ + svga_wcrt_mask(regs, 0xD3, + ctrl_type ? 0x00 : BIT(0), BIT(0)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary + * software controlled back light. + */ +static inline void via_lvds_set_secondary_soft_back_light(void __iomem *regs, + bool soft_on) +{ + /* 3X5.D3[1] - FP Secondary Software Back Light On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0xD3, + soft_on ? BIT(1) : 0x00, BIT(1)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary software + * controlled VEE. + */ +static inline void via_lvds_set_secondary_soft_vee(void __iomem *regs, + bool soft_on) +{ + /* 3X5.D3[2] - FP Secondary Software VEE On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0xD3, + soft_on ? BIT(2) : 0x00, BIT(2)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary software + * controlled data. + */ +static inline void via_lvds_set_secondary_soft_data(void __iomem *regs, + bool soft_on) +{ + /* 3X5.D3[3] - FP Secondary Software Data On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0xD3, + soft_on ? BIT(3) : 0x00, BIT(3)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary software + * controlled VDD. + */ +static inline void via_lvds_set_secondary_soft_vdd(void __iomem *regs, + bool soft_on) +{ + /* 3X5.D3[4] - FP Secondary Software VDD On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0xD3, + soft_on ? BIT(4) : 0x00, BIT(4)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary direct back + * light control. + */ +static inline void via_lvds_set_secondary_direct_back_light_ctrl( + void __iomem *regs, bool direct_on) +{ + /* 3X5.D3[6] - FP Secondary Direct Back Light Control + * 0: On + * 1: Off */ + svga_wcrt_mask(regs, 0xD3, + direct_on ? 0x00 : BIT(6), BIT(6)); +} + +/* + * Sets CX700 / VX700 or later chipset's FP secondary direct + * display period control. + */ +static inline void via_lvds_set_secondary_direct_display_period( + void __iomem *regs, bool direct_on) +{ + /* 3X5.D3[7] - FP Secondary Direct Display Period Control + * 0: On + * 1: Off */ + svga_wcrt_mask(regs, 0xD3, + direct_on ? 0x00 : BIT(7), BIT(7)); +} + +/* + * Sets FP secondary hardware controlled power sequence enable. + */ +static inline void via_lvds_set_secondary_hard_power(void __iomem *regs, + bool power_state) +{ + /* 3X5.D4[1] - Secondary Power Hardware Power Sequence Enable + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0xD4, + power_state ? BIT(1) : 0x00, BIT(1)); +} + +/* + * Sets FPDP (Flat Panel Display Port) Low I/O pad state. + */ +static inline void +via_fpdp_low_set_io_pad_state(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.2A[1:0] - FPDP Low I/O Pad Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x2A, + io_pad_state, BIT(1) | BIT(0)); +} + +/* + * Sets FPDP (Flat Panel Display Port) Low adjustment register. + */ +static inline void +via_fpdp_low_set_adjustment(void __iomem *regs, u8 adjustment) +{ + /* 3X5.99[3:0] - FPDP Low Adjustment */ + svga_wcrt_mask(regs, 0x99, + adjustment, BIT(3) | BIT(2) | BIT(1) | BIT(0)); +} + +/* + * Sets FPDP (Flat Panel Display Port) Low interface display source. + */ +static inline void +via_fpdp_low_set_display_source(void __iomem *regs, u8 display_source) +{ + /* 3X5.99[4] - FPDP Low Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x99, + display_source << 4, BIT(4)); +} + +/* + * Sets FPDP (Flat Panel Display Port) High I/O pad state. + */ +static inline void +via_fpdp_high_set_io_pad_state(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.2A[3:2] - FPDP High I/O Pad Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x2A, + io_pad_state << 2, BIT(3) | BIT(2)); +} + +/* + * Sets FPDP (Flat Panel Display Port) High adjustment register. + */ +static inline void +via_fpdp_high_set_adjustment(void __iomem *regs, u8 adjustment) +{ + /* 3X5.97[3:0] - FPDP High Adjustment */ + svga_wcrt_mask(regs, 0x97, + adjustment, BIT(3) | BIT(2) | BIT(1) | BIT(0)); +} + +/* + * Sets FPDP (Flat Panel Display Port) High interface display source. + */ +static inline void +via_fpdp_high_set_display_source(void __iomem *regs, u8 display_source) +{ + /* 3X5.97[4] - FPDP High Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x97, + display_source << 4, BIT(4)); +} + +/* + * Sets CX700 / VX700 or later chipset's LVDS1 power state. + */ +static inline void +via_lvds1_set_power(void __iomem *regs, bool power_state) +{ + /* 3X5.D2[7] - Power Down (Active High) for Channel 1 LVDS + * 0: Power on + * 1: Power off */ + svga_wcrt_mask(regs, 0xD2, + power_state ? 0x00 : BIT(7), BIT(7)); +} + +/* + * Sets CX700 or later single chipset's LVDS1 power sequence type. + */ +static inline void +via_lvds1_set_power_seq(void __iomem *regs, bool softCtrl) +{ + /* Set LVDS1 power sequence type. */ + /* 3X5.91[0] - LVDS1 Hardware or Software Control Power Sequence + * 0: Hardware Control + * 1: Software Control */ + svga_wcrt_mask(regs, 0x91, softCtrl ? BIT(0) : 0, BIT(0)); +} + +/* + * Sets CX700 or later single chipset's LVDS1 software controlled + * data path state. + */ +static inline void +via_lvds1_set_soft_data(void __iomem *regs, bool softOn) +{ + /* Set LVDS1 software controlled data path state. */ + /* 3X5.91[3] - Software Data On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0x91, softOn ? BIT(3) : 0, BIT(3)); +} + +/* + * Sets CX700 or later single chipset's LVDS1 software controlled Vdd. + */ +static inline void +via_lvds1_set_soft_vdd(void __iomem *regs, bool softOn) +{ + /* Set LVDS1 software controlled Vdd. */ + /* 3X5.91[4] - Software VDD On + * 0: Off + * 1: On */ + svga_wcrt_mask(regs, 0x91, softOn ? BIT(4) : 0, BIT(4)); +} + +/* + * Sets CX700 or later single chipset's LVDS1 software controlled + * display period. + */ +static inline void +via_lvds1_set_soft_display_period(void __iomem *regs, bool softOn) +{ + /* Set LVDS1 software controlled display period state. */ + /* 3X5.91[7] - Software Direct On / Off Display Period + * in the Panel Path + * 0: On + * 1: Off */ + svga_wcrt_mask(regs, 0x91, softOn ? 0 : BIT(7), BIT(7)); +} + +/* + * Sets LVDS1 I/O pad state. + */ +static inline void +via_lvds1_set_io_pad_setting(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.2A[1:0] - LVDS1 I/O Pad Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x2A, + io_pad_state, BIT(1) | BIT(0)); +} + +/* + * Sets LVDS1 format. + */ +static inline void +via_lvds1_set_format(void __iomem *regs, u8 format) +{ + /* 3X5.D2[1] - LVDS Channel 1 Format Selection + * 0: SPWG Mode + * 1: OPENLDI Mode */ + svga_wcrt_mask(regs, 0xd2, + format << 1, BIT(1)); +} + +/* + * Sets LVDS1 output format (rotation or sequential mode). + */ +static inline void +via_lvds1_set_output_format(void __iomem *regs, u8 output_format) +{ + /* 3X5.88[6] - LVDS Channel 1 Output Format + * 0: Rotation + * 1: Sequential */ + svga_wcrt_mask(regs, 0x88, + output_format << 6, BIT(6)); +} + +/* + * Sets LVDS1 output color dithering (18-bit color display vs. + * 24-bit color display). + */ +static inline void +via_lvds1_set_dithering(void __iomem *regs, bool dithering) +{ + /* 3X5.88[0] - LVDS Channel 1 Output Bits + * 0: 24 bits (dithering off) + * 1: 18 bits (dithering on) */ + svga_wcrt_mask(regs, 0x88, + dithering ? BIT(0) : 0x00, BIT(0)); +} + +/* + * Sets LVDS1 display source. + */ +static inline void +via_lvds1_set_display_source(void __iomem *regs, u8 display_source) +{ + /* 3X5.99[4] - LVDS Channel 1 Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x99, + display_source << 4, BIT(4)); +} + +/* + * Sets CX700 / VX700 and VX800 chipset's LVDS2 power state. + */ +static inline void +via_lvds2_set_power(void __iomem *regs, bool power_state) +{ + /* 3X5.D2[6] - Power Down (Active High) for Channel 2 LVDS + * 0: Power on + * 1: Power off */ + svga_wcrt_mask(regs, 0xD2, + power_state ? 0x00 : BIT(6), BIT(6)); +} + +/* + * Sets LVDS2 I/O pad state. + */ +static inline void +via_lvds2_set_io_pad_setting(void __iomem *regs, u8 io_pad_state) +{ + /* 3C5.2A[3:2] - LVDS2 I/O Pad Control + * 0x: Pad always off + * 10: Depend on the other control signal + * 11: Pad on/off according to the + * Power Management Status (PMS) */ + svga_wseq_mask(regs, 0x2A, + io_pad_state << 2, BIT(3) | BIT(2)); +} + +/* + * Sets LVDS2 format. + */ +static inline void +via_lvds2_set_format(void __iomem *regs, u8 format) +{ + /* 3X5.D2[0] - LVDS Channel 2 Format Selection + * 0: SPWG Mode + * 1: OPENLDI Mode */ + svga_wcrt_mask(regs, 0xd2, format, BIT(0)); +} + +/* + * Sets LVDS2 output format (rotation or sequential mode). + */ +static inline void +via_lvds2_set_output_format(void __iomem *regs, u8 output_format) +{ + /* 3X5.D4[7] - LVDS Channel 2 Output Format + * 0: Rotation + * 1: Sequential */ + svga_wcrt_mask(regs, 0xd4, output_format << 7, BIT(7)); +} + +/* + * Sets LVDS2 output color dithering (18-bit color display vs. + * 24-bit color display). + */ +static inline void +via_lvds2_set_dithering(void __iomem *regs, bool dithering) +{ + /* 3X5.D4[6] - LVDS Channel 2 Output Bits + * 0: 24 bits (dithering off) + * 1: 18 bits (dithering on) */ + svga_wcrt_mask(regs, 0xd4, + dithering ? BIT(6) : 0x00, BIT(6)); +} + +/* + * Sets LVDS2 display source. + */ +static inline void +via_lvds2_set_display_source(void __iomem *regs, u8 display_source) +{ + /* 3X5.97[4] - LVDS Channel 2 Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x97, + display_source << 4, BIT(4)); +} + +/* + * Sets CX700 / VX700 and VX800 chipsets' TMDS (DVI) power state. + */ +static inline void +via_tmds_set_power(void __iomem *regs, bool powerState) +{ + /* 3X5.D2[3] - Power Down (Active High) for DVI + * 0: TMDS power on + * 1: TMDS power down */ + svga_wcrt_mask(regs, 0xD2, + powerState ? 0x00 : BIT(3), BIT(3)); +} + +/* + * Sets CX700 / VX700 and VX800 chipsets' TMDS (DVI) sync polarity. + */ +static inline void +via_tmds_set_sync_polarity(void __iomem *regs, u8 syncPolarity) +{ + /* Set TMDS (DVI) sync polarity. */ + /* 3X5.97[6] - DVI (TMDS) VSYNC Polarity + * 0: Positive + * 1: Negative + * 3X5.97[5] - DVI (TMDS) HSYNC Polarity + * 0: Positive + * 1: Negative */ + svga_wcrt_mask(regs, 0x97, + syncPolarity << 5, BIT(6) | BIT(5)); +} + +/* + * Sets TMDS (DVI) display source. + */ +static inline void +via_tmds_set_display_source(void __iomem *regs, u8 displaySource) +{ + /* The integrated TMDS transmitter appears to utilize LVDS1's + * data source selection bit (3X5.99[4]). */ + /* 3X5.99[4] - LVDS Channel1 Data Source Selection + * 0: Primary Display + * 1: Secondary Display */ + svga_wcrt_mask(regs, 0x99, + displaySource << 4, BIT(4)); +} + + +void load_register_tables(void __iomem *regbase, struct vga_registers *regs); +void load_value_to_registers(void __iomem *regbase, struct vga_registers *regs, + unsigned int value); + +#endif /* __CRTC_HW_H__ */ diff --git a/drivers/gpu/drm/via/via_cursor.c b/drivers/gpu/drm/via/via_cursor.c new file mode 100644 index 000000000000..9e38b9c37d55 --- /dev/null +++ b/drivers/gpu/drm/via/via_cursor.c @@ -0,0 +1,415 @@ +/* + * Copyright © 2019-2020 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem.h> +#include <drm/drm_mode.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h> + +#include <drm/ttm/ttm_bo.h> + +#include "via_drv.h" + + +static void via_hide_cursor(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + uint32_t temp; + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + if (iga->index) { + temp = VIA_READ(HI_CONTROL); + VIA_WRITE(HI_CONTROL, temp & 0xFFFFFFFA); + } else { + temp = VIA_READ(PRIM_HI_CTRL); + VIA_WRITE(PRIM_HI_CTRL, temp & 0xFFFFFFFA); + } + + break; + default: + temp = VIA_READ(HI_CONTROL); + VIA_WRITE(HI_CONTROL, temp & 0xFFFFFFFA); + break; + } +} + +static void via_show_cursor(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* + * Program Hardware Icon (HI) FIFO, foreground color, + * and background color. + */ + if (iga->index) { + VIA_WRITE(HI_TRANSPARENT_COLOR, 0x00000000); + VIA_WRITE(HI_INVTCOLOR, 0x00FFFFFF); + VIA_WRITE(ALPHA_V3_PREFIFO_CONTROL, + 0x000E0000); + VIA_WRITE(ALPHA_V3_FIFO_CONTROL, 0x0E0F0000); + } else { + VIA_WRITE(PRIM_HI_TRANSCOLOR, 0x00000000); + VIA_WRITE(PRIM_HI_INVTCOLOR, 0x00FFFFFF); + VIA_WRITE(V327_HI_INVTCOLOR, 0x00FFFFFF); + VIA_WRITE(PRIM_HI_FIFO, 0x0D000D0F); + } + + break; + default: + /* + * Program Hardware Icon (HI) FIFO, foreground color, + * and background color. + */ + VIA_WRITE(HI_TRANSPARENT_COLOR, 0x00000000); + VIA_WRITE(HI_INVTCOLOR, 0x00FFFFFF); + VIA_WRITE(ALPHA_V3_PREFIFO_CONTROL, 0x000E0000); + VIA_WRITE(ALPHA_V3_FIFO_CONTROL, 0xE0F0000); + break; + } + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* + * Turn on Hardware Icon (HI). + */ + if (iga->index) { + VIA_WRITE(HI_CONTROL, 0xB6000005); + } else { + VIA_WRITE(PRIM_HI_CTRL, 0x36000005); + } + + break; + default: + /* + * Turn on Hardware Icon (HI). + */ + if (iga->index) { + VIA_WRITE(HI_CONTROL, 0xB6000005); + } else { + VIA_WRITE(HI_CONTROL, 0x36000005); + } + + break; + } +} + +static void via_cursor_address(struct drm_crtc *crtc, + struct ttm_buffer_object *ttm_bo) +{ + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* + * Program Hardware Icon (HI) offset. + */ + if (iga->index) { + VIA_WRITE(HI_FBOFFSET, + ttm_bo->resource->start << PAGE_SHIFT); + } else { + VIA_WRITE(PRIM_HI_FBOFFSET, + ttm_bo->resource->start << PAGE_SHIFT); + } + break; + default: + /* + * Program Hardware Icon (HI) offset. + */ + VIA_WRITE(HI_FBOFFSET, ttm_bo->resource->start << PAGE_SHIFT); + break; + } + + return; +} + +static void via_set_hi_location(struct drm_crtc *crtc, int crtc_x, int crtc_y) +{ + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_crtc *iga = container_of(crtc, + struct via_crtc, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + uint32_t location_x = 0, location_y = 0; + uint32_t offset_x = 0, offset_y = 0; + + if (crtc_x < 0) { + offset_x = -crtc_x; + } else { + location_x = crtc_x; + } + + if (crtc_y < 0) { + offset_y = -crtc_y; + } else { + location_y = crtc_y; + } + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + if (iga->index) { + VIA_WRITE(HI_POSSTART, + (((location_x & 0x07ff) << 16) | + (location_y & 0x07ff))); + VIA_WRITE(HI_CENTEROFFSET, + (((offset_x & 0x07ff) << 16) | + (offset_y & 0x07ff))); + } else { + VIA_WRITE(PRIM_HI_POSSTART, + (((location_x & 0x07ff) << 16) | + (location_y & 0x07ff))); + VIA_WRITE(PRIM_HI_CENTEROFFSET, + (((offset_x & 0x07ff) << 16) | + (offset_y & 0x07ff))); + } + + break; + default: + VIA_WRITE(HI_POSSTART, + (((location_x & 0x07ff) << 16) | + (location_y & 0x07ff))); + VIA_WRITE(HI_CENTEROFFSET, + (((offset_x & 0x07ff) << 16) | + (offset_y & 0x07ff))); + break; + } +} + +static int via_cursor_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + struct via_bo *bo; + int ret = 0; + + if (!new_state->fb) { + goto exit; + } + + gem = new_state->fb->obj[0]; + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + bo = to_ttm_bo(ttm_bo); + + ret = ttm_bo_reserve(ttm_bo, true, false, NULL); + if (ret) { + goto exit; + } + + ret = via_bo_pin(bo, TTM_PL_VRAM); + ttm_bo_unreserve(ttm_bo); + ret = ttm_bo_kmap(ttm_bo, 0, PFN_UP(ttm_bo->resource->size), &bo->kmap); + if (ret) { + goto exit; + } + +exit: + return ret; +} + +static void via_cursor_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + struct via_bo *bo; + int ret; + + if (!old_state->fb) { + goto exit; + } + + gem = old_state->fb->obj[0]; + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + bo = to_ttm_bo(ttm_bo); + + ttm_bo_kunmap(&bo->kmap); + ret = ttm_bo_reserve(ttm_bo, true, false, NULL); + if (ret) { + goto exit; + } + + via_bo_unpin(bo); + ttm_bo_unreserve(ttm_bo); + +exit: + return; +} + +static int via_cursor_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *new_crtc_state; + struct drm_device *dev = plane->dev; + struct drm_framebuffer *fb = new_plane_state->fb; + int ret = 0; + + if ((!new_plane_state->crtc) || (!new_plane_state->visible)) { + goto exit; + } + + if (fb->width != fb->height) { + drm_err(dev, "Hardware cursor is expected to have " + "square dimensions.\n"); + ret = -EINVAL; + goto exit; + } + + new_crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); + ret = drm_atomic_helper_check_plane_state( + new_plane_state, + new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); +exit: + return ret; +} + +static void via_cursor_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_plane_state *old_state = + drm_atomic_get_old_plane_state(state, plane); + struct drm_crtc *crtc = new_state->crtc; + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + + if (new_state->fb != old_state->fb) { + gem = new_state->fb->obj[0]; + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + via_cursor_address(crtc, ttm_bo); + } + + via_set_hi_location(crtc, new_state->crtc_x, new_state->crtc_y); + via_show_cursor(crtc); +} + +static void via_cursor_atomic_enable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *crtc = new_state->crtc; + + if (crtc) { + via_show_cursor(crtc); + } +} + +void via_cursor_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *crtc = new_state->crtc; + + if (crtc) { + via_hide_cursor(crtc); + } +} + +const struct drm_plane_helper_funcs via_cursor_drm_plane_helper_funcs = { + .prepare_fb = via_cursor_prepare_fb, + .cleanup_fb = via_cursor_cleanup_fb, + .atomic_check = via_cursor_atomic_check, + .atomic_update = via_cursor_atomic_update, + .atomic_enable = via_cursor_atomic_enable, + .atomic_disable = via_cursor_atomic_disable, +}; + +const struct drm_plane_funcs via_cursor_drm_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +const uint32_t via_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +const unsigned int via_cursor_formats_size = + ARRAY_SIZE(via_cursor_formats); diff --git a/drivers/gpu/drm/via/via_dac.c b/drivers/gpu/drm/via/via_dac.c new file mode 100644 index 000000000000..9331b3509b11 --- /dev/null +++ b/drivers/gpu/drm/via/via_dac.c @@ -0,0 +1,474 @@ +/* + * Copyright © 2016-2018 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/pci.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include "via_crtc_hw.h" +#include "via_drv.h" + + +/* + * Set DAC (VGA) sync polarity. + */ +static void via_dac_sync_polarity(struct via_drm_priv *dev_priv, + unsigned int flags) +{ + struct drm_device *dev = &dev_priv->dev; + u8 syncPolarity = 0x00; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (flags & DRM_MODE_FLAG_NHSYNC) { + syncPolarity |= BIT(0); + } + + if (flags & DRM_MODE_FLAG_NVSYNC) { + syncPolarity |= BIT(1); + } + + via_dac_set_sync_polarity(VGABASE, syncPolarity); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Routines for controlling stuff on the DAC port + */ +static const struct drm_encoder_funcs via_dac_enc_funcs = { + .destroy = via_encoder_destroy, +}; + +/* + * Manage the power state of DAC (VGA). + */ +static void via_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (mode) { + case DRM_MODE_DPMS_ON: + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_ON); + via_dac_set_power(VGABASE, true); + break; + case DRM_MODE_DPMS_STANDBY: + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_STANDBY); + via_dac_set_power(VGABASE, true); + break; + case DRM_MODE_DPMS_SUSPEND: + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_SUSPEND); + via_dac_set_power(VGABASE, true); + break; + case DRM_MODE_DPMS_OFF: + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_OFF); + via_dac_set_power(VGABASE, false); + break; + default: + drm_err(dev, "Bad DPMS mode."); + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* Pass our mode to the connectors and the CRTC to give them a chance to + * adjust it according to limitations or connector properties, and also + * a chance to reject the mode entirely. Useful for things like scaling. + */ +static bool via_dac_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + drm_mode_set_crtcinfo(adjusted_mode, 0); + return true; +} + +/* + * Handle DAC (VGA) mode setting. + */ +static void via_dac_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_dac_sync_polarity(dev_priv, adjusted_mode->flags); + via_dac_set_display_source(VGABASE, iga->index); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_dac_prepare(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (encoder->crtc) { + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_OFF); + via_dac_set_power(VGABASE, false); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_dac_commit(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (encoder->crtc) { + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_ON); + via_dac_set_power(VGABASE, true); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_dac_disable(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_dac_set_dpms_control(VGABASE, VIA_DAC_DPMS_OFF); + via_dac_set_power(VGABASE, false); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static const struct drm_encoder_helper_funcs via_dac_enc_helper_funcs = { + .dpms = via_dac_dpms, + .mode_fixup = via_dac_mode_fixup, + .mode_set = via_dac_mode_set, + .prepare = via_dac_prepare, + .commit = via_dac_commit, + .disable = via_dac_disable, +}; + +static enum drm_connector_status +via_dac_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + enum drm_connector_status ret = connector_status_disconnected; + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else { + i2c_bus = NULL; + } + + if (i2c_bus) { + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) { + ret = connector_status_connected; + } + + kfree(edid); + goto exit; + } + } + + if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else { + i2c_bus = NULL; + } + + if (i2c_bus) { + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) { + ret = connector_status_connected; + } + + kfree(edid); + goto exit; + } + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static const struct drm_connector_funcs via_dac_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_dac_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = via_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = + drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_connector_destroy_state, +}; + +static enum drm_mode_status via_dac_mode_valid( + struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + int min_clock, max_clock; + enum drm_mode_status status = MODE_OK; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + min_clock = 25000; + switch (pdev->device) { + /* CLE266 Chipset */ + case PCI_DEVICE_ID_VIA_CLE266_GFX: + /* KM400(A) / KN400(A) / P4M800 Chipset */ + case PCI_DEVICE_ID_VIA_KM400_GFX: + max_clock = 250000; + break; + /* K8M800(A) / K8N800(A) Chipset */ + case PCI_DEVICE_ID_VIA_K8M800_GFX: + /* P4M800 Pro / P4M800CE / VN800 / CN700 / CN333 / CN400 Chipset */ + case PCI_DEVICE_ID_VIA_P4M800_PRO_GFX: + max_clock = 300000; + break; + /* PM800 / PN800 / PM880 / PN880 Chipset */ + case PCI_DEVICE_ID_VIA_PM800_GFX: + /* P4M890 / P4N890 Chipset */ + case PCI_DEVICE_ID_VIA_P4M890_GFX: + /* K8M890 / K8N890 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9: + /* P4M900 / VN896 / CN896 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HC: + /* CX700(M/M2) / VX700(M/M2) Chipset */ + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + /* VX800 / VX820 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + /* VX855 / VX875 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + /* VX900(H) Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HD: + max_clock = 350000; + break; + /* Illegal condition (should never get here) */ + default: + max_clock = 0; + break; + } + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { + status = MODE_NO_DBLESCAN; + goto exit; + } + + if (mode->clock < min_clock) { + status = MODE_CLOCK_LOW; + goto exit; + } + + if (mode->clock > max_clock) { + status = MODE_CLOCK_HIGH; + goto exit; + } + +exit: + drm_dbg_kms(dev, "status: %u\n", status); + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return status; +} + +static int via_dac_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + int count = 0; + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else { + i2c_bus = NULL; + } + + if (i2c_bus) { + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) { + drm_connector_update_edid_property(connector, + edid); + count = drm_add_edid_modes(connector, edid); + } + + kfree(edid); + goto exit; + } + } + + if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else { + i2c_bus = NULL; + } + + if (i2c_bus) { + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) { + drm_connector_update_edid_property(connector, + edid); + count = drm_add_edid_modes(connector, edid); + } + + kfree(edid); + goto exit; + } + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return count; +} + +static const struct drm_connector_helper_funcs +via_dac_connector_helper_funcs = { + .mode_valid = via_dac_mode_valid, + .get_modes = via_dac_get_modes, +}; + +void via_dac_probe(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + u8 sr13, sr5a; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + sr5a = vga_rseq(VGABASE, 0x5a); + drm_dbg_kms(dev, "SR5A: 0x%02x\n", sr5a); + + /* Setting SR5A[0] to 1. + * This allows the reading out the alternative + * pin strapping information from SR12 and SR13. */ + svga_wseq_mask(VGABASE, 0x5a, BIT(0), BIT(0)); + drm_dbg_kms(dev, "SR5A: 0x%02x\n", sr5a); + + sr13 = vga_rseq(VGABASE, 0x13); + drm_dbg_kms(dev, "SR13: 0x%02x\n", sr13); + + if (!(sr13 & BIT(2))) { + dev_priv->dac_presence = true; + drm_dbg_kms(dev, "Detected the presence of VGA.\n"); + } else { + dev_priv->dac_presence = false; + } + + /* Restore SR5A. */ + vga_wseq(VGABASE, 0x5a, sr5a); + break; + default: + dev_priv->dac_presence = true; + drm_dbg_kms(dev, "Detected the presence of VGA.\n"); + break; + } + + dev_priv->dac_i2c_bus = VIA_I2C_NONE; + + if (dev_priv->dac_presence) { + dev_priv->dac_i2c_bus = VIA_I2C_BUS2 | VIA_I2C_BUS1; + } + + dev_priv->mapped_i2c_bus |= dev_priv->dac_i2c_bus; + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_dac_init(struct drm_device *dev) +{ + struct via_connector *con; + struct via_encoder *enc; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL); + if (!enc) { + drm_err(dev, "Failed to allocate connector and encoder\n"); + return; + } + con = &enc->cons[0]; + INIT_LIST_HEAD(&con->props); + + /* Piece together our connector */ + drm_connector_init(dev, &con->base, &via_dac_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + drm_connector_helper_add(&con->base, &via_dac_connector_helper_funcs); + drm_connector_register(&con->base); + + con->i2c_bus = dev_priv->dac_i2c_bus; + con->base.doublescan_allowed = false; + con->base.interlace_allowed = true; + + /* Setup the encoders and attach them */ + drm_encoder_init(dev, &enc->base, &via_dac_enc_funcs, + DRM_MODE_ENCODER_DAC, NULL); + drm_encoder_helper_add(&enc->base, &via_dac_enc_helper_funcs); + + enc->base.possible_crtcs = BIT(1) | BIT(0); + enc->base.possible_clones = 0; + enc->di_port = VIA_DI_PORT_NONE; + + drm_connector_attach_encoder(&con->base, &enc->base); +} diff --git a/drivers/gpu/drm/via/via_disp_reg.h b/drivers/gpu/drm/via/via_disp_reg.h new file mode 100644 index 000000000000..e2bd895bb495 --- /dev/null +++ b/drivers/gpu/drm/via/via_disp_reg.h @@ -0,0 +1,513 @@ +/* + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + */ + +#ifndef __VIA_DISP_REG_H__ +#define __VIA_DISP_REG_H__ + +/********************************************************/ +/* Definition IGA Design Method of FIFO Registers */ +/********************************************************/ +#define IGA1_FIFO_DEPTH_SELECT_FORMULA(x) ((x >> 1) - 1) +#define IGA2_FIFO_DEPTH_SELECT_FORMULA(x) ((x >> 3) - 1) + +/* Define Display OFFSET */ +/* VT3314 chipset */ +#define CN700_IGA1_FIFO_MAX_DEPTH 96 /* location: {SR17,0,7}*/ +#define CN700_IGA1_FIFO_THRESHOLD 80 /* location: {SR16,0,5},{SR16,7,7}*/ +#define CN700_IGA1_FIFO_HIGH_THRESHOLD 64 /* location: {SR18,0,5},{SR18,7,7}*/ +#define CN700_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 128 /* location: {SR22,0,4}. (128/4) =64, + * P800 must be set zero, because HW + * only 5 bits */ +#define CN700_IGA2_FIFO_MAX_DEPTH 96 /* location: {CR68,4,7},{CR94,7,7},{CR95,7,7}*/ +#define CN700_IGA2_FIFO_THRESHOLD 80 /* location: {CR68,0,3},{CR95,4,6}*/ +#define CN700_IGA2_FIFO_HIGH_THRESHOLD 32 /* location: {CR92,0,3},{CR95,0,2}*/ +#define CN700_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 128 /* location: {CR94,0,6}*/ + +/* For VT3324, these values are suggested by HW */ +#define CX700_IGA1_FIFO_MAX_DEPTH 192 /* location: {SR17,0,7}*/ +#define CX700_IGA1_FIFO_THRESHOLD 128 /* location: {SR16,0,5},{SR16,7,7}*/ +#define CX700_IGA1_FIFO_HIGH_THRESHOLD 128 /* location: {SR18,0,5},{SR18,7,7} */ +#define CX700_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 124 /* location: {SR22,0,4} */ + +#define CX700_IGA2_FIFO_MAX_DEPTH 96 /* location: {CR68,4,7},{CR94,7,7},{CR95,7,7}*/ +#define CX700_IGA2_FIFO_THRESHOLD 64 /* location: {CR68,0,3},{CR95,4,6}*/ +#define CX700_IGA2_FIFO_HIGH_THRESHOLD 32 /* location: {CR92,0,3},{CR95,0,2} */ +#define CX700_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 128 /* location: {CR94,0,6}*/ + +/* VT3336 chipset */ +#define K8M890_IGA1_FIFO_MAX_DEPTH 360 /* location: {SR17,0,7}*/ +#define K8M890_IGA1_FIFO_THRESHOLD 328 /* location: {SR16,0,5},{SR16,7,7}*/ +#define K8M890_IGA1_FIFO_HIGH_THRESHOLD 296 /* location: {SR18,0,5},{SR18,7,7}*/ +#define K8M890_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 124 /* location: {SR22,0,4}.*/ + +#define K8M890_IGA2_FIFO_MAX_DEPTH 360 /* location: {CR68,4,7},{CR94,7,7},{CR95,7,7}*/ +#define K8M890_IGA2_FIFO_THRESHOLD 328 /* location: {CR68,0,3},{CR95,4,6}*/ +#define K8M890_IGA2_FIFO_HIGH_THRESHOLD 296 /* location: {CR92,0,3},{CR95,0,2}*/ +#define K8M890_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 124 /* location: {CR94,0,6}*/ + +/* VT3327 chipset */ +#define P4M890_IGA1_FIFO_MAX_DEPTH 96 /* location: {SR17,0,7}*/ +#define P4M890_IGA1_FIFO_THRESHOLD 76 /* location: {SR16,0,5},{SR16,7,7}*/ +#define P4M890_IGA1_FIFO_HIGH_THRESHOLD 64 /* location: {SR18,0,5},{SR18,7,7}*/ +#define P4M890_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 32 /* location: {SR22,0,4}. (32/4) =8*/ + +#define P4M890_IGA2_FIFO_MAX_DEPTH 96 /* location: {CR68,4,7},{CR94,7,7},{CR95,7,7}*/ +#define P4M890_IGA2_FIFO_THRESHOLD 76 /* location: {CR68,0,3},{CR95,4,6}*/ +#define P4M890_IGA2_FIFO_HIGH_THRESHOLD 64 /* location: {CR92,0,3},{CR95,0,2}*/ +#define P4M890_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 32 /* location: {CR94,0,6}*/ + +/* VT3364 chipset */ +#define P4M900_IGA1_FIFO_MAX_DEPTH 96 /* location: {SR17,0,7}*/ +#define P4M900_IGA1_FIFO_THRESHOLD 76 /* location: {SR16,0,5},{SR16,7,7}*/ +#define P4M900_IGA1_FIFO_HIGH_THRESHOLD 76 /* location: {SR18,0,5},{SR18,7,7}*/ +#define P4M900_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 32 /* location: {SR22,0,4}.*/ + +#define P4M900_IGA2_FIFO_MAX_DEPTH 96 /* location: {CR68,4,7},{CR94,7,7},{CR95,7,7}*/ +#define P4M900_IGA2_FIFO_THRESHOLD 76 /* location: {CR68,0,3},{CR95,4,6}*/ +#define P4M900_IGA2_FIFO_HIGH_THRESHOLD 76 /* location: {CR92,0,3},{CR95,0,2}*/ +#define P4M900_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 32 /* location: {CR94,0,6}*/ + +/* For VT3353, these values are suggested by HW */ +#define VX800_IGA1_FIFO_MAX_DEPTH 192 /* location: {SR17,0,7}*/ +#define VX800_IGA1_FIFO_THRESHOLD 152 /* location: {SR16,0,5},{SR16,7,7}*/ +#define VX800_IGA1_FIFO_HIGH_THRESHOLD 152 /* location: {SR18,0,5},{SR18,7,7} */ +#define VX800_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 64 /* location: {SR22,0,4} */ + +#define VX800_IGA2_FIFO_MAX_DEPTH 96 /* location: {CR68,4,7},{CR94,7,7},{CR95,7,7}*/ +#define VX800_IGA2_FIFO_THRESHOLD 64 /* location: {CR68,0,3},{CR95,4,6}*/ +#define VX800_IGA2_FIFO_HIGH_THRESHOLD 32 /* location: {CR92,0,3},{CR95,0,2} */ +#define VX800_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 128 /* location: {CR94,0,6}*/ + +/* For VT3409 */ +#define VX855_IGA1_FIFO_MAX_DEPTH 400 +#define VX855_IGA1_FIFO_THRESHOLD 320 +#define VX855_IGA1_FIFO_HIGH_THRESHOLD 320 +#define VX855_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 160 + +#define VX855_IGA2_FIFO_MAX_DEPTH 200 +#define VX855_IGA2_FIFO_THRESHOLD 160 +#define VX855_IGA2_FIFO_HIGH_THRESHOLD 160 +#define VX855_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 320 + +/* For VT3410 */ +#define VX900_IGA1_FIFO_MAX_DEPTH 400 +#define VX900_IGA1_FIFO_THRESHOLD 320 +#define VX900_IGA1_FIFO_HIGH_THRESHOLD 320 +#define VX900_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 160 + +#define VX900_IGA2_FIFO_MAX_DEPTH 192 +#define VX900_IGA2_FIFO_THRESHOLD 160 +#define VX900_IGA2_FIFO_HIGH_THRESHOLD 160 +#define VX900_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 320 + +#ifdef VIA_VT3293_SUPPORT +/* For VT3293 */ +#define CN750_IGA1_FIFO_MAX_DEPTH 96 +#define CN750_IGA1_FIFO_THRESHOLD 76 +#define CN750_IGA1_FIFO_HIGH_THRESHOLD 76 +#define CN750_IGA1_DISPLAY_QUEUE_EXPIRE_NUM 32 + +#define CN750_IGA2_FIFO_MAX_DEPTH 96 +#define CN750_IGA2_FIFO_THRESHOLD 76 +#define CN750_IGA2_FIFO_HIGH_THRESHOLD 76 +#define CN750_IGA2_DISPLAY_QUEUE_EXPIRE_NUM 32 +#endif + +/* CLE266 and KM400 IGA1 FIFO Depth Select */ +static struct vga_regset iga1_cle266_fifo_depth_select[] = { + { VGA_SEQ_I, 0x17, 0, 6 } +}; + +/* K8M800 or later IGA1 FIFO Depth Select */ +static struct vga_regset iga1_k8m800_fifo_depth_select[] = { + { VGA_SEQ_I, 0x17, 0, 7 } +}; + +/* CLE266 and KM400 IGA2 FIFO Depth_Select */ +static struct vga_regset iga2_cle266_fifo_depth_select[] = { + { VGA_CRT_IC, 0x68, 4, 7 } +}; + +/* K8M800 or later IGA2 FIFO Depth_Select */ +static struct vga_regset iga2_k8m800_fifo_depth_select[] = { + { VGA_CRT_IC, 0x68, 4, 7 }, + { VGA_CRT_IC, 0x94, 7, 7 }, + { VGA_CRT_IC, 0x95, 7, 7 } +}; + +/* CLE266 and KM400 IGA1 FIFO Threshold Select */ +static struct vga_regset iga1_cle266_fifo_threshold_select[] = { + { VGA_SEQ_I, 0x16, 0, 5 } +}; + +/* K8M800 or later IGA1 FIFO Threshold Select */ +static struct vga_regset iga1_k8m800_fifo_threshold_select[] = { + { VGA_SEQ_I, 0x16, 0, 5 }, + { VGA_SEQ_I, 0x16, 7, 7 } +}; + +/* CLE266 and KM400 IGA2 FIFO Threshold Select */ +static struct vga_regset iga2_cle266_fifo_threshold_select[] = { + { VGA_CRT_IC, 0x68, 0, 3 } +}; + +/* K8M800 or later IGA2 FIFO Threshold Select */ +static struct vga_regset iga2_k8m800_fifo_threshold_select[] = { + { VGA_CRT_IC, 0x68, 0, 3 }, + { VGA_CRT_IC, 0x95, 4, 6 } +}; + +/* CLE266 and KM400 IGA1 FIFO High Threshold Select */ +static struct vga_regset iga1_cle266_fifo_high_threshold_select[] = { + { VGA_SEQ_I, 0x18, 0, 5 } +}; + +/* K8M800 or later IGA1 FIFO High Threshold Select */ +static struct vga_regset iga1_k8m800_fifo_high_threshold_select[] = { + { VGA_SEQ_I, 0x18, 0, 5 }, + { VGA_SEQ_I, 0x18, 7, 7 } +}; + +/* IGA2 FIFO High Threshold Select */ +static struct vga_regset iga2_fifo_high_threshold_select[] = { + { VGA_CRT_IC, 0x92, 0, 3 }, + { VGA_CRT_IC, 0x95, 0, 2 } +}; + +/* CLE266 and KM400 IGA1 FIFO Display Queue Expire */ +static struct vga_regset iga1_cle266_display_queue_expire_num[] = { + { VGA_SEQ_I, 0x22, 0, 4 } +}; + +/* K8M800 and later IGA1 FIFO Display Queue Expire */ +static struct vga_regset iga1_k8m800_display_queue_expire_num[] = { + { VGA_SEQ_I, 0x22, 0, 4 } +}; + +/* IGA2 FIFO display queue expire */ +static struct vga_regset iga2_display_queue_expire_num[] = { + { VGA_CRT_IC, 0x94, 0, 6 } +}; + +/***********************************************/ +/************* Offset register *****************/ +/***********************************************/ + +/* IGA1 Offset Register */ +static struct vga_regset iga1_offset[] = { + { VGA_CRT_IC, 0x13, 0, 7 }, + { VGA_CRT_IC, 0x35, 5, 7 } +}; + +/* IGA2 Offset Register */ +static struct vga_regset iga2_offset[] = { + { VGA_CRT_IC, 0x66, 0, 7 }, + { VGA_CRT_IC, 0x67, 0, 1 }, + { VGA_CRT_IC, 0x71, 7, 7 } +}; + +/***********************************************/ +/*********** Fetch count register **************/ +/***********************************************/ + +/* IGA1 Fetch Count Register */ +static struct vga_regset iga1_fetch_count[] = { + { VGA_SEQ_I, 0x1C, 0, 7 }, + { VGA_SEQ_I, 0x1D, 0, 1 } +}; + +/* IGA2 Fetch Count Register */ +static struct vga_regset iga2_fetch_count[] = { + { VGA_CRT_IC, 0x65, 0, 7 }, + { VGA_CRT_IC, 0x67, 2, 3 } +}; + +/************************************************/ +/*********** IGA Scaling Factor Registers *******/ +/************************************************/ +#define LCD_HOR_SCALE_FACTOR_FORMULA(x, y) (((x - 1) * 4096) / (y - 1)) +#define LCD_VER_SCALE_FACTOR_FORMULA(x, y) (((x - 1) * 2048) / (y - 1)) + +static struct vga_regset lcd_hor_scaling[] = { + { VGA_CRT_IC, 0x9F, 0, 1 }, + { VGA_CRT_IC, 0x77, 0, 7 }, + { VGA_CRT_IC, 0x79, 4, 5 } +}; + +static struct vga_regset lcd_ver_scaling[] = { + { VGA_CRT_IC, 0x79, 3, 3 }, + { VGA_CRT_IC, 0x78, 0, 7 }, + { VGA_CRT_IC, 0x79, 6, 7 } +}; + +/***********************************************/ +/*********** CRTC timing register **************/ +/***********************************************/ + +/***************************************************/ +/* Definition IGA1 Design Method of CRTC Registers */ +/***************************************************/ +#define IGA1_HOR_TOTAL_FORMULA(x) (x / 8) - 5 +#define IGA1_HOR_ADDR_FORMULA(x) (x / 8) - 1 +#define IGA1_HOR_BLANK_START_FORMULA(x) (x / 8) - 1 +#define IGA1_HOR_BLANK_END_FORMULA(x) (x / 8) - 1 +#define IGA1_HOR_SYNC_START_FORMULA(x) (x / 8) - 1 +#define IGA1_HOR_SYNC_END_FORMULA(x) (x / 8) - 1 + +#define IGA1_VER_TOTAL_FORMULA(x) (x - 2) +#define IGA1_VER_ADDR_FORMULA(x) (x - 1) +#define IGA1_VER_BLANK_START_FORMULA(x) (x - 1) +#define IGA1_VER_BLANK_END_FORMULA(x) (x - 1) +#define IGA1_VER_SYNC_START_FORMULA(x) (x - 1) +#define IGA1_VER_SYNC_END_FORMULA(x) (x - 1) + +/***************************************************/ +/* Definition IGA2 Design Method of CRTC Registers */ +/***************************************************/ +#define IGA2_HOR_TOTAL_FORMULA(x) (x - 1) +#define IGA2_HOR_ADDR_FORMULA(x) (x - 1) +#define IGA2_HOR_BLANK_START_FORMULA(x) (x - 1) +#define IGA2_HOR_BLANK_END_FORMULA(x) (x - 1) +#define IGA2_HOR_SYNC_START_FORMULA(x) (x - 1) +#define IGA2_HOR_SYNC_END_FORMULA(x) (x - 1) + +#define IGA2_VER_TOTAL_FORMULA(x) (x - 1) +#define IGA2_VER_ADDR_FORMULA(x) (x - 1) +#define IGA2_VER_BLANK_START_FORMULA(x) (x - 1) +#define IGA2_VER_BLANK_END_FORMULA(x) (x - 1) +#define IGA2_VER_SYNC_START_FORMULA(x) (x - 1) +#define IGA2_VER_SYNC_END_FORMULA(x) (x - 1) + +/****************************************************************/ +/* Definition IGA1 Design Method of CRTC Pixel timing Registers */ +/****************************************************************/ +#define IGA1_PIXELTIMING_HOR_TOTAL_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_HOR_ADDR_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_HOR_BLANK_START_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_HOR_BLANK_END_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_HOR_SYNC_START_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_HOR_SYNC_END_FORMULA(x) (x - 1) + +#define IGA1_PIXELTIMING_VER_TOTAL_FORMULA(x) (x - 2) +#define IGA1_PIXELTIMING_VER_ADDR_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_VER_BLANK_START_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_VER_BLANK_END_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_VER_SYNC_START_FORMULA(x) (x - 1) +#define IGA1_PIXELTIMING_VER_SYNC_END_FORMULA(x) (x - 1) + +#define IGA1_PIXELTIMING_HVSYNC_OFFSET_END_FORMULA(x, y) \ + ((x / 2) - 1 - (x - y)) + +/************************************************/ +/* Define IGA1 Display Timing */ +/************************************************/ + +/* IGA1 Horizontal Total */ +static struct vga_regset iga1_hor_total[] = { + { VGA_CRT_IC, 0x00, 0, 7 }, + { VGA_CRT_IC, 0x36, 3, 3 } +}; + +/* IGA1 Horizontal Addressable Video */ +static struct vga_regset iga1_hor_addr[] = { + { VGA_CRT_IC, 0x01, 0, 7 }, + { VGA_CRT_IC, 0x45, 1, 1 } +}; + +/* IGA1 Horizontal Blank Start */ +static struct vga_regset iga1_hor_blank_start[] = { + { VGA_CRT_IC, 0x02, 0, 7 }, + { VGA_CRT_IC, 0x45, 2, 2 } +}; + +/* IGA1 Horizontal Blank End */ +static struct vga_regset iga1_hor_blank_end[] = { + { VGA_CRT_IC, 0x03, 0, 4 }, + { VGA_CRT_IC, 0x05, 7, 7 }, + { VGA_CRT_IC, 0x33, 5, 5 } +}; + +/* IGA1 Horizontal Sync Start */ +static struct vga_regset iga1_hor_sync_start[] = { + { VGA_CRT_IC, 0x04, 0, 7 }, + { VGA_CRT_IC, 0x33, 4, 4 } +}; + +/* IGA1 Horizontal Sync End */ +static struct vga_regset iga1_hor_sync_end[] = { + { VGA_CRT_IC, 0x05, 0, 4 } +}; + +/* IGA1 Vertical Total */ +static struct vga_regset iga1_ver_total[] = { + { VGA_CRT_IC, 0x06, 0, 7 }, + { VGA_CRT_IC, 0x07, 0, 0 }, + { VGA_CRT_IC, 0x07, 5, 5 }, + { VGA_CRT_IC, 0x35, 0, 0 } +}; + +/* IGA1 Vertical Addressable Video */ +static struct vga_regset iga1_ver_addr[] = { + { VGA_CRT_IC, 0x12, 0, 7 }, + { VGA_CRT_IC, 0x07, 1, 1 }, + { VGA_CRT_IC, 0x07, 6, 6 }, + { VGA_CRT_IC, 0x35, 2, 2 } +}; + +/* IGA1 Vertical Blank Start */ +static struct vga_regset iga1_ver_blank_start[] = { + { VGA_CRT_IC, 0x15, 0, 7 }, + { VGA_CRT_IC, 0x07, 3, 3 }, + { VGA_CRT_IC, 0x09, 5, 5 }, + { VGA_CRT_IC, 0x35, 3, 3 } +}; + +/* IGA1 Vertical Blank End */ +static struct vga_regset iga1_ver_blank_end[] = { + { VGA_CRT_IC, 0x16, 0, 7 } +}; + +/* IGA1 Vertical Sync Start */ +static struct vga_regset iga1_ver_sync_start[] = { + { VGA_CRT_IC, 0x10, 0, 7 }, + { VGA_CRT_IC, 0x07, 2, 2 }, + { VGA_CRT_IC, 0x07, 7, 7 }, + { VGA_CRT_IC, 0x35, 1, 1 } +}; + +/* IGA1 Vertical Sync End */ +static struct vga_regset iga1_ver_sync_end[] = { + { VGA_CRT_IC, 0x11, 0, 3 } +}; + +/************************************************/ +/* Define IGA2 Display Timing */ +/************************************************/ + +/* IGA2 Horizontal Total */ +static struct vga_regset iga2_hor_total[] = { + { VGA_CRT_IC, 0x50, 0, 7 }, + { VGA_CRT_IC, 0x55, 0, 3 } +}; + +/* IGA2 Horizontal Addressable Video */ +static struct vga_regset iga2_hor_addr[] = { + { VGA_CRT_IC, 0x51, 0, 7 }, + { VGA_CRT_IC, 0x55, 4, 6 }, + { VGA_CRT_IC, 0x55, 7, 7 } +}; + +/* IGA2 Horizontal Blank Start */ +static struct vga_regset iga2_hor_blank_start[] = { + { VGA_CRT_IC, 0x52, 0, 7 }, + { VGA_CRT_IC, 0x54, 0, 2 }, + { VGA_CRT_IC, 0x6B, 0, 0 } +}; + +/* IGA2 Horizontal Blank End */ +static struct vga_regset iga2_hor_blank_end[] = { + { VGA_CRT_IC, 0x53, 0, 7 }, + { VGA_CRT_IC, 0x54, 3, 5 }, + { VGA_CRT_IC, 0x5D, 6, 6 } +}; + +/* IGA2 Horizontal Sync Start */ +static struct vga_regset iga2_hor_sync_start[] = { + { VGA_CRT_IC, 0x56, 0, 7 }, + { VGA_CRT_IC, 0x54, 6, 7 }, + { VGA_CRT_IC, 0x5C, 7, 7 }, + { VGA_CRT_IC, 0x5D, 7, 7 } +}; + +/* IGA2 Horizontal Sync End */ +static struct vga_regset iga2_hor_sync_end[] = { + { VGA_CRT_IC, 0x57, 0, 7 }, + { VGA_CRT_IC, 0x5C, 6, 6 } +}; + +/* IGA2 Vertical Total */ +static struct vga_regset iga2_ver_total[] = { + { VGA_CRT_IC, 0x58, 0, 7 }, + { VGA_CRT_IC, 0x5D, 0, 2 } +}; + +/* IGA2 Vertical Addressable Video */ +static struct vga_regset iga2_ver_addr[] = { + { VGA_CRT_IC, 0x59, 0, 7 }, + { VGA_CRT_IC, 0x5D, 3, 5 } +}; + +/* IGA2 Vertical Blank Start */ +static struct vga_regset iga2_ver_blank_start[] = { + { VGA_CRT_IC, 0x5A, 0, 7 }, + { VGA_CRT_IC, 0x5C, 0, 2 } +}; + +/* IGA2 Vertical Blank End */ +static struct vga_regset iga2_ver_blank_end[] = { + { VGA_CRT_IC, 0x5B, 0, 7 }, + { VGA_CRT_IC, 0x5C, 3, 5 } +}; + +/* IGA2 Vertical Sync Start */ +static struct vga_regset iga2_ver_sync_start[] = { + { VGA_CRT_IC, 0x5E, 0, 7 }, + { VGA_CRT_IC, 0x5F, 5, 7 } +}; + +/* IGA2 Vertical Sync End */ +static struct vga_regset iga2_ver_sync_end[] = { + { VGA_CRT_IC, 0x5F, 0, 4 } +}; + +/* IGA1 pixel timing Registers */ +#define IGA1_PIX_H_TOTAL_REG 0x8400 /* [15:0] */ +#define IGA1_PIX_H_ADDR_REG 0x8400 /* [31:16] */ +#define IGA1_PIX_H_BNK_ST_REG 0x8404 /* [15:0] */ +#define IGA1_PIX_H_BNK_END_REG 0x8404 /* [31:16] */ +#define IGA1_PIX_H_SYNC_ST_REG 0x8408 /* [15:0] */ +#define IGA1_PIX_H_SYNC_END_REG 0x8408 /* [31:16] */ +#define IGA1_PIX_V_TOTAL_REG 0x8424 /* [10:0] */ +#define IGA1_PIX_V_ADDR_REG 0x8424 /* [26:16] */ +#define IGA1_PIX_V_BNK_ST_REG 0x8428 /* [10:0] */ +#define IGA1_PIX_V_BNK_END_REG 0x8428 /* [26:16] */ +#define IGA1_PIX_V_SYNC_ST_REG 0x842C /* [10:0] */ +#define IGA1_PIX_V_SYNC_END_REG 0x842C /* [15:12] */ +#define IGA1_PIX_HALF_LINE_REG 0x8434 /* [15:0] */ + +#define IGA1_PIX_H_TOTAL_MASK 0x0000FFFF /* [15:0] */ +#define IGA1_PIX_H_ADDR_MASK 0xFFFF0000 /* [31:16] */ +#define IGA1_PIX_H_BNK_ST_MASK 0x0000FFFF /* [15:0] */ +#define IGA1_PIX_H_BNK_END_MASK 0xFFFF0000 /* [31:16] */ +#define IGA1_PIX_H_SYNC_ST_MASK 0x0000FFFF /* [15:0] */ +#define IGA1_PIX_H_SYNC_END_MASK 0xFFFF0000 /* [31:16] */ +#define IGA1_PIX_V_TOTAL_MASK 0x000007FF /* [10:0] */ +#define IGA1_PIX_V_ADDR_MASK 0x07FF0000 /* [26:16] */ +#define IGA1_PIX_V_BNK_ST_MASK 0x000007FF /* [10:0] */ +#define IGA1_PIX_V_BNK_END_MASK 0x07FF0000 /* [26:16] */ +#define IGA1_PIX_V_SYNC_ST_MASK 0x000007FF /* [10:0] */ +#define IGA1_PIX_V_SYNC_END_MASK 0x0000F000 /* [15:12] */ +#define IGA1_PIX_HALF_LINE_MASK 0x0000FFFF /* [15:0] */ + +#endif diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c new file mode 100644 index 000000000000..2f5155a193f4 --- /dev/null +++ b/drivers/gpu/drm/via/via_drv.c @@ -0,0 +1,340 @@ +/* + * Copyright © 2019-2021 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/pci.h> + +#include <drm/drm_aperture.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_prime.h> + +#include <drm/ttm/ttm_bo.h> + +#include <uapi/drm/via_drm.h> + +#include "via_drv.h" + + +/* + * For now, this device driver will be disabled, unless the + * user decides to enable it. + */ +int via_modeset = 0; + +MODULE_PARM_DESC(modeset, "Enable DRM device driver " + "(Default: Disabled, " + "0 = Disabled," + "1 = Enabled)"); +module_param_named(modeset, via_modeset, int, 0400); + +static int via_driver_open(struct drm_device *dev, + struct drm_file *file_priv) +{ + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_driver_postclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static void via_driver_lastclose(struct drm_device *dev) +{ + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + drm_fb_helper_lastclose(dev); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static int via_driver_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + struct ttm_buffer_object *ttm_bo; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_bo *bo; + uint32_t handle, pitch; + uint64_t size; + int ret; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + /* + * Calculate the parameters for the dumb buffer. + */ + pitch = args->width * ((args->bpp + 7) >> 3); + size = pitch * args->height; + + ret = via_bo_create(dev, &dev_priv->bdev, size, + ttm_bo_type_device, TTM_PL_VRAM, false, &bo); + if (ret) { + goto exit; + } + + ttm_bo = &bo->ttm_bo; + + ret = drm_gem_handle_create(file_priv, &ttm_bo->base, &handle); + drm_gem_object_put(&ttm_bo->base); + if (ret) { + via_bo_destroy(bo, false); + goto exit; + } + + args->handle = handle; + args->pitch = pitch; + args->size = size; +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +static int via_driver_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + struct via_bo *bo; + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + gem = drm_gem_object_lookup(file_priv, handle); + if (!gem) { + ret = -ENOENT; + goto exit; + } + + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + bo = to_ttm_bo(ttm_bo); + *offset = drm_vma_node_offset_addr(&ttm_bo->base.vma_node); + + drm_gem_object_put(gem); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +static const struct drm_ioctl_desc via_driver_ioctls[] = { + DRM_IOCTL_DEF_DRV(VIA_ALLOCMEM, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_FREEMEM, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_AGP_INIT, drm_invalid_op, DRM_AUTH | DRM_MASTER), + DRM_IOCTL_DEF_DRV(VIA_FB_INIT, drm_invalid_op, DRM_AUTH | DRM_MASTER), + DRM_IOCTL_DEF_DRV(VIA_MAP_INIT, drm_invalid_op, DRM_AUTH | DRM_MASTER), + DRM_IOCTL_DEF_DRV(VIA_DEC_FUTEX, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_DMA_INIT, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_CMDBUFFER, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_FLUSH, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_PCICMD, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_CMDBUF_SIZE, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_WAIT_IRQ, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_DMA_BLIT, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_BLIT_SYNC, drm_invalid_op, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_GEM_ALLOC, via_gem_alloc_ioctl, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_GEM_MMAP, via_gem_mmap_ioctl, DRM_AUTH), +}; + +static const struct file_operations via_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_gem_mmap, + .poll = drm_poll, + .llseek = noop_llseek, +}; + +static struct drm_driver via_driver = { + .open = via_driver_open, + .postclose = via_driver_postclose, + .lastclose = via_driver_lastclose, + + .dumb_create = via_driver_dumb_create, + .dumb_map_offset = via_driver_dumb_map_offset, + + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + + .driver_features = DRIVER_GEM | + DRIVER_MODESET | + DRIVER_ATOMIC, + + .ioctls = via_driver_ioctls, + .num_ioctls = ARRAY_SIZE(via_driver_ioctls), + + .fops = &via_driver_fops, +}; + +static struct pci_device_id via_pci_table[] = { + {0x1106, 0x3122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x7205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3344, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3118, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3343, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3230, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x3157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x1122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x5122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0x1106, 0x7122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0, 0, 0} +}; + +MODULE_DEVICE_TABLE(pci, via_pci_table); + +static int via_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct drm_device *dev; + struct via_drm_priv *dev_priv; + int ret; + + dev_info(&pdev->dev, "Entered %s.\n", __func__); + + ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, + &via_driver); + if (ret) { + goto exit; + } + + ret = pci_enable_device(pdev); + if (ret) { + goto exit; + } + + dev_priv = devm_drm_dev_alloc(&pdev->dev, + &via_driver, + struct via_drm_priv, + dev); + if (IS_ERR(dev_priv)) { + ret = PTR_ERR(dev_priv); + goto exit; + } + + dev = &dev_priv->dev; + + pci_set_drvdata(pdev, dev); + + ret = via_drm_init(dev); + if (ret) { + goto error_disable_pci; + } + + ret = drm_dev_register(dev, ent->driver_data); + if (ret) { + goto error_disable_pci; + } + + drm_fbdev_generic_setup(dev, 32); + goto exit; +error_disable_pci: + pci_disable_device(pdev); +exit: + dev_info(&pdev->dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + dev_info(&pdev->dev, "Entered %s.\n", __func__); + + via_drm_fini(dev); + drm_dev_unregister(dev); + + dev_info(&pdev->dev, "Exiting %s.\n", __func__); +} + +static const struct dev_pm_ops via_dev_pm_ops = { + .suspend = via_dev_pm_ops_suspend, + .resume = via_dev_pm_ops_resume, +}; + +static struct pci_driver via_pci_driver = { + .name = DRIVER_NAME, + .id_table = via_pci_table, + .probe = via_pci_probe, + .remove = via_pci_remove, + .driver.pm = &via_dev_pm_ops, +}; + +static int __init via_init(void) +{ + int ret = 0; + + if ((via_modeset == -1) && + (drm_firmware_drivers_only())) { + via_modeset = 0; + } + + if (!via_modeset) { + ret = -EINVAL; + goto exit; + } + + ret = pci_register_driver(&via_pci_driver); + +exit: + return ret; +} + +static void __exit via_exit(void) +{ + if (!via_modeset) { + return; + } + + pci_unregister_driver(&via_pci_driver); +} + +module_init(via_init); +module_exit(via_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h new file mode 100644 index 000000000000..64ff475c1305 --- /dev/null +++ b/drivers/gpu/drm/via/via_drv.h @@ -0,0 +1,400 @@ +/* + * Copyright © 2019 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#ifndef _VIA_DRV_H +#define _VIA_DRV_H + + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> +#include <drm/drm_plane.h> + +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> + +#include "via_crtc_hw.h" +#include "via_regs.h" + + +#define DRIVER_MAJOR 3 +#define DRIVER_MINOR 6 +#define DRIVER_PATCHLEVEL 13 +#define DRIVER_NAME "via" +#define DRIVER_DESC "OpenChrome DRM for VIA Technologies Chrome" +#define DRIVER_DATE "20240115" +#define DRIVER_AUTHOR "OpenChrome Project" + + +#define VIA_TTM_PL_NUM 2 + +#define VIA_MAX_CRTC 2 + +#define VIA_CURSOR_SIZE 64 + +#define VIA_MM_ALIGN_SIZE 16 + + +#define CLE266_REVISION_AX 0x0A +#define CLE266_REVISION_CX 0x0C + +#define CX700_REVISION_700 0x0 +#define CX700_REVISION_700M 0x1 +#define CX700_REVISION_700M2 0x2 + +#define VIA_MEM_NONE 0x00 +#define VIA_MEM_SDR66 0x01 +#define VIA_MEM_SDR100 0x02 +#define VIA_MEM_SDR133 0x03 +#define VIA_MEM_DDR_200 0x04 +#define VIA_MEM_DDR_266 0x05 +#define VIA_MEM_DDR_333 0x06 +#define VIA_MEM_DDR_400 0x07 +#define VIA_MEM_DDR2_400 0x08 +#define VIA_MEM_DDR2_533 0x09 +#define VIA_MEM_DDR2_667 0x0A +#define VIA_MEM_DDR2_800 0x0B +#define VIA_MEM_DDR2_1066 0x0C +#define VIA_MEM_DDR3_533 0x0D +#define VIA_MEM_DDR3_667 0x0E +#define VIA_MEM_DDR3_800 0x0F +#define VIA_MEM_DDR3_1066 0x10 +#define VIA_MEM_DDR3_1333 0x11 +#define VIA_MEM_DDR3_1600 0x12 + +/* IGA Scaling disable */ +#define VIA_NO_SCALING 0 + +/* IGA Scaling down */ +#define VIA_HOR_SHRINK BIT(0) +#define VIA_VER_SHRINK BIT(1) +#define VIA_SHRINK (BIT(0) | BIT(1)) + +/* IGA Scaling up */ +#define VIA_HOR_EXPAND BIT(2) +#define VIA_VER_EXPAND BIT(3) +#define VIA_EXPAND (BIT(2) | BIT(3)) + +/* Define IGA Scaling up/down status : Horizontal or Vertical */ +/* Is IGA Hor scaling up/down status */ +#define HOR_SCALE BIT(0) +/* Is IGA Ver scaling up/down status */ +#define VER_SCALE BIT(1) +/* Is IGA Hor and Ver scaling up/down status */ +#define HOR_VER_SCALE (BIT(0) | BIT(1)) + +#define VIA_I2C_NONE 0x0 +#define VIA_I2C_BUS1 BIT(0) +#define VIA_I2C_BUS2 BIT(1) +#define VIA_I2C_BUS3 BIT(2) +#define VIA_I2C_BUS4 BIT(3) +#define VIA_I2C_BUS5 BIT(4) + +#define VIA_DI_PORT_NONE 0x0 +#define VIA_DI_PORT_DIP0 BIT(0) +#define VIA_DI_PORT_DIP1 BIT(1) +#define VIA_DI_PORT_DVP0 BIT(2) +#define VIA_DI_PORT_DVP1 BIT(3) +#define VIA_DI_PORT_DFPL BIT(4) +#define VIA_DI_PORT_FPDPLOW BIT(4) +#define VIA_DI_PORT_DFPH BIT(5) +#define VIA_DI_PORT_FPDPHIGH BIT(5) +#define VIA_DI_PORT_DFP BIT(6) +#define VIA_DI_PORT_LVDS1 BIT(7) +#define VIA_DI_PORT_TMDS BIT(7) +#define VIA_DI_PORT_LVDS2 BIT(8) + +/* External TMDS (DVI) Transmitter Type */ +#define VIA_TMDS_NONE 0x0 +#define VIA_TMDS_VT1632 BIT(0) +#define VIA_TMDS_SII164 BIT(1) + + +struct via_lvds_info { + u32 x; + u32 y; +}; + +struct via_crtc { + struct drm_crtc base; + struct crtc_timings pixel_timings; + struct crtc_timings timings; + struct vga_registers display_queue; + struct vga_registers high_threshold; + struct vga_registers threshold; + struct vga_registers fifo_depth; + struct vga_registers offset; + struct vga_registers fetch; + int scaling_mode; + uint32_t index; +}; + +struct via_connector { + struct drm_connector base; + u32 i2c_bus; + struct list_head props; + uint32_t flags; +}; + +struct via_encoder { + struct drm_encoder base; + u32 i2c_bus; + u32 di_port; + struct via_connector cons[]; +}; + +struct via_bo { + struct ttm_buffer_object ttm_bo; + struct ttm_bo_kmap_obj kmap; + struct ttm_placement placement; + struct ttm_place placements[VIA_TTM_PL_NUM]; +}; + +struct via_drm_priv { + struct drm_device dev; + + struct ttm_device bdev; + + /* Set this flag for ttm_bo_device_init. */ + bool need_dma32; + + int revision; + + u8 vram_type; + resource_size_t vram_start; + resource_size_t vram_size; + int vram_mtrr; + + resource_size_t mmio_base; + resource_size_t mmio_size; + void __iomem *mmio; + + bool spread_spectrum; + + /* + * Frame Buffer Size Control register (SR14) needs to be saved and + * restored upon standby resume or can lead to a display corruption + * issue. These registers are only available on VX800, VX855, and + * VX900 chipsets. This bug was observed on VIA EPIA-M830 mainboard. + */ + uint8_t saved_sr14; + + /* + * GTI registers (SR66 through SR6F) need to be saved and restored + * upon standby resume or can lead to a display corruption issue. + * These registers are only available on VX800, VX855, and VX900 + * chipsets. This bug was observed on VIA EPIA-M830 mainboard. + */ + uint8_t saved_sr66; + uint8_t saved_sr67; + uint8_t saved_sr68; + uint8_t saved_sr69; + uint8_t saved_sr6a; + uint8_t saved_sr6b; + uint8_t saved_sr6c; + uint8_t saved_sr6d; + uint8_t saved_sr6e; + uint8_t saved_sr6f; + + /* 3X5.3B through 3X5.3F are scratch pad registers. + * They are important for FP detection. */ + uint8_t saved_cr3b; + uint8_t saved_cr3c; + uint8_t saved_cr3d; + uint8_t saved_cr3e; + uint8_t saved_cr3f; + + /* + * Due to the way VIA NanoBook reference design implemented + * the pin strapping settings, DRM needs to ignore them for + * FP and DVI to be properly detected. + */ + bool is_via_nanobook; + + /* + * Quanta IL1 netbook has its FP connected to DVP1 + * rather than LVDS, hence, a special flag register + * is needed for properly controlling its FP. + */ + bool is_quanta_il1; + + /* + * Samsung NC20 netbook has its FP connected to LVDS2 + * rather than the more logical LVDS1, hence, a special + * flag register is needed for properly controlling its + * FP. + */ + bool is_samsung_nc20; + + bool dac_presence; + u32 dac_i2c_bus; + + bool int_tmds_presence; + u32 int_tmds_di_port; + u32 int_tmds_i2c_bus; + + bool ext_tmds_presence; + u32 ext_tmds_di_port; + u32 ext_tmds_i2c_bus; + u32 ext_tmds_transmitter; + + bool int_fp1_presence; + u32 int_fp1_di_port; + u32 int_fp1_i2c_bus; + + bool int_fp2_presence; + u32 int_fp2_di_port; + u32 int_fp2_i2c_bus; + + /* Keeping track of the number of DVI connectors. */ + u32 number_dvi; + + /* Keeping track of the number of FP (Flat Panel) connectors. */ + u32 number_fp; + + u32 mapped_i2c_bus; +}; + + +/* + * Shortcut for using container_of macro. + */ +#define to_via_drm_priv(x) container_of(x, struct via_drm_priv, dev) +#define to_ttm_bo(x) container_of(x, struct via_bo, ttm_bo) + + +/* VIA MMIO register access */ +#define VIA_BASE ((dev_priv->mmio)) + +#define VIA_READ(reg) ioread32(VIA_BASE + reg) +#define VIA_WRITE(reg, val) iowrite32(val, VIA_BASE + reg) +#define VIA_READ8(reg) ioread8(VIA_BASE + reg) +#define VIA_WRITE8(reg, val) iowrite8(val, VIA_BASE + reg) + +#define VIA_WRITE_MASK(reg, data, mask) \ + VIA_WRITE(reg, (data & mask) | (VIA_READ(reg) & ~mask)) \ + +#define VGABASE (VIA_BASE+VIA_MMIO_VGABASE) + + +/* via_connector.c */ +void via_connector_destroy(struct drm_connector *connector); + +/* via_crtc.c */ +void via_load_crtc_pixel_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode); +int via_crtc_init(struct via_drm_priv *dev_priv, uint32_t index); +void via_dac_probe(struct drm_device *dev); +bool via_vt1632_probe(struct drm_device *dev, + struct i2c_adapter *i2c_bus); +bool via_sii164_probe(struct drm_device *dev, + struct i2c_adapter *i2c_bus); +void via_ext_dvi_probe(struct drm_device *dev); +void via_tmds_probe(struct drm_device *dev); +void via_lvds_probe(struct drm_device *dev); +void via_hdmi_init(struct drm_device *dev, u32 di_port); +void via_dac_init(struct drm_device *dev); +void via_vt1632_init(struct drm_device *dev); +void via_sii164_init(struct drm_device *dev); +void via_ext_dvi_init(struct drm_device *dev); +void via_tmds_init(struct drm_device *dev); +void via_lvds_init(struct drm_device *dev); + +/* via_cursor.c */ +extern const struct drm_plane_helper_funcs via_cursor_drm_plane_helper_funcs; +extern const struct drm_plane_funcs via_cursor_drm_plane_funcs; +extern const uint32_t via_cursor_formats[]; +extern const unsigned int via_cursor_formats_size; + +/* via_encoder.c */ +void via_encoder_destroy(struct drm_encoder *encoder); + +/* via_i2c.c */ +struct i2c_adapter *via_find_ddc_bus(int port); +void via_i2c_readbytes(struct i2c_adapter *adapter, + u8 slave_addr, char offset, + u8 *buffer, unsigned int size); +void via_i2c_writebytes(struct i2c_adapter *adapter, + u8 slave_addr, char offset, + u8 *data, unsigned int size); +void via_i2c_reg_init(struct via_drm_priv *dev_priv); +int via_i2c_init(struct drm_device *dev); +void via_i2c_exit(void); + +/* via_init.c */ +int via_drm_init(struct drm_device *dev); +void via_drm_fini(struct drm_device *dev); + +/* via_ioctl.c */ +int via_gem_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int via_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* via_object.c */ +void via_ttm_domain_to_placement(struct via_bo *bo, uint32_t ttm_domain); +void via_ttm_bo_destroy(struct ttm_buffer_object *tbo); +int via_bo_pin(struct via_bo *bo, uint32_t ttm_domain); +void via_bo_unpin(struct via_bo *bo); +int via_bo_create(struct drm_device *dev, struct ttm_device *bdev, + uint64_t size, enum ttm_bo_type type, + uint32_t ttm_domain, bool kmap, + struct via_bo **bo_ptr); +void via_bo_destroy(struct via_bo *bo, bool kmap); +int via_mm_init(struct drm_device *dev); +void via_mm_fini(struct drm_device *dev); + +/* via_pll.c */ +u32 via_get_clk_value(struct drm_device *dev, u32 clk); +void via_set_vclock(struct drm_crtc *crtc, u32 clk); + +/* via_pm.c */ +int via_dev_pm_ops_suspend(struct device *dev); +int via_dev_pm_ops_resume(struct device *dev); + +/* via_ttm.c */ +extern struct ttm_device_funcs via_bo_driver; + +/* via_tx.c */ +void via_transmitter_io_pad_state(struct drm_device *dev, + uint32_t di_port, + bool io_pad_on); +void via_output_enable(struct drm_device *dev, + uint32_t di_port, bool output_enable); +void via_clock_source(struct drm_device *dev, + uint32_t di_port, bool clock_source); +void via_transmitter_clock_drive_strength(struct drm_device *dev, + u32 di_port, + u8 drive_strength); +void via_transmitter_data_drive_strength(struct drm_device *dev, + u32 di_port, + u8 drive_strength); +void via_transmitter_display_source(struct drm_device *dev, + u32 di_port, int index); + +#endif /* _VIA_DRV_H */ diff --git a/drivers/gpu/drm/via/via_encoder.c b/drivers/gpu/drm/via/via_encoder.c new file mode 100644 index 000000000000..8782329e2740 --- /dev/null +++ b/drivers/gpu/drm/via/via_encoder.c @@ -0,0 +1,38 @@ +/* + * Copyright 2012 James Simmons. All Rights Reserved. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * James Simmons <jsimmons@infradead.org> + */ + +#include <drm/drm_encoder.h> + +#include "via_drv.h" + + +void via_encoder_destroy(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, struct via_encoder, base); + + drm_encoder_cleanup(encoder); + kfree(enc); +} diff --git a/drivers/gpu/drm/via/via_hdmi.c b/drivers/gpu/drm/via/via_hdmi.c new file mode 100644 index 000000000000..b01552e6b49e --- /dev/null +++ b/drivers/gpu/drm/via/via_hdmi.c @@ -0,0 +1,664 @@ +/* + * Copyright 2013 James Simmons. All Rights Reserved. + * Copyright 1998-2012 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2012 S3 Graphics, Inc. All Rights Reserved. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/delay.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> + +#include "via_drv.h" + + +#define HDMI_AUDIO_ENABLED BIT(0) + +int via_hdmi_audio = 0; + +MODULE_PARM_DESC(audio, "HDMI Audio enable (1 = enable)"); +module_param_named(audio, via_hdmi_audio, int, 0444); + +/* + * Routines for controlling stuff on the HDMI port + */ +static const struct drm_encoder_funcs via_hdmi_enc_funcs = { + .destroy = via_encoder_destroy, +}; + +static void via_hdmi_enc_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + switch (mode) { + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_OFF: + /* disable HDMI */ + VIA_WRITE_MASK(0xC280, 0x0, 0x2); + break; + + case DRM_MODE_DPMS_ON: + default: + /* enable band gap */ + VIA_WRITE_MASK(0xC740, BIT(0), BIT(0)); + /* enable video */ + VIA_WRITE_MASK(0xC640, BIT(3), BIT(3)); + /* enable HDMI */ + VIA_WRITE_MASK(0xC280, BIT(1), BIT(1)); + break; + } +} + +static bool via_hdmi_enc_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + uint32_t panelHSyncTime = 0, panelHBlankStart = 0, newHBlankStart = 0; + uint32_t panelVSyncTime = 0, panelVBlankStart = 0, newVBlankStart = 0; + uint32_t left_border = 0, right_border = 0; + uint32_t top_border = 0, bottom_border = 0; + + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { + /* when interlace mode, we should consider halve vertical + * timings. */ + panelHSyncTime = adjusted_mode->hsync_end - + adjusted_mode->hsync_start; + panelVSyncTime = adjusted_mode->vsync_end / 2 - + adjusted_mode->vsync_start / 2; + panelHBlankStart = adjusted_mode->hdisplay; + panelVBlankStart = adjusted_mode->vdisplay / 2; + newHBlankStart = adjusted_mode->hdisplay - left_border; + newVBlankStart = adjusted_mode->vdisplay / 2 - top_border; + + adjusted_mode->hdisplay = + adjusted_mode->hdisplay - left_border - right_border; + adjusted_mode->hsync_start = + (adjusted_mode->hsync_start - panelHBlankStart) + + newHBlankStart; + adjusted_mode->hsync_end = + adjusted_mode->hsync_start + panelHSyncTime; + + adjusted_mode->vdisplay = adjusted_mode->vdisplay / 2 - + top_border - bottom_border; + adjusted_mode->vsync_start = + (adjusted_mode->vsync_start / 2 - panelVBlankStart) + + newVBlankStart; + adjusted_mode->vsync_end = + adjusted_mode->vsync_start + panelVSyncTime; + } else { + panelHSyncTime = + adjusted_mode->hsync_end - adjusted_mode->hsync_start; + panelVSyncTime = + adjusted_mode->vsync_end - adjusted_mode->vsync_start; + panelHBlankStart = adjusted_mode->hdisplay; + panelVBlankStart = adjusted_mode->vdisplay; + newHBlankStart = adjusted_mode->hdisplay - left_border; + newVBlankStart = adjusted_mode->vdisplay - top_border; + + adjusted_mode->hdisplay = + adjusted_mode->hdisplay - left_border - right_border; + adjusted_mode->hsync_start = + (adjusted_mode->hsync_start - panelHBlankStart) + + newHBlankStart; + adjusted_mode->hsync_end = + adjusted_mode->hsync_start + panelHSyncTime; + + adjusted_mode->vdisplay = + adjusted_mode->vdisplay - top_border - bottom_border; + adjusted_mode->vsync_start = + (adjusted_mode->vsync_start - panelVBlankStart) + + newVBlankStart; + adjusted_mode->vsync_end = + adjusted_mode->vsync_start + panelVSyncTime; + } + + /* Adjust crtc H and V */ + adjusted_mode->crtc_hdisplay = adjusted_mode->hdisplay; + adjusted_mode->crtc_hblank_start = newHBlankStart; + adjusted_mode->crtc_hblank_end = + adjusted_mode->crtc_htotal - left_border; + adjusted_mode->crtc_hsync_start = adjusted_mode->hsync_start; + adjusted_mode->crtc_hsync_end = adjusted_mode->hsync_end; + + adjusted_mode->crtc_vdisplay = adjusted_mode->vdisplay; + adjusted_mode->crtc_vblank_start = newVBlankStart; + adjusted_mode->crtc_vblank_end = + adjusted_mode->crtc_vtotal - top_border; + adjusted_mode->crtc_vsync_start = adjusted_mode->vsync_start; + adjusted_mode->crtc_vsync_end = adjusted_mode->vsync_end; + + drm_mode_set_crtcinfo(adjusted_mode, 0); + return true; +} + +static void via_hdmi_native_mode_set(struct via_crtc *iga, + struct drm_display_mode *mode, + bool audio_off) +{ + struct drm_device *dev = iga->base.dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u32 reg_c280, reg_c284; + int max_packet, delay; + u8 value = BIT(0); + + /* 135MHz ~ 270MHz */ + if (mode->clock >= 135000) + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x00000000, 0xC0000000); + /* 67.5MHz ~ <135MHz */ + else if (mode->clock >= 67500) + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x40000000, 0xC0000000); + /* 33.75MHz ~ <67.5MHz */ + else if (mode->clock >= 33750) + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x80000000, 0xC0000000); + /* 25MHz ~ <33.75MHz */ + else + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0xC0000000, 0xC0000000); + + /* touch C282 when init HDMI by mode 720x576, 720x480, + * or other modes */ + if ((mode->hdisplay == 720) && (mode->vdisplay == 576)) + VIA_WRITE(0xC280, 0x18232402); + else if ((mode->hdisplay == 720) && (mode->vdisplay == 480)) + VIA_WRITE(0xC280, 0x181f2402); + else + VIA_WRITE(0xC280, 0x18330002); + + /* init C280 */ + reg_c280 = 0x18000002 | (VIA_READ(0xC280) & 0x40); + /* sync polarity */ + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + reg_c280 |= BIT(10); + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + reg_c280 |= BIT(13); + + /* calculate correct delay of regC280[22:16] */ + if ((mode->crtc_hsync_start - mode->crtc_hdisplay) > (58 - 11)) + delay = 0; + else + delay = 58 - (mode->crtc_hsync_start - mode->crtc_hdisplay) - 11; + + /* calculate max_packet */ + max_packet = (mode->crtc_hblank_end - mode->crtc_hsync_start - 16 - 11 - delay) / 32; + if (0 == delay) + delay = mode->crtc_hblank_end - mode->crtc_hsync_start - (32 * max_packet + 16 + 11); + + reg_c280 |= (delay << 16); + VIA_WRITE(0xC280, reg_c280); + reg_c284 = 0x00000102 | (max_packet << 28); + + /* power down to reset */ + VIA_WRITE_MASK(0xC740, 0x00000000, 0x06000000); + /* enable DP data pass */ + VIA_WRITE_MASK(DP_DATA_PASS_ENABLE_REG, 0x00000001, 0x00000001); + /* select HDMI mode */ + VIA_WRITE_MASK(0xC748, 0, BIT(0)); + /* enable HDMI with HDMI mode */ + VIA_WRITE_MASK(0xC280, 0x00, 0x40); + /* select AC mode */ + VIA_WRITE_MASK(0xC74C, 0x40, 0x40); + /* enable InfoFrame */ + VIA_WRITE(0xC284, reg_c284); + /* set status of Lane0~3 */ + VIA_WRITE_MASK(0xC744, 0x00FFFF82, 0x00FFFF82); + VIA_WRITE(0xC0B4, 0x92000000); + /* enable audio packet */ + if (!audio_off) + VIA_WRITE_MASK(0xC294, 0x10000000, 0x10000000); + /* enable InfoFrame */ + VIA_WRITE(0xC284, reg_c284); + VIA_WRITE_MASK(0xC740, 0x1E4CBE7F, 0x3FFFFFFF); + VIA_WRITE_MASK(0xC748, 0x84509180, 0x001FFFFF); + /* Select PHY Function as HDMI */ + /* Select HDTV0 source */ + if (iga->index) + value |= BIT(1); + svga_wcrt_mask(VGABASE, 0xFF, value, BIT(1) | BIT(0)); +} + +static void via_hdmi_enc_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct via_encoder *enc = container_of(encoder, struct via_encoder, base); + struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base); + struct drm_connector *connector = NULL, *con; + struct drm_device *dev = encoder->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + list_for_each_entry(con, &dev->mode_config.connector_list, head) { + if (encoder == con->encoder) { + connector = con; + break; + } + } + + if (!connector) { + drm_dbg_driver(dev, "HDMI encoder is not used by any connector\n"); + return; + } + + if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) { + struct via_connector *con = container_of(connector, struct via_connector, base); + bool audio_off = (con->flags & HDMI_AUDIO_ENABLED); + + if (enc->di_port == VIA_DI_PORT_NONE) + via_hdmi_native_mode_set(iga, adjusted_mode, audio_off); + + if (!iga->index) + via_load_crtc_pixel_timing(encoder->crtc, adjusted_mode); + + /* Set Hsync Offset, delay one clock (To meet 861-D spec.) */ + svga_wcrt_mask(VGABASE, 0x8A, 0x01, 0x7); + + /* If CR8A +1, HSyc must -1 */ + vga_wcrt(VGABASE, 0x56, vga_rcrt(VGABASE, 0x56) - 1); + vga_wcrt(VGABASE, 0x57, vga_rcrt(VGABASE, 0x57) - 1); + + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { + if (iga->index) { + /* FIXME VIA where do you get this value from ??? */ + u32 v_sync_adjust = 0; + + svga_wcrt_mask(VGABASE, 0xAB, v_sync_adjust & 0xFF, 0xFF); + svga_wcrt_mask(VGABASE, 0xAC, (v_sync_adjust & 0x700) >> 8, 0x07); + } + } else { /* non-interlace, clear interlace setting. */ + if (iga->index) { + vga_wcrt(VGABASE, 0xFB, 0); + svga_wcrt_mask(VGABASE, 0xFC, 0, 0x07); + } + } + } else if (connector->connector_type == DRM_MODE_CONNECTOR_DVID) { + /* 135MHz ~ 270MHz */ + if (mode->clock >= 135000) + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x00000000, 0xC0000000); + /* 67.5MHz ~ < 135MHz */ + else if (mode->clock >= 67500) + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x40000000, 0xC0000000); + /* 33.75MHz ~ < 67.5MHz */ + else if (mode->clock >= 33750) + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x80000000, 0xC0000000); + /* 25MHz ~ < 33.75MHz */ + else + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0xC0000000, 0xC0000000); + + /* Power down TPLL to reset */ + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x00000000, 0x06000000); + /* Enable DP data pass */ + VIA_WRITE_MASK(DP_DATA_PASS_ENABLE_REG, 0x00000001, 0x00000001); + /* Select EPHY as HDMI mode */ + VIA_WRITE_MASK(DP_EPHY_MISC_PWR_REG, 0, BIT(0)); + /* Enable HDMI with DVI mode */ + VIA_WRITE_MASK(0xC280, 0x40, 0x40); + /* select AC mode */ + VIA_WRITE_MASK(0xC74C, 0x40, 0x40); + /* Set status of Lane0~3 */ + VIA_WRITE_MASK(0xC744, 0x00FFFF00, 0x00FFFF00); + /* Disable InfoFrame */ + VIA_WRITE_MASK(0xC284, 0x00000000, 0x00000002); + /* EPHY Control Register */ + VIA_WRITE_MASK(DP_EPHY_PLL_REG, 0x1EC46E6F, 0x3FFFFFFF); + /* Select PHY Function as HDMI */ + svga_wcrt_mask(VGABASE, 0xFF, BIT(0), BIT(0)); + /* Select HDTV0 source */ + if (!iga->index) + svga_wcrt_mask(VGABASE, 0xFF, 0, BIT(1)); + else + svga_wcrt_mask(VGABASE, 0xFF, BIT(1), BIT(1)); + + /* in 640x480 case, MPLL is different */ + /* For VT3410 internal transmitter 640x480 issue */ + if (mode->hdisplay == 640 && mode->vdisplay == 480) { + VIA_WRITE(DP_EPHY_PLL_REG, 0xD8C29E6F); + VIA_WRITE(DP_EPHY_PLL_REG, 0xDEC29E6F); + } + } +} + +static const struct drm_encoder_helper_funcs via_hdmi_enc_helper_funcs = { + .dpms = via_hdmi_enc_dpms, + .mode_fixup = via_hdmi_enc_mode_fixup, + .mode_set = via_hdmi_enc_mode_set, +}; + +static unsigned int via_check_hdmi_i2c_status(struct via_drm_priv *dev_priv, + unsigned int check_bits, + unsigned int condition) +{ + unsigned int status = true, max = 50, loop = 0; + + if (condition) { + while ((VIA_READ(0xC0B8) & check_bits) && loop < max) { + /* delay 20 us */ + udelay(20); + + if (++loop == max) + status = false; + } + } else { + while (!(VIA_READ(0xC0B8) & check_bits) && loop < max) { + /* delay 20 us */ + udelay(20); + + if (++loop == max) + status = false; + } + } + return status; +} + +unsigned int via_ddc_read_bytes_by_hdmi(struct via_drm_priv *dev_priv, + unsigned int offset, + unsigned char *block) +{ + unsigned int status = true, temp = 0, i; + + /* Enable DDC */ + VIA_WRITE_MASK(0xC000, 0x00000001, 0x00000001); + VIA_WRITE(0xC0C4, (VIA_READ(0xC0C4) & 0xFC7FFFFF) | 0x00800000); + VIA_WRITE(0xC0B8, 0x00000001); + + /* START */ + VIA_WRITE(0xC0B8, 0x0011); + VIA_WRITE(0xC0B8, 0x0019); + if (status) + status = via_check_hdmi_i2c_status(dev_priv, 0x0018, true); + + /* Slave Address */ + temp = 0xA0; + temp <<= 16; + temp |= VIA_READ(0xC0B4) & 0xFF00FFFF; + VIA_WRITE(0xC0B4, temp); + VIA_WRITE(0xC0B8, 0x0009); + if (status) + status = via_check_hdmi_i2c_status(dev_priv, 0x0008, true); + + /* Offset */ + temp = offset; + temp <<= 16; + temp |= VIA_READ(0xC0B4) & 0xFF00FFFF; + VIA_WRITE(0xC0B4, temp); + VIA_WRITE(0xC0B8, 0x0009); + if (status) + status = via_check_hdmi_i2c_status(dev_priv, 0x0008, true); + + /* START */ + VIA_WRITE(0xC0B8, 0x0011); + VIA_WRITE(0xC0B8, 0x0019); + if (status) + status = via_check_hdmi_i2c_status(dev_priv, 0x0018, true); + + /* Slave Address + 1 */ + temp = 0xA1; + temp <<= 16; + temp |= VIA_READ(0xC0B4) & 0xFF00FFFF; + VIA_WRITE(0xC0B4, temp); + VIA_WRITE(0xC0B8, 0x0009); + if (status) + status = via_check_hdmi_i2c_status(dev_priv, 0x0008, true); + + for (i = 0; i < EDID_LENGTH; i++) { + /* Read Data */ + VIA_WRITE(0xC0B8, 0x0009); + via_check_hdmi_i2c_status(dev_priv, 0x0008, true); + via_check_hdmi_i2c_status(dev_priv, 0x0080, false); + *block++ = (unsigned char) ((VIA_READ(0xC0B4) & 0x0000FF00) >> 8); + VIA_WRITE(0xC0B8, (VIA_READ(0xC0B8) & ~0x80)); + } + + /* STOP */ + VIA_WRITE(0xC0B8, 0x0021); + VIA_WRITE(0xC0B8, 0x0029); + + status = via_check_hdmi_i2c_status(dev_priv, 0x0828, true); + if (!status) { + /* Reset */ + VIA_WRITE_MASK(0xC0C4, 0x00000080, 0x00000080); + VIA_WRITE_MASK(0xC0C4, 0x00000000, 0x00000080); + } + return status; +} + +struct edid* via_hdmi_get_edid(struct drm_connector *connector) +{ + bool print_bad_edid = !connector->bad_edid_counter || (drm_debug_enabled(DRM_UT_KMS)); + struct drm_device *dev = connector->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct edid *edid = NULL; + int i, j = 0; + u8 *block; + + /* Clear out old EDID block */ + drm_connector_update_edid_property(connector, edid); + + block = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (!block) + return edid; + + /* base block fetch */ + for (i = 0; i < 4; i++) { + if (!via_ddc_read_bytes_by_hdmi(dev_priv, 0, block)) + goto out; + + if (drm_edid_block_valid(block, 0, print_bad_edid, NULL)) + break; + + if (i == 0 && !memchr_inv(block, 0, EDID_LENGTH)) { + connector->null_edid_counter++; + goto carp; + } + } + if (i == 4) + goto carp; + + /* parse the extensions if present */ + if (block[0x7e]) { + u8 *new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); + int valid_extensions = 0, offset = 0; + + if (!new) + goto out; + block = new; + + for (j = 1; j <= block[0x7e]; j++) { + for (i = 0; i < 4; i++) { + offset = (valid_extensions + 1) * EDID_LENGTH; + new = block + offset; + + if (!via_ddc_read_bytes_by_hdmi(dev_priv, + offset, new)) + goto out; + + if (drm_edid_block_valid(new, j, print_bad_edid, NULL)) { + valid_extensions++; + break; + } + } + + if (i == 4 && print_bad_edid) { + dev_warn(connector->dev->dev, + "%s: Ignoring invalid EDID block %d.\n", + connector->name, j); + + connector->bad_edid_counter++; + } + } + + if (valid_extensions != block[0x7e]) { + block[EDID_LENGTH - 1] += block[0x7e] - valid_extensions; + block[0x7e] = valid_extensions; + + new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); + if (!new) + goto out; + block = new; + } + } + edid = (struct edid *) block; + drm_connector_update_edid_property(connector, edid); + return edid; + +carp: + if (print_bad_edid) { + dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", + connector->name, j); + } + connector->bad_edid_counter++; +out: + kfree(block); + return edid; +} + +static enum drm_connector_status +via_hdmi_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + enum drm_connector_status ret = connector_status_disconnected; + u32 mm_c730 = VIA_READ(0xc730) & 0xC0000000; + struct edid *edid = NULL; + + if (VIA_IRQ_DP_HOT_UNPLUG == mm_c730) { + drm_connector_update_edid_property(connector, NULL); + return ret; + } + + edid = via_hdmi_get_edid(connector); + if (edid) { + if ((connector->connector_type != DRM_MODE_CONNECTOR_HDMIA) ^ + (drm_detect_hdmi_monitor(edid))) + ret = connector_status_connected; + } + return ret; +} + +static int via_hdmi_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = connector->dev; + + if (property == dev->mode_config.dpms_property && connector->encoder) + via_hdmi_enc_dpms(connector->encoder, (uint32_t)(value & 0xf)); + return 0; +} + +static const struct drm_connector_funcs via_hdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_hdmi_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = via_hdmi_set_property, + .destroy = via_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = + drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_connector_destroy_state, +}; + +static int via_hdmi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +int via_hdmi_get_modes(struct drm_connector *connector) +{ + struct edid *edid = via_hdmi_get_edid(connector); + + if (edid) { + struct via_connector *con; + + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + con = container_of(connector, struct via_connector, base); + + if (via_hdmi_audio) + con->flags |= drm_detect_monitor_audio(edid); + } + } + return drm_add_edid_modes(connector, edid); +} + +static const struct drm_connector_helper_funcs via_hdmi_connector_helper_funcs = { + .mode_valid = via_hdmi_mode_valid, + .get_modes = via_hdmi_get_modes, +}; + +void via_hdmi_init(struct drm_device *dev, u32 di_port) +{ + struct via_connector *dvi, *hdmi; + struct via_encoder *enc; + + enc = kzalloc(sizeof(*enc) + 2 * sizeof(*hdmi), GFP_KERNEL); + if (!enc) { + drm_err(dev, "Failed to allocate connector and encoder\n"); + return; + } + hdmi = &enc->cons[0]; + dvi = &enc->cons[1]; + + /* Setup the encoders and attach them */ + drm_encoder_init(dev, &enc->base, &via_hdmi_enc_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + drm_encoder_helper_add(&enc->base, &via_hdmi_enc_helper_funcs); + + enc->base.possible_crtcs = BIT(1) | BIT(0); + enc->base.possible_clones = 0; + enc->di_port = di_port; + + /* Setup the HDMI connector */ + drm_connector_init(dev, &hdmi->base, &via_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + drm_connector_helper_add(&hdmi->base, &via_hdmi_connector_helper_funcs); + drm_connector_register(&hdmi->base); + + hdmi->base.polled = DRM_CONNECTOR_POLL_HPD; + hdmi->base.doublescan_allowed = false; + hdmi->base.interlace_allowed = true; + INIT_LIST_HEAD(&hdmi->props); + + drm_connector_attach_encoder(&hdmi->base, &enc->base); + + /* Setup the DVI connector */ + drm_connector_init(dev, &dvi->base, &via_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + drm_connector_helper_add(&dvi->base, &via_hdmi_connector_helper_funcs); + drm_connector_register(&dvi->base); + + dvi->base.polled = DRM_CONNECTOR_POLL_HPD; + dvi->base.doublescan_allowed = false; + dvi->base.interlace_allowed = true; + INIT_LIST_HEAD(&dvi->props); + + drm_connector_attach_encoder(&dvi->base, &enc->base); +} diff --git a/drivers/gpu/drm/via/via_i2c.c b/drivers/gpu/drm/via/via_i2c.c new file mode 100644 index 000000000000..228e6fd11b42 --- /dev/null +++ b/drivers/gpu/drm/via/via_i2c.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * + * Author(s): + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#include <uapi/linux/i2c.h> + +#include "via_drv.h" + +#define SERIAL 0 +#define GPIO 1 + +struct via_i2c_stuff { + u16 i2c_port; /* GPIO or I2C port */ + u16 is_active; /* Being used as I2C? */ + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; +}; + +static struct via_i2c_stuff via_i2c_par[5]; + +static void via_i2c_setsda(void *data, int state) +{ + struct via_i2c_stuff *i2c = data; + struct drm_device *dev = i2c_get_adapdata(&i2c->adapter); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 value, mask; + + if (i2c->is_active == GPIO) { + mask = state ? BIT(6) : BIT(6) | BIT(4); + value = state ? 0x00 : BIT(6); + } else { + value = state ? BIT(4) | BIT(0) : BIT(0); + mask = BIT(4) | BIT(0); + } + + svga_wseq_mask(VGABASE, i2c->i2c_port, value, mask); +} + +static void via_i2c_setscl(void *data, int state) +{ + struct via_i2c_stuff *i2c = data; + struct drm_device *dev = i2c_get_adapdata(&i2c->adapter); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 value, mask; + + if (i2c->is_active == GPIO) { + mask = state ? BIT(7) : BIT(7) | BIT(5); + value = state ? 0x00 : BIT(7); + } else { + value = state ? BIT(5) | BIT(0) : BIT(0); + mask = BIT(5) | BIT(0); + } + + svga_wseq_mask(VGABASE, i2c->i2c_port, value, mask); +} + +static int via_i2c_getsda(void *data) +{ + struct via_i2c_stuff *i2c = data; + struct drm_device *dev = i2c_get_adapdata(&i2c->adapter); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + return vga_rseq(VGABASE, i2c->i2c_port) & BIT(2); +} + +static int via_i2c_getscl(void *data) +{ + struct via_i2c_stuff *i2c = data; + struct drm_device *dev = i2c_get_adapdata(&i2c->adapter); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + return vga_rseq(VGABASE, i2c->i2c_port) & BIT(3); +} + +struct i2c_adapter *via_find_ddc_bus(int port) +{ + struct i2c_adapter *adapter = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(via_i2c_par); i++) { + struct via_i2c_stuff *i2c = &via_i2c_par[i]; + + if (i2c->i2c_port == port) { + adapter = &i2c->adapter; + break; + } + } + return adapter; +} + +static int create_i2c_bus(struct drm_device *dev, + struct via_i2c_stuff *i2c_par) +{ + struct i2c_adapter *adapter = &i2c_par->adapter; + struct i2c_algo_bit_data *algo = &i2c_par->algo; + + algo->setsda = via_i2c_setsda; + algo->setscl = via_i2c_setscl; + algo->getsda = via_i2c_getsda; + algo->getscl = via_i2c_getscl; + algo->udelay = 15; + algo->timeout = usecs_to_jiffies(2200); /* from VESA */ + algo->data = i2c_par; + + sprintf(adapter->name, "via i2c bit bus 0x%02x", i2c_par->i2c_port); + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_DDC; + adapter->algo_data = algo; + i2c_set_adapdata(adapter, dev); + + /* Raise SCL and SDA */ + via_i2c_setsda(i2c_par, 1); + via_i2c_setscl(i2c_par, 1); + udelay(20); + + return i2c_bit_add_bus(adapter); +} + +void via_i2c_readbytes(struct i2c_adapter *adapter, + u8 slave_addr, char offset, + u8 *buffer, unsigned int size) +{ + u8 out_buf[2]; + u8 in_buf[2]; + struct i2c_msg msgs[] = { + { + .addr = slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = slave_addr, + .flags = I2C_M_RD, + .len = size, + .buf = in_buf, + } + }; + + out_buf[0] = offset; + out_buf[1] = 0; + + if (i2c_transfer(adapter, msgs, 2) != 2) + printk(KERN_WARNING "%s failed\n", __func__); + else + *buffer = in_buf[0]; +} + +void via_i2c_writebytes(struct i2c_adapter *adapter, + u8 slave_addr, char offset, + u8 *data, unsigned int size) +{ + struct i2c_msg msg = { 0 }; + u8 *out_buf; + + out_buf = kzalloc(size + 1, GFP_KERNEL); + out_buf[0] = offset; + memcpy(&out_buf[1], data, size); + msg.addr = slave_addr; + msg.flags = 0; + msg.len = size + 1; + msg.buf = out_buf; + + if (i2c_transfer(adapter, &msg, 1) != 1) + printk(KERN_WARNING "%s failed\n", __func__); + + kfree(out_buf); +} + +void via_i2c_reg_init(struct via_drm_priv *dev_priv) +{ + svga_wseq_mask(VGABASE, 0x31, 0x30, 0x30); + svga_wseq_mask(VGABASE, 0x26, 0x30, 0x30); + vga_wseq(VGABASE, 0x2C, 0xc2); + vga_wseq(VGABASE, 0x3D, 0xc0); + svga_wseq_mask(VGABASE, 0x2C, 0x30, 0x30); + svga_wseq_mask(VGABASE, 0x3D, 0x30, 0x30); +} + +int via_i2c_init(struct drm_device *dev) +{ + int types[] = { SERIAL, SERIAL, GPIO, GPIO, GPIO }; + int ports[] = { 0x26, 0x31, 0x25, 0x2C, 0x3D }; + int count = ARRAY_SIZE(via_i2c_par), ret, i; + struct via_i2c_stuff *i2c = via_i2c_par; + + for (i = 0; i < count; i++) { + i2c->is_active = types[i]; + i2c->i2c_port = ports[i]; + + ret = create_i2c_bus(dev, i2c); + if (ret < 0) + drm_err(dev, "cannot create i2c bus %x:%d\n", + ports[i], ret); + i2c++; + } + return 0; +} + +void via_i2c_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(via_i2c_par); i++) + i2c_del_adapter(&via_i2c_par->adapter); +} diff --git a/drivers/gpu/drm/via/via_init.c b/drivers/gpu/drm/via/via_init.c new file mode 100644 index 000000000000..be1c2783703b --- /dev/null +++ b/drivers/gpu/drm/via/via_init.c @@ -0,0 +1,1472 @@ +/* + * Copyright © 2019 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 2006-2009 Luc Verhaegen. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + * Luc Verhaegen + */ + +#include <linux/pci.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_mode_config.h> +#include <drm/drm_probe_helper.h> + +#include "via_drv.h" + + +static int cle266_mem_type(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn0_dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 fsb, freq, type; + int ret; + + bridge_fn0_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 0)); + if (!bridge_fn0_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 0 not found! errno: %d\n", + ret); + goto exit; + } + + ret = pci_read_config_byte(bridge_fn0_dev, 0x54, &fsb); + if (ret) { + goto error_pci_cfg_read; + } + + ret = pci_read_config_byte(bridge_fn0_dev, 0x69, &freq); + if (ret) { + goto error_pci_cfg_read; + } + + fsb >>= 6; + freq >>= 6; + + /* FSB frequency */ + switch (fsb) { + case 0x01: /* 100MHz */ + switch (freq) { + case 0x00: + freq = 100; + break; + case 0x01: + freq = 133; + break; + case 0x02: + freq = 66; + break; + default: + freq = 0; + break; + } + + break; + case 0x02: /* 133 MHz */ + case 0x03: + switch (freq) { + case 0x00: + freq = 133; + break; + case 0x02: + freq = 100; + break; + default: + freq = 0; + break; + } + + break; + default: + freq = 0; + break; + } + + ret = pci_read_config_byte(bridge_fn0_dev, 0x60, &fsb); + if (ret) { + goto error_pci_cfg_read; + } + + ret = pci_read_config_byte(bridge_fn0_dev, 0xe3, &type); + if (ret) { + goto error_pci_cfg_read; + } + + /* On bank 2/3 */ + if (type & 0x02) { + fsb >>= 2; + } + + /* Memory type */ + switch (fsb & 0x03) { + case 0x00: /* SDR */ + switch (freq) { + case 66: + dev_priv->vram_type = VIA_MEM_SDR66; + break; + case 100: + dev_priv->vram_type = VIA_MEM_SDR100; + break; + case 133: + dev_priv->vram_type = VIA_MEM_SDR133; + break; + default: + break; + } + + break; + case 0x02: /* DDR */ + switch (freq) { + case 100: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 133: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + default: + break; + } + + break; + default: + break; + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static int km400_mem_type(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn0_dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 fsb, freq, rev; + int ret; + + bridge_fn0_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 0)); + if (!bridge_fn0_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 0 not found! errno: %d\n", + ret); + goto exit; + } + + ret = pci_read_config_byte(bridge_fn0_dev, 0x54, &fsb); + if (ret) { + goto error_pci_cfg_read; + } + + ret = pci_read_config_byte(bridge_fn0_dev, 0x69, &freq); + if (ret) { + goto error_pci_cfg_read; + } + + fsb >>= 6; + freq >>= 6; + + ret = pci_read_config_byte(bridge_fn0_dev, 0xf6, &rev); + if (ret) { + goto error_pci_cfg_read; + } + + /* KM400 */ + if (rev < 0x80) { + /* FSB frequency */ + switch (fsb) { + case 0x00: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + default: + break; + } + + break; + case 0x01: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + + break; + case 0x02: + case 0x03: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + default: + break; + } + + break; + default: + break; + } + } else { + /* KM400A */ + ret = pci_read_config_byte(bridge_fn0_dev, 0x67, &rev); + if (ret) { + goto error_pci_cfg_read; + } + + if (rev & 0x80) + freq |= 0x04; + + switch (fsb) { + case 0x00: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x07: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + + break; + case 0x01: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + + break; + case 0x02: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + default: + break; + } + + break; + case 0x03: + switch (freq) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + default: + break; + } + + break; + default: + break; + } + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static int p4m800_mem_type(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn3_dev, *bridge_fn4_dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + int freq = 0; + u8 fsb, type; + int ret; + + bridge_fn3_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 3)); + if (!bridge_fn3_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 3 not found! errno: %d\n", + ret); + goto exit; + } + + bridge_fn4_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 4)); + if (!bridge_fn4_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 4 not found! errno: %d\n", + ret); + goto exit; + } + + /* VIA Scratch region */ + ret = pci_read_config_byte(bridge_fn4_dev, 0xf3, &fsb); + if (ret) { + goto error_pci_cfg_read; + } + + fsb >>= 5; + switch (fsb) { + case 0x00: + freq = 0x03; /* 100 MHz */ + break; + case 0x01: + freq = 0x04; /* 133 MHz */ + break; + case 0x02: + freq = 0x06; /* 200 MHz */ + break; + default: + break; + } + + ret = pci_read_config_byte(bridge_fn3_dev, 0x68, &type); + if (ret) { + goto error_pci_cfg_read; + } + + type &= 0x0f; + if (type & 0x02) { + freq -= type >> 2; + } else { + freq += type >> 2; + if (type & 0x01) + freq++; + } + + switch (freq) { + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static int km8xx_mem_type(struct drm_device *dev) +{ + struct pci_dev *dram, *misc = NULL; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 type, tmp; + int ret = -ENXIO; + + dram = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_K8_NB_MEMCTL, NULL); + if (dram) { + misc = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_K8_NB_MISC, NULL); + + ret = pci_read_config_byte(misc, 0xfd, &type); + if (ret) { + goto error_pci_cfg_read; + } + + if (type) { + pci_read_config_byte(dram, 0x94, &type); + if (ret) { + goto error_pci_cfg_read; + } + + switch (type & 0x03) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR2_400; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR2_533; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR2_667; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR2_800; + break; + default: + break; + } + } else { + ret = pci_read_config_byte(dram, 0x96, &type); + if (ret) { + goto error_pci_cfg_read; + } + + type >>= 4; + type &= 0x07; + + switch (type) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x07: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + } + } + + /* AMD 10h DRAM Controller */ + dram = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_10H_NB_DRAM, NULL); + if (dram) { + ret = pci_read_config_byte(misc, 0x94, &tmp); + if (ret) { + goto error_pci_cfg_read; + } + + ret = pci_read_config_byte(misc, 0x95, &type); + if (ret) { + goto error_pci_cfg_read; + } + + if (type & 0x01) { /* DDR3 */ + switch (tmp & 0x07) { + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR3_800; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR3_1066; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR3_1333; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR3_1600; + break; + default: + break; + } + } else { /* DDR2 */ + switch (tmp & 0x07) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR2_400; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR2_533; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR2_667; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR2_800; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR2_1066; + break; + default: + break; + } + } + } + + /* AMD 11h DRAM Controller */ + dram = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_11H_NB_DRAM, NULL); + if (dram) { + ret = pci_read_config_byte(misc, 0x94, &type); + if (ret) { + goto error_pci_cfg_read; + } + + switch (tmp & 0x07) { + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR2_533; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR2_667; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR2_800; + break; + default: + break; + } + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static int cn400_mem_type(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn2_dev, *bridge_fn3_dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + int freq = 0; + u8 fsb, type; + int ret; + + bridge_fn2_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 2)); + if (!bridge_fn2_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 2 not found! errno: %d\n", + ret); + goto exit; + } + + bridge_fn3_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 3)); + if (!bridge_fn3_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 3 not found! errno: %d\n", + ret); + goto exit; + } + + ret = pci_read_config_byte(bridge_fn2_dev, 0x54, &fsb); + if (ret) { + goto error_pci_cfg_read; + } + + fsb >>= 5; + switch (fsb) { + case 0x00: + freq = 0x03; /* 100 MHz */ + break; + case 0x01: + freq = 0x04; /* 133 MHz */ + break; + case 0x02: + freq = 0x06; /* 200 MHz */ + break; + case 0x04: + freq = 0x07; /* 233 MHz */ + break; + default: + break; + } + + ret = pci_read_config_byte(bridge_fn3_dev, 0x68, &type); + if (ret) { + goto error_pci_cfg_read; + } + + type &= 0x0f; + if (type & 0x01) { + freq += 1 + (type >> 2); + } else { + freq -= 1 + (type >> 2); + } + + switch (freq) { + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static int cn700_mem_type(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn3_dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 type, clock; + int ret; + + bridge_fn3_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 3)); + if (!bridge_fn3_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 3 not found! errno: %d\n", + ret); + goto exit; + } + + ret = pci_read_config_byte(bridge_fn3_dev, 0x6c, &type); + if (ret) { + goto error_pci_cfg_read; + } + + ret = pci_read_config_byte(bridge_fn3_dev, 0x90, &clock); + if (ret) { + goto error_pci_cfg_read; + } + + type &= 0x40; + type >>= 6; + switch (type) { + case 0x00: + switch (clock & 0x07) { + case 0x00: + dev_priv->vram_type = VIA_MEM_DDR_200; + break; + case 0x01: + dev_priv->vram_type = VIA_MEM_DDR_266; + break; + case 0x02: + dev_priv->vram_type = VIA_MEM_DDR_333; + break; + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR_400; + break; + default: + break; + } + + break; + case 0x01: + switch (clock & 0x07) { + case 0x03: + dev_priv->vram_type = VIA_MEM_DDR2_400; + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR2_533; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR2_667; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR2_800; + break; + default: + break; + } + + break; + default: + break; + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static int vx900_mem_type(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn3_dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 type, clock, volt; + int ret; + + bridge_fn3_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 3)); + if (!bridge_fn3_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 3 not found! errno: %d\n", + ret); + goto exit; + } + + ret = pci_read_config_byte(bridge_fn3_dev, 0x6c, &type); + if (ret) { + goto error_pci_cfg_read; + } + + ret = pci_read_config_byte(bridge_fn3_dev, 0x90, &clock); + if (ret) { + goto error_pci_cfg_read; + } + + volt = type; + type &= 0xc0; + type >>= 6; + volt &= 0x20; + volt >>= 5; + switch (type) { + case 0x01: + switch (clock & 0x0f) { + case 0x00: + if (volt) { + dev_priv->vram_type = VIA_MEM_DDR2_800; + } else { + dev_priv->vram_type = VIA_MEM_DDR2_533; + } + + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR2_533; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR2_667; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR2_800; + break; + case 0x07: + dev_priv->vram_type = VIA_MEM_DDR2_1066; + break; + default: + break; + } + + break; + case 0x02: + switch (clock & 0x0f) { + case 0x00: + if (volt) { + dev_priv->vram_type = VIA_MEM_DDR3_800; + } else { + dev_priv->vram_type = VIA_MEM_DDR3_533; + } + + break; + case 0x04: + dev_priv->vram_type = VIA_MEM_DDR3_533; + break; + case 0x05: + dev_priv->vram_type = VIA_MEM_DDR3_667; + break; + case 0x06: + dev_priv->vram_type = VIA_MEM_DDR3_800; + break; + case 0x07: + dev_priv->vram_type = VIA_MEM_DDR3_1066; + break; + default: + break; + } + + break; + } + + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + return ret; +} + +static void via_quirks_init(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + /* + * Checking for VIA Technologies NanoBook reference design. + * Examples include Everex CloudBook and Sylvania g netbook. + * It is also called FIC CE260 or CE261 by its ODM (Original + * Design Manufacturer) name. + * This device has its strapping resistors set to a wrong + * setting to handle DVI. As a result, the code needs to know + * this in order to support DVI properly. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_UNICHROME_PRO_II) && + (pdev->subsystem_vendor == 0x1509) && + (pdev->subsystem_device == 0x2d30)) { + dev_priv->is_via_nanobook = true; + } else { + dev_priv->is_via_nanobook = false; + } + + /* + * Check for Quanta IL1 netbook. This is necessary + * due to its flat panel connected to DVP1 (Digital + * Video Port 1) rather than its LVDS channel. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) && + (pdev->subsystem_vendor == 0x152d) && + (pdev->subsystem_device == 0x0771)) { + dev_priv->is_quanta_il1 = true; + } else { + dev_priv->is_quanta_il1 = false; + } + + /* + * Samsung NC20 netbook has its FP connected to LVDS2 + * rather than the more logical LVDS1, hence, a special + * flag register is needed for properly controlling its + * FP. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) && + (pdev->subsystem_vendor == 0x144d) && + (pdev->subsystem_device == 0xc04e)) { + dev_priv->is_samsung_nc20 = true; + } else { + dev_priv->is_samsung_nc20 = false; + } + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static int via_vram_init(struct drm_device *dev) +{ + struct pci_dev *gfx_dev = to_pci_dev(dev->dev); + struct pci_dev *bridge_fn0_dev = NULL; + struct pci_dev *bridge_fn3_dev = NULL; + char *name = "unknown"; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 size; + int ret = 0; + + bridge_fn0_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), 0, + PCI_DEVFN(0, 0)); + if (!bridge_fn0_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 0 not found! " + "errno: %d\n", ret); + goto exit; + } + + if ((gfx_dev->device != PCI_DEVICE_ID_VIA_CLE266_GFX) || + (gfx_dev->device != PCI_DEVICE_ID_VIA_KM400_GFX)) { + bridge_fn3_dev = + pci_get_domain_bus_and_slot(pci_domain_nr(gfx_dev->bus), + 0, PCI_DEVFN(0, 3)); + if (!bridge_fn3_dev) { + ret = -ENODEV; + drm_err(dev, "Host Bridge Function 3 not found! " + "errno: %d\n", ret); + goto exit; + } + } + + switch (bridge_fn0_dev->vendor) { + case PCI_VENDOR_ID_VIA: + switch (bridge_fn0_dev->device) { + + /* CLE266 */ + case PCI_DEVICE_ID_VIA_862X_0: + ret = pci_read_config_byte(bridge_fn0_dev, 0xe1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + ret = cle266_mem_type(dev); + break; + + /* KM400(A) / KN400(A) */ + case PCI_DEVICE_ID_VIA_8378_0: + ret = pci_read_config_byte(bridge_fn0_dev, 0xe1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + ret = km400_mem_type(dev); + break; + + /* P4M800 */ + case PCI_DEVICE_ID_VIA_3296_0: + ret = pci_read_config_byte(bridge_fn3_dev, 0xa1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + ret = p4m800_mem_type(dev); + break; + + /* K8M800(A) / K8N800(A) */ + case PCI_DEVICE_ID_VIA_8380_0: + /* K8M890 / K8N890 */ + case PCI_DEVICE_ID_VIA_VT3336: + ret = pci_read_config_byte(bridge_fn3_dev, 0xa1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + if (bridge_fn0_dev->device != + PCI_DEVICE_ID_VIA_8380_0) { + dev_priv->vram_size <<= 2; + } + + ret = km8xx_mem_type(dev); + break; + + /* CN400 / PM800 / PN800 / PM880 / PN880 */ + case PCI_DEVICE_ID_VIA_PX8X0_0: + ret = pci_read_config_byte(bridge_fn3_dev, 0xa1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + + ret = cn400_mem_type(dev); + break; + + /* P4M800CE / P4M800 Pro / VN800 / CN700 */ + case PCI_DEVICE_ID_VIA_P4M800CE: + /* P4M890 / VN890 / CN800 */ + case PCI_DEVICE_ID_VIA_P4M890: + /* CX700(M / M2) / VX700(M / M2) */ + case PCI_DEVICE_ID_VIA_VT3324: + /* P4M900 / VN896 / CN896 */ + case PCI_DEVICE_ID_VIA_VT3364: + /* VX800 / VX820 */ + case PCI_DEVICE_ID_VIA_VX800_HB: + /* VX855 / VX875 */ + case PCI_DEVICE_ID_VIA_VX855_HB: + ret = pci_read_config_byte(bridge_fn3_dev, 0xa1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 20; + if (bridge_fn0_dev->device != + PCI_DEVICE_ID_VIA_P4M800CE) { + dev_priv->vram_size <<= 2; + } + + ret = cn700_mem_type(dev); + break; + + /* VX900(H) */ + case PCI_DEVICE_ID_VIA_VX900_HB: + ret = pci_read_config_byte(bridge_fn3_dev, 0xa1, &size); + if (ret) { + goto error_pci_cfg_read; + } + + dev_priv->vram_size = (1 << ((size & 0x70) >> 4)) << 22; + + ret = vx900_mem_type(dev); + break; + + default: + ret = -ENODEV; + drm_err(dev, "Unknown Host Bridge device: 0x%04x\n", + bridge_fn0_dev->device); + break; + } + + break; + default: + ret = -ENODEV; + drm_err(dev, "Unknown Host Bridge vendor: 0x%04x\n", + bridge_fn0_dev->vendor); + break; + } + + if (ret) { + goto exit; + } + + switch (dev_priv->vram_type) { + case VIA_MEM_SDR66: + name = "SDR 66"; + break; + case VIA_MEM_SDR100: + name = "SDR 100"; + break; + case VIA_MEM_SDR133: + name = "SDR 133"; + break; + case VIA_MEM_DDR_200: + name = "DDR 200"; + break; + case VIA_MEM_DDR_266: + name = "DDR 266"; + break; + case VIA_MEM_DDR_333: + name = "DDR 333"; + break; + case VIA_MEM_DDR_400: + name = "DDR 400"; + break; + case VIA_MEM_DDR2_400: + name = "DDR2 400"; + break; + case VIA_MEM_DDR2_533: + name = "DDR2 533"; + break; + case VIA_MEM_DDR2_667: + name = "DDR2 667"; + break; + case VIA_MEM_DDR2_800: + name = "DDR2 800"; + break; + case VIA_MEM_DDR2_1066: + name = "DDR2 1066"; + break; + case VIA_MEM_DDR3_533: + name = "DDR3 533"; + break; + case VIA_MEM_DDR3_667: + name = "DDR3 667"; + break; + case VIA_MEM_DDR3_800: + name = "DDR3 800"; + break; + case VIA_MEM_DDR3_1066: + name = "DDR3 1066"; + break; + case VIA_MEM_DDR3_1333: + name = "DDR3 1333"; + break; + case VIA_MEM_DDR3_1600: + name = "DDR3 1600"; + break; + default: + break; + } + + drm_dbg_driver(dev, "Found %s video RAM.\n", name); + + if (gfx_dev->device == PCI_DEVICE_ID_VIA_CHROME9_HD) { + dev_priv->vram_start = pci_resource_start(gfx_dev, 2); + } else { + dev_priv->vram_start = pci_resource_start(gfx_dev, 0); + } + + /* Add an MTRR for the video RAM. */ + dev_priv->vram_mtrr = arch_phys_wc_add(dev_priv->vram_start, + dev_priv->vram_size); + goto exit; +error_pci_cfg_read: + drm_err(dev, "PCI configuration space read error! errno: %d\n", ret); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_vram_fini(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + if (dev_priv->vram_mtrr) { + arch_phys_wc_del(dev_priv->vram_mtrr); + arch_io_free_memtype_wc(dev_priv->vram_start, + dev_priv->vram_size); + dev_priv->vram_mtrr = 0; + } + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static int via_mmio_init(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + /* + * PCI BAR1 is the MMIO memory window for all + * VIA Technologies Chrome IGPs. + * Obtain the starting base address and size, and + * map it to the OS for use. + */ + dev_priv->mmio_base = pci_resource_start(pdev, 1); + dev_priv->mmio_size = pci_resource_len(pdev, 1); + dev_priv->mmio = ioremap(dev_priv->mmio_base, + dev_priv->mmio_size); + if (!dev_priv->mmio) { + ret = -ENOMEM; + goto exit; + } + +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_mmio_fini(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + if (dev_priv->mmio) { + iounmap(dev_priv->mmio); + dev_priv->mmio = NULL; + } + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static void via_graphics_unlock(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + uint8_t temp; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + /* + * Enable VGA subsystem. + */ + temp = vga_io_r(0x03c3); + vga_io_w(0x03c3, temp | 0x01); + svga_wmisc_mask(VGABASE, BIT(0), BIT(0)); + + /* + * Unlock VIA Technologies Chrome IGP extended + * registers. + */ + svga_wseq_mask(VGABASE, 0x10, BIT(0), BIT(0)); + + /* + * Unlock VIA Technologies Chrome IGP extended + * graphics functionality. + */ + svga_wseq_mask(VGABASE, 0x1a, BIT(3), BIT(3)); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static void via_chip_revision_info(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + u8 tmp; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + switch (pdev->device) { + /* CLE266 Chipset */ + case PCI_DEVICE_ID_VIA_CLE266_GFX: + /* CR4F only defined in CLE266.CX chipset. */ + tmp = vga_rcrt(VGABASE, 0x4f); + vga_wcrt(VGABASE, 0x4f, 0x55); + if (vga_rcrt(VGABASE, 0x4f) != 0x55) { + dev_priv->revision = CLE266_REVISION_AX; + } else { + dev_priv->revision = CLE266_REVISION_CX; + } + + /* Restore original CR4F value. */ + vga_wcrt(VGABASE, 0x4f, tmp); + break; + /* CX700 / VX700 Chipset */ + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + tmp = vga_rseq(VGABASE, 0x43); + if (tmp & 0x02) { + dev_priv->revision = CX700_REVISION_700M2; + } else if (tmp & 0x40) { + dev_priv->revision = CX700_REVISION_700M; + } else { + dev_priv->revision = CX700_REVISION_700; + } + + break; + /* VX800 / VX820 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + break; + /* VX855 / VX875 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + /* VX900 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HD: + dev_priv->revision = vga_rseq(VGABASE, 0x3b); + break; + default: + break; + } + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static int via_device_init(struct drm_device *dev) +{ + int ret; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + via_quirks_init(dev); + + /* + * Map VRAM. + */ + ret = via_vram_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize video RAM.\n"); + goto exit; + } + + ret = via_mmio_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize MMIO.\n"); + goto error_mmio_init; + } + + via_graphics_unlock(dev); + goto exit; +error_mmio_init: + via_vram_fini(dev); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_device_fini(struct drm_device *dev) +{ + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + via_mmio_fini(dev); + via_vram_fini(dev); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static const struct drm_mode_config_funcs via_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +int via_modeset_init(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + uint32_t i; + int ret = 0; + + /* Initialize the number of display connectors. */ + dev_priv->number_fp = 0; + dev_priv->number_dvi = 0; + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 2044; + dev->mode_config.max_height = 4096; + + dev->mode_config.funcs = &via_drm_mode_config_funcs; + + dev->mode_config.preferred_depth = 24; + + dev->mode_config.cursor_width = + dev->mode_config.cursor_height = VIA_CURSOR_SIZE; + + ret = drmm_mode_config_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize mode setting " + "configuration!\n"); + goto exit; + } + + via_i2c_reg_init(dev_priv); + ret = via_i2c_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize I2C bus!\n"); + goto exit; + } + + for (i = 0; i < VIA_MAX_CRTC; i++) { + ret = via_crtc_init(dev_priv, i); + if (ret) { + drm_err(dev, "Failed to initialize CRTC %u!\n", i + 1); + goto error_crtc_init; + } + } + + via_ext_dvi_probe(dev); + via_tmds_probe(dev); + + via_lvds_probe(dev); + + via_dac_probe(dev); + + + via_ext_dvi_init(dev); + via_tmds_init(dev); + + via_dac_init(dev); + + via_lvds_init(dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CHROME9_HD: + via_hdmi_init(dev, VIA_DI_PORT_NONE); + break; + default: + break; + } + + drm_mode_config_reset(dev); + + drm_kms_helper_poll_init(dev); + goto exit; +error_crtc_init: + via_i2c_exit(); +exit: + return ret; +} + +void via_modeset_fini(struct drm_device *dev) +{ + drm_kms_helper_poll_fini(dev); + + drm_helper_force_disable_all(dev); + + via_i2c_exit(); +} + +int via_drm_init(struct drm_device *dev) +{ + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + ret = via_device_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize Chrome IGP!\n"); + goto exit; + } + + ret = via_mm_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize TTM!\n"); + goto error_mm_init; + } + + via_chip_revision_info(dev); + + ret = via_modeset_init(dev); + if (ret) { + drm_err(dev, "Failed to initialize mode setting!\n"); + goto error_modeset_init; + } + + goto exit; +error_modeset_init: + via_mm_fini(dev); +error_mm_init: + via_device_fini(dev); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +void via_drm_fini(struct drm_device *dev) +{ + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + via_modeset_fini(dev); + via_mm_fini(dev); + via_device_fini(dev); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} diff --git a/drivers/gpu/drm/via/via_ioctl.c b/drivers/gpu/drm/via/via_ioctl.c new file mode 100644 index 000000000000..8f8ce7137061 --- /dev/null +++ b/drivers/gpu/drm/via/via_ioctl.c @@ -0,0 +1,94 @@ +/* + * Copyright © 2020 Kevin Brace. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ + +#include <drm/drm_gem.h> + +#include <drm/ttm/ttm_bo.h> + +#include <uapi/drm/via_drm.h> + +#include "via_drv.h" + + +int via_gem_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_via_gem_alloc *args = data; + struct ttm_buffer_object *ttm_bo; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_bo *bo; + uint32_t handle; + int ret; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + ret = via_bo_create(dev, &dev_priv->bdev, args->size, + ttm_bo_type_device, args->domain, false, &bo); + if (ret) { + goto exit; + } + + ttm_bo = &bo->ttm_bo; + + ret = drm_gem_handle_create(file_priv, &ttm_bo->base, &handle); + drm_gem_object_put(&ttm_bo->base); + if (ret) { + via_bo_destroy(bo, false); + goto exit; + } + + args->size = ttm_bo->base.size; + args->domain = ttm_bo->resource->placement; + args->handle = handle; + args->offset = ttm_bo->resource->start << PAGE_SHIFT; +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +int via_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_via_gem_mmap *args = data; + struct drm_gem_object *gem; + struct ttm_buffer_object *ttm_bo; + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + gem = drm_gem_object_lookup(file_priv, args->handle); + if (!gem) { + ret = -EINVAL; + goto exit; + } + + ttm_bo = container_of(gem, struct ttm_buffer_object, base); + + args->offset = drm_vma_node_offset_addr(&ttm_bo->base.vma_node); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} diff --git a/drivers/gpu/drm/via/via_lvds.c b/drivers/gpu/drm/via/via_lvds.c new file mode 100644 index 000000000000..4be95e063ca0 --- /dev/null +++ b/drivers/gpu/drm/via/via_lvds.c @@ -0,0 +1,1233 @@ +/* + * Copyright © 2016-2018 Kevin Brace. + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/pci.h> + +#include <asm/olpc.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include <uapi/linux/i2c.h> + +#include "via_drv.h" + +#define TD0 200 +#define TD1 25 +#define TD2 0 +#define TD3 25 + +/* Non-I2C bus FP native screen resolution information table.*/ +static struct via_lvds_info via_lvds_info_table[] = { + { 640, 480}, + { 800, 600}, + {1024, 768}, + {1280, 768}, + {1280, 1024}, + {1400, 1050}, + {1600, 1200}, + {1280, 800}, + { 800, 480}, + {1024, 768}, + {1366, 768}, + {1024, 768}, + {1280, 768}, + {1280, 1024}, + {1400, 1050}, + {1600, 1200} +}; + +/* Power sequence relations */ +struct td_timer { + struct vga_regset tdRegs[2]; +}; + +static struct td_timer td_timer_regs[] = { + /* td_timer0 */ + { { { VGA_CRT_IC, 0x8B, 0, 7 }, { VGA_CRT_IC, 0x8F, 0, 3 } } }, + /* td_timer1 */ + { { { VGA_CRT_IC, 0x8C, 0, 7 }, { VGA_CRT_IC, 0x8F, 4, 7 } } }, + /* td_timer2 */ + { { { VGA_CRT_IC, 0x8D, 0, 7 }, { VGA_CRT_IC, 0x90, 0, 3 } } }, + /* td_timer3 */ + { { { VGA_CRT_IC, 0x8E, 0, 7 }, { VGA_CRT_IC, 0x90, 4, 7 } } } +}; + +/* + * Function Name: via_init_td_timing_regs + * Description: Init TD timing register (power sequence) + */ +static void via_init_td_timing_regs(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + unsigned int td_timer[4] = { 500, 50, 0, 510 }, i; + struct vga_registers timings; + u32 reg_value; + + /* Fill primary power sequence */ + for (i = 0; i < 4; i++) { + /* Calculate TD Timer, every step is 572.1uSec */ + reg_value = td_timer[i] * 10000 / 5721; + + timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs); + timings.regs = td_timer_regs[i].tdRegs; + load_value_to_registers(VGABASE, &timings, reg_value); + } + + /* Note: VT3353 have two hardware power sequences + * other chips only have one hardware power sequence */ + if (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) { + /* set CRD4[0] to "1" to select 2nd LCD power sequence. */ + svga_wcrt_mask(VGABASE, 0xD4, BIT(0), BIT(0)); + /* Fill secondary power sequence */ + for (i = 0; i < 4; i++) { + /* Calculate TD Timer, every step is 572.1uSec */ + reg_value = td_timer[i] * 10000 / 5721; + + timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs); + timings.regs = td_timer_regs[i].tdRegs; + load_value_to_registers(VGABASE, &timings, reg_value); + } + } +} + +static bool via_fp_probe_edid(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 out = 0x0; + u8 buf[8]; + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &out, + }, + { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = 8, + .buf = buf, + } + }; + int i2c_ret; + bool ret = false; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + i2c_ret = i2c_transfer(i2c_bus, msgs, 2); + if (i2c_ret != 2) { + goto exit; + } + + if (drm_edid_header_is_valid(buf) < 6) { + goto exit; + } + + ret = true; +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static void via_lvds_cle266_soft_power_seq(struct drm_device *dev, + bool power_state) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (power_state) { + /* Wait for 25 ms. */ + mdelay(25); + + /* Turn on FP VDD rail. */ + via_lvds_set_primary_soft_vdd(VGABASE, true); + + /* Wait for 510 ms. */ + mdelay(510); + + /* Turn on FP data transmission. */ + via_lvds_set_primary_soft_data(VGABASE, true); + + /* Wait for 1 ms. */ + mdelay(1); + + /* Turn on FP VEE rail. */ + via_lvds_set_primary_soft_vee(VGABASE, true); + + /* Turn on FP back light. */ + via_lvds_set_primary_soft_back_light(VGABASE, true); + } else { + /* Wait for 1 ms. */ + mdelay(1); + + /* Turn off FP back light. */ + via_lvds_set_primary_soft_back_light(VGABASE, false); + + /* Turn off FP VEE rail. */ + via_lvds_set_primary_soft_vee(VGABASE, false); + + /* Wait for 510 ms. */ + mdelay(510); + + /* Turn off FP data transmission. */ + via_lvds_set_primary_soft_data(VGABASE, false); + + /* Wait for 25 ms. */ + mdelay(25); + + /* Turn off FP VDD rail. */ + via_lvds_set_primary_soft_vdd(VGABASE, false); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_primary_soft_power_seq(struct drm_device *dev, + bool power_state) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Turn off FP hardware power sequence. */ + via_lvds_set_primary_hard_power(VGABASE, false); + + /* Use software FP power sequence control. */ + via_lvds_set_primary_power_seq_type(VGABASE, false); + + if (power_state) { + /* Turn on FP display period. */ + via_lvds_set_primary_direct_display_period(VGABASE, true); + + /* Wait for TD0 ms. */ + mdelay(TD0); + + /* Turn on FP VDD rail. */ + via_lvds_set_primary_soft_vdd(VGABASE, true); + + /* Wait for TD1 ms. */ + mdelay(TD1); + + /* Turn on FP data transmission. */ + via_lvds_set_primary_soft_data(VGABASE, true); + + /* Wait for TD2 ms. */ + mdelay(TD2); + + /* Turn on FP VEE rail. */ + via_lvds_set_primary_soft_vee(VGABASE, true); + + /* Wait for TD3 ms. */ + mdelay(TD3); + + /* Turn on FP back light. */ + via_lvds_set_primary_soft_back_light(VGABASE, true); + } else { + /* Turn off FP back light. */ + via_lvds_set_primary_soft_back_light(VGABASE, false); + + /* Wait for TD3 ms. */ + mdelay(TD3); + + /* Turn off FP VEE rail. */ + via_lvds_set_primary_soft_vee(VGABASE, false); + + /* Wait for TD2 ms. */ + mdelay(TD2); + + /* Turn off FP data transmission. */ + via_lvds_set_primary_soft_data(VGABASE, false); + + /* Wait for TD1 ms. */ + mdelay(TD1); + + /* Turn off FP VDD rail. */ + via_lvds_set_primary_soft_vdd(VGABASE, false); + + /* Turn off FP display period. */ + via_lvds_set_primary_direct_display_period(VGABASE, false); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_secondary_soft_power_seq(struct drm_device *dev, + bool power_state) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Turn off FP hardware power sequence. */ + via_lvds_set_secondary_hard_power(VGABASE, false); + + /* Use software FP power sequence control. */ + via_lvds_set_secondary_power_seq_type(VGABASE, false); + + if (power_state) { + /* Turn on FP display period. */ + via_lvds_set_secondary_direct_display_period(VGABASE, true); + + /* Wait for TD0 ms. */ + mdelay(TD0); + + /* Turn on FP VDD rail. */ + via_lvds_set_secondary_soft_vdd(VGABASE, true); + + /* Wait for TD1 ms. */ + mdelay(TD1); + + /* Turn on FP data transmission. */ + via_lvds_set_secondary_soft_data(VGABASE, true); + + /* Wait for TD2 ms. */ + mdelay(TD2); + + /* Turn on FP VEE rail. */ + via_lvds_set_secondary_soft_vee(VGABASE, true); + + /* Wait for TD3 ms. */ + mdelay(TD3); + + /* Turn on FP back light. */ + via_lvds_set_secondary_soft_back_light(VGABASE, true); + } else { + /* Turn off FP back light. */ + via_lvds_set_secondary_soft_back_light(VGABASE, false); + + /* Wait for TD3 ms. */ + mdelay(TD3); + + /* Turn off FP VEE rail. */ + via_lvds_set_secondary_soft_vee(VGABASE, false); + + /* Wait for TD2 ms. */ + mdelay(TD2); + + /* Turn off FP data transmission. */ + via_lvds_set_secondary_soft_data(VGABASE, false); + + /* Wait for TD1 ms. */ + mdelay(TD1); + + /* Turn off FP VDD rail. */ + via_lvds_set_secondary_soft_vdd(VGABASE, false); + + /* Turn off FP display period. */ + via_lvds_set_secondary_direct_display_period(VGABASE, false); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_primary_hard_power_seq(struct drm_device *dev, + bool power_state) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Use hardware FP power sequence control. */ + via_lvds_set_primary_power_seq_type(VGABASE, true); + + if (power_state) { + /* Turn on FP display period. */ + via_lvds_set_primary_direct_display_period(VGABASE, true); + + /* Turn on FP hardware power sequence. */ + via_lvds_set_primary_hard_power(VGABASE, true); + + /* Turn on FP back light. */ + via_lvds_set_primary_direct_back_light_ctrl(VGABASE, true); + } else { + /* Turn off FP back light. */ + via_lvds_set_primary_direct_back_light_ctrl(VGABASE, false); + + /* Turn off FP hardware power sequence. */ + via_lvds_set_primary_hard_power(VGABASE, false); + + /* Turn on FP display period. */ + via_lvds_set_primary_direct_display_period(VGABASE, false); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_power(struct drm_device *dev, + u32 di_port, bool power_state) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + via_lvds_cle266_soft_power_seq(dev, power_state); + break; + case PCI_DEVICE_ID_VIA_KM400_GFX: + case PCI_DEVICE_ID_VIA_P4M800_PRO_GFX: + case PCI_DEVICE_ID_VIA_PM800_GFX: + case PCI_DEVICE_ID_VIA_K8M800_GFX: + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + via_lvds_primary_hard_power_seq(dev, power_state); + break; + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + if (di_port & VIA_DI_PORT_LVDS1) { + via_lvds_primary_soft_power_seq(dev, power_state); + via_lvds1_set_power(VGABASE, power_state); + } + + if (di_port & VIA_DI_PORT_LVDS2) { + via_lvds_secondary_soft_power_seq(dev, power_state); + via_lvds2_set_power(VGABASE, power_state); + } + + break; + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + via_lvds_primary_hard_power_seq(dev, power_state); + via_lvds1_set_power(VGABASE, power_state); + break; + default: + drm_dbg_kms(dev, "VIA Technologies Chrome IGP " + "FP Power: Unrecognized " + "PCI Device ID.\n"); + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Sets flat panel I/O pad state. + */ +static void via_lvds_io_pad_setting(struct drm_device *dev, + u32 di_port, bool io_pad_on) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DVP0: + via_dvp0_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_DVP1: + via_dvp1_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_FPDPLOW: + via_fpdp_low_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_FPDPHIGH: + via_fpdp_high_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case (VIA_DI_PORT_FPDPLOW | + VIA_DI_PORT_FPDPHIGH): + via_fpdp_low_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00); + via_fpdp_high_set_io_pad_state(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_LVDS1: + via_lvds1_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00); + via_lvds2_set_io_pad_setting(VGABASE, io_pad_on ? 0x03 : 0x00); + break; + default: + break; + } + + drm_dbg_kms(dev, "FP I/O Pad: %s\n", io_pad_on ? "On": "Off"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_format(struct drm_device *dev, + u32 di_port, u8 format) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 temp = format & 0x01; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_LVDS1: + via_lvds1_set_format(VGABASE, temp); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_format(VGABASE, temp); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_format(VGABASE, temp); + via_lvds2_set_format(VGABASE, temp); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_output_format(struct drm_device *dev, + u32 di_port, u8 output_format) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 temp = output_format & 0x01; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_LVDS1: + via_lvds1_set_output_format(VGABASE, temp); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_output_format(VGABASE, temp); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_output_format(VGABASE, temp); + via_lvds2_set_output_format(VGABASE, temp); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_dithering(struct drm_device *dev, + u32 di_port, bool dithering) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_LVDS1: + via_lvds1_set_dithering(VGABASE, dithering); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_dithering(VGABASE, dithering); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_dithering(VGABASE, dithering); + via_lvds2_set_dithering(VGABASE, dithering); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Sets flat panel display source. + */ +static void via_lvds_display_source(struct drm_device *dev, + u32 di_port, int index) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 display_source = index & 0x01; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DVP0: + via_dvp0_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_DVP1: + via_dvp1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_FPDPLOW: + via_fpdp_low_set_display_source(VGABASE, display_source); + via_dvp1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_FPDPHIGH: + via_fpdp_high_set_display_source(VGABASE, display_source); + via_dvp0_set_display_source(VGABASE, display_source); + break; + case (VIA_DI_PORT_FPDPLOW | + VIA_DI_PORT_FPDPHIGH): + via_fpdp_low_set_display_source(VGABASE, display_source); + via_fpdp_high_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_LVDS1: + via_lvds1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_display_source(VGABASE, display_source); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_display_source(VGABASE, display_source); + via_lvds2_set_display_source(VGABASE, display_source); + break; + default: + break; + } + + drm_dbg_kms(dev, "FP Display Source: IGA%d\n", + display_source + 1); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (mode) { + case DRM_MODE_DPMS_ON: + via_lvds_power(dev, enc->di_port, true); + via_lvds_io_pad_setting(dev, enc->di_port, true); + break; + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_OFF: + via_lvds_power(dev, enc->di_port, false); + via_lvds_io_pad_setting(dev, enc->di_port, false); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_prepare(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_lvds_power(dev, enc->di_port, false); + via_lvds_io_pad_setting(dev, enc->di_port, false); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_commit(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_lvds_power(dev, enc->di_port, true); + via_lvds_io_pad_setting(dev, enc->di_port, true); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void +via_lvds_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base); + struct via_encoder *enc = container_of(encoder, struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Temporary implementation.*/ + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CHROME9_HC: + via_fpdp_low_set_adjustment(VGABASE, 0x08); + break; + default: + break; + } + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* OPENLDI Mode */ + via_lvds_format(dev, enc->di_port, 0x01); + + /* Sequential Mode */ + via_lvds_output_format(dev, enc->di_port, 0x01); + + /* Turn on dithering. */ + via_lvds_dithering(dev, enc->di_port, true); + break; + default: + break; + } + + via_lvds_display_source(dev, enc->di_port, iga->index); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_lvds_disable(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_lvds_power(dev, enc->di_port, false); + via_lvds_io_pad_setting(dev, enc->di_port, false); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +const struct drm_encoder_helper_funcs via_lvds_helper_funcs = { + .dpms = via_lvds_dpms, + .prepare = via_lvds_prepare, + .commit = via_lvds_commit, + .mode_set = via_lvds_mode_set, + .disable = via_lvds_disable, +}; + +const struct drm_encoder_funcs via_lvds_enc_funcs = { + .destroy = via_encoder_destroy, +}; + +/* Detect FP presence. */ +static enum drm_connector_status +via_lvds_detect(struct drm_connector *connector, bool force) +{ + struct drm_device *dev = connector->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_connector *con = container_of(connector, + struct via_connector, base); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + enum drm_connector_status ret = connector_status_disconnected; + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + u8 mask; + uint32_t i, i2c_bus_bit; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (machine_is_olpc()) { + ret = connector_status_connected; + goto exit; + } + + i2c_bus_bit = VIA_I2C_BUS2; + for (i = 0; i < 2; i++) { + if (con->i2c_bus & i2c_bus_bit) { + if (i2c_bus_bit & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (i2c_bus_bit & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x2c); + } else { + i2c_bus = NULL; + i2c_bus_bit = i2c_bus_bit << 1; + continue; + } + } else { + i2c_bus = NULL; + i2c_bus_bit = i2c_bus_bit << 1; + continue; + } + + if (!via_fp_probe_edid(dev, i2c_bus)) { + i2c_bus_bit = i2c_bus_bit << 1; + continue; + } + + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + ret = connector_status_connected; + kfree(edid); + drm_dbg_kms(dev, "FP detected.\n"); + drm_dbg_kms(dev, "i2c_bus_bit: %x\n", i2c_bus_bit); + goto exit; + } else { + kfree(edid); + } + } + + i2c_bus_bit = i2c_bus_bit << 1; + } + + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + mask = BIT(3); + } else { + mask = BIT(1); + } + + if (vga_rcrt(VGABASE, 0x3B) & mask) { + ret = connector_status_connected; + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +struct drm_connector_funcs via_lvds_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_lvds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = via_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = + drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_connector_destroy_state, +}; + +static int +via_lvds_get_modes(struct drm_connector *connector) +{ + struct via_connector *con = container_of(connector, struct via_connector, base); + struct drm_device *dev = connector->dev; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + struct drm_display_mode *native_mode = NULL; + u8 reg_value; + int hdisplay, vdisplay; + int count = 0; + uint32_t i, i2c_bus_bit; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* OLPC is very special */ + if (machine_is_olpc()) { + native_mode = drm_mode_create(dev); + + native_mode->clock = 56519; + native_mode->hdisplay = 1200; + native_mode->hsync_start = 1211; + native_mode->hsync_end = 1243; + native_mode->htotal = 1264; + native_mode->hskew = 0; + native_mode->vdisplay = 900; + native_mode->vsync_start = 901; + native_mode->vsync_end = 911; + native_mode->vtotal = 912; + native_mode->vscan = 0; + + native_mode->type = DRM_MODE_TYPE_PREFERRED | + DRM_MODE_TYPE_DRIVER; + drm_mode_set_name(native_mode); + drm_mode_probed_add(connector, native_mode); + count = 1; + goto exit; + } + + i2c_bus_bit = VIA_I2C_BUS2; + for (i = 0; i < 2; i++) { + if (con->i2c_bus & i2c_bus_bit) { + if (i2c_bus_bit & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (i2c_bus_bit & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x2c); + } else { + i2c_bus = NULL; + i2c_bus_bit = i2c_bus_bit << 1; + continue; + } + } else { + i2c_bus = NULL; + i2c_bus_bit = i2c_bus_bit << 1; + continue; + } + + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + drm_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + kfree(edid); + drm_dbg_kms(dev, "FP EDID information was obtained.\n"); + drm_dbg_kms(dev, "i2c_bus_bit: %x\n", i2c_bus_bit); + break; + } else { + kfree(edid); + } + } + + i2c_bus_bit = i2c_bus_bit << 1; + } + + reg_value = (vga_rcrt(VGABASE, 0x3f) & 0x0f); + hdisplay = vdisplay = 0; + hdisplay = via_lvds_info_table[reg_value].x; + vdisplay = via_lvds_info_table[reg_value].y; + + if (hdisplay && vdisplay) { + native_mode = drm_cvt_mode(dev, hdisplay, vdisplay, + 60, false, false, false); + } + + if (native_mode) { + native_mode->type = DRM_MODE_TYPE_PREFERRED | + DRM_MODE_TYPE_DRIVER; + drm_mode_set_name(native_mode); + drm_mode_probed_add(connector, native_mode); + count = 1; + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return count; +} + +struct drm_connector_helper_funcs via_lvds_connector_helper_funcs = { + .get_modes = via_lvds_get_modes, +}; + +/* + * Probe (pre-initialization detection) FP. + */ +void via_lvds_probe(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct drm_connector connector; + struct i2c_adapter *i2c_bus; + struct edid *edid; + u8 sr12, sr13, sr5a; + u8 cr3b; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + sr12 = vga_rseq(VGABASE, 0x12); + sr13 = vga_rseq(VGABASE, 0x13); + cr3b = vga_rcrt(VGABASE, 0x3b); + + drm_dbg_kms(dev, "sr12: 0x%02x\n", sr12); + drm_dbg_kms(dev, "sr13: 0x%02x\n", sr13); + drm_dbg_kms(dev, "cr3b: 0x%02x\n", cr3b); + + /* Detect the presence of FPs. */ + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + /* + * 3C5.12[4] - FPD17 pin strapping (DIP1) + * 0: DVI / Capture + * 1: Panel + */ + if ((sr12 & BIT(4)) || (cr3b & BIT(3))) { + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = VIA_DI_PORT_DIP1; + } else { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + } + + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + break; + case PCI_DEVICE_ID_VIA_KM400_GFX: + case PCI_DEVICE_ID_VIA_P4M800_PRO_GFX: + case PCI_DEVICE_ID_VIA_PM800_GFX: + case PCI_DEVICE_ID_VIA_K8M800_GFX: + /* 3C5.13[3] - DVP0D8 pin strapping + * 0: AGP pins are used for AGP + * 1: AGP pins are used by FPDP + * (Flat Panel Display Port) */ + if ((sr13 & BIT(3)) && (cr3b & BIT(1))) { + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = VIA_DI_PORT_FPDPHIGH | + VIA_DI_PORT_FPDPLOW; + } else { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + } + + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + break; + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + if (cr3b & BIT(1)) { + /* 3C5.12[4] - DVP0D4 pin strapping + * 0: 12-bit FPDP (Flat Panel + * Display Port) + * 1: 24-bit FPDP (Flat Panel + * Display Port) */ + if (sr12 & BIT(4)) { + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = VIA_DI_PORT_FPDPLOW | + VIA_DI_PORT_FPDPHIGH; + } else { + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = + VIA_DI_PORT_FPDPLOW; + } + } else { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + } + + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + break; + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* Save SR5A. */ + sr5a = vga_rseq(VGABASE, 0x5a); + + drm_dbg_kms(dev, "sr5a: 0x%02x\n", sr5a); + + /* Set SR5A[0] to 1. + * This allows the read out of the alternative + * pin strapping settings from SR12 and SR13. */ + svga_wseq_mask(VGABASE, 0x5a, BIT(0), BIT(0)); + + sr13 = vga_rseq(VGABASE, 0x13); + if (cr3b & BIT(1)) { + if (dev_priv->is_via_nanobook) { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + dev_priv->int_fp2_presence = true; + dev_priv->int_fp2_di_port = VIA_DI_PORT_LVDS2; + } else if (dev_priv->is_quanta_il1) { + /* From the Quanta IL1 schematic. */ + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = VIA_DI_PORT_DVP1; + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + + } else if (dev_priv->is_samsung_nc20) { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + dev_priv->int_fp2_presence = true; + dev_priv->int_fp2_di_port = VIA_DI_PORT_LVDS2; + + /* 3C5.13[7:6] - Integrated LVDS / DVI + * Mode Select (DVP1D15-14 pin + * strapping) + * 00: LVDS1 + LVDS2 + * 01: DVI + LVDS2 + * 10: Dual LVDS Channel + * (High Resolution Panel) + * 11: One DVI only (decrease + * the clock jitter) */ + } else if ((!(sr13 & BIT(7))) && + (!(sr13 & BIT(6)))) { + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = VIA_DI_PORT_LVDS1; + + /* + * For now, don't support the second + * FP. + */ + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + } else if ((!(sr13 & BIT(7))) && + (sr13 & BIT(6))) { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + dev_priv->int_fp2_presence = true; + dev_priv->int_fp2_di_port = VIA_DI_PORT_LVDS2; + } else if ((sr13 & BIT(7)) && + (!(sr13 & BIT(6)))) { + dev_priv->int_fp1_presence = true; + dev_priv->int_fp1_di_port = VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2; + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + } else { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + } + } else { + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + } + + /* Restore SR5A. */ + vga_wseq(VGABASE, 0x5a, sr5a); + break; + default: + dev_priv->int_fp1_presence = false; + dev_priv->int_fp1_di_port = VIA_DI_PORT_NONE; + dev_priv->int_fp2_presence = false; + dev_priv->int_fp2_di_port = VIA_DI_PORT_NONE; + break; + } + + dev_priv->int_fp1_i2c_bus = VIA_I2C_NONE; + dev_priv->int_fp2_i2c_bus = VIA_I2C_NONE; + + /* Zero clear connector struct. + * Not doing so leads to a crash. */ + memset(&connector, 0, sizeof(connector)); + + /* Register a connector only for I2C bus probing. */ + drm_connector_init(dev, &connector, &via_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(&connector, + &via_lvds_connector_helper_funcs); + drm_connector_register(&connector); + + if ((dev_priv->int_fp1_presence) + && (!(dev_priv->mapped_i2c_bus & VIA_I2C_BUS2))) { + i2c_bus = via_find_ddc_bus(0x31); + edid = drm_get_edid(&connector, i2c_bus); + if (edid) { + dev_priv->int_fp1_i2c_bus = VIA_I2C_BUS2; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2; + kfree(edid); + } + } + + if ((dev_priv->int_fp2_presence) + && (!(dev_priv->mapped_i2c_bus & VIA_I2C_BUS2))) { + i2c_bus = via_find_ddc_bus(0x31); + edid = drm_get_edid(&connector, i2c_bus); + if (edid) { + dev_priv->int_fp2_i2c_bus = VIA_I2C_BUS2; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2; + kfree(edid); + } + } + + /* Release the connector resource. */ + drm_connector_unregister(&connector); + drm_connector_cleanup(&connector); + + drm_dbg_kms(dev, "int_fp1_presence: %x\n", + dev_priv->int_fp1_presence); + drm_dbg_kms(dev, "int_fp1_di_port: 0x%08x\n", + dev_priv->int_fp1_di_port); + drm_dbg_kms(dev, "int_fp1_i2c_bus: 0x%08x\n", + dev_priv->int_fp1_i2c_bus); + drm_dbg_kms(dev, "int_fp2_presence: %x\n", + dev_priv->int_fp2_presence); + drm_dbg_kms(dev, "int_fp2_di_port: 0x%08x\n", + dev_priv->int_fp2_di_port); + drm_dbg_kms(dev, "int_fp2_i2c_bus: 0x%08x\n", + dev_priv->int_fp2_i2c_bus); + drm_dbg_kms(dev, "mapped_i2c_bus: 0x%08x\n", + dev_priv->mapped_i2c_bus); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_lvds_init(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_connector *con; + struct via_encoder *enc; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if ((!(dev_priv->int_fp1_presence)) && + (!(dev_priv->int_fp2_presence))) { + goto exit; + } + + enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL); + if (!enc) { + drm_err(dev, "Failed to allocate FP.\n"); + goto exit; + } + + con = &enc->cons[0]; + INIT_LIST_HEAD(&con->props); + + drm_connector_init(dev, &con->base, &via_lvds_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(&con->base, &via_lvds_connector_helper_funcs); + drm_connector_register(&con->base); + + if (dev_priv->int_fp1_presence) { + con->i2c_bus = dev_priv->int_fp1_i2c_bus; + } else if (dev_priv->int_fp2_presence) { + con->i2c_bus = dev_priv->int_fp2_i2c_bus; + } else { + con->i2c_bus = VIA_I2C_NONE; + } + + con->base.doublescan_allowed = false; + con->base.interlace_allowed = false; + + /* Now setup the encoder */ + drm_encoder_init(dev, &enc->base, &via_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS, NULL); + drm_encoder_helper_add(&enc->base, &via_lvds_helper_funcs); + + enc->base.possible_crtcs = BIT(1) | BIT(0); + + if (dev_priv->int_fp1_presence) { + enc->di_port = dev_priv->int_fp1_di_port; + } else if (dev_priv->int_fp2_presence) { + enc->di_port = dev_priv->int_fp2_di_port; + } else { + enc->di_port = VIA_DI_PORT_NONE; + } + + /* Put it all together */ + drm_connector_attach_encoder(&con->base, &enc->base); + + /* Init TD timing register (power sequence) */ + via_init_td_timing_regs(dev); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return; +} diff --git a/drivers/gpu/drm/via/via_object.c b/drivers/gpu/drm/via/via_object.c new file mode 100644 index 000000000000..f4e9d33ff319 --- /dev/null +++ b/drivers/gpu/drm/via/via_object.c @@ -0,0 +1,327 @@ +/* + * Copyright © 2018-2019 Kevin Brace. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ +/* + * via_object.c + * + * Manages Buffer Objects (BO) via TTM. + * Part of the TTM memory allocator. + * + */ + +#include <drm/drm_file.h> +#include <drm/drm_gem.h> +#include <drm/drm_gem_ttm_helper.h> + +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_range_manager.h> + +#include "via_drv.h" + + +static void via_gem_free(struct drm_gem_object *obj) +{ + struct ttm_buffer_object *ttm_bo = container_of(obj, + struct ttm_buffer_object, base); + struct drm_device *dev = ttm_bo->base.dev; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + ttm_bo_put(ttm_bo); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static const struct vm_operations_struct via_ttm_bo_vm_ops = { + .fault = ttm_bo_vm_fault, + .open = ttm_bo_vm_open, + .close = ttm_bo_vm_close, + .access = ttm_bo_vm_access +}; + +static const struct drm_gem_object_funcs via_gem_object_funcs = { + .free = via_gem_free, + .vmap = drm_gem_ttm_vmap, + .vunmap = drm_gem_ttm_vunmap, + .mmap = drm_gem_ttm_mmap, + .vm_ops = &via_ttm_bo_vm_ops, +}; + +void via_ttm_domain_to_placement(struct via_bo *bo, + uint32_t ttm_domain) +{ + struct ttm_buffer_object *ttm_bo = &bo->ttm_bo; + struct drm_device *dev = ttm_bo->base.dev; + unsigned i = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + + if (ttm_domain == TTM_PL_SYSTEM) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + bo->placements[i].mem_type = TTM_PL_SYSTEM; + bo->placements[i].flags = 0; + i++; + } + + if (ttm_domain == TTM_PL_TT) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + bo->placements[i].mem_type = TTM_PL_TT; + bo->placements[i].flags = 0; + i++; + } + + if (ttm_domain == TTM_PL_VRAM) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + bo->placements[i].mem_type = TTM_PL_VRAM; + bo->placements[i].flags = 0; + i++; + } + + bo->placement.num_placement = i; + bo->placement.num_busy_placement = i; + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +void via_ttm_bo_destroy(struct ttm_buffer_object *tbo) +{ + struct via_bo *bo; + struct drm_device *dev = tbo->base.dev; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + bo = to_ttm_bo(tbo); + + drm_gem_object_release(&tbo->base); + kfree(bo); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +int via_bo_pin(struct via_bo *bo, uint32_t ttm_domain) +{ + struct ttm_buffer_object *ttm_bo = &bo->ttm_bo; + struct drm_device *dev = ttm_bo->base.dev; + struct ttm_operation_ctx ctx = {false, false}; + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + if (ttm_bo->pin_count) { + goto pin; + } + + via_ttm_domain_to_placement(bo, ttm_domain); + ret = ttm_bo_validate(ttm_bo, &bo->placement, &ctx); + if (ret) { + goto exit; + } + +pin: + ttm_bo_pin(ttm_bo); +exit: + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +void via_bo_unpin(struct via_bo *bo) +{ + struct ttm_buffer_object *ttm_bo = &bo->ttm_bo; + struct drm_device *dev = ttm_bo->base.dev; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + ttm_bo_unpin(ttm_bo); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +int via_bo_create(struct drm_device *dev, + struct ttm_device *bdev, + uint64_t size, + enum ttm_bo_type type, + uint32_t ttm_domain, + bool kmap, + struct via_bo **bo_ptr) +{ + struct ttm_buffer_object *ttm_bo; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_bo *bo; + int ret; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) { + drm_err(dev, "Cannot allocate a TTM buffer object!\n"); + ret = -ENOMEM; + goto exit; + } + + ttm_bo = &bo->ttm_bo; + + /* + * It is an imperative to page align the requested buffer size + * prior to a memory allocation request, or various memory + * allocation related system instabilities may occur. + */ + size = ALIGN(size, PAGE_SIZE); + + ret = drm_gem_object_init(dev, &ttm_bo->base, size); + if (ret) { + drm_err(dev, "Cannot initialize a GEM object!\n"); + goto error; + } + + ttm_bo->base.funcs = &via_gem_object_funcs; + + via_ttm_domain_to_placement(bo, ttm_domain); + ret = ttm_bo_init_validate(&dev_priv->bdev, ttm_bo, + type, &bo->placement, + PAGE_SIZE >> PAGE_SHIFT, false, + NULL, NULL, via_ttm_bo_destroy); + if (ret) { + drm_err(dev, "Cannot initialize a TTM object!\n"); + goto exit; + } + + if (kmap) { + ret = ttm_bo_reserve(ttm_bo, true, false, NULL); + if (ret) { + ttm_bo_put(ttm_bo); + goto exit; + } + + ret = via_bo_pin(bo, ttm_domain); + ttm_bo_unreserve(ttm_bo); + if (ret) { + ttm_bo_put(ttm_bo); + goto exit; + } + + ret = ttm_bo_kmap(ttm_bo, 0, PFN_UP(ttm_bo->resource->size), + &bo->kmap); + if (ret) { + ttm_bo_put(ttm_bo); + goto exit; + } + } + + *bo_ptr = bo; + goto exit; +error: + kfree(bo); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +void via_bo_destroy(struct via_bo *bo, bool kmap) +{ + struct ttm_buffer_object *ttm_bo = &bo->ttm_bo; + struct drm_device *dev = ttm_bo->base.dev; + int ret; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + if (kmap) { + ttm_bo_kunmap(&bo->kmap); + + ret = ttm_bo_reserve(ttm_bo, true, false, NULL); + if (ret) { + goto exit; + } + + via_bo_unpin(bo); + ttm_bo_unreserve(ttm_bo); + if (ret) { + goto exit; + } + } + + ttm_bo_put(ttm_bo); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +int via_mm_init(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + int ret; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + /* + * Initialize bdev ttm_bo_device struct. + */ + ret = ttm_device_init(&dev_priv->bdev, + &via_bo_driver, + dev->dev, + dev->anon_inode->i_mapping, + dev->vma_offset_manager, + false, + true); + if (ret) { + drm_err(dev, "Failed initializing buffer object driver!\n"); + goto exit; + } + + /* + * Initialize TTM range manager for VRAM management. + */ + ret = ttm_range_man_init(&dev_priv->bdev, TTM_PL_VRAM, false, + dev_priv->vram_size >> PAGE_SHIFT); + if (ret) { + drm_err(dev, "Failed initializing TTM VRAM memory manager!\n"); + goto error_ttm_range_man; + } + + goto exit; +error_ttm_range_man: + ttm_device_fini(&dev_priv->bdev); +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +void via_mm_fini(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + ttm_range_man_fini(&dev_priv->bdev, TTM_PL_VRAM); + + ttm_device_fini(&dev_priv->bdev); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} diff --git a/drivers/gpu/drm/via/via_pll.c b/drivers/gpu/drm/via/via_pll.c new file mode 100644 index 000000000000..4ebaa5ce1b70 --- /dev/null +++ b/drivers/gpu/drm/via/via_pll.c @@ -0,0 +1,255 @@ +/* + * Copyright 2012 James Simmons. All Rights Reserved. + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + * + * Author(s): + * James Simmons <jsimmons@infradead.org> + */ + +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include "via_drv.h" + + +#define CSR_VCO_UP 600000000 +#define CSR_VCO_DOWN 300000000 + +#define PLL_DTZ_DEFAULT (BIT(0) | BIT(1)) + +#define VIA_CLK_REFERENCE 14318180 + +struct pll_mrn_value { + u32 pll_m; + u32 pll_r; + u32 pll_n; + u32 diff_clk; + u32 pll_fout; +}; + +/* + * This function first gets the best frequency M, R, N value + * to program the PLL according to the supplied frequence + * passed in. After we get the MRN values the results are + * formatted to fit properly into the PLL clock registers. + * + * PLL registers M, R, N value + * [31:16] DM[7:0] + * [15:8 ] DR[2:0] + * [7 :0 ] DN[6:0] + */ +u32 via_get_clk_value(struct drm_device *dev, u32 freq) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + u32 best_pll_n = 2, best_pll_r = 0, best_pll_m = 2, best_clk_diff = freq; + u32 pll_fout, pll_fvco, pll_mrn = 0; + u32 pll_n, pll_r, pll_m, clk_diff; + struct pll_mrn_value pll_tmp[5] = { + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0 } }; + int count; + + if ((pdev->device != PCI_DEVICE_ID_VIA_CLE266_GFX) && + (pdev->device != PCI_DEVICE_ID_VIA_KM400_GFX)) { + /* DN[6:0] */ + for (pll_n = 2; pll_n < 6; pll_n++) { + /* DR[2:0] */ + for (pll_r = 0; pll_r < 6; pll_r++) { + /* DM[9:0] */ + for (pll_m = 2; pll_m < 512; pll_m++) { + /* first divide pll_n then multiply + * pll_m. We have to reduce pll_m + * to 512 to get rid of the overflow */ + pll_fvco = (VIA_CLK_REFERENCE / pll_n) * pll_m; + if ((pll_fvco >= CSR_VCO_DOWN) && (pll_fvco <= CSR_VCO_UP)) { + pll_fout = pll_fvco >> pll_r; + if (pll_fout < freq) + clk_diff = freq - pll_fout; + else + clk_diff = pll_fout - freq; + + /* if frequency (which is the PLL we want + * to set) > 150MHz, the MRN value we + * write in register must < frequency, and + * get MRN value whose M is the largeset */ + if (freq >= 150000000) { + if ((clk_diff <= pll_tmp[0].diff_clk) || pll_tmp[0].pll_fout == 0) { + for (count = ARRAY_SIZE(pll_tmp) - 1; count >= 1; count--) + pll_tmp[count] = pll_tmp[count - 1]; + + pll_tmp[0].pll_m = pll_m; + pll_tmp[0].pll_r = pll_r; + pll_tmp[0].pll_n = pll_n; + pll_tmp[0].diff_clk = clk_diff; + pll_tmp[0].pll_fout = pll_fout; + } + } + + if (clk_diff < best_clk_diff) { + best_clk_diff = clk_diff; + best_pll_m = pll_m; + best_pll_n = pll_n; + best_pll_r = pll_r; + } + } /* if pll_fvco in VCO range */ + } /* for PLL M */ + } /* for PLL R */ + } /* for PLL N */ + + /* if frequency(which is the PLL we want to set) > 150MHz, + * the MRN value we write in register must < frequency, + * and get MRN value whose M is the largeset */ + if (freq > 150000000) { + best_pll_m = pll_tmp[0].pll_m; + best_pll_r = pll_tmp[0].pll_r; + best_pll_n = pll_tmp[0].pll_n; + } + /* UniChrome IGP (CLE266, KM400(A), KN400, and P4M800 chipsets) + * requires a different formula for calculating the PLL parameters. + * The code was borrowed from OpenChrome DDX device driver UMS + * (User Mode Setting) section, but was modified to not use float type + * variables. */ + } else { + for (pll_r = 0; pll_r < 4; ++pll_r) { + for (pll_n = (pll_r == 0) ? 2 : 1; pll_n <= 7; ++pll_n) { + for (pll_m = 1; pll_m <= 127; ++pll_m) { + pll_fout = VIA_CLK_REFERENCE * pll_m; + pll_fout /= (pll_n << pll_r); + if (pll_fout < freq) + clk_diff = freq - pll_fout; + else + clk_diff = pll_fout - freq; + + if (clk_diff < best_clk_diff) { + best_clk_diff = clk_diff; + best_pll_m = pll_m & 0x7F; + best_pll_n = pll_n & 0x1F; + best_pll_r = pll_r & 0x03; + } + } + } + } + } + + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + case PCI_DEVICE_ID_VIA_KM400_GFX: + /* Clock Synthesizer Value 0[7:6]: DR[1:0] + * Clock Synthesizer Value 0[5:0]: DN[5:0] */ + pll_mrn = ((best_pll_r & 0x3) << 14 | + (best_pll_n & 0x1F) << 8); + /* Clock Synthesizer Value 1[6:0]: DM[6:0] */ + pll_mrn |= (best_pll_m & 0x7F); + break; + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + /* Clock Synthesizer Value 0 : DM[7:0] */ + pll_mrn = (best_pll_m & 0xFF) << 16; + /* Clock Synthesizer Value 1[1:0] : DM[9:8] + * Clock Synthesizer Value 1[4:2] : DR[2:0] + * Clock Synthesizer Value 1[7] : DTZ[0] */ + pll_mrn |= (((PLL_DTZ_DEFAULT & 0x1) << 7) | + ((best_pll_r & 0x7) << 2) | + (((best_pll_m) >> 8) & 0x3)) << 8; + /* Clock Synthesizer Value 2[6:0] : DN[6:0] + * Clock Synthesizer Value 2[7] : DTZ[1] */ + pll_mrn |= (((PLL_DTZ_DEFAULT >> 1) & 0x1) << 7) | + ((best_pll_n) & 0x7F); + break; + default: + /* Clock Synthesizer Value 0 : DM[7:0] */ + pll_mrn = ((best_pll_m - 2) & 0xFF) << 16; + /* Clock Synthesizer Value 1[1:0] : DM[9:8] + * Clock Synthesizer Value 1[4:2] : DR[2:0] + * Clock Synthesizer Value 1[7] : DTZ[0] */ + pll_mrn |= (((PLL_DTZ_DEFAULT & 0x1) << 7) | + ((best_pll_r & 0x7) << 2) | + (((best_pll_m - 2) >> 8) & 0x3)) << 8; + /* Clock Synthesizer Value 2[6:0] : DN[6:0] + * Clock Synthesizer Value 2[7] : DTZ[1] */ + pll_mrn |= (((PLL_DTZ_DEFAULT >> 1) & 0x1) << 7) | + ((best_pll_n - 2) & 0x7F); + break; + } + return pll_mrn; +} + +/* Set VCLK */ +void via_set_vclock(struct drm_crtc *crtc, u32 clk) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_device *dev = crtc->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + unsigned long max_loop = 50, i = 0; + + if (!iga->index) { + /* IGA1 HW Reset Enable */ + svga_wcrt_mask(VGABASE, 0x17, 0x00, BIT(7)); + + /* set clk */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + vga_wseq(VGABASE, 0x46, (clk & 0xFF00) >> 8); /* rshift + divisor */ + vga_wseq(VGABASE, 0x47, (clk & 0x00FF)); /* multiplier */ + } else { + vga_wseq(VGABASE, 0x44, (clk & 0xFF0000) >> 16); + vga_wseq(VGABASE, 0x45, (clk & 0x00FF00) >> 8); + vga_wseq(VGABASE, 0x46, (clk & 0x0000FF)); + } + /* Fire */ + svga_wmisc_mask(VGABASE, BIT(3) | BIT(2), BIT(3) | BIT(2)); + + /* reset pll */ + svga_wseq_mask(VGABASE, 0x40, 0x02, 0x02); + svga_wseq_mask(VGABASE, 0x40, 0x00, 0x02); + + /* exit hw reset */ + while ((vga_rseq(VGABASE, 0x3C) & BIT(3)) == 0 && i++ < max_loop) + udelay(20); + + /* IGA1 HW Reset Disable */ + svga_wcrt_mask(VGABASE, 0x17, BIT(7), BIT(7)); + } else { + /* IGA2 HW Reset Enable */ + svga_wcrt_mask(VGABASE, 0x6A, 0x00, BIT(6)); + + /* set clk */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) || + (pdev->device == PCI_DEVICE_ID_VIA_KM400_GFX)) { + vga_wseq(VGABASE, 0x44, (clk & 0xFF00) >> 8); + vga_wseq(VGABASE, 0x45, (clk & 0x00FF)); + } else { + vga_wseq(VGABASE, 0x4A, (clk & 0xFF0000) >> 16); + vga_wseq(VGABASE, 0x4B, (clk & 0x00FF00) >> 8); + vga_wseq(VGABASE, 0x4C, (clk & 0x0000FF)); + } + + /* reset pll */ + svga_wseq_mask(VGABASE, 0x40, 0x04, 0x04); + svga_wseq_mask(VGABASE, 0x40, 0x00, 0x04); + + /* exit hw reset */ + while ((vga_rseq(VGABASE, 0x3C) & BIT(2)) == 0 && i++ < max_loop) + udelay(20); + + /* IGA2 HW Reset Disble, CR6A[6] = 1 */ + svga_wcrt_mask(VGABASE, 0x6A, BIT(6), BIT(6)); + } +} diff --git a/drivers/gpu/drm/via/via_pm.c b/drivers/gpu/drm/via/via_pm.c new file mode 100644 index 000000000000..563525c41af8 --- /dev/null +++ b/drivers/gpu/drm/via/via_pm.c @@ -0,0 +1,189 @@ +/* + * Copyright © 2017-2020 Kevin Brace. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ + + +#include <linux/console.h> +#include <linux/pci.h> + +#include <drm/drm_modeset_helper.h> + +#include "via_drv.h" + + +int via_dev_pm_ops_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct via_drm_priv *dev_priv = to_via_drm_priv(drm_dev); + int ret = 0; + + drm_dbg_driver(drm_dev, "Entered %s.\n", __func__); + + console_lock(); + + /* + * Frame Buffer Size Control register (SR14) and GTI registers + * (SR66 through SR6F) need to be saved and restored upon standby + * resume or can lead to a display corruption issue. These registers + * are only available on VX800, VX855, and VX900 chipsets. This bug + * was observed on VIA Embedded EPIA-M830 mainboard. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) || + (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HCM) || + (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HD)) { + dev_priv->saved_sr14 = vga_rseq(VGABASE, 0x14); + + dev_priv->saved_sr66 = vga_rseq(VGABASE, 0x66); + dev_priv->saved_sr67 = vga_rseq(VGABASE, 0x67); + dev_priv->saved_sr68 = vga_rseq(VGABASE, 0x68); + dev_priv->saved_sr69 = vga_rseq(VGABASE, 0x69); + dev_priv->saved_sr6a = vga_rseq(VGABASE, 0x6a); + dev_priv->saved_sr6b = vga_rseq(VGABASE, 0x6b); + dev_priv->saved_sr6c = vga_rseq(VGABASE, 0x6c); + dev_priv->saved_sr6d = vga_rseq(VGABASE, 0x6d); + dev_priv->saved_sr6e = vga_rseq(VGABASE, 0x6e); + dev_priv->saved_sr6f = vga_rseq(VGABASE, 0x6f); + } + + /* + * 3X5.3B through 3X5.3F are scratch pad registers. + * They are important for FP detection. + * Their values need to be saved because they get lost + * when resuming from standby. + */ + dev_priv->saved_cr3b = vga_rcrt(VGABASE, 0x3b); + dev_priv->saved_cr3c = vga_rcrt(VGABASE, 0x3c); + dev_priv->saved_cr3d = vga_rcrt(VGABASE, 0x3d); + dev_priv->saved_cr3e = vga_rcrt(VGABASE, 0x3e); + dev_priv->saved_cr3f = vga_rcrt(VGABASE, 0x3f); + + console_unlock(); + + ret = drm_mode_config_helper_suspend(drm_dev); + if (ret) { + drm_err(drm_dev, "Failed to prepare for suspend!\n"); + goto exit; + } + + pci_save_state(pdev); + pci_disable_device(pdev); +exit: + drm_dbg_driver(drm_dev, "Exiting %s.\n", __func__); + return ret; +} + +int via_dev_pm_ops_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct via_drm_priv *dev_priv = to_via_drm_priv(drm_dev); + void __iomem *regs = ioport_map(0x3c0, 100); + u8 val; + int ret = 0; + + drm_dbg_driver(drm_dev, "Entered %s.\n", __func__); + + if (pci_enable_device(pdev)) { + drm_err(drm_dev, "Failed to reinitialize a PCI device " + "after resume!\n"); + ret = -EIO; + goto exit; + } + + console_lock(); + + val = ioread8(regs + 0x03); + iowrite8(val | 0x1, regs + 0x03); + val = ioread8(regs + 0x0C); + iowrite8(val | 0x1, regs + 0x02); + + /* + * Unlock Extended IO Space. + */ + iowrite8(0x10, regs + 0x04); + iowrite8(0x01, regs + 0x05); + + /* + * Unlock CRTC register protect. + */ + iowrite8(0x47, regs + 0x14); + + /* + * Enable MMIO. + */ + iowrite8(0x1a, regs + 0x04); + val = ioread8(regs + 0x05); + iowrite8(val | 0x38, regs + 0x05); + + /* + * Frame Buffer Size Control register (SR14) and GTI registers + * (SR66 through SR6F) need to be saved and restored upon standby + * resume or can lead to a display corruption issue. These registers + * are only available on VX800, VX855, and VX900 chipsets. This bug + * was observed on VIA Embedded EPIA-M830 mainboard. + */ + if ((pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HC3) || + (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HCM) || + (pdev->device == PCI_DEVICE_ID_VIA_CHROME9_HD)) { + vga_wseq(VGABASE, 0x14, dev_priv->saved_sr14); + + vga_wseq(VGABASE, 0x66, dev_priv->saved_sr66); + vga_wseq(VGABASE, 0x67, dev_priv->saved_sr67); + vga_wseq(VGABASE, 0x68, dev_priv->saved_sr68); + vga_wseq(VGABASE, 0x69, dev_priv->saved_sr69); + vga_wseq(VGABASE, 0x6a, dev_priv->saved_sr6a); + vga_wseq(VGABASE, 0x6b, dev_priv->saved_sr6b); + vga_wseq(VGABASE, 0x6c, dev_priv->saved_sr6c); + vga_wseq(VGABASE, 0x6d, dev_priv->saved_sr6d); + vga_wseq(VGABASE, 0x6e, dev_priv->saved_sr6e); + vga_wseq(VGABASE, 0x6f, dev_priv->saved_sr6f); + } + + /* + * 3X5.3B through 3X5.3F are scratch pad registers. + * They are important for FP detection. + * Their values need to be restored because they are undefined + * after resuming from standby. + */ + vga_wcrt(VGABASE, 0x3b, dev_priv->saved_cr3b); + vga_wcrt(VGABASE, 0x3c, dev_priv->saved_cr3c); + vga_wcrt(VGABASE, 0x3d, dev_priv->saved_cr3d); + vga_wcrt(VGABASE, 0x3e, dev_priv->saved_cr3e); + vga_wcrt(VGABASE, 0x3f, dev_priv->saved_cr3f); + + console_unlock(); + + ret = drm_mode_config_helper_resume(drm_dev); + if (ret) { + drm_err(drm_dev, "Failed to perform a mode setting " + "after resume!\n"); + goto exit; + } + +exit: + drm_dbg_driver(drm_dev, "Exiting %s.\n", __func__); + return ret; +} diff --git a/drivers/gpu/drm/via/via_regs.h b/drivers/gpu/drm/via/via_regs.h new file mode 100644 index 000000000000..5ac06d75f0b5 --- /dev/null +++ b/drivers/gpu/drm/via/via_regs.h @@ -0,0 +1,296 @@ +/* + * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2009 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. + * + */ + +/************************************************************************* + * + * File: via_regs.h + * Content: The defines of VIA Technologies Chrome registers. + * + ************************************************************************/ + +#ifndef _VIA_REGS_H_ +#define _VIA_REGS_H_ 1 + +#define BIOS_BSIZE 1024 +#define BIOS_BASE 0xc0000 + +#define VIA_MMIO_REGSIZE 0xD000 /* DisplayPort:0xC610~0xC7D4 */ +#define VIA_MMIO_REGBASE 0x0 +#define VIA_MMIO_VGABASE 0x8000 +#define VIA_MMIO_BLTBASE 0x200000 +#define VIA_MMIO_BLTSIZE 0x200000 + +/* defines for VIA 2D registers */ +#define VIA_REG_GECMD 0x000 +#define VIA_REG_GEMODE 0x004 +#define VIA_REG_GESTATUS 0x004 /* as same as VIA_REG_GEMODE */ +#define VIA_REG_SRCPOS 0x008 +#define VIA_REG_DSTPOS 0x00C +#define VIA_REG_LINE_K1K2 0x008 +#define VIA_REG_LINE_XY 0x00C +#define VIA_REG_DIMENSION 0x010 /* width and height */ +#define VIA_REG_PATADDR 0x014 +#define VIA_REG_FGCOLOR 0x018 +#define VIA_REG_DSTCOLORKEY 0x018 /* as same as VIA_REG_FG */ +#define VIA_REG_BGCOLOR 0x01C +#define VIA_REG_SRCCOLORKEY 0x01C /* as same as VIA_REG_BG */ +#define VIA_REG_CLIPTL 0x020 /* top and left of clipping */ +#define VIA_REG_CLIPBR 0x024 /* bottom and right of clipping */ +#define VIA_REG_OFFSET 0x028 +#define VIA_REG_LINE_ERROR 0x028 +#define VIA_REG_KEYCONTROL 0x02C /* color key control */ +#define VIA_REG_SRCBASE 0x030 +#define VIA_REG_DSTBASE 0x034 +#define VIA_REG_PITCH 0x038 /* pitch of src and dst */ +#define VIA_REG_MONOPAT0 0x03C +#define VIA_REG_MONOPAT1 0x040 +#define VIA_REG_COLORPAT 0x100 /* from 0x100 to 0x1ff */ + +/* defineds vor VIA 2D registers for VT3353 (M1 engine) */ +#define VIA_REG_GECMD_M1 0x000 +#define VIA_REG_GEMODE_M1 0x004 +#define VIA_REG_GESTATUS_M1 0x004 /* as same as VIA_REG_GEMODE */ +#define VIA_REG_PITCH_M1 0x008 /* pitch of src and dst */ +#define VIA_REG_DIMENSION_M1 0x00C /* width and height */ +#define VIA_REG_DSTPOS_M1 0x010 +#define VIA_REG_LINE_XY_M1 0x010 +#define VIA_REG_DSTBASE_M1 0x014 +#define VIA_REG_SRCPOS_M1 0x018 +#define VIA_REG_LINE_K1K2_M1 0x018 +#define VIA_REG_SRCBASE_M1 0x01C +#define VIA_REG_PATADDR_M1 0x020 +#define VIA_REG_MONOPAT0_M1 0x024 +#define VIA_REG_MONOPAT1_M1 0x028 +#define VIA_REG_OFFSET_M1 0x02C +#define VIA_REG_LINE_ERROR_M1 0x02C +#define VIA_REG_CLIPTL_M1 0x040 /* top and left of clipping */ +#define VIA_REG_CLIPBR_M1 0x044 /* bottom and right of clipping */ +#define VIA_REG_KEYCONTROL_M1 0x048 /* color key control */ +#define VIA_REG_FGCOLOR_M1 0x04C +#define VIA_REG_DSTCOLORKEY_M1 0x04C /* as same as VIA_REG_FG */ +#define VIA_REG_BGCOLOR_M1 0x050 +#define VIA_REG_SRCCOLORKEY_M1 0x050 /* as same as VIA_REG_BG */ +#define VIA_REG_MONOPATFGC_M1 0x058 /* Add foreground color of Pattern */ +#define VIA_REG_MONOPATBGC_M1 0x05C /* Add background color of Pattern */ +#define VIA_REG_COLORPAT_M1 0x100 /* from 0x100 to 0x1ff */ + +/* defines for VIA video registers */ +#define VIA_REG_INTERRUPT 0x200 +#define VIA_REG_CRTCSTART 0x214 + +/*CN400 and older Hardware Icon engine register*/ +#define HI_POSSTART 0x208 +#define HI_CENTEROFFSET 0x20C +#define HI_FBOFFSET 0x224 +#define HI_CONTROL 0x260 +#define HI_TRANSPARENT_COLOR 0x270 +#define HI_INVTCOLOR 0x274 +/* VT3324 primary Hardware Icon engine register */ +#define PRIM_HI_POSEND 0x290 +#define V327_HI_INVTCOLOR 0x2E4 +#define PRIM_HI_FIFO 0x2E8 +#define PRIM_HI_TRANSCOLOR 0x2EC +#define PRIM_HI_CTRL 0x2F0 +#define PRIM_HI_FBOFFSET 0x2F4 +#define PRIM_HI_POSSTART 0x2F8 +#define PRIM_HI_CENTEROFFSET 0x2FC +#define PRIM_HI_INVTCOLOR 0x120C + +#define ALPHA_V3_PREFIFO_CONTROL 0x268 +#define ALPHA_V3_FIFO_CONTROL 0x278 + +/* defines for VIA 3D registers */ +#define VIA_REG_STATUS 0x400 +#define VIA_REG_TRANSET 0x43C +#define VIA_REG_TRANSPACE 0x440 + +/* VIA_REG_STATUS(0x400): Engine Status */ +#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */ +#define VIA_2D_ENG_BUSY 0x00000002 /* 2D Engine is busy */ +#define VIA_3D_ENG_BUSY 0x00000001 /* 3D Engine is busy */ +#define VIA_VR_QUEUE_EMPTY 0x00020000 /* Virtual Queue is busy */ + +/* VIA_REG_STATUS(0x400): Egine Status */ +#define VIA_CMD_RGTR_BUSY_H5 0x00000010 /* Command Regulator is busy */ +#define VIA_2D_ENG_BUSY_H5 0x00000002 /* 2D Engine is busy */ +#define VIA_3D_ENG_BUSY_H5 0x00001FE1 /* 3D Engine is busy */ +#define VIA_VR_QUEUE_BUSY_H5 0x00000004 /* Virtual Queue is busy */ + +/* VIA_REG_GECMD(0x00): 2D Engine Command */ +#define VIA_GEC_NOOP 0x00000000 +#define VIA_GEC_BLT 0x00000001 +#define VIA_GEC_LINE 0x00000005 + +#define VIA_GEC_SRC_XY 0x00000000 +#define VIA_GEC_SRC_LINEAR 0x00000010 +#define VIA_GEC_DST_XY 0x00000000 +#define VIA_GEC_DST_LINRAT 0x00000020 + +#define VIA_GEC_SRC_FB 0x00000000 +#define VIA_GEC_SRC_SYS 0x00000040 +#define VIA_GEC_DST_FB 0x00000000 +#define VIA_GEC_DST_SYS 0x00000080 + +#define VIA_GEC_SRC_MONO 0x00000100 /* source is mono */ +#define VIA_GEC_PAT_MONO 0x00000200 /* pattern is mono */ + +#define VIA_GEC_MSRC_OPAQUE 0x00000000 /* mono src is opaque */ +#define VIA_GEC_MSRC_TRANS 0x00000400 /* mono src is transparent */ + +#define VIA_GEC_PAT_FB 0x00000000 /* pattern is in frame buffer */ +#define VIA_GEC_PAT_REG 0x00000800 /* pattern is from reg setting */ + +#define VIA_GEC_CLIP_DISABLE 0x00000000 +#define VIA_GEC_CLIP_ENABLE 0x00001000 + +#define VIA_GEC_FIXCOLOR_PAT 0x00002000 + +#define VIA_GEC_INCX 0x00000000 +#define VIA_GEC_DECY 0x00004000 +#define VIA_GEC_INCY 0x00000000 +#define VIA_GEC_DECX 0x00008000 + +#define VIA_GEC_MPAT_OPAQUE 0x00000000 /* mono pattern is opaque */ +#define VIA_GEC_MPAT_TRANS 0x00010000 /* mono pattern is transparent */ + +#define VIA_GEC_MONO_UNPACK 0x00000000 +#define VIA_GEC_MONO_PACK 0x00020000 +#define VIA_GEC_MONO_DWORD 0x00000000 +#define VIA_GEC_MONO_WORD 0x00040000 +#define VIA_GEC_MONO_BYTE 0x00080000 + +#define VIA_GEC_LASTPIXEL_ON 0x00000000 +#define VIA_GEC_LASTPIXEL_OFF 0x00100000 +#define VIA_GEC_X_MAJOR 0x00000000 +#define VIA_GEC_Y_MAJOR 0x00200000 +#define VIA_GEC_QUICK_START 0x00800000 + + +/* VIA_REG_GEMODE(0x04): GE mode */ +#define VIA_GEM_8bpp 0x00000000 +#define VIA_GEM_16bpp 0x00000100 +#define VIA_GEM_32bpp 0x00000300 + +#define VIA_GEM_640 0x00000000 /* 640*480 */ +#define VIA_GEM_800 0x00000400 /* 800*600 */ +#define VIA_GEM_1024 0x00000800 /* 1024*768 */ +#define VIA_GEM_1280 0x00000C00 /* 1280*1024 */ +#define VIA_GEM_1600 0x00001000 /* 1600*1200 */ +#define VIA_GEM_2048 0x00001400 /* 2048*1536 */ + +/* VIA_REG_PITCH(0x38): Pitch Setting */ +#define VIA_PITCH_ENABLE 0x80000000 + +/* CN400 HQV offset */ +#define REG_HQV1_INDEX 0x00001000 + +/************************************************ + * DisplayPort Register + ************************************************/ +#define DP_DATA_PASS_ENABLE_REG 0xC000 + +#define DP_ATTR_DATA_REG 0xC610 +#define DP_LINK_TRAINING_REG 0xC614 +#define DP_VIDEO_CTRL_REG 0xC618 +#define DP_VER_EXT_PKT_HEAD_REG 0xC61C + +/* DP Display Port Enable and InfoFrame Control */ +#define DP_ENABLE_IF_REG 0xC640 +#define DP_HWIDTH_TUSIZE_REG 0xC644 +#define DP_HLINE_DUR_REG 0xC648 +#define DP_MVID_MISC0_REG 0xC64C + +#define DP_H_ATTR_REG 0xC650 +#define DP_HV_START_REG 0xC654 +#define DP_POLARITY_WIDTH_REG 0xC658 +#define DP_ACITVE_WH_REG 0xC65C + +#define AUX_W_DATA0_REG 0xC710 +#define AUX_W_DATA1_REG 0xC714 +#define AUX_W_DATA2_REG 0xC718 +#define AUX_W_DATA3_REG 0xC71C + +#define AUX_R_DATA0_REG 0xC720 +#define AUX_R_DATA1_REG 0xC724 +#define AUX_R_DATA2_REG 0xC728 +#define AUX_R_DATA3_REG 0xC72C +#define VIA_IRQ_DP_HOT_IRQ 0xC0000000 +#define VIA_IRQ_DP_HOT_UNPLUG 0x80000000 +#define VIA_IRQ_DP_HOT_PLUG 0x40000000 +#define VIA_IRQ_DP_NO_INT 0x00000000 + +#define AUX_TIMER_REG 0xC730 +#define AUX_CMD_REG 0xC734 +#define DP_NAUD_MUTE_REG 0xC738 + +#define DP_EPHY_PLL_REG 0xC740 +#define DP_EPHY_TX_PWR_REG 0xC744 +#define DP_EPHY_MISC_PWR_REG 0xC748 + +/************************************************* + * DisplayPort2 Register + *************************************************/ +#define DP2_NVID_MISC0_REG 0xC690 +#define DP2_LINK_TRAINING_REG 0xC694 +#define DP2_VIDEO_CTRL_REG 0xC698 +#define DP2_EXT_REG 0xC69C +#define DP2_VER_EXT_PKT_HEAD_REG 0xC61C + +/* DP2 Display Port Enable and InfoFrame Control */ +#define DP2_ENABLE_IF_REG 0xC6C0 +#define DP2_HWIDTH_TUSIZE_REG 0xC6C4 +#define DP2_HLINE_DUR_REG 0xC6C8 +#define DP2_MVID_MISC0_REG 0xC6CC + +#define DP2_H_ATTR_REG 0xC6D0 +#define DP2_HV_START_REG 0xC6D4 +#define DP2_POLARITY_WIDTH_REG 0xC6D8 +#define DP2_ACITVE_WH_REG 0xC6DC + +/* the same with DP1 */ +#define DP2_EPHY_SSC_REG 0xC740 +/* the same with DP1 */ +#define DP2_EPHY_RT_REG 0xC744 + +#define DP2_AUX_W_DATA0_REG 0xC790 +#define DP2_AUX_W_DATA1_REG 0xC794 +#define DP2_AUX_W_DATA2_REG 0xC798 +#define DP2_AUX_W_DATA3_REG 0xC79C + +#define DP2_AUX_R_DATA0_REG 0xC7A0 +#define DP2_AUX_R_DATA1_REG 0xC7A4 +#define DP2_AUX_R_DATA2_REG 0xC7A8 +#define DP2_AUX_R_DATA3_REG 0xC7AC + +#define DP2_AUX_TIMER_REG 0xC7B0 +#define DP2_AUX_CMD_REG 0xC7B4 +#define DP2_NAUD_MUTE_REG 0xC7B8 + +#define DP2_EPHY_TX_PWR_REG2 0xC7C0 +#define DP2_EPHY_TX_IDLE_REG 0xC7C4 +#define DP2_EPHY_TX_PWR_REG 0xC7C8 +#define DP2_EPHY_PLL_REG 0xC7CC + +#endif /* _VIA_REGS_H_ */ diff --git a/drivers/gpu/drm/via/via_sii164.c b/drivers/gpu/drm/via/via_sii164.c new file mode 100644 index 000000000000..f55304347534 --- /dev/null +++ b/drivers/gpu/drm/via/via_sii164.c @@ -0,0 +1,588 @@ +/* + * Copyright © 2016-2018 Kevin Brace. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include "via_drv.h" + + +#define SII164_VEN BIT(5) +#define SII164_HEN BIT(4) +#define SII164_DSEL BIT(3) +#define SII164_BSEL BIT(2) +#define SII164_EDGE BIT(1) +#define SII164_PDB BIT(0) + + +static void via_sii164_power(struct drm_device *dev, + struct i2c_adapter *i2c_bus, + bool power_state) +{ + u8 buf; + u8 power_bit; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_i2c_readbytes(i2c_bus, 0x38, 0x08, &buf, 1); + power_bit = power_state ? SII164_PDB : 0x00; + buf &= ~power_bit; + buf |= power_bit; + via_i2c_writebytes(i2c_bus, 0x38, 0x08, &buf, 1); + drm_dbg_kms(dev, "SiI 164 (DVI) Power: %s\n", + power_state ? "On" : "Off"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + + +static bool via_sii164_sense(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 buf; + bool rx_detected = false; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_i2c_readbytes(i2c_bus, 0x38, 0x09, &buf, 1); + if (buf & BIT(2)) { + rx_detected = true; + } + + drm_dbg_kms(dev, "SiI 164 (DVI) Connector Sense: %s\n", + rx_detected ? "Connected" : "Not Connected"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return rx_detected; +} + +static void via_sii164_display_registers(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + uint8_t i; + u8 buf; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + drm_dbg_kms(dev, "SiI 164 Registers:\n"); + for (i = 0; i < 0x10; i++) { + via_i2c_readbytes(i2c_bus, 0x38, i, &buf, 1); + drm_dbg_kms(dev, "0x%02x: 0x%02x\n", i, buf); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_sii164_init_registers(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 buf; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + buf = SII164_VEN | SII164_HEN | + SII164_DSEL | + SII164_EDGE | SII164_PDB; + via_i2c_writebytes(i2c_bus, 0x38, 0x08, &buf, 1); + + /* + * Route receiver detect bit (Offset 0x09[2]) as the output + * of MSEN pin. + */ + buf = BIT(5); + via_i2c_writebytes(i2c_bus, 0x38, 0x09, &buf, 1); + + buf = 0x90; + via_i2c_writebytes(i2c_bus, 0x38, 0x0a, &buf, 1); + + buf = 0x89; + via_i2c_writebytes(i2c_bus, 0x38, 0x0c, &buf, 1); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + + +static const struct drm_encoder_funcs via_sii164_drm_encoder_funcs = { + .destroy = via_encoder_destroy, +}; + +static void via_sii164_dpms(struct drm_encoder *encoder, int mode) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_sii164_display_registers(dev, i2c_bus); + switch (mode) { + case DRM_MODE_DPMS_ON: + via_sii164_power(dev, i2c_bus, true); + via_transmitter_io_pad_state(dev, enc->di_port, true); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + via_sii164_power(dev, i2c_bus, false); + via_transmitter_io_pad_state(dev, enc->di_port, false); + break; + default: + drm_err(dev, "Bad DPMS mode."); + break; + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static bool via_sii164_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return true; +} + +static void via_sii164_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base); + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_transmitter_clock_drive_strength(dev, enc->di_port, 0x03); + via_transmitter_data_drive_strength(dev, enc->di_port, 0x03); + via_transmitter_io_pad_state(dev, enc->di_port, true); + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + via_clock_source(dev, enc->di_port, true); + } + + via_sii164_display_registers(dev, i2c_bus); + via_sii164_init_registers(dev, i2c_bus); + via_sii164_display_registers(dev, i2c_bus); + + via_transmitter_display_source(dev, enc->di_port, iga->index); +exit: + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_sii164_prepare(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_sii164_power(dev, i2c_bus, false); + via_transmitter_io_pad_state(dev, enc->di_port, false); + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + via_output_enable(dev, enc->di_port, false); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_sii164_commit(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_sii164_power(dev, i2c_bus, true); + via_transmitter_io_pad_state(dev, enc->di_port, true); + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + via_output_enable(dev, enc->di_port, true); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_sii164_disable(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_sii164_power(dev, i2c_bus, false); + via_transmitter_io_pad_state(dev, enc->di_port, false); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + + +static const struct drm_encoder_helper_funcs +via_sii164_drm_encoder_helper_funcs = { + .dpms = via_sii164_dpms, + .mode_fixup = via_sii164_mode_fixup, + .mode_set = via_sii164_mode_set, + .prepare = via_sii164_prepare, + .commit = via_sii164_commit, + .disable = via_sii164_disable, +}; + + +static enum drm_connector_status via_sii164_detect( + struct drm_connector *connector, + bool force) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + struct i2c_adapter *i2c_bus; + enum drm_connector_status ret = connector_status_disconnected; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (con->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (con->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + if (via_sii164_sense(dev, i2c_bus)) { + ret = connector_status_connected; + drm_dbg_kms(dev, "DVI detected.\n"); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static const struct drm_connector_funcs via_sii164_drm_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_sii164_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = via_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = + drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_connector_destroy_state, +}; + + +int via_sii164_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + struct i2c_adapter *i2c_bus; + u8 buf; + uint32_t low_freq_limit, high_freq_limit; + int ret; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (con->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (con->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + ret = MODE_ERROR; + goto exit; + } + + via_i2c_readbytes(i2c_bus, 0x38, 0x06, &buf, 1); + low_freq_limit = buf * 1000; + via_i2c_readbytes(i2c_bus, 0x38, 0x07, &buf, 1); + high_freq_limit = (buf + 65) * 1000; + drm_dbg_kms(dev, "Low Frequency Limit: %u KHz\n", low_freq_limit); + drm_dbg_kms(dev, "High Frequency Limit: %u KHz\n", high_freq_limit); + + if (mode->clock < low_freq_limit) { + ret = MODE_CLOCK_LOW; + goto exit; + } + + if (mode->clock > high_freq_limit) { + ret = MODE_CLOCK_HIGH; + goto exit; + } + + ret = MODE_OK; +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static int via_sii164_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + int count = 0; + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (con->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (con->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + drm_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + drm_dbg_kms(dev, "DVI EDID information was obtained.\n"); + } + + kfree(edid); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return count; +} + +static const struct drm_connector_helper_funcs +via_sii164_drm_connector_helper_funcs = { + .mode_valid = via_sii164_mode_valid, + .get_modes = via_sii164_get_modes, +}; + +bool via_sii164_probe(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 buf; + u16 vendor_id, device_id, revision; + bool device_detected = false; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_i2c_readbytes(i2c_bus, 0x38, 0x00, &buf, 1); + vendor_id = buf; + via_i2c_readbytes(i2c_bus, 0x38, 0x01, &buf, 1); + vendor_id |= (buf << 8); + drm_dbg_kms(dev, "Vendor ID: %x\n", vendor_id); + via_i2c_readbytes(i2c_bus, 0x38, 0x02, &buf, 1); + device_id = buf; + via_i2c_readbytes(i2c_bus, 0x38, 0x03, &buf, 1); + device_id |= (buf << 8); + drm_dbg_kms(dev, "Device ID: %x\n", device_id); + via_i2c_readbytes(i2c_bus, 0x38, 0x04, &buf, 1); + revision = buf; + drm_dbg_kms(dev, "Revision: %x\n", revision); + + if ((vendor_id != 0x0001) || (device_id != 0x0006)) { + goto exit; + } + + device_detected = true; +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return device_detected; +} + +void via_sii164_init(struct drm_device *dev) +{ + struct via_connector *con; + struct via_encoder *enc; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!dev_priv->ext_tmds_presence) { + goto exit; + } + + enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL); + if (!enc) { + drm_err(dev, "Failed to allocate connector " + "and encoder.\n"); + goto exit; + } + + drm_encoder_init(dev, &enc->base, &via_sii164_drm_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + drm_encoder_helper_add(&enc->base, + &via_sii164_drm_encoder_helper_funcs); + + enc->base.possible_crtcs = BIT(1) | BIT(0); + enc->base.possible_clones = 0; + + enc->i2c_bus = dev_priv->ext_tmds_i2c_bus; + enc->di_port = dev_priv->ext_tmds_di_port; + + /* Increment the number of DVI connectors. */ + dev_priv->number_dvi++; + + + con = &enc->cons[0]; + + drm_connector_init(dev, &con->base, &via_sii164_drm_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + drm_connector_helper_add(&con->base, + &via_sii164_drm_connector_helper_funcs); + drm_connector_register(&con->base); + + con->base.doublescan_allowed = false; + con->base.interlace_allowed = false; + + con->i2c_bus = dev_priv->ext_tmds_i2c_bus; + + INIT_LIST_HEAD(&con->props); + drm_connector_attach_encoder(&con->base, &enc->base); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} diff --git a/drivers/gpu/drm/via/via_tmds.c b/drivers/gpu/drm/via/via_tmds.c new file mode 100644 index 000000000000..948a47d1314e --- /dev/null +++ b/drivers/gpu/drm/via/via_tmds.c @@ -0,0 +1,719 @@ +/* + * Copyright © 2016-2018 Kevin Brace. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include "via_drv.h" + + +static void via_tmds_power(struct drm_device *dev, bool power_state) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (power_state) { + via_lvds1_set_soft_display_period(VGABASE, true); + via_lvds1_set_soft_data(VGABASE, true); + via_tmds_set_power(VGABASE, true); + } else { + via_tmds_set_power(VGABASE, false); + via_lvds1_set_soft_data(VGABASE, false); + via_lvds1_set_soft_display_period(VGABASE, false); + } + + drm_dbg_driver(dev, "DVI Power: %s\n", + power_state ? "On" : "Off"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_tmds_io_pad_setting(struct drm_device *dev, + u32 di_port, bool io_pad_on) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_TMDS: + via_lvds1_set_io_pad_setting(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + default: + break; + } + + drm_dbg_kms(dev, "DVI I/O Pad: %s\n", io_pad_on ? "On": "Off"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Initializes most registers related to VIA Technologies IGP + * integrated TMDS transmitter. Synchronization polarity and + * display output source need to be set separately. + */ +static void via_tmds_init_reg(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Turn off hardware controlled FP power on / off circuit. */ + via_lvds_set_primary_hard_power(VGABASE, false); + + /* Use software FP power sequence control. */ + via_lvds_set_primary_power_seq_type(VGABASE, false); + + /* Turn off software controlled primary FP power rails. */ + via_lvds_set_primary_soft_vdd(VGABASE, false); + via_lvds_set_primary_soft_vee(VGABASE, false); + + /* Turn off software controlled primary FP back light + * control. */ + via_lvds_set_primary_soft_back_light(VGABASE, false); + + /* Turn off direct control of FP back light. */ + via_lvds_set_primary_direct_back_light_ctrl(VGABASE, false); + + /* Activate DVI + LVDS2 mode. */ + /* 3X5.D2[5:4] - Display Channel Select + * 00: LVDS1 + LVDS2 + * 01: DVI + LVDS2 + * 10: One Dual LVDS Channel (High Resolution Pannel) + * 11: Single Channel DVI */ + svga_wcrt_mask(VGABASE, 0xd2, 0x10, 0x30); + + /* Various DVI PLL settings should be set to default settings. */ + /* 3X5.D1[7] - PLL2 Reference Clock Edge Select Bit + * 0: PLLCK lock to rising edge of reference clock + * 1: PLLCK lock to falling edge of reference clock + * 3X5.D1[6:5] - PLL2 Charge Pump Current Set Bits + * 00: ICH = 12.5 uA + * 01: ICH = 25.0 uA + * 10: ICH = 37.5 uA + * 11: ICH = 50.0 uA + * 3X5.D1[4:1] - Reserved + * 3X5.D1[0] - PLL2 Control Voltage Measurement Enable Bit */ + svga_wcrt_mask(VGABASE, 0xd1, 0x00, 0xe1); + + /* Disable DVI test mode. */ + /* 3X5.D5[7] - PD1 Enable Selection + * 1: Select by power flag + * 0: By register + * 3X5.D5[5] - DVI Testing Mode Enable + * 3X5.D5[4] - DVI Testing Format Selection + * 0: Half cycle + * 1: LFSR mode */ + svga_wcrt_mask(VGABASE, 0xd5, 0x00, 0xb0); + + /* Disable DVI sense interrupt. */ + /* 3C5.2B[7] - DVI Sense Interrupt Enable + * 0: Disable + * 1: Enable */ + svga_wseq_mask(VGABASE, 0x2b, 0x00, 0x80); + + /* Clear DVI sense interrupt status. */ + /* 3C5.2B[6] - DVI Sense Interrupt Status + * (This bit has a RW1C attribute.) */ + svga_wseq_mask(VGABASE, 0x2b, 0x40, 0x40); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Set TMDS (DVI) sync polarity. + */ +static void via_tmds_sync_polarity(struct drm_device *dev, + unsigned int flags) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + u8 syncPolarity = 0x00; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (flags & DRM_MODE_FLAG_NHSYNC) { + syncPolarity |= BIT(0); + } + + if (flags & DRM_MODE_FLAG_NVSYNC) { + syncPolarity |= BIT(1); + } + + via_tmds_set_sync_polarity(VGABASE, syncPolarity); + drm_dbg_driver(dev, "TMDS (DVI) Horizontal Sync Polarity: %s\n", + (syncPolarity & BIT(0)) ? "-" : "+"); + drm_dbg_driver(dev, "TMDS (DVI) Vertical Sync Polarity: %s\n", + (syncPolarity & BIT(1)) ? "-" : "+"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Sets TMDS (DVI) display source. + */ +static void via_tmds_display_source(struct drm_device *dev, int index) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 displaySource = index; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_tmds_set_display_source(VGABASE, displaySource & 0x01); + drm_dbg_driver(dev, "TMDS (DVI) Display Source: IGA%d\n", + (displaySource & 0x01) + 1); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Routines for controlling stuff on the TMDS port + */ +static const struct drm_encoder_funcs via_tmds_enc_funcs = { + .destroy = via_encoder_destroy, +}; + +static void via_tmds_dpms(struct drm_encoder *encoder, int mode) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch (mode) { + case DRM_MODE_DPMS_ON: + via_tmds_power(dev, true); + via_tmds_io_pad_setting(dev, enc->di_port, true); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + via_tmds_power(dev, false); + via_tmds_io_pad_setting(dev, enc->di_port, false); + break; + default: + drm_err(dev, "Bad DPMS mode."); + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* Pass our mode to the connectors and the CRTC to give them a chance to + * adjust it according to limitations or connector properties, and also + * a chance to reject the mode entirely. Usefule for things like scaling. + */ +static bool via_tmds_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + drm_mode_set_crtcinfo(adjusted_mode, 0); + return true; +} + +static void via_tmds_prepare(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_tmds_power(dev, false); + via_tmds_io_pad_setting(dev, enc->di_port, false); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_tmds_commit(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_tmds_power(dev, true); + via_tmds_io_pad_setting(dev, enc->di_port, true); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Handle CX700 / VX700 and VX800 integrated TMDS (DVI) mode setting. + */ +static void via_tmds_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct via_crtc *iga = container_of(encoder->crtc, + struct via_crtc, base); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_tmds_init_reg(dev); + via_tmds_sync_polarity(dev, adjusted_mode->flags); + via_tmds_display_source(dev, iga->index); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_tmds_disable(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_tmds_power(dev, false); + via_tmds_io_pad_setting(dev, enc->di_port, false); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static const struct drm_encoder_helper_funcs +via_tmds_enc_helper_funcs = { + .dpms = via_tmds_dpms, + .mode_fixup = via_tmds_mode_fixup, + .prepare = via_tmds_prepare, + .commit = via_tmds_commit, + .mode_set = via_tmds_mode_set, + .disable = via_tmds_disable, +}; + +static enum drm_connector_status via_tmds_detect( + struct drm_connector *connector, + bool force) +{ + struct drm_device *dev = connector->dev; + + struct via_connector *con = container_of(connector, struct via_connector, base); + enum drm_connector_status ret = connector_status_disconnected; + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x2c); + } else { + i2c_bus = NULL; + } + + if (i2c_bus) { + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + drm_connector_update_edid_property(connector, edid); + ret = connector_status_connected; + } + + kfree(edid); + } + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static const struct drm_connector_funcs via_dvi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_tmds_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = via_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = + drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_connector_destroy_state, +}; + +static enum drm_mode_status via_tmds_mode_valid( + struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + int min_clock, max_clock; + enum drm_mode_status status = MODE_OK; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + min_clock = 25000; + switch (pdev->device) { + /* CX700(M/M2) / VX700(M/M2) Chipset */ + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + /* VX800 / VX820 Chipset */ + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + max_clock = 165000; + break; + /* Illegal condition (should never get here) */ + default: + max_clock = 0; + break; + } + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + status = MODE_NO_INTERLACE; + goto exit; + } + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { + status = MODE_NO_DBLESCAN; + goto exit; + } + + if (mode->clock < min_clock) { + status = MODE_CLOCK_LOW; + goto exit; + } + + if (mode->clock > max_clock) { + status = MODE_CLOCK_HIGH; + goto exit; + } + +exit: + drm_dbg_kms(dev, "status: %u\n", status); + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return status; +} + +static int via_tmds_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, struct via_connector, base); + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + int count = 0; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x2c); + } else { + i2c_bus = NULL; + } + + if (i2c_bus) { + edid = drm_get_edid(&con->base, i2c_bus); + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + drm_connector_update_edid_property(connector, + edid); + count = drm_add_edid_modes(connector, edid); + drm_dbg_kms(dev, "DVI EDID information was obtained.\n"); + } + + kfree(edid); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return count; +} + +static const struct drm_connector_helper_funcs +via_dvi_connector_helper_funcs = { + .mode_valid = via_tmds_mode_valid, + .get_modes = via_tmds_get_modes, +}; + +/* + * Probe (pre-initialization detection) of integrated TMDS transmitters. + */ +void via_tmds_probe(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 sr13, sr5a; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* Detect the presence of integrated TMDS transmitter. */ + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + sr5a = vga_rseq(VGABASE, 0x5a); + + /* Setting SR5A[0] to 1. + * This allows the reading out the alternative + * pin strapping information from SR12 and SR13. */ + svga_wseq_mask(VGABASE, 0x5a, BIT(0), BIT(0)); + + sr13 = vga_rseq(VGABASE, 0x13); + drm_dbg_kms(dev, "sr13: 0x%02x\n", sr13); + + vga_wseq(VGABASE, 0x5a, sr5a); + + /* 3C5.13[7:6] - Integrated LVDS / DVI Mode Select + * (DVP1D15-14 pin strapping) + * 00: LVDS1 + LVDS2 + * 01: DVI + LVDS2 + * 10: Dual LVDS Channel (High Resolution Panel) + * 11: One DVI only (decrease the clock jitter) */ + /* Check for DVI presence using pin strappings. + * VIA Technologies NanoBook reference design based products + * have their pin strappings set to a wrong setting to communicate + * the presence of DVI, so it requires special handling here. */ + if (dev_priv->is_via_nanobook) { + dev_priv->int_tmds_presence = true; + dev_priv->int_tmds_di_port = VIA_DI_PORT_TMDS; + dev_priv->int_tmds_i2c_bus = VIA_I2C_BUS2; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2; + drm_dbg_kms(dev, "Integrated TMDS (DVI) " + "transmitter detected.\n"); + } else if (((!(sr13 & BIT(7))) && (sr13 & BIT(6))) || + ((sr13 & BIT(7)) && (sr13 & BIT(6)))) { + dev_priv->int_tmds_presence = true; + dev_priv->int_tmds_di_port = VIA_DI_PORT_TMDS; + dev_priv->int_tmds_i2c_bus = VIA_I2C_BUS2; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2; + drm_dbg_kms(dev, "Integrated TMDS (DVI) " + "transmitter detected via pin " + "strapping.\n"); + } else { + dev_priv->int_tmds_presence = false; + dev_priv->int_tmds_di_port = VIA_DI_PORT_NONE; + dev_priv->int_tmds_i2c_bus = VIA_I2C_NONE; + } + + break; + default: + dev_priv->int_tmds_presence = false; + dev_priv->int_tmds_di_port = VIA_DI_PORT_NONE; + dev_priv->int_tmds_i2c_bus = VIA_I2C_NONE; + break; + } + + drm_dbg_kms(dev, "int_tmds_presence: %x\n", + dev_priv->int_tmds_presence); + drm_dbg_kms(dev, "int_tmds_di_port: 0x%08x\n", + dev_priv->int_tmds_di_port); + drm_dbg_kms(dev, "int_tmds_i2c_bus: 0x%08x\n", + dev_priv->int_tmds_i2c_bus); + drm_dbg_kms(dev, "mapped_i2c_bus: 0x%08x\n", + dev_priv->mapped_i2c_bus); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_tmds_init(struct drm_device *dev) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct via_connector *con; + struct via_encoder *enc; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!dev_priv->int_tmds_presence) { + goto exit; + } + + enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL); + if (!enc) { + drm_err(dev, "Failed to allocate connector " + "and encoder.\n"); + goto exit; + } + + /* Setup the encoders and attach them */ + drm_encoder_init(dev, &enc->base, &via_tmds_enc_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + drm_encoder_helper_add(&enc->base, &via_tmds_enc_helper_funcs); + + enc->base.possible_crtcs = BIT(1) | BIT(0); + enc->base.possible_clones = 0; + + enc->di_port = dev_priv->int_tmds_di_port; + + /* Increment the number of DVI connectors. */ + dev_priv->number_dvi++; + + + con = &enc->cons[0]; + drm_connector_init(dev, &con->base, &via_dvi_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + drm_connector_helper_add(&con->base, &via_dvi_connector_helper_funcs); + drm_connector_register(&con->base); + + con->i2c_bus = dev_priv->int_tmds_i2c_bus; + con->base.doublescan_allowed = false; + con->base.interlace_allowed = true; + INIT_LIST_HEAD(&con->props); + + drm_connector_attach_encoder(&con->base, &enc->base); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +/* + * Probe (pre-initialization detection) of external DVI transmitters. + */ +void via_ext_dvi_probe(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + struct i2c_adapter *i2c_bus; + u8 sr12, sr13; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + dev_priv->ext_tmds_presence = false; + dev_priv->ext_tmds_i2c_bus = VIA_I2C_NONE; + dev_priv->ext_tmds_transmitter = VIA_TMDS_NONE; + + if ((!dev_priv->ext_tmds_presence) && + (!(dev_priv->mapped_i2c_bus & VIA_I2C_BUS2))) { + i2c_bus = via_find_ddc_bus(0x31); + if (via_vt1632_probe(dev, i2c_bus)) { + dev_priv->ext_tmds_presence = true; + dev_priv->ext_tmds_i2c_bus = VIA_I2C_BUS2; + dev_priv->ext_tmds_transmitter = VIA_TMDS_VT1632; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2; + } else if (via_sii164_probe(dev, i2c_bus)) { + dev_priv->ext_tmds_presence = true; + dev_priv->ext_tmds_i2c_bus = VIA_I2C_BUS2; + dev_priv->ext_tmds_transmitter = VIA_TMDS_SII164; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS2; + } + } + + if ((!(dev_priv->ext_tmds_presence)) && + (!(dev_priv->mapped_i2c_bus & VIA_I2C_BUS4))) { + i2c_bus = via_find_ddc_bus(0x2c); + if (via_vt1632_probe(dev, i2c_bus)) { + dev_priv->ext_tmds_presence = true; + dev_priv->ext_tmds_i2c_bus = VIA_I2C_BUS4; + dev_priv->ext_tmds_transmitter = VIA_TMDS_VT1632; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS4; + } else if (via_sii164_probe(dev, i2c_bus)) { + dev_priv->ext_tmds_presence = true; + dev_priv->ext_tmds_i2c_bus = VIA_I2C_BUS4; + dev_priv->ext_tmds_transmitter = VIA_TMDS_SII164; + dev_priv->mapped_i2c_bus |= VIA_I2C_BUS4; + } + } + + sr12 = vga_rseq(VGABASE, 0x12); + sr13 = vga_rseq(VGABASE, 0x13); + drm_dbg_kms(dev, "SR12: 0x%02x\n", sr12); + drm_dbg_kms(dev, "SR13: 0x%02x\n", sr13); + + if (dev_priv->ext_tmds_presence) { + switch (pdev->device) { + case PCI_DEVICE_ID_VIA_CLE266_GFX: + /* 3C5.12[5] - FPD18 pin strapping (DIP0) + * 0: DVI + * 1: TV */ + if (!(sr12 & BIT(5))) { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_DIP0; + + /* 3C5.12[4] - FPD17 pin strapping (DIP1) + * 0: DVI / Capture + * 1: Panel */ + } else if (!(sr12 & BIT(4))) { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_DIP1; + } else { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_NONE; + } + + break; + case PCI_DEVICE_ID_VIA_KM400_GFX: + case PCI_DEVICE_ID_VIA_K8M800_GFX: + case PCI_DEVICE_ID_VIA_P4M800_PRO_GFX: + case PCI_DEVICE_ID_VIA_PM800_GFX: + /* + * For DVP0 to be configured to not be used for + * a TV encoder, DVP0D[6] (SR12[6]) needs to be + * strapped low (0). In addition, DVP0D[5] + * (SR12[5]) also needs to be strapped low (0) + * for DVP0 to be configured for DVI + * transmitter use. + */ + if (!(sr12 & BIT(6)) && (!(sr12 & BIT(5)))) { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_DVP0; + } else { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_NONE; + } + + break; + case PCI_DEVICE_ID_VIA_P4M890_GFX: + case PCI_DEVICE_ID_VIA_CHROME9: + case PCI_DEVICE_ID_VIA_CHROME9_HC: + /* Assume DVP2 as DVP0. Hence, VIA_DI_PORT_DVP0 + * is used. */ + /* 3C5.12[6] - DVP2D6 pin strapping + * 0: Disable DVP2 (Digital Video Port 2) + * 1: Enable DVP2 (Digital Video Port 2) + * 3C5.12[5] - DVP2D5 pin strapping + * 0: TMDS transmitter (DVI) + * 1: TV encoder */ + if ((sr12 & BIT(6)) && (!(sr12 & BIT(5)))) { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_DVP0; + } else { + dev_priv->ext_tmds_di_port = VIA_DI_PORT_NONE; + } + + break; + case PCI_DEVICE_ID_VIA_UNICHROME_PRO_II: + case PCI_DEVICE_ID_VIA_CHROME9_HC3: + case PCI_DEVICE_ID_VIA_CHROME9_HCM: + case PCI_DEVICE_ID_VIA_CHROME9_HD: + dev_priv->ext_tmds_di_port = VIA_DI_PORT_DVP1; + break; + default: + dev_priv->ext_tmds_di_port = VIA_DI_PORT_NONE; + break; + } + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_ext_dvi_init(struct drm_device *dev) +{ + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_vt1632_init(dev); + via_sii164_init(dev); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} diff --git a/drivers/gpu/drm/via/via_ttm.c b/drivers/gpu/drm/via/via_ttm.c new file mode 100644 index 000000000000..68f44446011f --- /dev/null +++ b/drivers/gpu/drm/via/via_ttm.c @@ -0,0 +1,182 @@ +/* + * Copyright © 2018-2019 Kevin Brace. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHOR(S) OR COPYRIGHT HOLDER(S) 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ +/* + * via_ttm.c + * + * TTM code as part of the TTM memory allocator. + * Currently a basic implementation with no DMA support. + * + */ + +#include <linux/pci.h> + +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_tt.h> + +#include "via_drv.h" + + +static void via_bo_move_notify(struct ttm_buffer_object *bo, bool evict, + struct ttm_resource *new_mem) +{ + struct drm_device *dev = bo->base.dev; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return; +} + +static struct ttm_tt *via_ttm_tt_create(struct ttm_buffer_object *bo, + uint32_t page_flags) +{ + struct ttm_tt *tt; + int ret; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) + return NULL; + + ret = ttm_tt_init(tt, bo, page_flags, ttm_cached, 0); + if (ret < 0) + goto err_ttm_tt_init; + + return tt; + +err_ttm_tt_init: + kfree(tt); + return NULL; +} + +static void via_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static void via_bo_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + struct drm_device *dev = bo->base.dev; + struct via_bo *driver_bo = to_ttm_bo(bo); + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + if (bo->destroy == &via_ttm_bo_destroy) { + goto exit; + } + + switch (bo->resource->mem_type) { + case TTM_PL_VRAM: + via_ttm_domain_to_placement(driver_bo, TTM_PL_VRAM); + break; + default: + via_ttm_domain_to_placement(driver_bo, TTM_PL_SYSTEM); + break; + } + + *placement = driver_bo->placement; +exit: + drm_dbg_driver(dev, "Exiting %s.\n", __func__); +} + +static int via_bo_move(struct ttm_buffer_object *bo, bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem, + struct ttm_place *hop) +{ + int ret; + + if (!bo->resource) { + if (new_mem->mem_type != TTM_PL_SYSTEM) { + hop->mem_type = TTM_PL_SYSTEM; + hop->flags = TTM_PL_FLAG_TEMPORARY; + return -EMULTIHOP; + } + + ttm_bo_move_null(bo, new_mem); + return 0; + } + + via_bo_move_notify(bo, evict, new_mem); + ret = ttm_bo_move_memcpy(bo, ctx, new_mem); + if (ret) { + swap(*new_mem, *bo->resource); + via_bo_move_notify(bo, false, new_mem); + swap(*new_mem, *bo->resource); + } + + return ret; +} + +static void via_bo_delete_mem_notify(struct ttm_buffer_object *bo) +{ + struct drm_device *dev = bo->base.dev; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + via_bo_move_notify(bo, false, NULL); + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return; +} + +static int via_bo_io_mem_reserve(struct ttm_device *bdev, + struct ttm_resource *mem) +{ + struct via_drm_priv *dev_priv = container_of(bdev, + struct via_drm_priv, bdev); + struct drm_device *dev = &dev_priv->dev; + int ret = 0; + + drm_dbg_driver(dev, "Entered %s.\n", __func__); + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + break; + case TTM_PL_VRAM: + mem->bus.offset = dev_priv->vram_start + + (mem->start << PAGE_SHIFT); + mem->bus.is_iomem = true; + break; + default: + ret = -EINVAL; + break; + } + + drm_dbg_driver(dev, "Exiting %s.\n", __func__); + return ret; +} + +struct ttm_device_funcs via_bo_driver = { + .ttm_tt_create = via_ttm_tt_create, + .ttm_tt_destroy = via_ttm_tt_destroy, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = via_bo_evict_flags, + .move = via_bo_move, + .delete_mem_notify = via_bo_delete_mem_notify, + .io_mem_reserve = via_bo_io_mem_reserve, +}; diff --git a/drivers/gpu/drm/via/via_tx.c b/drivers/gpu/drm/via/via_tx.c new file mode 100644 index 000000000000..0a41bf663aa2 --- /dev/null +++ b/drivers/gpu/drm/via/via_tx.c @@ -0,0 +1,231 @@ +/* + * Copyright © 2017-2018 Kevin Brace. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ + +#include "via_drv.h" + + +void via_transmitter_io_pad_state(struct drm_device *dev, + uint32_t di_port, + bool io_pad_on) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DIP0: + via_dip0_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_DIP1: + via_dip1_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_DVP0: + via_dvp0_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_DVP1: + via_dvp1_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_FPDPLOW: + via_fpdp_low_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_FPDPHIGH: + via_fpdp_high_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case (VIA_DI_PORT_FPDPLOW | + VIA_DI_PORT_FPDPHIGH): + via_fpdp_low_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + via_fpdp_high_set_io_pad_state(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_LVDS1: + via_lvds1_set_io_pad_setting(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_io_pad_setting(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_io_pad_setting(VGABASE, + io_pad_on ? 0x03 : 0x00); + via_lvds2_set_io_pad_setting(VGABASE, + io_pad_on ? 0x03 : 0x00); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_output_enable(struct drm_device *dev, + uint32_t di_port, bool output_enable) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DIP0: + via_dip0_set_output_enable(VGABASE, output_enable); + break; + case VIA_DI_PORT_DIP1: + via_dip1_set_output_enable(VGABASE, output_enable); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_clock_source(struct drm_device *dev, + uint32_t di_port, bool clock_source) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DIP0: + via_dip0_set_clock_source(VGABASE, clock_source); + break; + case VIA_DI_PORT_DIP1: + via_dip1_set_clock_source(VGABASE, clock_source); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_transmitter_clock_drive_strength(struct drm_device *dev, + u32 di_port, + u8 drive_strength) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DVP0: + via_dvp0_set_clock_drive_strength(VGABASE, + drive_strength); + break; + case VIA_DI_PORT_DVP1: + via_dvp1_set_clock_drive_strength(VGABASE, + drive_strength); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_transmitter_data_drive_strength(struct drm_device *dev, + u32 di_port, u8 drive_strength) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DVP0: + via_dvp0_set_data_drive_strength(VGABASE, + drive_strength); + break; + case VIA_DI_PORT_DVP1: + via_dvp1_set_data_drive_strength(VGABASE, + drive_strength); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +void via_transmitter_display_source(struct drm_device *dev, + u32 di_port, int index) +{ + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + u8 display_source = index & 0x01; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + switch(di_port) { + case VIA_DI_PORT_DIP0: + via_dip0_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_DIP1: + via_dip1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_DVP0: + via_dvp0_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_DVP1: + via_dvp1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_FPDPLOW: + via_fpdp_low_set_display_source(VGABASE, display_source); + via_dvp1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_FPDPHIGH: + via_fpdp_high_set_display_source(VGABASE, display_source); + via_dvp0_set_display_source(VGABASE, display_source); + break; + case (VIA_DI_PORT_FPDPLOW | + VIA_DI_PORT_FPDPHIGH): + via_fpdp_low_set_display_source(VGABASE, display_source); + via_fpdp_high_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_LVDS1: + via_lvds1_set_display_source(VGABASE, display_source); + break; + case VIA_DI_PORT_LVDS2: + via_lvds2_set_display_source(VGABASE, display_source); + break; + case (VIA_DI_PORT_LVDS1 | + VIA_DI_PORT_LVDS2): + via_lvds1_set_display_source(VGABASE, display_source); + via_lvds2_set_display_source(VGABASE, display_source); + break; + default: + break; + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} diff --git a/drivers/gpu/drm/via/via_vt1632.c b/drivers/gpu/drm/via/via_vt1632.c new file mode 100644 index 000000000000..99b6da9f4192 --- /dev/null +++ b/drivers/gpu/drm/via/via_vt1632.c @@ -0,0 +1,608 @@ +/* + * Copyright © 2016-2018 Kevin Brace. + * + * 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, sublicense, + * 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 above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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 NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS 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. + * + * Author(s): + * Kevin Brace <kevinbrace@bracecomputerlab.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include "via_drv.h" + + +#define VIA_VT1632_VEN BIT(5) +#define VIA_VT1632_HEN BIT(4) +#define VIA_VT1632_DSEL BIT(3) +#define VIA_VT1632_BSEL BIT(2) +#define VIA_VT1632_EDGE BIT(1) +#define VIA_VT1632_PDB BIT(0) + + +static void via_vt1632_power(struct drm_device *dev, + struct i2c_adapter *i2c_bus, + bool power_state) +{ + u8 buf; + u8 power_bit; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_i2c_readbytes(i2c_bus, 0x08, 0x08, &buf, 1); + power_bit = power_state ? VIA_VT1632_PDB : 0x00; + buf &= ~power_bit; + buf |= power_bit; + via_i2c_writebytes(i2c_bus, 0x08, 0x08, &buf, 1); + drm_dbg_kms(dev, "VT1632 (DVI) Power: %s\n", + power_state ? "On" : "Off"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + + +static bool via_vt1632_sense(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 buf; + bool rx_detected = false; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_i2c_readbytes(i2c_bus, 0x08, 0x09, &buf, 1); + if (buf & BIT(2)) { + rx_detected = true; + } + + drm_dbg_kms(dev, "VT1632 (DVI) Connector Sense: %s\n", + rx_detected ? "Connected" : "Not Connected"); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return rx_detected; +} + +static void via_vt1632_display_registers(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + uint8_t i; + u8 buf; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + drm_dbg_kms(dev, "VT1632(A) Registers:\n"); + for (i = 0; i < 0x10; i++) { + via_i2c_readbytes(i2c_bus, 0x08, i, &buf, 1); + drm_dbg_kms(dev, "0x%02x: 0x%02x\n", i, buf); + } + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_vt1632_init_registers(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 buf; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + /* + * For Wyse Cx0 thin client VX855 chipset DVP1 (Digital Video + * Port 1), use 12-bit mode with dual edge transfer, along + * with rising edge data capture first mode. This is likely + * true for CX700, VX700, VX800, and VX900 chipsets as well. + */ + buf = VIA_VT1632_VEN | VIA_VT1632_HEN | + VIA_VT1632_DSEL | + VIA_VT1632_EDGE | VIA_VT1632_PDB; + via_i2c_writebytes(i2c_bus, 0x08, 0x08, &buf, 1); + + /* + * Route receiver detect bit (Offset 0x09[2]) as the output + * of MSEN pin. + */ + buf = BIT(5); + via_i2c_writebytes(i2c_bus, 0x08, 0x09, &buf, 1); + + /* + * Turning on deskew feature caused screen display issues. + * This was observed with Wyse Cx0. + */ + buf = 0x00; + via_i2c_writebytes(i2c_bus, 0x08, 0x0a, &buf, 1); + + /* + * While VIA Technologies VT1632A datasheet insists on setting + * this register to 0x89 as the recommended setting, in + * practice, this leads to a blank screen on the display with + * Wyse Cx0. According to Silicon Image SiI 164 datasheet + * (VT1632(A) is a pin and mostly register compatible chip), + * offset 0x0C is for PLL filter enable, PLL filter setting, + * and continuous SYNC enable bits. All of these are turned + * off for proper operation. + */ + buf = 0x00; + via_i2c_writebytes(i2c_bus, 0x08, 0x0c, &buf, 1); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + + +static const struct drm_encoder_funcs via_vt1632_drm_encoder_funcs = { + .destroy = via_encoder_destroy, +}; + +static void via_vt1632_dpms(struct drm_encoder *encoder, int mode) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_vt1632_display_registers(dev, i2c_bus); + switch (mode) { + case DRM_MODE_DPMS_ON: + via_vt1632_power(dev, i2c_bus, true); + via_transmitter_io_pad_state(dev, enc->di_port, true); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + via_vt1632_power(dev, i2c_bus, false); + via_transmitter_io_pad_state(dev, enc->di_port, false); + break; + default: + drm_err(dev, "Bad DPMS mode."); + break; + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static bool via_vt1632_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return true; +} + +static void via_vt1632_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base); + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_transmitter_clock_drive_strength(dev, enc->di_port, 0x03); + via_transmitter_data_drive_strength(dev, enc->di_port, 0x03); + via_transmitter_io_pad_state(dev, enc->di_port, true); + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + via_clock_source(dev, enc->di_port, true); + } + + via_vt1632_display_registers(dev, i2c_bus); + via_vt1632_init_registers(dev, i2c_bus); + via_vt1632_display_registers(dev, i2c_bus); + + via_transmitter_display_source(dev, enc->di_port, iga->index); +exit: + + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_vt1632_prepare(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_vt1632_power(dev, i2c_bus, false); + via_transmitter_io_pad_state(dev, enc->di_port, false); + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + via_output_enable(dev, enc->di_port, false); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_vt1632_commit(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_vt1632_power(dev, i2c_bus, true); + via_transmitter_io_pad_state(dev, enc->di_port, true); + if (pdev->device == PCI_DEVICE_ID_VIA_CLE266_GFX) { + via_output_enable(dev, enc->di_port, true); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + +static void via_vt1632_disable(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, + struct via_encoder, base); + struct drm_device *dev = encoder->dev; + struct i2c_adapter *i2c_bus; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (enc->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (enc->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (enc->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (enc->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (enc->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + via_vt1632_power(dev, i2c_bus, false); + via_transmitter_io_pad_state(dev, enc->di_port, false); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} + + +static const struct drm_encoder_helper_funcs +via_vt1632_drm_encoder_helper_funcs = { + .dpms = via_vt1632_dpms, + .mode_fixup = via_vt1632_mode_fixup, + .mode_set = via_vt1632_mode_set, + .prepare = via_vt1632_prepare, + .commit = via_vt1632_commit, + .disable = via_vt1632_disable, +}; + + +static enum drm_connector_status via_vt1632_detect( + struct drm_connector *connector, + bool force) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + struct i2c_adapter *i2c_bus; + enum drm_connector_status ret = connector_status_disconnected; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (con->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (con->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + if (via_vt1632_sense(dev, i2c_bus)) { + ret = connector_status_connected; + drm_dbg_kms(dev, "DVI detected.\n"); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static const struct drm_connector_funcs via_vt1632_drm_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_vt1632_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = via_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = + drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = + drm_atomic_helper_connector_destroy_state, +}; + + +int via_vt1632_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + struct i2c_adapter *i2c_bus; + u8 buf; + uint32_t low_freq_limit, high_freq_limit; + int ret; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (con->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (con->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + ret = MODE_ERROR; + goto exit; + } + + via_i2c_readbytes(i2c_bus, 0x08, 0x06, &buf, 1); + low_freq_limit = buf * 1000; + via_i2c_readbytes(i2c_bus, 0x08, 0x07, &buf, 1); + high_freq_limit = (buf + 65) * 1000; + drm_dbg_kms(dev, "Low Frequency Limit: %u KHz\n", low_freq_limit); + drm_dbg_kms(dev, "High Frequency Limit: %u KHz\n", high_freq_limit); + + if (mode->clock < low_freq_limit) { + ret = MODE_CLOCK_LOW; + goto exit; + } + + if (mode->clock > high_freq_limit) { + ret = MODE_CLOCK_HIGH; + goto exit; + } + + ret = MODE_OK; +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return ret; +} + +static int via_vt1632_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct via_connector *con = container_of(connector, + struct via_connector, base); + int count = 0; + struct i2c_adapter *i2c_bus; + struct edid *edid = NULL; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (con->i2c_bus & VIA_I2C_BUS1) { + i2c_bus = via_find_ddc_bus(0x26); + } else if (con->i2c_bus & VIA_I2C_BUS2) { + i2c_bus = via_find_ddc_bus(0x31); + } else if (con->i2c_bus & VIA_I2C_BUS3) { + i2c_bus = via_find_ddc_bus(0x25); + } else if (con->i2c_bus & VIA_I2C_BUS4) { + i2c_bus = via_find_ddc_bus(0x2c); + } else if (con->i2c_bus & VIA_I2C_BUS5) { + i2c_bus = via_find_ddc_bus(0x3d); + } else { + i2c_bus = NULL; + goto exit; + } + + edid = drm_get_edid(&con->base, i2c_bus); + if (edid) { + if (edid->input & DRM_EDID_INPUT_DIGITAL) { + drm_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + drm_dbg_kms(dev, "DVI EDID information was obtained.\n"); + } + + kfree(edid); + } + +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return count; +} + +static const struct drm_connector_helper_funcs +via_vt1632_drm_connector_helper_funcs = { + .mode_valid = via_vt1632_mode_valid, + .get_modes = via_vt1632_get_modes, +}; + +bool via_vt1632_probe(struct drm_device *dev, + struct i2c_adapter *i2c_bus) +{ + u8 buf; + u16 vendor_id, device_id, revision; + bool device_detected = false; + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + via_i2c_readbytes(i2c_bus, 0x08, 0x00, &buf, 1); + vendor_id = buf; + via_i2c_readbytes(i2c_bus, 0x08, 0x01, &buf, 1); + vendor_id |= (buf << 8); + drm_dbg_kms(dev, "Vendor ID: %x\n", vendor_id); + via_i2c_readbytes(i2c_bus, 0x08, 0x02, &buf, 1); + device_id = buf; + via_i2c_readbytes(i2c_bus, 0x08, 0x03, &buf, 1); + device_id |= (buf << 8); + drm_dbg_kms(dev, "Device ID: %x\n", device_id); + via_i2c_readbytes(i2c_bus, 0x08, 0x04, &buf, 1); + revision = buf; + drm_dbg_kms(dev, "Revision: %x\n", revision); + + if ((vendor_id != 0x1106) || (device_id != 0x3192)) { + goto exit; + } + + device_detected = true; +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); + return device_detected; +} + +void via_vt1632_init(struct drm_device *dev) +{ + struct via_connector *con; + struct via_encoder *enc; + struct via_drm_priv *dev_priv = to_via_drm_priv(dev); + + drm_dbg_kms(dev, "Entered %s.\n", __func__); + + if (!dev_priv->ext_tmds_presence) { + goto exit; + } + + enc = kzalloc(sizeof(*enc) + sizeof(*con), GFP_KERNEL); + if (!enc) { + drm_err(dev, "Failed to allocate connector " + "and encoder.\n"); + goto exit; + } + + drm_encoder_init(dev, &enc->base, &via_vt1632_drm_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + drm_encoder_helper_add(&enc->base, + &via_vt1632_drm_encoder_helper_funcs); + + enc->base.possible_crtcs = BIT(1) | BIT(0); + enc->base.possible_clones = 0; + + enc->i2c_bus = dev_priv->ext_tmds_i2c_bus; + enc->di_port = dev_priv->ext_tmds_di_port; + + /* Increment the number of DVI connectors. */ + dev_priv->number_dvi++; + + + con = &enc->cons[0]; + + drm_connector_init(dev, &con->base, &via_vt1632_drm_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + drm_connector_helper_add(&con->base, + &via_vt1632_drm_connector_helper_funcs); + drm_connector_register(&con->base); + + con->base.doublescan_allowed = false; + con->base.interlace_allowed = false; + + con->i2c_bus = dev_priv->ext_tmds_i2c_bus; + + INIT_LIST_HEAD(&con->props); + drm_connector_attach_encoder(&con->base, &enc->base); +exit: + drm_dbg_kms(dev, "Exiting %s.\n", __func__); +} diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 275799b5f535..27c931c241e4 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1446,8 +1446,11 @@ #define PCI_DEVICE_ID_VIA_VT3324 0x0324 #define PCI_DEVICE_ID_VIA_VT3336 0x0336 #define PCI_DEVICE_ID_VIA_VT3351 0x0351 +#define PCI_DEVICE_ID_VIA_VX800_HB 0x0353 #define PCI_DEVICE_ID_VIA_VT3364 0x0364 #define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_VX855_HB 0x0409 +#define PCI_DEVICE_ID_VIA_VX900_HB 0x0410 #define PCI_DEVICE_ID_VIA_6415 0x0415 #define PCI_DEVICE_ID_VIA_8501_0 0x0501 #define PCI_DEVICE_ID_VIA_82C561 0x0561 @@ -1461,6 +1464,7 @@ #define PCI_DEVICE_ID_VIA_8605_0 0x0605 #define PCI_DEVICE_ID_VIA_82C686 0x0686 #define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#define PCI_DEVICE_ID_VIA_CHROME9_HC3 0x1122 #define PCI_DEVICE_ID_VIA_82C576_1 0x1571 #define PCI_DEVICE_ID_VIA_82C586_2 0x3038 #define PCI_DEVICE_ID_VIA_82C586_3 0x3040 @@ -1475,16 +1479,20 @@ #define PCI_DEVICE_ID_VIA_8653_0 0x3101 #define PCI_DEVICE_ID_VIA_8622 0x3102 #define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104 +#define PCI_DEVICE_ID_VIA_K8M800_GFX 0x3108 #define PCI_DEVICE_ID_VIA_8233C_0 0x3109 #define PCI_DEVICE_ID_VIA_8361 0x3112 #define PCI_DEVICE_ID_VIA_XM266 0x3116 +#define PCI_DEVICE_ID_VIA_PM800_GFX 0x3118 #define PCI_DEVICE_ID_VIA_612X 0x3119 +#define PCI_DEVICE_ID_VIA_CLE266_GFX 0x3122 #define PCI_DEVICE_ID_VIA_862X_0 0x3123 #define PCI_DEVICE_ID_VIA_8753_0 0x3128 #define PCI_DEVICE_ID_VIA_8233A 0x3147 #define PCI_DEVICE_ID_VIA_8703_51_0 0x3148 #define PCI_DEVICE_ID_VIA_8237_SATA 0x3149 #define PCI_DEVICE_ID_VIA_XN266 0x3156 +#define PCI_DEVICE_ID_VIA_UNICHROME_PRO_II 0x3157 #define PCI_DEVICE_ID_VIA_6410 0x3164 #define PCI_DEVICE_ID_VIA_8754C_0 0x3168 #define PCI_DEVICE_ID_VIA_8235 0x3177 @@ -1492,12 +1500,20 @@ #define PCI_DEVICE_ID_VIA_8377_0 0x3189 #define PCI_DEVICE_ID_VIA_8378_0 0x3205 #define PCI_DEVICE_ID_VIA_8783_0 0x3208 +#define PCI_DEVICE_ID_VIA_CN750 0x3225 #define PCI_DEVICE_ID_VIA_8237 0x3227 +#define PCI_DEVICE_ID_VIA_CHROME9 0x3230 #define PCI_DEVICE_ID_VIA_8251 0x3287 #define PCI_DEVICE_ID_VIA_8261 0x3402 #define PCI_DEVICE_ID_VIA_8237A 0x3337 +#define PCI_DEVICE_ID_VIA_P4M890_GFX 0x3343 +#define PCI_DEVICE_ID_VIA_P4M800_PRO_GFX 0x3344 +#define PCI_DEVICE_ID_VIA_CHROME9_HC 0x3371 #define PCI_DEVICE_ID_VIA_8237S 0x3372 +#define PCI_DEVICE_ID_VIA_CHROME9_HCM 0x5122 #define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324 +#define PCI_DEVICE_ID_VIA_CHROME9_HD 0x7122 +#define PCI_DEVICE_ID_VIA_KM400_GFX 0x7205 #define PCI_DEVICE_ID_VIA_8231 0x8231 #define PCI_DEVICE_ID_VIA_8231_4 0x8235 #define PCI_DEVICE_ID_VIA_8365_1 0x8305 diff --git a/include/uapi/drm/via_drm.h b/include/uapi/drm/via_drm.h new file mode 100644 index 000000000000..e36c1656f237 --- /dev/null +++ b/include/uapi/drm/via_drm.h @@ -0,0 +1,337 @@ +/* + * Copyright © 2020-2022 Kevin Brace + * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * 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 AUTHORS, COPYRIGHT HOLDERS, 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. + */ +#ifndef _VIA_DRM_H_ +#define _VIA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _VIA_DEFINES_ +#define _VIA_DEFINES_ + + +#define VIA_NR_SAREA_CLIPRECTS 8 +#define VIA_NR_XVMC_PORTS 10 +#define VIA_NR_XVMC_LOCKS 5 +#define VIA_MAX_CACHELINE_SIZE 64 +#define XVMCLOCKPTR(saPriv,lockNo) \ + ((volatile struct drm_hw_lock *)(((((unsigned long) (saPriv)->XvMCLockArea) + \ + (VIA_MAX_CACHELINE_SIZE - 1)) & \ + ~(VIA_MAX_CACHELINE_SIZE - 1)) + \ + VIA_MAX_CACHELINE_SIZE*(lockNo))) + +/* Each region is a minimum of 64k, and there are at most 64 of them. + */ +#define VIA_NR_TEX_REGIONS 64 +#define VIA_LOG_MIN_TEX_REGION_SIZE 16 +#endif + +#define VIA_UPLOAD_TEX0IMAGE 0x1 /* handled clientside */ +#define VIA_UPLOAD_TEX1IMAGE 0x2 /* handled clientside */ +#define VIA_UPLOAD_CTX 0x4 +#define VIA_UPLOAD_BUFFERS 0x8 +#define VIA_UPLOAD_TEX0 0x10 +#define VIA_UPLOAD_TEX1 0x20 +#define VIA_UPLOAD_CLIPRECTS 0x40 +#define VIA_UPLOAD_ALL 0xff + +/* VIA specific ioctls */ +#define DRM_VIA_ALLOCMEM 0x00 +#define DRM_VIA_FREEMEM 0x01 +#define DRM_VIA_AGP_INIT 0x02 +#define DRM_VIA_FB_INIT 0x03 +#define DRM_VIA_MAP_INIT 0x04 +#define DRM_VIA_DEC_FUTEX 0x05 +#define NOT_USED +#define DRM_VIA_DMA_INIT 0x07 +#define DRM_VIA_CMDBUFFER 0x08 +#define DRM_VIA_FLUSH 0x09 +#define DRM_VIA_PCICMD 0x0a +#define DRM_VIA_CMDBUF_SIZE 0x0b +#define NOT_USED +#define DRM_VIA_WAIT_IRQ 0x0d +#define DRM_VIA_DMA_BLIT 0x0e +#define DRM_VIA_BLIT_SYNC 0x0f + +/* + * OpenChrome DRM IOCTLs + */ +#define DRM_VIA_GEM_ALLOC 0x20 +#define DRM_VIA_GEM_MMAP 0x21 + + +#define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_AGP_INIT, drm_via_agp_t) +#define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) +#define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) +#define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) +#define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) +#define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) +#define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ + drm_via_cmdbuf_size_t) +#define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) +#define DRM_IOCTL_VIA_DMA_BLIT DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_DMA_BLIT, drm_via_dmablit_t) +#define DRM_IOCTL_VIA_BLIT_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_BLIT_SYNC, drm_via_blitsync_t) + +/* + * OpenChrome DRM IOCTLs + */ +#define DRM_IOCTL_VIA_GEM_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_GEM_ALLOC, struct drm_via_gem_alloc) +#define DRM_IOCTL_VIA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_GEM_MMAP, struct drm_via_gem_mmap) + +/* Indices into buf.Setup where various bits of state are mirrored per + * context and per buffer. These can be fired at the card as a unit, + * or in a piecewise fashion as required. + */ + +#define VIA_TEX_SETUP_SIZE 8 + +/* Flags for clear ioctl + */ +#define VIA_FRONT 0x1 +#define VIA_BACK 0x2 +#define VIA_DEPTH 0x4 +#define VIA_STENCIL 0x8 +#define VIA_MEM_VIDEO 0 /* matches drm constant */ +#define VIA_MEM_AGP 1 /* matches drm constant */ +#define VIA_MEM_SYSTEM 2 +#define VIA_MEM_MIXED 3 +#define VIA_MEM_UNKNOWN 4 + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_agp_t; + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_fb_t; + +typedef struct { + __u32 context; + __u32 type; + __u32 size; + unsigned long index; + unsigned long offset; +} drm_via_mem_t; + +typedef struct _drm_via_init { + enum { + VIA_INIT_MAP = 0x01, + VIA_CLEANUP_MAP = 0x02 + } func; + + unsigned long sarea_priv_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long agpAddr; +} drm_via_init_t; + +typedef struct _drm_via_futex { + enum { + VIA_FUTEX_WAIT = 0x00, + VIA_FUTEX_WAKE = 0X01 + } func; + __u32 ms; + __u32 lock; + __u32 val; +} drm_via_futex_t; + +typedef struct _drm_via_dma_init { + enum { + VIA_INIT_DMA = 0x01, + VIA_CLEANUP_DMA = 0x02, + VIA_DMA_INITIALIZED = 0x03 + } func; + + unsigned long offset; + unsigned long size; + unsigned long reg_pause_addr; +} drm_via_dma_init_t; + +typedef struct _drm_via_cmdbuffer { + char __user *buf; + unsigned long size; +} drm_via_cmdbuffer_t; + +/* Warning: If you change the SAREA structure you must change the Xserver + * structure as well */ + +typedef struct _drm_via_tex_region { + unsigned char next, prev; /* indices to form a circular LRU */ + unsigned char inUse; /* owned by a client, or free? */ + int age; /* tracked by clients to update local LRU's */ +} drm_via_tex_region_t; + +typedef struct _drm_via_sarea { + unsigned int dirty; + unsigned int nbox; + struct drm_clip_rect boxes[VIA_NR_SAREA_CLIPRECTS]; + drm_via_tex_region_t texList[VIA_NR_TEX_REGIONS + 1]; + int texAge; /* last time texture was uploaded */ + int ctxOwner; /* last context to upload state */ + int vertexPrim; + + /* + * Below is for XvMC. + * We want the lock integers alone on, and aligned to, a cache line. + * Therefore this somewhat strange construct. + */ + + char XvMCLockArea[VIA_MAX_CACHELINE_SIZE * (VIA_NR_XVMC_LOCKS + 1)]; + + unsigned int XvMCDisplaying[VIA_NR_XVMC_PORTS]; + unsigned int XvMCSubPicOn[VIA_NR_XVMC_PORTS]; + unsigned int XvMCCtxNoGrabbed; /* Last context to hold decoder */ + + /* Used by the 3d driver only at this point, for pageflipping: + */ + unsigned int pfCurrentOffset; +} drm_via_sarea_t; + +typedef struct _drm_via_cmdbuf_size { + enum { + VIA_CMDBUF_SPACE = 0x01, + VIA_CMDBUF_LAG = 0x02 + } func; + int wait; + __u32 size; +} drm_via_cmdbuf_size_t; + +typedef enum { + VIA_IRQ_ABSOLUTE = 0x0, + VIA_IRQ_RELATIVE = 0x1, + VIA_IRQ_SIGNAL = 0x10000000, + VIA_IRQ_FORCE_SEQUENCE = 0x20000000 +} via_irq_seq_type_t; + +#define VIA_IRQ_FLAGS_MASK 0xF0000000 + +enum drm_via_irqs { + drm_via_irq_hqv0 = 0, + drm_via_irq_hqv1, + drm_via_irq_dma0_dd, + drm_via_irq_dma0_td, + drm_via_irq_dma1_dd, + drm_via_irq_dma1_td, + drm_via_irq_num +}; + +struct drm_via_wait_irq_request { + unsigned irq; + via_irq_seq_type_t type; + __u32 sequence; + __u32 signal; +}; + +typedef union drm_via_irqwait { + struct drm_via_wait_irq_request request; + struct drm_wait_vblank_reply reply; +} drm_via_irqwait_t; + +typedef struct drm_via_blitsync { + __u32 sync_handle; + unsigned engine; +} drm_via_blitsync_t; + +/* - * Below,"flags" is currently unused but will be used for possible future + * extensions like kernel space bounce buffers for bad alignments and + * blit engine busy-wait polling for better latency in the absence of + * interrupts. + */ + +typedef struct drm_via_dmablit { + __u32 num_lines; + __u32 line_length; + + __u32 fb_addr; + __u32 fb_stride; + + unsigned char *mem_addr; + __u32 mem_stride; + + __u32 flags; + int to_fb; + + drm_via_blitsync_t sync; +} drm_via_dmablit_t; + +/* + * OpenChrome DRM IOCTL structs + */ + +/** + * struct drm_via_gem_alloc - IOCTL argument for allocating a GEM based BO + * (Buffer Object). + */ +struct drm_via_gem_alloc { + /* Alignment of the BO. */ + __u32 alignment; + __u32 pad; + + /* Size of the BO. Note that the actual size gets returned from DRM.*/ + __u64 size; + + /* + * TTM domain of the BO. Note that the actual domain gets returned + * from DRM. + */ + __u32 domain; + + /* GEM handle to the BO returned from DRM. */ + __u32 handle; + + /* Offset returned from DRM. */ + __u64 offset; +}; + +/** + * struct drm_via_gem_mmap - IOCTL argument for mapping a GEM based BO. + */ +struct drm_via_gem_mmap { + /* GEM handle of the BO. */ + __u32 handle; + __u32 pad; + + /* Offset returned from DRM. */ + __u64 offset; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VIA_DRM_H_ */ |