1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved.
4*4882a593Smuzhiyun * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved.
5*4882a593Smuzhiyun * Copyright 2010 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun /*
8*4882a593Smuzhiyun * basic modesetting functions
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/via-core.h>
13*4882a593Smuzhiyun #include "via_modesetting.h"
14*4882a593Smuzhiyun #include "share.h"
15*4882a593Smuzhiyun #include "debug.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun
via_set_primary_timing(const struct via_display_timing * timing)18*4882a593Smuzhiyun void via_set_primary_timing(const struct via_display_timing *timing)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun struct via_display_timing raw;
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun raw.hor_total = timing->hor_total / 8 - 5;
23*4882a593Smuzhiyun raw.hor_addr = timing->hor_addr / 8 - 1;
24*4882a593Smuzhiyun raw.hor_blank_start = timing->hor_blank_start / 8 - 1;
25*4882a593Smuzhiyun raw.hor_blank_end = timing->hor_blank_end / 8 - 1;
26*4882a593Smuzhiyun raw.hor_sync_start = timing->hor_sync_start / 8;
27*4882a593Smuzhiyun raw.hor_sync_end = timing->hor_sync_end / 8;
28*4882a593Smuzhiyun raw.ver_total = timing->ver_total - 2;
29*4882a593Smuzhiyun raw.ver_addr = timing->ver_addr - 1;
30*4882a593Smuzhiyun raw.ver_blank_start = timing->ver_blank_start - 1;
31*4882a593Smuzhiyun raw.ver_blank_end = timing->ver_blank_end - 1;
32*4882a593Smuzhiyun raw.ver_sync_start = timing->ver_sync_start - 1;
33*4882a593Smuzhiyun raw.ver_sync_end = timing->ver_sync_end - 1;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /* unlock timing registers */
36*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x11, 0x00, 0x80);
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun via_write_reg(VIACR, 0x00, raw.hor_total & 0xFF);
39*4882a593Smuzhiyun via_write_reg(VIACR, 0x01, raw.hor_addr & 0xFF);
40*4882a593Smuzhiyun via_write_reg(VIACR, 0x02, raw.hor_blank_start & 0xFF);
41*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x03, raw.hor_blank_end & 0x1F, 0x1F);
42*4882a593Smuzhiyun via_write_reg(VIACR, 0x04, raw.hor_sync_start & 0xFF);
43*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x05, (raw.hor_sync_end & 0x1F)
44*4882a593Smuzhiyun | (raw.hor_blank_end << (7 - 5) & 0x80), 0x9F);
45*4882a593Smuzhiyun via_write_reg(VIACR, 0x06, raw.ver_total & 0xFF);
46*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x07, (raw.ver_total >> 8 & 0x01)
47*4882a593Smuzhiyun | (raw.ver_addr >> (8 - 1) & 0x02)
48*4882a593Smuzhiyun | (raw.ver_sync_start >> (8 - 2) & 0x04)
49*4882a593Smuzhiyun | (raw.ver_blank_start >> (8 - 3) & 0x08)
50*4882a593Smuzhiyun | (raw.ver_total >> (9 - 5) & 0x20)
51*4882a593Smuzhiyun | (raw.ver_addr >> (9 - 6) & 0x40)
52*4882a593Smuzhiyun | (raw.ver_sync_start >> (9 - 7) & 0x80), 0xEF);
53*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x09, raw.ver_blank_start >> (9 - 5) & 0x20,
54*4882a593Smuzhiyun 0x20);
55*4882a593Smuzhiyun via_write_reg(VIACR, 0x10, raw.ver_sync_start & 0xFF);
56*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x11, raw.ver_sync_end & 0x0F, 0x0F);
57*4882a593Smuzhiyun via_write_reg(VIACR, 0x12, raw.ver_addr & 0xFF);
58*4882a593Smuzhiyun via_write_reg(VIACR, 0x15, raw.ver_blank_start & 0xFF);
59*4882a593Smuzhiyun via_write_reg(VIACR, 0x16, raw.ver_blank_end & 0xFF);
60*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x33, (raw.hor_sync_start >> (8 - 4) & 0x10)
61*4882a593Smuzhiyun | (raw.hor_blank_end >> (6 - 5) & 0x20), 0x30);
62*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x35, (raw.ver_total >> 10 & 0x01)
63*4882a593Smuzhiyun | (raw.ver_sync_start >> (10 - 1) & 0x02)
64*4882a593Smuzhiyun | (raw.ver_addr >> (10 - 2) & 0x04)
65*4882a593Smuzhiyun | (raw.ver_blank_start >> (10 - 3) & 0x08), 0x0F);
66*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x36, raw.hor_total >> (8 - 3) & 0x08, 0x08);
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun /* lock timing registers */
69*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x11, 0x80, 0x80);
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun /* reset timing control */
72*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x17, 0x00, 0x80);
73*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x17, 0x80, 0x80);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
via_set_secondary_timing(const struct via_display_timing * timing)76*4882a593Smuzhiyun void via_set_secondary_timing(const struct via_display_timing *timing)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun struct via_display_timing raw;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun raw.hor_total = timing->hor_total - 1;
81*4882a593Smuzhiyun raw.hor_addr = timing->hor_addr - 1;
82*4882a593Smuzhiyun raw.hor_blank_start = timing->hor_blank_start - 1;
83*4882a593Smuzhiyun raw.hor_blank_end = timing->hor_blank_end - 1;
84*4882a593Smuzhiyun raw.hor_sync_start = timing->hor_sync_start - 1;
85*4882a593Smuzhiyun raw.hor_sync_end = timing->hor_sync_end - 1;
86*4882a593Smuzhiyun raw.ver_total = timing->ver_total - 1;
87*4882a593Smuzhiyun raw.ver_addr = timing->ver_addr - 1;
88*4882a593Smuzhiyun raw.ver_blank_start = timing->ver_blank_start - 1;
89*4882a593Smuzhiyun raw.ver_blank_end = timing->ver_blank_end - 1;
90*4882a593Smuzhiyun raw.ver_sync_start = timing->ver_sync_start - 1;
91*4882a593Smuzhiyun raw.ver_sync_end = timing->ver_sync_end - 1;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun via_write_reg(VIACR, 0x50, raw.hor_total & 0xFF);
94*4882a593Smuzhiyun via_write_reg(VIACR, 0x51, raw.hor_addr & 0xFF);
95*4882a593Smuzhiyun via_write_reg(VIACR, 0x52, raw.hor_blank_start & 0xFF);
96*4882a593Smuzhiyun via_write_reg(VIACR, 0x53, raw.hor_blank_end & 0xFF);
97*4882a593Smuzhiyun via_write_reg(VIACR, 0x54, (raw.hor_blank_start >> 8 & 0x07)
98*4882a593Smuzhiyun | (raw.hor_blank_end >> (8 - 3) & 0x38)
99*4882a593Smuzhiyun | (raw.hor_sync_start >> (8 - 6) & 0xC0));
100*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x55, (raw.hor_total >> 8 & 0x0F)
101*4882a593Smuzhiyun | (raw.hor_addr >> (8 - 4) & 0x70), 0x7F);
102*4882a593Smuzhiyun via_write_reg(VIACR, 0x56, raw.hor_sync_start & 0xFF);
103*4882a593Smuzhiyun via_write_reg(VIACR, 0x57, raw.hor_sync_end & 0xFF);
104*4882a593Smuzhiyun via_write_reg(VIACR, 0x58, raw.ver_total & 0xFF);
105*4882a593Smuzhiyun via_write_reg(VIACR, 0x59, raw.ver_addr & 0xFF);
106*4882a593Smuzhiyun via_write_reg(VIACR, 0x5A, raw.ver_blank_start & 0xFF);
107*4882a593Smuzhiyun via_write_reg(VIACR, 0x5B, raw.ver_blank_end & 0xFF);
108*4882a593Smuzhiyun via_write_reg(VIACR, 0x5C, (raw.ver_blank_start >> 8 & 0x07)
109*4882a593Smuzhiyun | (raw.ver_blank_end >> (8 - 3) & 0x38)
110*4882a593Smuzhiyun | (raw.hor_sync_end >> (8 - 6) & 0x40)
111*4882a593Smuzhiyun | (raw.hor_sync_start >> (10 - 7) & 0x80));
112*4882a593Smuzhiyun via_write_reg(VIACR, 0x5D, (raw.ver_total >> 8 & 0x07)
113*4882a593Smuzhiyun | (raw.ver_addr >> (8 - 3) & 0x38)
114*4882a593Smuzhiyun | (raw.hor_blank_end >> (11 - 6) & 0x40)
115*4882a593Smuzhiyun | (raw.hor_sync_start >> (11 - 7) & 0x80));
116*4882a593Smuzhiyun via_write_reg(VIACR, 0x5E, raw.ver_sync_start & 0xFF);
117*4882a593Smuzhiyun via_write_reg(VIACR, 0x5F, (raw.ver_sync_end & 0x1F)
118*4882a593Smuzhiyun | (raw.ver_sync_start >> (8 - 5) & 0xE0));
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun
via_set_primary_address(u32 addr)121*4882a593Smuzhiyun void via_set_primary_address(u32 addr)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun DEBUG_MSG(KERN_DEBUG "via_set_primary_address(0x%08X)\n", addr);
124*4882a593Smuzhiyun via_write_reg(VIACR, 0x0D, addr & 0xFF);
125*4882a593Smuzhiyun via_write_reg(VIACR, 0x0C, (addr >> 8) & 0xFF);
126*4882a593Smuzhiyun via_write_reg(VIACR, 0x34, (addr >> 16) & 0xFF);
127*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x48, (addr >> 24) & 0x1F, 0x1F);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
via_set_secondary_address(u32 addr)130*4882a593Smuzhiyun void via_set_secondary_address(u32 addr)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun DEBUG_MSG(KERN_DEBUG "via_set_secondary_address(0x%08X)\n", addr);
133*4882a593Smuzhiyun /* secondary display supports only quadword aligned memory */
134*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x62, (addr >> 2) & 0xFE, 0xFE);
135*4882a593Smuzhiyun via_write_reg(VIACR, 0x63, (addr >> 10) & 0xFF);
136*4882a593Smuzhiyun via_write_reg(VIACR, 0x64, (addr >> 18) & 0xFF);
137*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0xA3, (addr >> 26) & 0x07, 0x07);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
via_set_primary_pitch(u32 pitch)140*4882a593Smuzhiyun void via_set_primary_pitch(u32 pitch)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun DEBUG_MSG(KERN_DEBUG "via_set_primary_pitch(0x%08X)\n", pitch);
143*4882a593Smuzhiyun /* spec does not say that first adapter skips 3 bits but old
144*4882a593Smuzhiyun * code did it and seems to be reasonable in analogy to 2nd adapter
145*4882a593Smuzhiyun */
146*4882a593Smuzhiyun pitch = pitch >> 3;
147*4882a593Smuzhiyun via_write_reg(VIACR, 0x13, pitch & 0xFF);
148*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x35, (pitch >> (8 - 5)) & 0xE0, 0xE0);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
via_set_secondary_pitch(u32 pitch)151*4882a593Smuzhiyun void via_set_secondary_pitch(u32 pitch)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun DEBUG_MSG(KERN_DEBUG "via_set_secondary_pitch(0x%08X)\n", pitch);
154*4882a593Smuzhiyun pitch = pitch >> 3;
155*4882a593Smuzhiyun via_write_reg(VIACR, 0x66, pitch & 0xFF);
156*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x67, (pitch >> 8) & 0x03, 0x03);
157*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x71, (pitch >> (10 - 7)) & 0x80, 0x80);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
via_set_primary_color_depth(u8 depth)160*4882a593Smuzhiyun void via_set_primary_color_depth(u8 depth)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun u8 value;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun DEBUG_MSG(KERN_DEBUG "via_set_primary_color_depth(%d)\n", depth);
165*4882a593Smuzhiyun switch (depth) {
166*4882a593Smuzhiyun case 8:
167*4882a593Smuzhiyun value = 0x00;
168*4882a593Smuzhiyun break;
169*4882a593Smuzhiyun case 15:
170*4882a593Smuzhiyun value = 0x04;
171*4882a593Smuzhiyun break;
172*4882a593Smuzhiyun case 16:
173*4882a593Smuzhiyun value = 0x14;
174*4882a593Smuzhiyun break;
175*4882a593Smuzhiyun case 24:
176*4882a593Smuzhiyun value = 0x0C;
177*4882a593Smuzhiyun break;
178*4882a593Smuzhiyun case 30:
179*4882a593Smuzhiyun value = 0x08;
180*4882a593Smuzhiyun break;
181*4882a593Smuzhiyun default:
182*4882a593Smuzhiyun printk(KERN_WARNING "via_set_primary_color_depth: "
183*4882a593Smuzhiyun "Unsupported depth: %d\n", depth);
184*4882a593Smuzhiyun return;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun via_write_reg_mask(VIASR, 0x15, value, 0x1C);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
via_set_secondary_color_depth(u8 depth)190*4882a593Smuzhiyun void via_set_secondary_color_depth(u8 depth)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun u8 value;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun DEBUG_MSG(KERN_DEBUG "via_set_secondary_color_depth(%d)\n", depth);
195*4882a593Smuzhiyun switch (depth) {
196*4882a593Smuzhiyun case 8:
197*4882a593Smuzhiyun value = 0x00;
198*4882a593Smuzhiyun break;
199*4882a593Smuzhiyun case 16:
200*4882a593Smuzhiyun value = 0x40;
201*4882a593Smuzhiyun break;
202*4882a593Smuzhiyun case 24:
203*4882a593Smuzhiyun value = 0xC0;
204*4882a593Smuzhiyun break;
205*4882a593Smuzhiyun case 30:
206*4882a593Smuzhiyun value = 0x80;
207*4882a593Smuzhiyun break;
208*4882a593Smuzhiyun default:
209*4882a593Smuzhiyun printk(KERN_WARNING "via_set_secondary_color_depth: "
210*4882a593Smuzhiyun "Unsupported depth: %d\n", depth);
211*4882a593Smuzhiyun return;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun via_write_reg_mask(VIACR, 0x67, value, 0xC0);
215*4882a593Smuzhiyun }
216