1// SPDX-License-Identifier: MIT
2/*
3 * Copyright 2012 Red Hat Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
16 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
17 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19 * USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * The above copyright notice and this permission notice (including the
22 * next paragraph) shall be included in all copies or substantial portions
23 * of the Software.
24 */
25/*
26 * Authors: Dave Airlie <airlied@redhat.com>
27 */
28
29#include <linux/delay.h>
30#include <linux/pci.h>
31
32#include <drm/drm_drv.h>
33
34#include "ast_drv.h"
35#include "ast_post.h"
36
37/*
38 * DRAM type
39 */
40
41static enum ast_dram_layout ast_2100_get_dram_layout_p2a(struct ast_device *ast)
42{
43 u32 mcr_cfg;
44 enum ast_dram_layout dram_layout;
45
46 ast_write32(ast, reg: 0xf004, val: 0x1e6e0000);
47 ast_write32(ast, reg: 0xf000, val: 0x1);
48 mcr_cfg = ast_read32(ast, reg: 0x10004);
49
50 switch (mcr_cfg & 0x0c) {
51 case 0:
52 case 4:
53 default:
54 dram_layout = AST_DRAM_512Mx16;
55 break;
56 case 8:
57 if (mcr_cfg & 0x40)
58 dram_layout = AST_DRAM_1Gx16;
59 else
60 dram_layout = AST_DRAM_512Mx32;
61 break;
62 case 0xc:
63 dram_layout = AST_DRAM_1Gx32;
64 break;
65 }
66
67 return dram_layout;
68}
69
70/*
71 * POST
72 */
73
74static const struct ast_dramstruct ast1100_dram_table_data[] = {
75 { 0x2000, 0x1688a8a8 },
76 { 0x2020, 0x000041f0 },
77 AST_DRAMSTRUCT_UDELAY(67u),
78 { 0x0000, 0xfc600309 },
79 { 0x006C, 0x00909090 },
80 { 0x0064, 0x00050000 },
81 AST_DRAMSTRUCT_INIT(DRAM_TYPE, 0x00000585),
82 { 0x0008, 0x0011030f },
83 { 0x0010, 0x22201724 },
84 { 0x0018, 0x1e29011a },
85 { 0x0020, 0x00c82222 },
86 { 0x0014, 0x01001523 },
87 { 0x001C, 0x1024010d },
88 { 0x0024, 0x00cb2522 },
89 { 0x0038, 0xffffff82 },
90 { 0x003C, 0x00000000 },
91 { 0x0040, 0x00000000 },
92 { 0x0044, 0x00000000 },
93 { 0x0048, 0x00000000 },
94 { 0x004C, 0x00000000 },
95 { 0x0050, 0x00000000 },
96 { 0x0054, 0x00000000 },
97 { 0x0058, 0x00000000 },
98 { 0x005C, 0x00000000 },
99 { 0x0060, 0x032aa02a },
100 { 0x0064, 0x002d3000 },
101 { 0x0068, 0x00000000 },
102 { 0x0070, 0x00000000 },
103 { 0x0074, 0x00000000 },
104 { 0x0078, 0x00000000 },
105 { 0x007C, 0x00000000 },
106 { 0x0034, 0x00000001 },
107 AST_DRAMSTRUCT_UDELAY(67u),
108 { 0x002C, 0x00000732 },
109 { 0x0030, 0x00000040 },
110 { 0x0028, 0x00000005 },
111 { 0x0028, 0x00000007 },
112 { 0x0028, 0x00000003 },
113 { 0x0028, 0x00000001 },
114 { 0x000C, 0x00005a08 },
115 { 0x002C, 0x00000632 },
116 { 0x0028, 0x00000001 },
117 { 0x0030, 0x000003c0 },
118 { 0x0028, 0x00000003 },
119 { 0x0030, 0x00000040 },
120 { 0x0028, 0x00000003 },
121 { 0x000C, 0x00005a21 },
122 { 0x0034, 0x00007c03 },
123 { 0x0120, 0x00004c41 },
124 AST_DRAMSTRUCT_INVALID,
125};
126
127static const struct ast_dramstruct ast2100_dram_table_data[] = {
128 { 0x2000, 0x1688a8a8 },
129 { 0x2020, 0x00004120 },
130 AST_DRAMSTRUCT_UDELAY(67u),
131 { 0x0000, 0xfc600309 },
132 { 0x006C, 0x00909090 },
133 { 0x0064, 0x00070000 },
134 AST_DRAMSTRUCT_INIT(DRAM_TYPE, 0x00000489),
135 { 0x0008, 0x0011030f },
136 { 0x0010, 0x32302926 },
137 { 0x0018, 0x274c0122 },
138 { 0x0020, 0x00ce2222 },
139 { 0x0014, 0x01001523 },
140 { 0x001C, 0x1024010d },
141 { 0x0024, 0x00cb2522 },
142 { 0x0038, 0xffffff82 },
143 { 0x003C, 0x00000000 },
144 { 0x0040, 0x00000000 },
145 { 0x0044, 0x00000000 },
146 { 0x0048, 0x00000000 },
147 { 0x004C, 0x00000000 },
148 { 0x0050, 0x00000000 },
149 { 0x0054, 0x00000000 },
150 { 0x0058, 0x00000000 },
151 { 0x005C, 0x00000000 },
152 { 0x0060, 0x0f2aa02a },
153 { 0x0064, 0x003f3005 },
154 { 0x0068, 0x02020202 },
155 { 0x0070, 0x00000000 },
156 { 0x0074, 0x00000000 },
157 { 0x0078, 0x00000000 },
158 { 0x007C, 0x00000000 },
159 { 0x0034, 0x00000001 },
160 AST_DRAMSTRUCT_UDELAY(67u),
161 { 0x002C, 0x00000942 },
162 { 0x0030, 0x00000040 },
163 { 0x0028, 0x00000005 },
164 { 0x0028, 0x00000007 },
165 { 0x0028, 0x00000003 },
166 { 0x0028, 0x00000001 },
167 { 0x000C, 0x00005a08 },
168 { 0x002C, 0x00000842 },
169 { 0x0028, 0x00000001 },
170 { 0x0030, 0x000003c0 },
171 { 0x0028, 0x00000003 },
172 { 0x0030, 0x00000040 },
173 { 0x0028, 0x00000003 },
174 { 0x000C, 0x00005a21 },
175 { 0x0034, 0x00007c03 },
176 { 0x0120, 0x00005061 },
177 AST_DRAMSTRUCT_INVALID,
178};
179
180/*
181 * AST2100/2150 DLL CBR Setting
182 */
183#define CBR_SIZE_AST2150 ((16 << 10) - 1)
184#define CBR_PASSNUM_AST2150 5
185#define CBR_THRESHOLD_AST2150 10
186#define CBR_THRESHOLD2_AST2150 10
187#define TIMEOUT_AST2150 5000000
188
189#define CBR_PATNUM_AST2150 8
190
191static const u32 pattern_AST2150[14] = {
192 0xFF00FF00,
193 0xCC33CC33,
194 0xAA55AA55,
195 0xFFFE0001,
196 0x683501FE,
197 0x0F1929B0,
198 0x2D0B4346,
199 0x60767F02,
200 0x6FBE36A6,
201 0x3A253035,
202 0x3019686D,
203 0x41C6167E,
204 0x620152BF,
205 0x20F050E0
206};
207
208static u32 mmctestburst2_ast2150(struct ast_device *ast, u32 datagen)
209{
210 u32 data, timeout;
211
212 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000000);
213 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000001 | (datagen << 3));
214 timeout = 0;
215 do {
216 data = ast_mindwm(ast, r: 0x1e6e0070) & 0x40;
217 if (++timeout > TIMEOUT_AST2150) {
218 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000000);
219 return 0xffffffff;
220 }
221 } while (!data);
222 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000000);
223 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000003 | (datagen << 3));
224 timeout = 0;
225 do {
226 data = ast_mindwm(ast, r: 0x1e6e0070) & 0x40;
227 if (++timeout > TIMEOUT_AST2150) {
228 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000000);
229 return 0xffffffff;
230 }
231 } while (!data);
232 data = (ast_mindwm(ast, r: 0x1e6e0070) & 0x80) >> 7;
233 ast_moutdwm(ast, r: 0x1e6e0070, v: 0x00000000);
234 return data;
235}
236
237static int cbrtest_ast2150(struct ast_device *ast)
238{
239 int i;
240
241 for (i = 0; i < 8; i++)
242 if (mmctestburst2_ast2150(ast, datagen: i))
243 return 0;
244 return 1;
245}
246
247static int cbrscan_ast2150(struct ast_device *ast, int busw)
248{
249 u32 patcnt, loop;
250
251 for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
252 ast_moutdwm(ast, r: 0x1e6e007c, v: pattern_AST2150[patcnt]);
253 for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
254 if (cbrtest_ast2150(ast))
255 break;
256 }
257 if (loop == CBR_PASSNUM_AST2150)
258 return 0;
259 }
260 return 1;
261}
262
263static void cbrdlli_ast2150(struct ast_device *ast, int busw)
264{
265 u32 dll_min[4], dll_max[4], dlli, data, passcnt;
266
267cbr_start:
268 dll_min[0] = 0xff;
269 dll_min[1] = 0xff;
270 dll_min[2] = 0xff;
271 dll_min[3] = 0xff;
272 dll_max[0] = 0x00;
273 dll_max[1] = 0x00;
274 dll_max[2] = 0x00;
275 dll_max[3] = 0x00;
276 passcnt = 0;
277
278 for (dlli = 0; dlli < 100; dlli++) {
279 ast_moutdwm(ast, r: 0x1e6e0068, v: dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
280 data = cbrscan_ast2150(ast, busw);
281 if (data != 0) {
282 if (data & 0x1) {
283 if (dll_min[0] > dlli)
284 dll_min[0] = dlli;
285 if (dll_max[0] < dlli)
286 dll_max[0] = dlli;
287 }
288 passcnt++;
289 } else if (passcnt >= CBR_THRESHOLD_AST2150) {
290 goto cbr_start;
291 }
292 }
293 if (dll_max[0] == 0 || (dll_max[0] - dll_min[0]) < CBR_THRESHOLD_AST2150)
294 goto cbr_start;
295
296 dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
297 ast_moutdwm(ast, r: 0x1e6e0068, v: dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
298}
299
300static void ast_post_chip_2100(struct ast_device *ast)
301{
302 u8 j;
303 u32 data, temp, i;
304 const struct ast_dramstruct *dram_reg_info;
305 enum ast_dram_layout dram_layout = ast_2100_get_dram_layout_p2a(ast);
306
307 j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd0, preserve_mask: 0xff);
308
309 if ((j & 0x80) == 0) { /* VGA only */
310 if (ast->chip == AST2100 || ast->chip == AST2200)
311 dram_reg_info = ast2100_dram_table_data;
312 else
313 dram_reg_info = ast1100_dram_table_data;
314
315 ast_write32(ast, reg: 0xf004, val: 0x1e6e0000);
316 ast_write32(ast, reg: 0xf000, val: 0x1);
317 ast_write32(ast, reg: 0x12000, val: 0x1688A8A8);
318 do {
319 ;
320 } while (ast_read32(ast, reg: 0x12000) != 0x01);
321
322 ast_write32(ast, reg: 0x10000, val: 0xfc600309);
323 do {
324 ;
325 } while (ast_read32(ast, reg: 0x10000) != 0x01);
326
327 while (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) {
328 if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) {
329 for (i = 0; i < 15; i++)
330 udelay(usec: dram_reg_info->data);
331 } else if (AST_DRAMSTRUCT_IS(dram_reg_info, DRAM_TYPE)) {
332 switch (dram_layout) {
333 case AST_DRAM_1Gx16:
334 data = 0x00000d89;
335 break;
336 case AST_DRAM_1Gx32:
337 data = 0x00000c8d;
338 break;
339 default:
340 data = dram_reg_info->data;
341 break;
342 }
343
344 temp = ast_read32(ast, reg: 0x12070);
345 temp &= 0xc;
346 temp <<= 2;
347 ast_write32(ast, reg: 0x10000 + dram_reg_info->index, val: data | temp);
348 } else {
349 ast_write32(ast, reg: 0x10000 + dram_reg_info->index,
350 val: dram_reg_info->data);
351 }
352 dram_reg_info++;
353 }
354
355 /* AST 2100/2150 DRAM calibration */
356 data = ast_read32(ast, reg: 0x10120);
357 if (data == 0x5061) { /* 266Mhz */
358 data = ast_read32(ast, reg: 0x10004);
359 if (data & 0x40)
360 cbrdlli_ast2150(ast, busw: 16); /* 16 bits */
361 else
362 cbrdlli_ast2150(ast, busw: 32); /* 32 bits */
363 }
364
365 temp = ast_read32(ast, reg: 0x1200c);
366 ast_write32(ast, reg: 0x1200c, val: temp & 0xfffffffd);
367 temp = ast_read32(ast, reg: 0x12040);
368 ast_write32(ast, reg: 0x12040, val: temp | 0x40);
369 }
370
371 /* wait ready */
372 do {
373 j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd0, preserve_mask: 0xff);
374 } while ((j & 0x40) == 0);
375}
376
377int ast_2100_post(struct ast_device *ast)
378{
379 ast_2000_set_def_ext_reg(ast);
380
381 if (ast->config_mode == ast_use_p2a) {
382 ast_post_chip_2100(ast);
383 } else {
384 if (ast->tx_chip == AST_TX_SIL164) {
385 /* Enable DVO */
386 ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xa3, preserve_mask: 0xcf, val: 0x80);
387 }
388 }
389
390 return 0;
391}
392
393/*
394 * Widescreen detection
395 */
396
397/* Try to detect WSXGA+ on Gen2+ */
398bool __ast_2100_detect_wsxga_p(struct ast_device *ast)
399{
400 u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, index: 0xd0);
401
402 if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC))
403 return true;
404 if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN)
405 return true;
406
407 return false;
408}
409
410/* Try to detect WUXGA on Gen2+ */
411bool __ast_2100_detect_wuxga(struct ast_device *ast)
412{
413 u8 vgacrd1;
414
415 if (ast->support_fullhd) {
416 vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, index: 0xd1);
417 if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA))
418 return true;
419 }
420
421 return false;
422}
423
424static void ast_2100_detect_widescreen(struct ast_device *ast)
425{
426 if (__ast_2100_detect_wsxga_p(ast)) {
427 ast->support_wsxga_p = true;
428 if (ast->chip == AST2100)
429 ast->support_fullhd = true;
430 }
431 if (__ast_2100_detect_wuxga(ast))
432 ast->support_wuxga = true;
433}
434
435static const struct ast_device_quirks ast_2100_device_quirks = {
436 .crtc_mem_req_threshold_low = 47,
437 .crtc_mem_req_threshold_high = 63,
438};
439
440struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
441 const struct drm_driver *drv,
442 enum ast_chip chip,
443 enum ast_config_mode config_mode,
444 void __iomem *regs,
445 void __iomem *ioregs,
446 bool need_post)
447{
448 struct drm_device *dev;
449 struct ast_device *ast;
450 int ret;
451
452 ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
453 if (IS_ERR(ptr: ast))
454 return ERR_CAST(ptr: ast);
455 dev = &ast->base;
456
457 ast_device_init(ast, chip, config_mode, regs, ioregs, quirks: &ast_2100_device_quirks);
458
459 ast->dclk_table = ast_2000_dclk_table;
460
461 ast_2000_detect_tx_chip(ast, need_post);
462
463 if (need_post) {
464 ret = ast_post_gpu(ast);
465 if (ret)
466 return ERR_PTR(error: ret);
467 }
468
469 ret = ast_mm_init(ast);
470 if (ret)
471 return ERR_PTR(error: ret);
472
473 ast_2100_detect_widescreen(ast);
474
475 ret = ast_mode_config_init(ast);
476 if (ret)
477 return ERR_PTR(error: ret);
478
479 return dev;
480}
481

source code of linux/drivers/gpu/drm/ast/ast_2100.c