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