blob: c217af97878eabd4c85f3fa7364300d3a2cee896 [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>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010011#include <asm/io.h>
Niklas Schulze889ccde2019-07-27 12:07:13 +000012#include <i2c.h>
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +010013#include <media_bus_format.h>
Simon Glassdbd79542020-05-10 11:40:11 -060014#include <linux/delay.h>
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010015#include "dw_hdmi.h"
16
17struct tmds_n_cts {
18 u32 tmds;
19 u32 cts;
20 u32 n;
21};
22
23static const struct tmds_n_cts n_cts_table[] = {
24 {
25 .tmds = 25175000, .n = 6144, .cts = 25175,
26 }, {
27 .tmds = 25200000, .n = 6144, .cts = 25200,
28 }, {
29 .tmds = 27000000, .n = 6144, .cts = 27000,
30 }, {
31 .tmds = 27027000, .n = 6144, .cts = 27027,
32 }, {
33 .tmds = 40000000, .n = 6144, .cts = 40000,
34 }, {
35 .tmds = 54000000, .n = 6144, .cts = 54000,
36 }, {
37 .tmds = 54054000, .n = 6144, .cts = 54054,
38 }, {
39 .tmds = 65000000, .n = 6144, .cts = 65000,
40 }, {
41 .tmds = 74176000, .n = 11648, .cts = 140625,
42 }, {
43 .tmds = 74250000, .n = 6144, .cts = 74250,
44 }, {
45 .tmds = 83500000, .n = 6144, .cts = 83500,
46 }, {
47 .tmds = 106500000, .n = 6144, .cts = 106500,
48 }, {
49 .tmds = 108000000, .n = 6144, .cts = 108000,
50 }, {
51 .tmds = 148352000, .n = 5824, .cts = 140625,
52 }, {
53 .tmds = 148500000, .n = 6144, .cts = 148500,
54 }, {
55 .tmds = 297000000, .n = 5120, .cts = 247500,
56 }
57};
58
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +010059static const u16 csc_coeff_default[3][4] = {
60 { 0x2000, 0x0000, 0x0000, 0x0000 },
61 { 0x0000, 0x2000, 0x0000, 0x0000 },
62 { 0x0000, 0x0000, 0x2000, 0x0000 }
63};
64
65static const u16 csc_coeff_rgb_in_eitu601[3][4] = {
66 { 0x2591, 0x1322, 0x074b, 0x0000 },
67 { 0x6535, 0x2000, 0x7acc, 0x0200 },
68 { 0x6acd, 0x7534, 0x2000, 0x0200 }
69};
70
71static const u16 csc_coeff_rgb_out_eitu601[3][4] = {
72 { 0x2000, 0x6926, 0x74fd, 0x010e },
73 { 0x2000, 0x2cdd, 0x0000, 0x7e9a },
74 { 0x2000, 0x0000, 0x38b4, 0x7e3b }
75};
76
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +010077static void dw_hdmi_write(struct dw_hdmi *hdmi, u8 val, int offset)
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010078{
79 switch (hdmi->reg_io_width) {
80 case 1:
Khem Raja0971ed2024-01-27 14:54:59 -080081 writeb(val, (void *)(hdmi->ioaddr + offset));
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010082 break;
83 case 4:
Khem Raja0971ed2024-01-27 14:54:59 -080084 writel(val, (void *)(hdmi->ioaddr + (offset << 2)));
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010085 break;
86 default:
87 debug("reg_io_width has unsupported width!\n");
88 break;
89 }
90}
91
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +010092static u8 dw_hdmi_read(struct dw_hdmi *hdmi, int offset)
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010093{
94 switch (hdmi->reg_io_width) {
95 case 1:
Khem Raja0971ed2024-01-27 14:54:59 -080096 return readb((void *)(hdmi->ioaddr + offset));
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010097 case 4:
Khem Raja0971ed2024-01-27 14:54:59 -080098 return readl((void *)(hdmi->ioaddr + (offset << 2)));
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +010099 default:
100 debug("reg_io_width has unsupported width!\n");
101 break;
102 }
103
104 return 0;
105}
106
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +0100107static u8 (*hdmi_read)(struct dw_hdmi *hdmi, int offset) = dw_hdmi_read;
108static void (*hdmi_write)(struct dw_hdmi *hdmi, u8 val, int offset) =
109 dw_hdmi_write;
110
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100111static void hdmi_mod(struct dw_hdmi *hdmi, unsigned reg, u8 mask, u8 data)
112{
113 u8 val = hdmi_read(hdmi, reg) & ~mask;
114
115 val |= data & mask;
116 hdmi_write(hdmi, val, reg);
117}
118
119static void hdmi_set_clock_regenerator(struct dw_hdmi *hdmi, u32 n, u32 cts)
120{
121 uint cts3;
122 uint n3;
123
124 /* first set ncts_atomic_write (if present) */
125 n3 = HDMI_AUD_N3_NCTS_ATOMIC_WRITE;
126 hdmi_write(hdmi, n3, HDMI_AUD_N3);
127
128 /* set cts_manual (if present) */
129 cts3 = HDMI_AUD_CTS3_CTS_MANUAL;
130
131 cts3 |= HDMI_AUD_CTS3_N_SHIFT_1 << HDMI_AUD_CTS3_N_SHIFT_OFFSET;
132 cts3 |= (cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK;
133
134 /* write cts values; cts3 must be written first */
135 hdmi_write(hdmi, cts3, HDMI_AUD_CTS3);
136 hdmi_write(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
137 hdmi_write(hdmi, cts & 0xff, HDMI_AUD_CTS1);
138
139 /* write n values; n1 must be written last */
140 n3 |= (n >> 16) & HDMI_AUD_N3_AUDN19_16_MASK;
141 hdmi_write(hdmi, n3, HDMI_AUD_N3);
142 hdmi_write(hdmi, (n >> 8) & 0xff, HDMI_AUD_N2);
143 hdmi_write(hdmi, n & 0xff, HDMI_AUD_N3);
144
145 hdmi_write(hdmi, HDMI_AUD_INPUTCLKFS_128, HDMI_AUD_INPUTCLKFS);
146}
147
148static int hdmi_lookup_n_cts(u32 pixel_clk)
149{
150 int i;
151
152 for (i = 0; i < ARRAY_SIZE(n_cts_table); i++)
153 if (pixel_clk <= n_cts_table[i].tmds)
154 break;
155
156 if (i >= ARRAY_SIZE(n_cts_table))
157 return -1;
158
159 return i;
160}
161
162static void hdmi_audio_set_samplerate(struct dw_hdmi *hdmi, u32 pixel_clk)
163{
164 u32 clk_n, clk_cts;
165 int index;
166
167 index = hdmi_lookup_n_cts(pixel_clk);
168 if (index == -1) {
169 debug("audio not supported for pixel clk %d\n", pixel_clk);
170 return;
171 }
172
173 clk_n = n_cts_table[index].n;
174 clk_cts = n_cts_table[index].cts;
175 hdmi_set_clock_regenerator(hdmi, clk_n, clk_cts);
176}
177
178/*
179 * this submodule is responsible for the video data synchronization.
180 * for example, for rgb 4:4:4 input, the data map is defined as
181 * pin{47~40} <==> r[7:0]
182 * pin{31~24} <==> g[7:0]
183 * pin{15~8} <==> b[7:0]
184 */
185static void hdmi_video_sample(struct dw_hdmi *hdmi)
186{
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +0100187 u32 color_format;
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100188 uint val;
189
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +0100190 switch (hdmi->hdmi_data.enc_in_bus_format) {
191 case MEDIA_BUS_FMT_RGB888_1X24:
192 color_format = 0x01;
193 break;
194 case MEDIA_BUS_FMT_RGB101010_1X30:
195 color_format = 0x03;
196 break;
197 case MEDIA_BUS_FMT_RGB121212_1X36:
198 color_format = 0x05;
199 break;
200 case MEDIA_BUS_FMT_RGB161616_1X48:
201 color_format = 0x07;
202 break;
203 case MEDIA_BUS_FMT_YUV8_1X24:
204 case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
205 color_format = 0x09;
206 break;
207 case MEDIA_BUS_FMT_YUV10_1X30:
208 case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
209 color_format = 0x0B;
210 break;
211 case MEDIA_BUS_FMT_YUV12_1X36:
212 case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
213 color_format = 0x0D;
214 break;
215 case MEDIA_BUS_FMT_YUV16_1X48:
216 case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
217 color_format = 0x0F;
218 break;
219 case MEDIA_BUS_FMT_UYVY8_1X16:
220 color_format = 0x16;
221 break;
222 case MEDIA_BUS_FMT_UYVY10_1X20:
223 color_format = 0x14;
224 break;
225 case MEDIA_BUS_FMT_UYVY12_1X24:
226 color_format = 0x12;
227 break;
228 default:
229 color_format = 0x01;
230 break;
231 }
232
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100233 val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
234 ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
235 HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
236
237 hdmi_write(hdmi, val, HDMI_TX_INVID0);
238
239 /* enable tx stuffing: when de is inactive, fix the output data to 0 */
240 val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
241 HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
242 HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
243 hdmi_write(hdmi, val, HDMI_TX_INSTUFFING);
244 hdmi_write(hdmi, 0x0, HDMI_TX_GYDATA0);
245 hdmi_write(hdmi, 0x0, HDMI_TX_GYDATA1);
246 hdmi_write(hdmi, 0x0, HDMI_TX_RCRDATA0);
247 hdmi_write(hdmi, 0x0, HDMI_TX_RCRDATA1);
248 hdmi_write(hdmi, 0x0, HDMI_TX_BCBDATA0);
249 hdmi_write(hdmi, 0x0, HDMI_TX_BCBDATA1);
250}
251
252static void hdmi_video_packetize(struct dw_hdmi *hdmi)
253{
254 u32 output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
255 u32 remap_size = HDMI_VP_REMAP_YCC422_16BIT;
256 u32 color_depth = 0;
257 uint val, vp_conf;
258
259 /* set the packetizer registers */
260 val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
261 HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
262 ((0 << HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
263 HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
264 hdmi_write(hdmi, val, HDMI_VP_PR_CD);
265
266 hdmi_mod(hdmi, HDMI_VP_STUFF, HDMI_VP_STUFF_PR_STUFFING_MASK,
267 HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE);
268
269 /* data from pixel repeater block */
270 vp_conf = HDMI_VP_CONF_PR_EN_DISABLE |
271 HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
272
273 hdmi_mod(hdmi, HDMI_VP_CONF, HDMI_VP_CONF_PR_EN_MASK |
274 HDMI_VP_CONF_BYPASS_SELECT_MASK, vp_conf);
275
276 hdmi_mod(hdmi, HDMI_VP_STUFF, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK,
277 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET);
278
279 hdmi_write(hdmi, remap_size, HDMI_VP_REMAP);
280
281 vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE |
282 HDMI_VP_CONF_PP_EN_DISABLE |
283 HDMI_VP_CONF_YCC422_EN_DISABLE;
284
285 hdmi_mod(hdmi, HDMI_VP_CONF, HDMI_VP_CONF_BYPASS_EN_MASK |
286 HDMI_VP_CONF_PP_EN_ENMASK | HDMI_VP_CONF_YCC422_EN_MASK,
287 vp_conf);
288
289 hdmi_mod(hdmi, HDMI_VP_STUFF, HDMI_VP_STUFF_PP_STUFFING_MASK |
290 HDMI_VP_STUFF_YCC422_STUFFING_MASK,
291 HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
292 HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE);
293
294 hdmi_mod(hdmi, HDMI_VP_CONF, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
295 output_select);
296}
297
298static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, uint bit)
299{
300 hdmi_mod(hdmi, HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR_MASK,
301 bit << HDMI_PHY_TST0_TSTCLR_OFFSET);
302}
303
304static int hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, u32 msec)
305{
306 ulong start;
307 u32 val;
308
309 start = get_timer(0);
310 do {
311 val = hdmi_read(hdmi, HDMI_IH_I2CMPHY_STAT0);
312 if (val & 0x3) {
313 hdmi_write(hdmi, val, HDMI_IH_I2CMPHY_STAT0);
314 return 0;
315 }
316
317 udelay(100);
318 } while (get_timer(start) < msec);
319
320 return 1;
321}
322
323static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, uint data, uint addr)
324{
325 hdmi_write(hdmi, 0xff, HDMI_IH_I2CMPHY_STAT0);
326 hdmi_write(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
327 hdmi_write(hdmi, (u8)(data >> 8), HDMI_PHY_I2CM_DATAO_1_ADDR);
328 hdmi_write(hdmi, (u8)(data >> 0), HDMI_PHY_I2CM_DATAO_0_ADDR);
329 hdmi_write(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
330 HDMI_PHY_I2CM_OPERATION_ADDR);
331
332 hdmi_phy_wait_i2c_done(hdmi, 1000);
333}
334
335static void hdmi_phy_enable_power(struct dw_hdmi *hdmi, uint enable)
336{
337 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_PDZ_MASK,
338 enable << HDMI_PHY_CONF0_PDZ_OFFSET);
339}
340
341static void hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, uint enable)
342{
343 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_ENTMDS_MASK,
344 enable << HDMI_PHY_CONF0_ENTMDS_OFFSET);
345}
346
347static void hdmi_phy_enable_spare(struct dw_hdmi *hdmi, uint enable)
348{
349 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_SPARECTRL_MASK,
350 enable << HDMI_PHY_CONF0_SPARECTRL_OFFSET);
351}
352
353static void hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, uint enable)
354{
355 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_GEN2_PDDQ_MASK,
356 enable << HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET);
357}
358
359static void hdmi_phy_gen2_txpwron(struct dw_hdmi *hdmi, uint enable)
360{
361 hdmi_mod(hdmi, HDMI_PHY_CONF0,
362 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK,
363 enable << HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET);
364}
365
366static void hdmi_phy_sel_data_en_pol(struct dw_hdmi *hdmi, uint enable)
367{
368 hdmi_mod(hdmi, HDMI_PHY_CONF0,
369 HDMI_PHY_CONF0_SELDATAENPOL_MASK,
370 enable << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET);
371}
372
373static void hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi,
374 uint enable)
375{
376 hdmi_mod(hdmi, HDMI_PHY_CONF0, HDMI_PHY_CONF0_SELDIPIF_MASK,
377 enable << HDMI_PHY_CONF0_SELDIPIF_OFFSET);
378}
379
380static int hdmi_phy_configure(struct dw_hdmi *hdmi, u32 mpixelclock)
381{
382 ulong start;
383 uint i, val;
384
385 if (!hdmi->mpll_cfg || !hdmi->phy_cfg)
386 return -1;
387
388 /* gen2 tx power off */
389 hdmi_phy_gen2_txpwron(hdmi, 0);
390
391 /* gen2 pddq */
392 hdmi_phy_gen2_pddq(hdmi, 1);
393
394 /* phy reset */
395 hdmi_write(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
396 hdmi_write(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
397 hdmi_write(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
398
399 hdmi_phy_test_clear(hdmi, 1);
400 hdmi_write(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
401 HDMI_PHY_I2CM_SLAVE_ADDR);
402 hdmi_phy_test_clear(hdmi, 0);
403
404 /* pll/mpll cfg - always match on final entry */
405 for (i = 0; hdmi->mpll_cfg[i].mpixelclock != (~0ul); i++)
406 if (mpixelclock <= hdmi->mpll_cfg[i].mpixelclock)
407 break;
408
409 hdmi_phy_i2c_write(hdmi, hdmi->mpll_cfg[i].cpce, PHY_OPMODE_PLLCFG);
410 hdmi_phy_i2c_write(hdmi, hdmi->mpll_cfg[i].gmp, PHY_PLLGMPCTRL);
411 hdmi_phy_i2c_write(hdmi, hdmi->mpll_cfg[i].curr, PHY_PLLCURRCTRL);
412
413 hdmi_phy_i2c_write(hdmi, 0x0000, PHY_PLLPHBYCTRL);
414 hdmi_phy_i2c_write(hdmi, 0x0006, PHY_PLLCLKBISTPHASE);
415
416 for (i = 0; hdmi->phy_cfg[i].mpixelclock != (~0ul); i++)
417 if (mpixelclock <= hdmi->phy_cfg[i].mpixelclock)
418 break;
419
420 /*
421 * resistance term 133ohm cfg
422 * preemp cgf 0.00
423 * tx/ck lvl 10
424 */
425 hdmi_phy_i2c_write(hdmi, hdmi->phy_cfg[i].term, PHY_TXTERM);
426 hdmi_phy_i2c_write(hdmi, hdmi->phy_cfg[i].sym_ctr, PHY_CKSYMTXCTRL);
427 hdmi_phy_i2c_write(hdmi, hdmi->phy_cfg[i].vlev_ctr, PHY_VLEVCTRL);
428
429 /* remove clk term */
430 hdmi_phy_i2c_write(hdmi, 0x8000, PHY_CKCALCTRL);
431
432 hdmi_phy_enable_power(hdmi, 1);
433
434 /* toggle tmds enable */
435 hdmi_phy_enable_tmds(hdmi, 0);
436 hdmi_phy_enable_tmds(hdmi, 1);
437
438 /* gen2 tx power on */
439 hdmi_phy_gen2_txpwron(hdmi, 1);
440 hdmi_phy_gen2_pddq(hdmi, 0);
441
442 hdmi_phy_enable_spare(hdmi, 1);
443
444 /* wait for phy pll lock */
445 start = get_timer(0);
446 do {
447 val = hdmi_read(hdmi, HDMI_PHY_STAT0);
448 if (!(val & HDMI_PHY_TX_PHY_LOCK))
449 return 0;
450
451 udelay(100);
452 } while (get_timer(start) < 5);
453
454 return -1;
455}
456
457static void hdmi_av_composer(struct dw_hdmi *hdmi,
458 const struct display_timing *edid)
459{
460 bool mdataenablepolarity = true;
461 uint inv_val;
462 uint hbl;
463 uint vbl;
464
465 hbl = edid->hback_porch.typ + edid->hfront_porch.typ +
466 edid->hsync_len.typ;
467 vbl = edid->vback_porch.typ + edid->vfront_porch.typ +
468 edid->vsync_len.typ;
469
470 /* set up hdmi_fc_invidconf */
471 inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE;
472
Vasily Khoruzhickb5d6c872018-05-14 13:49:53 -0700473 inv_val |= (edid->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100474 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
475 HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW);
476
Vasily Khoruzhickb5d6c872018-05-14 13:49:53 -0700477 inv_val |= (edid->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100478 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH :
479 HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW);
480
481 inv_val |= (mdataenablepolarity ?
482 HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH :
483 HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW);
484
Jernej Skrabec7d553052017-04-29 14:43:37 +0200485 inv_val |= (edid->hdmi_monitor ?
486 HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE :
487 HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100488
489 inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW;
490
491 inv_val |= HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE;
492
493 hdmi_write(hdmi, inv_val, HDMI_FC_INVIDCONF);
494
495 /* set up horizontal active pixel width */
496 hdmi_write(hdmi, edid->hactive.typ >> 8, HDMI_FC_INHACTV1);
497 hdmi_write(hdmi, edid->hactive.typ, HDMI_FC_INHACTV0);
498
499 /* set up vertical active lines */
500 hdmi_write(hdmi, edid->vactive.typ >> 8, HDMI_FC_INVACTV1);
501 hdmi_write(hdmi, edid->vactive.typ, HDMI_FC_INVACTV0);
502
503 /* set up horizontal blanking pixel region width */
504 hdmi_write(hdmi, hbl >> 8, HDMI_FC_INHBLANK1);
505 hdmi_write(hdmi, hbl, HDMI_FC_INHBLANK0);
506
507 /* set up vertical blanking pixel region width */
508 hdmi_write(hdmi, vbl, HDMI_FC_INVBLANK);
509
510 /* set up hsync active edge delay width (in pixel clks) */
511 hdmi_write(hdmi, edid->hfront_porch.typ >> 8, HDMI_FC_HSYNCINDELAY1);
512 hdmi_write(hdmi, edid->hfront_porch.typ, HDMI_FC_HSYNCINDELAY0);
513
514 /* set up vsync active edge delay (in lines) */
515 hdmi_write(hdmi, edid->vfront_porch.typ, HDMI_FC_VSYNCINDELAY);
516
517 /* set up hsync active pulse width (in pixel clks) */
518 hdmi_write(hdmi, edid->hsync_len.typ >> 8, HDMI_FC_HSYNCINWIDTH1);
519 hdmi_write(hdmi, edid->hsync_len.typ, HDMI_FC_HSYNCINWIDTH0);
520
521 /* set up vsync active edge delay (in lines) */
522 hdmi_write(hdmi, edid->vsync_len.typ, HDMI_FC_VSYNCINWIDTH);
523}
524
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +0100525static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
526{
527 switch (bus_format) {
528 case MEDIA_BUS_FMT_RGB888_1X24:
529 case MEDIA_BUS_FMT_RGB101010_1X30:
530 case MEDIA_BUS_FMT_RGB121212_1X36:
531 case MEDIA_BUS_FMT_RGB161616_1X48:
532 return true;
533
534 default:
535 return false;
536 }
537}
538
539static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
540{
541 switch (bus_format) {
542 case MEDIA_BUS_FMT_YUV8_1X24:
543 case MEDIA_BUS_FMT_YUV10_1X30:
544 case MEDIA_BUS_FMT_YUV12_1X36:
545 case MEDIA_BUS_FMT_YUV16_1X48:
546 return true;
547
548 default:
549 return false;
550 }
551}
552
553static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
554{
555 switch (bus_format) {
556 case MEDIA_BUS_FMT_UYVY8_1X16:
557 case MEDIA_BUS_FMT_UYVY10_1X20:
558 case MEDIA_BUS_FMT_UYVY12_1X24:
559 return true;
560
561 default:
562 return false;
563 }
564}
565
566static int is_color_space_interpolation(struct dw_hdmi *hdmi)
567{
568 if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format))
569 return 0;
570
571 if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) ||
572 hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
573 return 1;
574
575 return 0;
576}
577
578static int is_color_space_decimation(struct dw_hdmi *hdmi)
579{
580 if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
581 return 0;
582
583 if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) ||
584 hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format))
585 return 1;
586
587 return 0;
588}
589
590static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
591{
592 switch (bus_format) {
593 case MEDIA_BUS_FMT_RGB888_1X24:
594 case MEDIA_BUS_FMT_YUV8_1X24:
595 case MEDIA_BUS_FMT_UYVY8_1X16:
596 case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
597 return 8;
598
599 case MEDIA_BUS_FMT_RGB101010_1X30:
600 case MEDIA_BUS_FMT_YUV10_1X30:
601 case MEDIA_BUS_FMT_UYVY10_1X20:
602 case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
603 return 10;
604
605 case MEDIA_BUS_FMT_RGB121212_1X36:
606 case MEDIA_BUS_FMT_YUV12_1X36:
607 case MEDIA_BUS_FMT_UYVY12_1X24:
608 case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
609 return 12;
610
611 case MEDIA_BUS_FMT_RGB161616_1X48:
612 case MEDIA_BUS_FMT_YUV16_1X48:
613 case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
614 return 16;
615
616 default:
617 return 0;
618 }
619}
620
621static int is_color_space_conversion(struct dw_hdmi *hdmi)
622{
623 return hdmi->hdmi_data.enc_in_bus_format !=
624 hdmi->hdmi_data.enc_out_bus_format;
625}
626
627static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
628{
629 const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
630 unsigned int i;
631 u32 csc_scale = 1;
632
633 if (is_color_space_conversion(hdmi)) {
634 if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
635 csc_coeff = &csc_coeff_rgb_out_eitu601;
636 } else if (hdmi_bus_fmt_is_rgb(
637 hdmi->hdmi_data.enc_in_bus_format)) {
638 csc_coeff = &csc_coeff_rgb_in_eitu601;
639 csc_scale = 0;
640 }
641 }
642
643 /* The CSC registers are sequential, alternating MSB then LSB */
644 for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) {
645 u16 coeff_a = (*csc_coeff)[0][i];
646 u16 coeff_b = (*csc_coeff)[1][i];
647 u16 coeff_c = (*csc_coeff)[2][i];
648
649 hdmi_write(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2);
650 hdmi_write(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
651 hdmi_write(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
652 hdmi_write(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
653 hdmi_write(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2);
654 hdmi_write(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
655 }
656
657 hdmi_mod(hdmi, HDMI_CSC_SCALE, HDMI_CSC_SCALE_CSCSCALE_MASK, csc_scale);
658}
659
660static void hdmi_video_csc(struct dw_hdmi *hdmi)
661{
662 int color_depth = 0;
663 int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE;
664 int decimation = 0;
665
666 /* YCC422 interpolation to 444 mode */
667 if (is_color_space_interpolation(hdmi))
668 interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
669 else if (is_color_space_decimation(hdmi))
670 decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
671
672 switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
673 case 8:
674 color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP;
675 break;
676 case 10:
677 color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP;
678 break;
679 case 12:
680 color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP;
681 break;
682 case 16:
683 color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP;
684 break;
685
686 default:
687 return;
688 }
689
690 /* Configure the CSC registers */
691 hdmi_write(hdmi, interpolation | decimation, HDMI_CSC_CFG);
692
693 hdmi_mod(hdmi, HDMI_CSC_SCALE, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
694 color_depth);
695
696 dw_hdmi_update_csc_coeffs(hdmi);
697}
698
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100699/* hdmi initialization step b.4 */
Jernej Skrabec7d553052017-04-29 14:43:37 +0200700static void hdmi_enable_video_path(struct dw_hdmi *hdmi, bool audio)
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100701{
702 uint clkdis;
703
704 /* control period minimum duration */
705 hdmi_write(hdmi, 12, HDMI_FC_CTRLDUR);
706 hdmi_write(hdmi, 32, HDMI_FC_EXCTRLDUR);
707 hdmi_write(hdmi, 1, HDMI_FC_EXCTRLSPAC);
708
709 /* set to fill tmds data channels */
710 hdmi_write(hdmi, 0x0b, HDMI_FC_CH0PREAM);
711 hdmi_write(hdmi, 0x16, HDMI_FC_CH1PREAM);
712 hdmi_write(hdmi, 0x21, HDMI_FC_CH2PREAM);
713
714 hdmi_write(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
715 HDMI_MC_FLOWCTRL);
716
717 /* enable pixel clock and tmds data path */
718 clkdis = 0x7f;
719 clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
720 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
721
722 clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
723 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
724
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +0100725 /* Enable csc path */
726 if (is_color_space_conversion(hdmi)) {
727 clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
728 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
729 }
730
731 /* Enable color space conversion if needed */
732 if (is_color_space_conversion(hdmi))
733 hdmi_write(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH,
734 HDMI_MC_FLOWCTRL);
735 else
736 hdmi_write(hdmi, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS,
737 HDMI_MC_FLOWCTRL);
738
Jernej Skrabec7d553052017-04-29 14:43:37 +0200739 if (audio) {
740 clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
741 hdmi_write(hdmi, clkdis, HDMI_MC_CLKDIS);
742 }
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100743}
744
745/* workaround to clear the overflow condition */
746static void hdmi_clear_overflow(struct dw_hdmi *hdmi)
747{
748 uint val, count;
749
750 /* tmds software reset */
751 hdmi_write(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
752
753 val = hdmi_read(hdmi, HDMI_FC_INVIDCONF);
754
755 for (count = 0; count < 4; count++)
756 hdmi_write(hdmi, val, HDMI_FC_INVIDCONF);
757}
758
759static void hdmi_audio_set_format(struct dw_hdmi *hdmi)
760{
761 hdmi_write(hdmi, HDMI_AUD_CONF0_I2S_SELECT | HDMI_AUD_CONF0_I2S_IN_EN_0,
762 HDMI_AUD_CONF0);
763
764
765 hdmi_write(hdmi, HDMI_AUD_CONF1_I2S_MODE_STANDARD_MODE |
766 HDMI_AUD_CONF1_I2S_WIDTH_16BIT, HDMI_AUD_CONF1);
767
768 hdmi_write(hdmi, 0x00, HDMI_AUD_CONF2);
769}
770
771static void hdmi_audio_fifo_reset(struct dw_hdmi *hdmi)
772{
773 hdmi_write(hdmi, (u8)~HDMI_MC_SWRSTZ_II2SSWRST_REQ, HDMI_MC_SWRSTZ);
774 hdmi_write(hdmi, HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST, HDMI_AUD_CONF0);
775
776 hdmi_write(hdmi, 0x00, HDMI_AUD_INT);
777 hdmi_write(hdmi, 0x00, HDMI_AUD_INT1);
778}
779
780static int hdmi_get_plug_in_status(struct dw_hdmi *hdmi)
781{
782 uint val = hdmi_read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD;
783
784 return !!val;
785}
786
787static int hdmi_ddc_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
788{
789 u32 val;
790 ulong start;
791
792 start = get_timer(0);
793 do {
794 val = hdmi_read(hdmi, HDMI_IH_I2CM_STAT0);
795 if (val & 0x2) {
796 hdmi_write(hdmi, val, HDMI_IH_I2CM_STAT0);
797 return 0;
798 }
799
800 udelay(100);
801 } while (get_timer(start) < msec);
802
803 return 1;
804}
805
806static void hdmi_ddc_reset(struct dw_hdmi *hdmi)
807{
808 hdmi_mod(hdmi, HDMI_I2CM_SOFTRSTZ, HDMI_I2CM_SOFTRSTZ_MASK, 0);
809}
810
811static int hdmi_read_edid(struct dw_hdmi *hdmi, int block, u8 *buff)
812{
813 int shift = (block % 2) * 0x80;
814 int edid_read_err = 0;
815 u32 trytime = 5;
816 u32 n;
817
Niklas Schulze889ccde2019-07-27 12:07:13 +0000818 if (CONFIG_IS_ENABLED(DM_I2C) && hdmi->ddc_bus) {
819 struct udevice *chip;
820
821 edid_read_err = i2c_get_chip(hdmi->ddc_bus,
822 HDMI_I2CM_SLAVE_DDC_ADDR,
823 1, &chip);
824 if (edid_read_err)
825 return edid_read_err;
826
827 return dm_i2c_read(chip, shift, buff, HDMI_EDID_BLOCK_SIZE);
828 }
829
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100830 /* set ddc i2c clk which devided from ddc_clk to 100khz */
831 hdmi_write(hdmi, hdmi->i2c_clk_high, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
832 hdmi_write(hdmi, hdmi->i2c_clk_low, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
833 hdmi_mod(hdmi, HDMI_I2CM_DIV, HDMI_I2CM_DIV_FAST_STD_MODE,
834 HDMI_I2CM_DIV_STD_MODE);
835
836 hdmi_write(hdmi, HDMI_I2CM_SLAVE_DDC_ADDR, HDMI_I2CM_SLAVE);
837 hdmi_write(hdmi, HDMI_I2CM_SEGADDR_DDC, HDMI_I2CM_SEGADDR);
838 hdmi_write(hdmi, block >> 1, HDMI_I2CM_SEGPTR);
839
840 while (trytime--) {
841 edid_read_err = 0;
842
843 for (n = 0; n < HDMI_EDID_BLOCK_SIZE; n++) {
844 hdmi_write(hdmi, shift + n, HDMI_I2CM_ADDRESS);
845
846 if (block == 0)
847 hdmi_write(hdmi, HDMI_I2CM_OP_RD8,
848 HDMI_I2CM_OPERATION);
849 else
850 hdmi_write(hdmi, HDMI_I2CM_OP_RD8_EXT,
851 HDMI_I2CM_OPERATION);
852
853 if (hdmi_ddc_wait_i2c_done(hdmi, 10)) {
854 hdmi_ddc_reset(hdmi);
855 edid_read_err = 1;
856 break;
857 }
858
859 buff[n] = hdmi_read(hdmi, HDMI_I2CM_DATAI);
860 }
861
862 if (!edid_read_err)
863 break;
864 }
865
866 return edid_read_err;
867}
868
869static const u8 pre_buf[] = {
870 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
871 0x04, 0x69, 0xfa, 0x23, 0xc8, 0x28, 0x01, 0x00,
872 0x10, 0x17, 0x01, 0x03, 0x80, 0x33, 0x1d, 0x78,
873 0x2a, 0xd9, 0x45, 0xa2, 0x55, 0x4d, 0xa0, 0x27,
874 0x12, 0x50, 0x54, 0xb7, 0xef, 0x00, 0x71, 0x4f,
875 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00,
876 0xd1, 0xc0, 0x81, 0xc0, 0x81, 0x00, 0x02, 0x3a,
877 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
878 0x45, 0x00, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e,
879 0x00, 0x00, 0x00, 0xff, 0x00, 0x44, 0x34, 0x4c,
880 0x4d, 0x54, 0x46, 0x30, 0x37, 0x35, 0x39, 0x37,
881 0x36, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
882 0x4b, 0x18, 0x53, 0x11, 0x00, 0x0a, 0x20, 0x20,
883 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
884 0x00, 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x53,
885 0x32, 0x33, 0x38, 0x0a, 0x20, 0x20, 0x01, 0xb0,
886 0x02, 0x03, 0x22, 0x71, 0x4f, 0x01, 0x02, 0x03,
887 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f,
888 0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, 0x17, 0x07,
889 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00,
890 0x10, 0x00, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0,
891 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xfd, 0x1e,
892 0x11, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72,
893 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
894 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e, 0x01, 0x1d,
895 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28,
896 0x55, 0x40, 0xfd, 0x1e, 0x11, 0x00, 0x00, 0x1e,
897 0x8c, 0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20,
898 0x0c, 0x40, 0x55, 0x00, 0xfd, 0x1e, 0x11, 0x00,
899 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9,
902};
903
904int dw_hdmi_phy_cfg(struct dw_hdmi *hdmi, uint mpixelclock)
905{
906 int i, ret;
907
908 /* hdmi phy spec says to do the phy initialization sequence twice */
909 for (i = 0; i < 2; i++) {
910 hdmi_phy_sel_data_en_pol(hdmi, 1);
911 hdmi_phy_sel_interface_control(hdmi, 0);
912 hdmi_phy_enable_tmds(hdmi, 0);
913 hdmi_phy_enable_power(hdmi, 0);
914
915 ret = hdmi_phy_configure(hdmi, mpixelclock);
916 if (ret) {
917 debug("hdmi phy config failure %d\n", ret);
918 return ret;
919 }
920 }
921
922 return 0;
923}
924
925int dw_hdmi_phy_wait_for_hpd(struct dw_hdmi *hdmi)
926{
927 ulong start;
928
929 start = get_timer(0);
930 do {
931 if (hdmi_get_plug_in_status(hdmi))
932 return 0;
933 udelay(100);
934 } while (get_timer(start) < 300);
935
936 return -1;
937}
938
Jagan Teki17d0f552024-01-17 13:21:40 +0530939int dw_hdmi_detect_hpd(struct dw_hdmi *hdmi)
940{
941 int ret;
942
943 ret = dw_hdmi_phy_wait_for_hpd(hdmi);
944 if (ret < 0) {
945 debug("hdmi can not get hpd signal\n");
946 return -ENODEV;
947 }
948
Jagan Tekib0b6c3b2024-01-17 13:21:41 +0530949 if (hdmi->ops && hdmi->ops->read_hpd)
950 hdmi->ops->read_hpd(hdmi, true);
951
Jagan Teki17d0f552024-01-17 13:21:40 +0530952 return 0;
953}
954
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +0100955void dw_hdmi_phy_init(struct dw_hdmi *hdmi)
956{
957 /* enable phy i2cm done irq */
958 hdmi_write(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
959 HDMI_PHY_I2CM_INT_ADDR);
960
961 /* enable phy i2cm nack & arbitration error irq */
962 hdmi_write(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
963 HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
964 HDMI_PHY_I2CM_CTLINT_ADDR);
965
966 /* enable cable hot plug irq */
967 hdmi_write(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
968
969 /* clear hotplug interrupts */
970 hdmi_write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
971}
972
973int dw_hdmi_read_edid(struct dw_hdmi *hdmi, u8 *buf, int buf_size)
974{
975 u32 edid_size = HDMI_EDID_BLOCK_SIZE;
976 int ret;
977
978 if (0) {
979 edid_size = sizeof(pre_buf);
980 memcpy(buf, pre_buf, edid_size);
981 } else {
982 ret = hdmi_read_edid(hdmi, 0, buf);
983 if (ret) {
984 debug("failed to read edid.\n");
985 return -1;
986 }
987
988 if (buf[0x7e] != 0) {
989 hdmi_read_edid(hdmi, 1, buf + HDMI_EDID_BLOCK_SIZE);
990 edid_size += HDMI_EDID_BLOCK_SIZE;
991 }
992 }
993
994 return edid_size;
995}
996
997int dw_hdmi_enable(struct dw_hdmi *hdmi, const struct display_timing *edid)
998{
999 int ret;
1000
Jernej Skrabec7d553052017-04-29 14:43:37 +02001001 debug("%s, mode info : clock %d hdis %d vdis %d\n",
1002 edid->hdmi_monitor ? "hdmi" : "dvi",
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001003 edid->pixelclock.typ, edid->hactive.typ, edid->vactive.typ);
1004
1005 hdmi_av_composer(hdmi, edid);
1006
Jagan Tekib3c66b62024-01-17 13:21:39 +05301007 ret = hdmi->ops->phy_set(hdmi, edid->pixelclock.typ);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001008 if (ret)
1009 return ret;
1010
Jernej Skrabec7d553052017-04-29 14:43:37 +02001011 hdmi_enable_video_path(hdmi, edid->hdmi_monitor);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001012
Jernej Skrabec7d553052017-04-29 14:43:37 +02001013 if (edid->hdmi_monitor) {
1014 hdmi_audio_fifo_reset(hdmi);
1015 hdmi_audio_set_format(hdmi);
1016 hdmi_audio_set_samplerate(hdmi, edid->pixelclock.typ);
1017 }
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001018
1019 hdmi_video_packetize(hdmi);
Jorge Ramirez-Ortiz597d3b32018-11-15 10:32:03 +01001020 hdmi_video_csc(hdmi);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001021 hdmi_video_sample(hdmi);
1022
1023 hdmi_clear_overflow(hdmi);
1024
1025 return 0;
1026}
1027
Jagan Tekib3c66b62024-01-17 13:21:39 +05301028static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
1029 .phy_set = dw_hdmi_phy_cfg,
1030};
1031
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001032void dw_hdmi_init(struct dw_hdmi *hdmi)
1033{
1034 uint ih_mute;
1035
Jagan Tekib3c66b62024-01-17 13:21:39 +05301036 /* hook Synopsys PHYs ops */
1037 if (!hdmi->ops)
1038 hdmi->ops = &dw_hdmi_synopsys_phy_ops;
1039
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001040 /*
1041 * boot up defaults are:
1042 * hdmi_ih_mute = 0x03 (disabled)
1043 * hdmi_ih_mute_* = 0x00 (enabled)
1044 *
1045 * disable top level interrupt bits in hdmi block
1046 */
1047 ih_mute = /*hdmi_read(hdmi, HDMI_IH_MUTE) |*/
1048 HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
1049 HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
1050
Jorge Ramirez-Ortiz92d58aa2018-11-08 16:51:01 +01001051 if (hdmi->write_reg)
1052 hdmi_write = hdmi->write_reg;
1053
1054 if (hdmi->read_reg)
1055 hdmi_read = hdmi->read_reg;
1056
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001057 hdmi_write(hdmi, ih_mute, HDMI_IH_MUTE);
1058
1059 /* enable i2c master done irq */
1060 hdmi_write(hdmi, ~0x04, HDMI_I2CM_INT);
1061
1062 /* enable i2c client nack % arbitration error irq */
1063 hdmi_write(hdmi, ~0x44, HDMI_I2CM_CTLINT);
Jagan Tekif0f4b6a2024-01-17 13:21:42 +05301064
1065 if (hdmi->ops && hdmi->ops->setup_hpd)
1066 hdmi->ops->setup_hpd(hdmi);
Jernej Skrabec2ae12ee2017-03-20 23:01:22 +01001067}