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