]>
Commit | Line | Data |
---|---|---|
a605ea7e DE |
1 | /* |
2 | * (C) Copyright 2010 | |
3 | * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
a605ea7e DE |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <i2c.h> | |
10 | #include <asm/io.h> | |
11 | ||
2da0fc0d | 12 | #include <gdsys_fpga.h> |
a605ea7e DE |
13 | |
14 | #define CH7301_I2C_ADDR 0x75 | |
15 | ||
2da0fc0d | 16 | #define ICS8N3QV01_I2C_ADDR 0x6E |
6853cc4b DE |
17 | #define ICS8N3QV01_FREF 114285000 |
18 | #define ICS8N3QV01_FREF_LL 114285000LL | |
19 | #define ICS8N3QV01_F_DEFAULT_0 156250000LL | |
20 | #define ICS8N3QV01_F_DEFAULT_1 125000000LL | |
21 | #define ICS8N3QV01_F_DEFAULT_2 100000000LL | |
22 | #define ICS8N3QV01_F_DEFAULT_3 25175000LL | |
2da0fc0d DE |
23 | |
24 | #define SIL1178_MASTER_I2C_ADDRESS 0x38 | |
25 | #define SIL1178_SLAVE_I2C_ADDRESS 0x39 | |
26 | ||
a605ea7e DE |
27 | #define PIXCLK_640_480_60 25180000 |
28 | ||
29 | #define BASE_WIDTH 32 | |
30 | #define BASE_HEIGHT 16 | |
31 | #define BUFSIZE (BASE_WIDTH * BASE_HEIGHT) | |
32 | ||
a605ea7e DE |
33 | enum { |
34 | CH7301_CM = 0x1c, /* Clock Mode Register */ | |
35 | CH7301_IC = 0x1d, /* Input Clock Register */ | |
36 | CH7301_GPIO = 0x1e, /* GPIO Control Register */ | |
37 | CH7301_IDF = 0x1f, /* Input Data Format Register */ | |
38 | CH7301_CD = 0x20, /* Connection Detect Register */ | |
39 | CH7301_DC = 0x21, /* DAC Control Register */ | |
40 | CH7301_HPD = 0x23, /* Hot Plug Detection Register */ | |
41 | CH7301_TCTL = 0x31, /* DVI Control Input Register */ | |
42 | CH7301_TPCP = 0x33, /* DVI PLL Charge Pump Ctrl Register */ | |
43 | CH7301_TPD = 0x34, /* DVI PLL Divide Register */ | |
44 | CH7301_TPVT = 0x35, /* DVI PLL Supply Control Register */ | |
45 | CH7301_TPF = 0x36, /* DVI PLL Filter Register */ | |
46 | CH7301_TCT = 0x37, /* DVI Clock Test Register */ | |
47 | CH7301_TSTP = 0x48, /* Test Pattern Register */ | |
48 | CH7301_PM = 0x49, /* Power Management register */ | |
49 | CH7301_VID = 0x4a, /* Version ID Register */ | |
50 | CH7301_DID = 0x4b, /* Device ID Register */ | |
51 | CH7301_DSP = 0x56, /* DVI Sync polarity Register */ | |
52 | }; | |
53 | ||
2da0fc0d DE |
54 | #if defined(CONFIG_SYS_ICS8N3QV01) || defined(CONFIG_SYS_SIL1178) |
55 | static void fpga_iic_write(unsigned screen, u8 slave, u8 reg, u8 data) | |
56 | { | |
0e60aa85 DE |
57 | struct ihs_fpga *fpga = (struct ihs_fpga *)CONFIG_SYS_FPGA_BASE(screen); |
58 | struct ihs_i2c *i2c = &fpga->i2c; | |
2da0fc0d DE |
59 | |
60 | while (in_le16(&fpga->extended_interrupt) & (1 << 12)) | |
61 | ; | |
62 | out_le16(&i2c->write_mailbox_ext, reg | (data << 8)); | |
63 | out_le16(&i2c->write_mailbox, 0xc400 | (slave << 1)); | |
64 | } | |
65 | ||
66 | static u8 fpga_iic_read(unsigned screen, u8 slave, u8 reg) | |
67 | { | |
0e60aa85 DE |
68 | struct ihs_fpga *fpga = (struct ihs_fpga *)CONFIG_SYS_FPGA_BASE(screen); |
69 | struct ihs_i2c *i2c = &fpga->i2c; | |
2da0fc0d DE |
70 | unsigned int ctr = 0; |
71 | ||
72 | while (in_le16(&fpga->extended_interrupt) & (1 << 12)) | |
73 | ; | |
74 | out_le16(&fpga->extended_interrupt, 1 << 14); | |
75 | out_le16(&i2c->write_mailbox_ext, reg); | |
76 | out_le16(&i2c->write_mailbox, 0xc000 | (slave << 1)); | |
77 | while (!(in_le16(&fpga->extended_interrupt) & (1 << 14))) { | |
78 | udelay(100000); | |
79 | if (ctr++ > 5) { | |
80 | printf("iic receive timeout\n"); | |
81 | break; | |
82 | } | |
83 | } | |
84 | return in_le16(&i2c->read_mailbox_ext) >> 8; | |
85 | } | |
86 | #endif | |
87 | ||
88 | #ifdef CONFIG_SYS_MPC92469AC | |
a605ea7e DE |
89 | static void mpc92469ac_calc_parameters(unsigned int fout, |
90 | unsigned int *post_div, unsigned int *feedback_div) | |
91 | { | |
92 | unsigned int n = *post_div; | |
93 | unsigned int m = *feedback_div; | |
94 | unsigned int a; | |
95 | unsigned int b = 14745600 / 16; | |
96 | ||
97 | if (fout < 50169600) | |
98 | n = 8; | |
99 | else if (fout < 100339199) | |
100 | n = 4; | |
101 | else if (fout < 200678399) | |
102 | n = 2; | |
103 | else | |
104 | n = 1; | |
105 | ||
106 | a = fout * n + (b / 2); /* add b/2 for proper rounding */ | |
107 | ||
108 | m = a / b; | |
109 | ||
110 | *post_div = n; | |
111 | *feedback_div = m; | |
112 | } | |
113 | ||
2da0fc0d | 114 | static void mpc92469ac_set(unsigned screen, unsigned int fout) |
a605ea7e | 115 | { |
0e60aa85 | 116 | struct ihs_fpga *fpga = (struct ihs_fpga *)CONFIG_SYS_FPGA_BASE(screen); |
a605ea7e DE |
117 | unsigned int n; |
118 | unsigned int m; | |
119 | unsigned int bitval = 0; | |
120 | mpc92469ac_calc_parameters(fout, &n, &m); | |
121 | ||
122 | switch (n) { | |
123 | case 1: | |
124 | bitval = 0x00; | |
125 | break; | |
126 | case 2: | |
127 | bitval = 0x01; | |
128 | break; | |
129 | case 4: | |
130 | bitval = 0x02; | |
131 | break; | |
132 | case 8: | |
133 | bitval = 0x03; | |
134 | break; | |
135 | } | |
136 | ||
2da0fc0d | 137 | out_le16(&fpga->mpc3w_control, (bitval << 9) | m); |
a605ea7e | 138 | } |
2da0fc0d | 139 | #endif |
a605ea7e | 140 | |
2da0fc0d | 141 | #ifdef CONFIG_SYS_ICS8N3QV01 |
6853cc4b DE |
142 | |
143 | static unsigned int ics8n3qv01_get_fout_calc(unsigned screen, unsigned index) | |
144 | { | |
145 | unsigned long long n; | |
146 | unsigned long long mint; | |
147 | unsigned long long mfrac; | |
148 | u8 reg_a, reg_b, reg_c, reg_d, reg_f; | |
149 | unsigned long long fout_calc; | |
150 | ||
151 | if (index > 3) | |
152 | return 0; | |
153 | ||
154 | reg_a = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 0 + index); | |
155 | reg_b = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 4 + index); | |
156 | reg_c = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 8 + index); | |
157 | reg_d = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 12 + index); | |
158 | reg_f = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 20 + index); | |
159 | ||
160 | mint = ((reg_a >> 1) & 0x1f) | (reg_f & 0x20); | |
161 | mfrac = ((reg_a & 0x01) << 17) | (reg_b << 9) | (reg_c << 1) | |
162 | | (reg_d >> 7); | |
163 | n = reg_d & 0x7f; | |
164 | ||
165 | fout_calc = (mint * ICS8N3QV01_FREF_LL | |
166 | + mfrac * ICS8N3QV01_FREF_LL / 262144LL | |
167 | + ICS8N3QV01_FREF_LL / 524288LL | |
168 | + n / 2) | |
169 | / n | |
170 | * 1000000 | |
171 | / (1000000 - 100); | |
172 | ||
173 | return fout_calc; | |
174 | } | |
175 | ||
176 | ||
2da0fc0d DE |
177 | static void ics8n3qv01_calc_parameters(unsigned int fout, |
178 | unsigned int *_mint, unsigned int *_mfrac, | |
179 | unsigned int *_n) | |
a605ea7e | 180 | { |
2da0fc0d DE |
181 | unsigned int n; |
182 | unsigned int foutiic; | |
183 | unsigned int fvcoiic; | |
184 | unsigned int mint; | |
185 | unsigned long long mfrac; | |
186 | ||
6853cc4b | 187 | n = (2215000000U + fout / 2) / fout; |
2da0fc0d DE |
188 | if ((n & 1) && (n > 5)) |
189 | n -= 1; | |
190 | ||
191 | foutiic = fout - (fout / 10000); | |
192 | fvcoiic = foutiic * n; | |
193 | ||
194 | mint = fvcoiic / 114285000; | |
195 | if ((mint < 17) || (mint > 63)) | |
196 | printf("ics8n3qv01_calc_parameters: cannot determine mint\n"); | |
197 | ||
198 | mfrac = ((unsigned long long)fvcoiic % 114285000LL) * 262144LL | |
199 | / 114285000LL; | |
200 | ||
201 | *_mint = mint; | |
202 | *_mfrac = mfrac; | |
203 | *_n = n; | |
204 | } | |
205 | ||
206 | static void ics8n3qv01_set(unsigned screen, unsigned int fout) | |
207 | { | |
208 | unsigned int n; | |
209 | unsigned int mint; | |
210 | unsigned int mfrac; | |
6853cc4b DE |
211 | unsigned int fout_calc; |
212 | unsigned long long fout_prog; | |
213 | long long off_ppm; | |
2da0fc0d DE |
214 | u8 reg0, reg4, reg8, reg12, reg18, reg20; |
215 | ||
6853cc4b DE |
216 | fout_calc = ics8n3qv01_get_fout_calc(screen, 1); |
217 | off_ppm = (fout_calc - ICS8N3QV01_F_DEFAULT_1) * 1000000 | |
218 | / ICS8N3QV01_F_DEFAULT_1; | |
219 | printf(" PLL is off by %lld ppm\n", off_ppm); | |
220 | fout_prog = (unsigned long long)fout * (unsigned long long)fout_calc | |
221 | / ICS8N3QV01_F_DEFAULT_1; | |
222 | ics8n3qv01_calc_parameters(fout_prog, &mint, &mfrac, &n); | |
2da0fc0d DE |
223 | |
224 | reg0 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 0) & 0xc0; | |
225 | reg0 |= (mint & 0x1f) << 1; | |
226 | reg0 |= (mfrac >> 17) & 0x01; | |
227 | fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 0, reg0); | |
228 | ||
229 | reg4 = mfrac >> 9; | |
230 | fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 4, reg4); | |
231 | ||
232 | reg8 = mfrac >> 1; | |
233 | fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 8, reg8); | |
234 | ||
235 | reg12 = mfrac << 7; | |
236 | reg12 |= n & 0x7f; | |
237 | fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 12, reg12); | |
238 | ||
239 | reg18 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 18) & 0x03; | |
240 | reg18 |= 0x20; | |
241 | fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 18, reg18); | |
242 | ||
243 | reg20 = fpga_iic_read(screen, ICS8N3QV01_I2C_ADDR, 20) & 0x1f; | |
244 | reg20 |= mint & (1 << 5); | |
245 | fpga_iic_write(screen, ICS8N3QV01_I2C_ADDR, 20, reg20); | |
246 | } | |
247 | #endif | |
248 | ||
249 | static int osd_write_videomem(unsigned screen, unsigned offset, | |
250 | u16 *data, size_t charcount) | |
251 | { | |
0e60aa85 DE |
252 | struct ihs_fpga *fpga = |
253 | (struct ihs_fpga *) CONFIG_SYS_FPGA_BASE(screen); | |
a605ea7e DE |
254 | unsigned int k; |
255 | ||
256 | for (k = 0; k < charcount; ++k) { | |
257 | if (offset + k >= BUFSIZE) | |
258 | return -1; | |
2da0fc0d | 259 | out_le16(&fpga->videomem + offset + k, data[k]); |
a605ea7e DE |
260 | } |
261 | ||
262 | return charcount; | |
263 | } | |
264 | ||
265 | static int osd_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
266 | { | |
2da0fc0d DE |
267 | unsigned screen; |
268 | ||
269 | for (screen = 0; screen < CONFIG_SYS_OSD_SCREENS; ++screen) { | |
270 | unsigned x; | |
271 | unsigned y; | |
272 | unsigned charcount; | |
273 | unsigned len; | |
274 | u8 color; | |
275 | unsigned int k; | |
276 | u16 buf[BUFSIZE]; | |
277 | char *text; | |
278 | int res; | |
279 | ||
280 | if (argc < 5) { | |
281 | cmd_usage(cmdtp); | |
282 | return 1; | |
283 | } | |
284 | ||
285 | x = simple_strtoul(argv[1], NULL, 16); | |
286 | y = simple_strtoul(argv[2], NULL, 16); | |
287 | color = simple_strtoul(argv[3], NULL, 16); | |
288 | text = argv[4]; | |
289 | charcount = strlen(text); | |
290 | len = (charcount > BUFSIZE) ? BUFSIZE : charcount; | |
291 | ||
292 | for (k = 0; k < len; ++k) | |
293 | buf[k] = (text[k] << 8) | color; | |
294 | ||
295 | res = osd_write_videomem(screen, y * BASE_WIDTH + x, buf, len); | |
296 | if (res < 0) | |
297 | return res; | |
a605ea7e DE |
298 | } |
299 | ||
2da0fc0d | 300 | return 0; |
a605ea7e DE |
301 | } |
302 | ||
2da0fc0d | 303 | int osd_probe(unsigned screen) |
a605ea7e | 304 | { |
0e60aa85 DE |
305 | struct ihs_fpga *fpga = (struct ihs_fpga *)CONFIG_SYS_FPGA_BASE(screen); |
306 | struct ihs_osd *osd = &fpga->osd; | |
2da0fc0d DE |
307 | u16 version = in_le16(&osd->version); |
308 | u16 features = in_le16(&osd->features); | |
a605ea7e DE |
309 | unsigned width; |
310 | unsigned height; | |
2da0fc0d | 311 | u8 value; |
a605ea7e DE |
312 | |
313 | width = ((features & 0x3f00) >> 8) + 1; | |
314 | height = (features & 0x001f) + 1; | |
315 | ||
2da0fc0d DE |
316 | printf("OSD%d: Digital-OSD version %01d.%02d, %d" "x%d characters\n", |
317 | screen, version/100, version%100, width, height); | |
a605ea7e | 318 | |
2da0fc0d | 319 | #ifdef CONFIG_SYS_CH7301 |
a605ea7e DE |
320 | value = i2c_reg_read(CH7301_I2C_ADDR, CH7301_DID); |
321 | if (value != 0x17) { | |
322 | printf(" Probing CH7301 failed, DID %02x\n", value); | |
323 | return -1; | |
324 | } | |
325 | i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPCP, 0x08); | |
326 | i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPD, 0x16); | |
327 | i2c_reg_write(CH7301_I2C_ADDR, CH7301_TPF, 0x60); | |
328 | i2c_reg_write(CH7301_I2C_ADDR, CH7301_DC, 0x09); | |
329 | i2c_reg_write(CH7301_I2C_ADDR, CH7301_PM, 0xc0); | |
2da0fc0d DE |
330 | #endif |
331 | ||
332 | #ifdef CONFIG_SYS_MPC92469AC | |
333 | mpc92469ac_set(screen, PIXCLK_640_480_60); | |
334 | #endif | |
a605ea7e | 335 | |
2da0fc0d DE |
336 | #ifdef CONFIG_SYS_ICS8N3QV01 |
337 | ics8n3qv01_set(screen, PIXCLK_640_480_60); | |
338 | #endif | |
a605ea7e | 339 | |
2da0fc0d DE |
340 | #ifdef CONFIG_SYS_SIL1178 |
341 | value = fpga_iic_read(screen, SIL1178_SLAVE_I2C_ADDRESS, 0x02); | |
342 | if (value != 0x06) { | |
343 | printf(" Probing CH7301 SIL1178, DEV_IDL %02x\n", value); | |
344 | return -1; | |
345 | } | |
346 | /* magic initialization sequence adapted from datasheet */ | |
347 | fpga_iic_write(screen, SIL1178_SLAVE_I2C_ADDRESS, 0x08, 0x36); | |
348 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x44); | |
349 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0f, 0x4c); | |
350 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0e, 0x10); | |
351 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0a, 0x80); | |
352 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x09, 0x30); | |
353 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0c, 0x89); | |
354 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x0d, 0x60); | |
355 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x36); | |
356 | fpga_iic_write(screen, SIL1178_MASTER_I2C_ADDRESS, 0x08, 0x37); | |
357 | #endif | |
358 | ||
359 | out_le16(&fpga->videocontrol, 0x0002); | |
360 | out_le16(&osd->control, 0x0049); | |
361 | ||
362 | out_le16(&osd->xy_size, ((32 - 1) << 8) | (16 - 1)); | |
52158e36 DE |
363 | out_le16(&osd->x_pos, 0x007f); |
364 | out_le16(&osd->y_pos, 0x005f); | |
a605ea7e DE |
365 | |
366 | return 0; | |
367 | } | |
368 | ||
369 | int osd_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
370 | { | |
2da0fc0d DE |
371 | unsigned screen; |
372 | ||
373 | for (screen = 0; screen < CONFIG_SYS_OSD_SCREENS; ++screen) { | |
374 | unsigned x; | |
375 | unsigned y; | |
376 | unsigned k; | |
377 | u16 buffer[BASE_WIDTH]; | |
378 | char *rp; | |
379 | u16 *wp = buffer; | |
380 | unsigned count = (argc > 4) ? | |
381 | simple_strtoul(argv[4], NULL, 16) : 1; | |
382 | ||
383 | if ((argc < 4) || (strlen(argv[3]) % 4)) { | |
384 | cmd_usage(cmdtp); | |
385 | return 1; | |
386 | } | |
387 | ||
388 | x = simple_strtoul(argv[1], NULL, 16); | |
389 | y = simple_strtoul(argv[2], NULL, 16); | |
390 | rp = argv[3]; | |
391 | ||
392 | ||
393 | while (*rp) { | |
394 | char substr[5]; | |
395 | ||
396 | memcpy(substr, rp, 4); | |
397 | substr[4] = 0; | |
398 | *wp = simple_strtoul(substr, NULL, 16); | |
399 | ||
400 | rp += 4; | |
401 | wp++; | |
402 | if (wp - buffer > BASE_WIDTH) | |
403 | break; | |
404 | } | |
405 | ||
406 | for (k = 0; k < count; ++k) { | |
407 | unsigned offset = | |
408 | y * BASE_WIDTH + x + k * (wp - buffer); | |
409 | osd_write_videomem(screen, offset, buffer, | |
410 | wp - buffer); | |
411 | } | |
a605ea7e DE |
412 | } |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | U_BOOT_CMD( | |
418 | osdw, 5, 0, osd_write, | |
419 | "write 16-bit hex encoded buffer to osd memory", | |
420 | "pos_x pos_y buffer count\n" | |
421 | ); | |
422 | ||
423 | U_BOOT_CMD( | |
424 | osdp, 5, 0, osd_print, | |
425 | "write ASCII buffer to osd memory", | |
426 | "pos_x pos_y color text\n" | |
427 | ); |