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