summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/Kconfig2
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/drm_hashtab.c205
-rw-r--r--drivers/gpu/drm/via/Kconfig9
-rw-r--r--drivers/gpu/drm/via/Makefile27
-rw-r--r--drivers/gpu/drm/via/via_3d_reg.h1845
-rw-r--r--drivers/gpu/drm/via/via_connector.c46
-rw-r--r--drivers/gpu/drm/via/via_crtc.c2335
-rw-r--r--drivers/gpu/drm/via/via_crtc_hw.c91
-rw-r--r--drivers/gpu/drm/via/via_crtc_hw.h1002
-rw-r--r--drivers/gpu/drm/via/via_cursor.c415
-rw-r--r--drivers/gpu/drm/via/via_dac.c474
-rw-r--r--drivers/gpu/drm/via/via_disp_reg.h513
-rw-r--r--drivers/gpu/drm/via/via_drv.c340
-rw-r--r--drivers/gpu/drm/via/via_drv.h400
-rw-r--r--drivers/gpu/drm/via/via_encoder.c38
-rw-r--r--drivers/gpu/drm/via/via_hdmi.c664
-rw-r--r--drivers/gpu/drm/via/via_i2c.c216
-rw-r--r--drivers/gpu/drm/via/via_init.c1472
-rw-r--r--drivers/gpu/drm/via/via_ioctl.c94
-rw-r--r--drivers/gpu/drm/via/via_lvds.c1233
-rw-r--r--drivers/gpu/drm/via/via_object.c327
-rw-r--r--drivers/gpu/drm/via/via_pll.c255
-rw-r--r--drivers/gpu/drm/via/via_pm.c189
-rw-r--r--drivers/gpu/drm/via/via_regs.h296
-rw-r--r--drivers/gpu/drm/via/via_sii164.c588
-rw-r--r--drivers/gpu/drm/via/via_tmds.c719
-rw-r--r--drivers/gpu/drm/via/via_ttm.c182
-rw-r--r--drivers/gpu/drm/via/via_tx.c231
-rw-r--r--drivers/gpu/drm/via/via_vt1632.c608
-rw-r--r--include/linux/pci_ids.h16
-rw-r--r--include/uapi/drm/via_drm.h337
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, &reg, 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, &reg, 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, &reg, hor_factor);
+
+ reg.count = ARRAY_SIZE(lcd_ver_scaling);
+ reg.regs = lcd_ver_scaling;
+ load_value_to_registers(VGABASE, &reg, 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_ */