blob: e03f24fa983c2d6fc5f58b08067671e1839b1250 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01002/*
3 * Copyright (c) 2015 Google, Inc
4 * Copyright 2014 Rockchip Inc.
5 * Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01006 */
7
8#include <common.h>
9#include <fdtdec.h>
10#include <asm/io.h>
11#include "dw_hdmi.h"
12
13struct tmds_n_cts {
14 u32 tmds;
15 u32 cts;
16 u32 n;
17};
18
19static const struct tmds_n_cts n_cts_table[] = {
20 {
21 .tmds = 25175000, .n = 6144, .cts = 25175,
22 }, {
23 .tmds = 25200000, .n = 6144, .cts = 25200,
24 }, {
25 .tmds = 27000000, .n = 6144, .cts = 27000,
26 }, {
27 .tmds = 27027000, .n = 6144, .cts = 27027,
28 }, {
29 .tmds = 40000000, .n = 6144, .cts = 40000,
30 }, {
31 .tmds = 54000000, .n = 6144, .cts = 54000,
32 }, {
33 .tmds = 54054000, .n = 6144, .cts = 54054,
34 }, {
35 .tmds = 65000000, .n = 6144, .cts = 65000,
36 }, {
37 .tmds = 74176000, .n = 11648, .cts = 140625,
38 }, {
39 .tmds = 74250000, .n = 6144, .cts = 74250,
40 }, {
41 .tmds = 83500000, .n = 6144, .cts = 83500,
42 }, {
43 .tmds = 106500000, .n = 6144, .cts = 106500,
44 }, {
45 .tmds = 108000000, .n = 6144, .cts = 108000,
46 }, {
47 .tmds = 148352000, .n = 5824, .cts = 140625,
48 }, {
49 .tmds = 148500000, .n = 6144, .cts = 148500,
50 }, {
51 .tmds = 297000000, .n = 5120, .cts = 247500,
52 }
53};
54
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +010055static void dw_hdmi_write(struct dw_hdmi *hdmi, u8 val, int offset)
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010056{
57 switch (hdmi->reg_io_width) {
58 case 1:
59 writeb(val, hdmi->ioaddr + offset);
60 break;
61 case 4:
62 writel(val, hdmi->ioaddr + (offset << 2));
63 break;
64 default:
65 debug("reg_io_width has unsupported width!\n");
66 break;
67 }
68}
69
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +010070static u8 dw_hdmi_read(struct dw_hdmi *hdmi, int offset)
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010071{
72 switch (hdmi->reg_io_width) {
73 case 1:
74 return readb(hdmi->ioaddr + offset);
75 case 4:
76 return readl(hdmi->ioaddr + (offset << 2));
77 default:
78 debug("reg_io_width has unsupported width!\n");
79 break;
80 }
81
82 return 0;
83}
84
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +010085static u8 (*hdmi_read)(struct dw_hdmi *hdmi, int offset) = dw_hdmi_read;
86static void (*hdmi_write)(struct dw_hdmi *hdmi, u8 val, int offset) =
87 dw_hdmi_write;
88
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010089static void hdmi_mod(struct dw_hdmi *hdmi, unsigned reg, u8 mask, u8 data)
90{
91 u8 val = hdmi_read(hdmi, reg) & ~mask;
92
93 val |= data & mask;
94 hdmi_write(hdmi, val, reg);
95}
96
97static void hdmi_set_clock_regenerator(struct dw_hdmi *hdmi, u32 n, u32 cts)
98{
99 uint cts3;
100 uint n3;
101
102 /* first set ncts_atomic_write (if present) */
103 n3 = HDMI_AUD_N3_NCTS_ATOMIC_WRITE;
104 hdmi_write(hdmi, n3, HDMI_AUD_N3);
105
106 /* set cts_manual (if present) */
107 cts3 = HDMI_AUD_CTS3_CTS_MANUAL;
108
109 cts3 |= HDMI_AUD_CTS3_N_SHIFT_1 << HDMI_AUD_CTS3_N_SHIFT_OFFSET;
110 cts3 |= (cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK;
111
112 /* write cts values; cts3 must be written first */
113 hdmi_write(hdmi, cts3, HDMI_AUD_CTS3);
114 hdmi_write(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
115 hdmi_write(hdmi, cts & 0xff, HDMI_AUD_CTS1);
116
117 /* write n values; n1 must be written last */
118 n3 |= (n >> 16) & HDMI_AUD_N3_AUDN19_16_MASK;
119 hdmi_write(hdmi, n3, HDMI_AUD_N3);
120 hdmi_write(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
121 hdmi_write(hdmi, n & 0xff, HDMI_AUD_N3);
122
123 hdmi_write(hdmi, HDMI_AUD_INPUTCLKFS_128, HDMI_AUD_INPUTCLKFS);
124}
125
126static int hdmi_lookup_n_cts(u32 pixel_clk)
127{
128 int i;
129
130 for (i = 0; i < ARRAY_SIZE(n_cts_table); i++)
131 if (pixel_clk <= n_cts_table[i].tmds)
132 break;
133
134 if (i >= ARRAY_SIZE(n_cts_table))
135 return -1;
136
137 return i;
138}
139
140static void hdmi_audio_set_samplerate(struct dw_hdmi *hdmi, u32 pixel_clk)
141{
142 u32 clk_n, clk_cts;
143 int index;
144
145 index = hdmi_lookup_n_cts(pixel_clk);
146 if (index == -1) {
147 debug("audio not supported for pixel clk %d\n", pixel_clk);
148 return;
149 }
150
151 clk_n = n_cts_table[index].n;
152 clk_cts = n_cts_table[index].cts;
153 hdmi_set_clock_regenerator(hdmi, clk_n, clk_cts);
154}
155
156/*
157 * this submodule is responsible for the video data synchronization.
158 * for example, for rgb 4:4:4 input, the data map is defined as
159 * pin{47~40} <==> r[7:0]
160 * pin{31~24} <==> g[7:0]
161 * pin{15~8} <==> b[7:0]
162 */
163static void hdmi_video_sample(struct dw_hdmi *hdmi)
164{
165 u32 color_format = 0x01;
166 uint val;
167
168 val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
169 ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
170 HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
171
172 hdmi_write(hdmi, val, HDMI_TX_INVID0);
173
174 /* enable tx stuffing: when de is inactive, fix the output data to 0 */
175 val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
176 HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
177 HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
178 hdmi_write(hdmi, val, HDMI_TX_INSTUFFING);
179 hdmi_write(hdmi, 0x0, HDMI_TX_GYDATA0);
180 hdmi_write(hdmi, 0x0, HDMI_TX_GYDATA1);
181 hdmi_write(hdmi, 0x0, HDMI_TX_RCRDATA0);
182 hdmi_write(hdmi, 0x0, HDMI_TX_RCRDATA1);
183 hdmi_write(hdmi, 0x0, HDMI_TX_BCBDATA0);
184 hdmi_write(hdmi, 0x0, HDMI_TX_BCBDATA1);
185}
186
187static void hdmi_video_packetize(struct dw_hdmi *hdmi)
188{
189 u32 output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
190 u32 remap_size = HDMI_VP_REMAP_YCC422_16BIT;
191 u32 color_depth = 0;
192 uint val, vp_conf;
193
194 /* set the packetizer registers */
195 val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
196 HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
197 ((0 << HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
198 HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
199 hdmi_write(hdmi, val, HDMI_VP_PR_CD);
200
201 hdmi_mod(hdmi, HDMI_VP_STUFF, HDMI_VP_STUFF_PR_STUFFING_MASK,
202 HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE);
203
204 /* data from pixel repeater block */
205 vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
206 HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
207
208 hdmi_mod(hdmi, HDMI_VP_CONF, HDMI_VP_CONF_PR_EN_MASK |
209 HDMI_VP_CONF_BYPASS_SELECT_MASK, vp_conf);
210
211 hdmi_mod(hdmi, HDMI_VP_STUFF, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK,
212 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET);
213
214 hdmi_write(hdmi, remap_size, HDMI_VP_REMAP);
215
216 vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
217 HDMI_VP_CONF_PP_EN_DISABLE |
218 HDMI_VP_CONF_YCC422_EN_DISABLE;
219
220 hdmi_mod(hdmi, HDMI_VP_CONF, HDMI_VP_CONF_BYPASS_EN_MASK |
221 HDMI_VP_CONF_PP_EN_ENMASK | HDMI_VP_CONF_YCC422_EN_MASK,
222 vp_conf);
223
224 hdmi_mod(hdmi, HDMI_VP_STUFF, HDMI_VP_STUFF_PP_STUFFING_MASK |
225 HDMI_VP_STUFF_YCC422_STUFFING_MASK,
226 HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
227 HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE);
228
229 hdmi_mod(hdmi, HDMI_VP_CONF, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
230 output_select);
231}
232
233static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, uint bit)
234{
235 hdmi_mod(hdmi, HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR_MASK,
236 bit << HDMI_PHY_TST0_TSTCLR_OFFSET);
237}
238
239static int hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, u32 msec)
240{
241 ulong start;
242 u32 val;
243
244 start = get_timer(0);
245 do {
246 val = hdmi_read(hdmi, HDMI_IH_I2CMPHY_STAT0);
247 if (val & 0x3) {
248 hdmi_write(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
249 return 0;
250 }
251
252 udelay(100);
253 } while (get_timer(start) < msec);
254
255 return 1;
256}
257
258static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, uint data, uint addr)
259{
260 hdmi_write(hdmi, 0xff, HDMI_IH_I2CMPHY_STAT0);
261 hdmi_write(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
262 hdmi_write(hdmi, (u8)(data >> 8), HDMI_PHY_I2CM_DATAO_1_ADDR);
263 hdmi_write(hdmi, (u8)(data >> 0), HDMI_PHY_I2CM_DATAO_0_ADDR);
264 hdmi_write(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
265 HDMI_PHY_I2CM_OPERATION_ADDR);
266
267 hdmi_phy_wait_i2c_done(hdmi, 1000);
268}
269
270static void hdmi_phy_enable_power(struct dw_hdmi *hdmi, uint enable)
271{
272 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_PDZ_MASK,
273 enable << HDMI_PHY_CONF0_PDZ_OFFSET);
274}
275
276static void hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, uint enable)
277{
278 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_ENTMDS_MASK,
279 enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
280}
281
282static void hdmi_phy_enable_spare(struct dw_hdmi *hdmi, uint enable)
283{
284 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_SPARECTRL_MASK,
285 enable << HDMI_PHY_CONF0_SPARECTRL_OFFSET);
286}
287
288static void hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, uint enable)
289{
290 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_GEN2_PDDQ_MASK,
291 enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
292}
293
294static void hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, uint enable)
295{
296 hdmi_mod(hdmi, HDMI_PHY_CONF0,
297 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK,
298 enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
299}
300
301static void hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, uint enable)
302{
303 hdmi_mod(hdmi, HDMI_PHY_CONF0,
304 HDMI_PHY_CONF0_SELDATAENPOL_MASK,
305 enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
306}
307
308static void hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi,
309 uint enable)
310{
311 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_SELDIPIF_MASK,
312 enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
313}
314
315static int hdmi_phy_configure(struct dw_hdmi *hdmi, u32 mpixelclock)
316{
317 ulong start;
318 uint i, val;
319
320 if (!hdmi->mpll_cfg || !hdmi->phy_cfg)
321 return -1;
322
323 /* gen2 tx power off */
324 hdmi_phy_gen2_txpwron(hdmi, 0);
325
326 /* gen2 pddq */
327 hdmi_phy_gen2_pddq(hdmi, 1);
328
329 /* phy reset */
330 hdmi_write(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
331 hdmi_write(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
332 hdmi_write(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
333
334 hdmi_phy_test_clear(hdmi, 1);
335 hdmi_write(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
336 HDMI_PHY_I2CM_SLAVE_ADDR);
337 hdmi_phy_test_clear(hdmi, 0);
338
339 /* pll/mpll cfg - always match on final entry */
340 for (i = 0; hdmi->mpll_cfg[i].mpixelclock != (~0ul); i++)
341 if (mpixelclock <= hdmi->mpll_cfg[i].mpixelclock)
342 break;
343
344 hdmi_phy_i2c_write(hdmi, hdmi->mpll_cfg[i].cpce, PHY_OPMODE_PLLCFG);
345 hdmi_phy_i2c_write(hdmi, hdmi->mpll_cfg[i].gmp, PHY_PLLGMPCTRL);
346 hdmi_phy_i2c_write(hdmi, hdmi->mpll_cfg[i].curr, PHY_PLLCURRCTRL);
347
348 hdmi_phy_i2c_write(hdmi, 0x0000, PHY_PLLPHBYCTRL);
349 hdmi_phy_i2c_write(hdmi, 0x0006, PHY_PLLCLKBISTPHASE);
350
351 for (i = 0; hdmi->phy_cfg[i].mpixelclock != (~0ul); i++)
352 if (mpixelclock <= hdmi->phy_cfg[i].mpixelclock)
353 break;
354
355 /*
356 * resistance term 133ohm cfg
357 * preemp cgf 0.00
358 * tx/ck lvl 10
359 */
360 hdmi_phy_i2c_write(hdmi, hdmi->phy_cfg[i].term, PHY_TXTERM);
361 hdmi_phy_i2c_write(hdmi, hdmi->phy_cfg[i].sym_ctr, PHY_CKSYMTXCTRL);
362 hdmi_phy_i2c_write(hdmi, hdmi->phy_cfg[i].vlev_ctr, PHY_VLEVCTRL);
363
364 /* remove clk term */
365 hdmi_phy_i2c_write(hdmi, 0x8000, PHY_CKCALCTRL);
366
367 hdmi_phy_enable_power(hdmi, 1);
368
369 /* toggle tmds enable */
370 hdmi_phy_enable_tmds(hdmi, 0);
371 hdmi_phy_enable_tmds(hdmi, 1);
372
373 /* gen2 tx power on */
374 hdmi_phy_gen2_txpwron(hdmi, 1);
375 hdmi_phy_gen2_pddq(hdmi, 0);
376
377 hdmi_phy_enable_spare(hdmi, 1);
378
379 /* wait for phy pll lock */
380 start = get_timer(0);
381 do {
382 val = hdmi_read(hdmi, HDMI_PHY_STAT0);
383 if (!(val & HDMI_PHY_TX_PHY_LOCK))
384 return 0;
385
386 udelay(100);
387 } while (get_timer(start) < 5);
388
389 return -1;
390}
391
392static void hdmi_av_composer(struct dw_hdmi *hdmi,
393 const struct display_timing *edid)
394{
395 bool mdataenablepolarity = true;
396 uint inv_val;
397 uint hbl;
398 uint vbl;
399
400 hbl = edid->hback_porch.typ + edid->hfront_porch.typ +
401 edid->hsync_len.typ;
402 vbl = edid->vback_porch.typ + edid->vfront_porch.typ +
403 edid->vsync_len.typ;
404
405 /* set up hdmi_fc_invidconf */
406 inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE;
407
Vasily Khoruzhickb5d6c872018-05-14 13:49:53 -0700408 inv_val |= (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100409 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
410 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
411
Vasily Khoruzhickb5d6c872018-05-14 13:49:53 -0700412 inv_val |= (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100413 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
414 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
415
416 inv_val |= (mdataenablepolarity ?
417 HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
418 HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
419
Jernej Skrabec7d553052017-04-29 14:43:37 +0200420 inv_val |= (edid->hdmi_monitor ?
421 HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
422 HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100423
424 inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
425
426 inv_val |= HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
427
428 hdmi_write(hdmi, inv_val, HDMI_FC_INVIDCONF);
429
430 /* set up horizontal active pixel width */
431 hdmi_write(hdmi, edid->hactive.typ >> 8, HDMI_FC_INHACTV1);
432 hdmi_write(hdmi, edid->hactive.typ, HDMI_FC_INHACTV0);
433
434 /* set up vertical active lines */
435 hdmi_write(hdmi, edid->vactive.typ >> 8, HDMI_FC_INVACTV1);
436 hdmi_write(hdmi, edid->vactive.typ, HDMI_FC_INVACTV0);
437
438 /* set up horizontal blanking pixel region width */
439 hdmi_write(hdmi, hbl >> 8, HDMI_FC_INHBLANK1);
440 hdmi_write(hdmi, hbl, HDMI_FC_INHBLANK0);
441
442 /* set up vertical blanking pixel region width */
443 hdmi_write(hdmi, vbl, HDMI_FC_INVBLANK);
444
445 /* set up hsync active edge delay width (in pixel clks) */
446 hdmi_write(hdmi, edid->hfront_porch.typ >> 8, HDMI_FC_HSYNCINDELAY1);
447 hdmi_write(hdmi, edid->hfront_porch.typ, HDMI_FC_HSYNCINDELAY0);
448
449 /* set up vsync active edge delay (in lines) */
450 hdmi_write(hdmi, edid->vfront_porch.typ, HDMI_FC_VSYNCINDELAY);
451
452 /* set up hsync active pulse width (in pixel clks) */
453 hdmi_write(hdmi, edid->hsync_len.typ >> 8, HDMI_FC_HSYNCINWIDTH1);
454 hdmi_write(hdmi, edid->hsync_len.typ, HDMI_FC_HSYNCINWIDTH0);
455
456 /* set up vsync active edge delay (in lines) */
457 hdmi_write(hdmi, edid->vsync_len.typ, HDMI_FC_VSYNCINWIDTH);
458}
459
460/* hdmi initialization step b.4 */
Jernej Skrabec7d553052017-04-29 14:43:37 +0200461static void hdmi_enable_video_path(struct dw_hdmi *hdmi, bool audio)
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100462{
463 uint clkdis;
464
465 /* control period minimum duration */
466 hdmi_write(hdmi, 12, HDMI_FC_CTRLDUR);
467 hdmi_write(hdmi, 32, HDMI_FC_EXCTRLDUR);
468 hdmi_write(hdmi, 1, HDMI_FC_EXCTRLSPAC);
469
470 /* set to fill tmds data channels */
471 hdmi_write(hdmi, 0x0b, HDMI_FC_CH0PREAM);
472 hdmi_write(hdmi, 0x16, HDMI_FC_CH1PREAM);
473 hdmi_write(hdmi, 0x21, HDMI_FC_CH2PREAM);
474
475 hdmi_write(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
476 HDMI_MC_FLOWCTRL);
477
478 /* enable pixel clock and tmds data path */
479 clkdis = 0x7f;
480 clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
481 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
482
483 clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
484 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
485
Jernej Skrabec7d553052017-04-29 14:43:37 +0200486 if (audio) {
487 clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
488 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
489 }
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100490}
491
492/* workaround to clear the overflow condition */
493static void hdmi_clear_overflow(struct dw_hdmi *hdmi)
494{
495 uint val, count;
496
497 /* tmds software reset */
498 hdmi_write(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
499
500 val = hdmi_read(hdmi, HDMI_FC_INVIDCONF);
501
502 for (count = 0; count < 4; count++)
503 hdmi_write(hdmi, val, HDMI_FC_INVIDCONF);
504}
505
506static void hdmi_audio_set_format(struct dw_hdmi *hdmi)
507{
508 hdmi_write(hdmi, HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_IN_EN_0,
509 HDMI_AUD_CONF0);
510
511
512 hdmi_write(hdmi, HDMI_AUD_CONF1_I2S_MODE_STANDARD_MODE |
513 HDMI_AUD_CONF1_I2S_WIDTH_16BIT, HDMI_AUD_CONF1);
514
515 hdmi_write(hdmi, 0x00, HDMI_AUD_CONF2);
516}
517
518static void hdmi_audio_fifo_reset(struct dw_hdmi *hdmi)
519{
520 hdmi_write(hdmi, (u8)~HDMI_MC_SWRSTZ_II2SSWRST_REQ, HDMI_MC_SWRSTZ);
521 hdmi_write(hdmi, HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST, HDMI_AUD_CONF0);
522
523 hdmi_write(hdmi, 0x00, HDMI_AUD_INT);
524 hdmi_write(hdmi, 0x00, HDMI_AUD_INT1);
525}
526
527static int hdmi_get_plug_in_status(struct dw_hdmi *hdmi)
528{
529 uint val = hdmi_read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD;
530
531 return !!val;
532}
533
534static int hdmi_ddc_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
535{
536 u32 val;
537 ulong start;
538
539 start = get_timer(0);
540 do {
541 val = hdmi_read(hdmi, HDMI_IH_I2CM_STAT0);
542 if (val & 0x2) {
543 hdmi_write(hdmi, val, HDMI_IH_I2CM_STAT0);
544 return 0;
545 }
546
547 udelay(100);
548 } while (get_timer(start) < msec);
549
550 return 1;
551}
552
553static void hdmi_ddc_reset(struct dw_hdmi *hdmi)
554{
555 hdmi_mod(hdmi, HDMI_I2CM_SOFTRSTZ, HDMI_I2CM_SOFTRSTZ_MASK, 0);
556}
557
558static int hdmi_read_edid(struct dw_hdmi *hdmi, int block, u8 *buff)
559{
560 int shift = (block % 2) * 0x80;
561 int edid_read_err = 0;
562 u32 trytime = 5;
563 u32 n;
564
565 /* set ddc i2c clk which devided from ddc_clk to 100khz */
566 hdmi_write(hdmi, hdmi->i2c_clk_high, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
567 hdmi_write(hdmi, hdmi->i2c_clk_low, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
568 hdmi_mod(hdmi, HDMI_I2CM_DIV, HDMI_I2CM_DIV_FAST_STD_MODE,
569 HDMI_I2CM_DIV_STD_MODE);
570
571 hdmi_write(hdmi, HDMI_I2CM_SLAVE_DDC_ADDR, HDMI_I2CM_SLAVE);
572 hdmi_write(hdmi, HDMI_I2CM_SEGADDR_DDC, HDMI_I2CM_SEGADDR);
573 hdmi_write(hdmi, block >> 1, HDMI_I2CM_SEGPTR);
574
575 while (trytime--) {
576 edid_read_err = 0;
577
578 for (n = 0; n < HDMI_EDID_BLOCK_SIZE; n++) {
579 hdmi_write(hdmi, shift + n, HDMI_I2CM_ADDRESS);
580
581 if (block == 0)
582 hdmi_write(hdmi, HDMI_I2CM_OP_RD8,
583 HDMI_I2CM_OPERATION);
584 else
585 hdmi_write(hdmi, HDMI_I2CM_OP_RD8_EXT,
586 HDMI_I2CM_OPERATION);
587
588 if (hdmi_ddc_wait_i2c_done(hdmi, 10)) {
589 hdmi_ddc_reset(hdmi);
590 edid_read_err = 1;
591 break;
592 }
593
594 buff[n] = hdmi_read(hdmi, HDMI_I2CM_DATAI);
595 }
596
597 if (!edid_read_err)
598 break;
599 }
600
601 return edid_read_err;
602}
603
604static const u8 pre_buf[] = {
605 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
606 0x04, 0x69, 0xfa, 0x23, 0xc8, 0x28, 0x01, 0x00,
607 0x10, 0x17, 0x01, 0x03, 0x80, 0x33, 0x1d, 0x78,
608 0x2a, 0xd9, 0x45, 0xa2, 0x55, 0x4d, 0xa0, 0x27,
609 0x12, 0x50, 0x54, 0xb7, 0xef, 0x00, 0x71, 0x4f,
610 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00,
611 0xd1, 0xc0, 0x81, 0xc0, 0x81, 0x00, 0x02, 0x3a,
612 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
613 0x45, 0x00, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e,
614 0x00, 0x00, 0x00, 0xff, 0x00, 0x44, 0x34, 0x4c,
615 0x4d, 0x54, 0x46, 0x30, 0x37, 0x35, 0x39, 0x37,
616 0x36, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
617 0x4b, 0x18, 0x53, 0x11, 0x00, 0x0a, 0x20, 0x20,
618 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
619 0x00, 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x53,
620 0x32, 0x33, 0x38, 0x0a, 0x20, 0x20, 0x01, 0xb0,
621 0x02, 0x03, 0x22, 0x71, 0x4f, 0x01, 0x02, 0x03,
622 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f,
623 0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, 0x17, 0x07,
624 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00,
625 0x10, 0x00, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0,
626 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xfd, 0x1e,
627 0x11, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72,
628 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
629 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e, 0x01, 0x1d,
630 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28,
631 0x55, 0x40, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e,
632 0x8c, 0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20,
633 0x0c, 0x40, 0x55, 0x00, 0xfd, 0x1e, 0x11, 0x00,
634 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
635 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
636 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9,
637};
638
639int dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
640{
641 int i, ret;
642
643 /* hdmi phy spec says to do the phy initialization sequence twice */
644 for (i = 0; i < 2; i++) {
645 hdmi_phy_sel_data_en_pol(hdmi, 1);
646 hdmi_phy_sel_interface_control(hdmi, 0);
647 hdmi_phy_enable_tmds(hdmi, 0);
648 hdmi_phy_enable_power(hdmi, 0);
649
650 ret = hdmi_phy_configure(hdmi, mpixelclock);
651 if (ret) {
652 debug("hdmi phy config failure %d\n", ret);
653 return ret;
654 }
655 }
656
657 return 0;
658}
659
660int dw_hdmi_phy_wait_for_hpd(struct dw_hdmi *hdmi)
661{
662 ulong start;
663
664 start = get_timer(0);
665 do {
666 if (hdmi_get_plug_in_status(hdmi))
667 return 0;
668 udelay(100);
669 } while (get_timer(start) < 300);
670
671 return -1;
672}
673
674void dw_hdmi_phy_init(struct dw_hdmi *hdmi)
675{
676 /* enable phy i2cm done irq */
677 hdmi_write(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
678 HDMI_PHY_I2CM_INT_ADDR);
679
680 /* enable phy i2cm nack & arbitration error irq */
681 hdmi_write(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
682 HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
683 HDMI_PHY_I2CM_CTLINT_ADDR);
684
685 /* enable cable hot plug irq */
686 hdmi_write(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
687
688 /* clear hotplug interrupts */
689 hdmi_write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
690}
691
692int dw_hdmi_read_edid(struct dw_hdmi *hdmi, u8 *buf, int buf_size)
693{
694 u32 edid_size = HDMI_EDID_BLOCK_SIZE;
695 int ret;
696
697 if (0) {
698 edid_size = sizeof(pre_buf);
699 memcpy(buf, pre_buf, edid_size);
700 } else {
701 ret = hdmi_read_edid(hdmi, 0, buf);
702 if (ret) {
703 debug("failed to read edid.\n");
704 return -1;
705 }
706
707 if (buf[0x7e] != 0) {
708 hdmi_read_edid(hdmi, 1, buf + HDMI_EDID_BLOCK_SIZE);
709 edid_size += HDMI_EDID_BLOCK_SIZE;
710 }
711 }
712
713 return edid_size;
714}
715
716int dw_hdmi_enable(struct dw_hdmi *hdmi, const struct display_timing *edid)
717{
718 int ret;
719
Jernej Skrabec7d553052017-04-29 14:43:37 +0200720 debug("%s, mode info : clock %d hdis %d vdis %d\n",
721 edid->hdmi_monitor ? "hdmi" : "dvi",
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100722 edid->pixelclock.typ, edid->hactive.typ, edid->vactive.typ);
723
724 hdmi_av_composer(hdmi, edid);
725
726 ret = hdmi->phy_set(hdmi, edid->pixelclock.typ);
727 if (ret)
728 return ret;
729
Jernej Skrabec7d553052017-04-29 14:43:37 +0200730 hdmi_enable_video_path(hdmi, edid->hdmi_monitor);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100731
Jernej Skrabec7d553052017-04-29 14:43:37 +0200732 if (edid->hdmi_monitor) {
733 hdmi_audio_fifo_reset(hdmi);
734 hdmi_audio_set_format(hdmi);
735 hdmi_audio_set_samplerate(hdmi, edid->pixelclock.typ);
736 }
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100737
738 hdmi_video_packetize(hdmi);
739 hdmi_video_sample(hdmi);
740
741 hdmi_clear_overflow(hdmi);
742
743 return 0;
744}
745
746void dw_hdmi_init(struct dw_hdmi *hdmi)
747{
748 uint ih_mute;
749
750 /*
751 * boot up defaults are:
752 * hdmi_ih_mute = 0x03 (disabled)
753 * hdmi_ih_mute_* = 0x00 (enabled)
754 *
755 * disable top level interrupt bits in hdmi block
756 */
757 ih_mute = /*hdmi_read(hdmi, HDMI_IH_MUTE) |*/
758 HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
759 HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
760
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +0100761 if (hdmi->write_reg)
762 hdmi_write = hdmi->write_reg;
763
764 if (hdmi->read_reg)
765 hdmi_read = hdmi->read_reg;
766
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100767 hdmi_write(hdmi, ih_mute, HDMI_IH_MUTE);
768
769 /* enable i2c master done irq */
770 hdmi_write(hdmi, ~0x04, HDMI_I2CM_INT);
771
772 /* enable i2c client nack % arbitration error irq */
773 hdmi_write(hdmi, ~0x44, HDMI_I2CM_CTLINT);
774}