blob: 153266c5226898a6d320ce3ad1ad6f70d918b988 [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Jitao Shi542919f2019-09-26 10:22:08 +08002
3#include <console/console.h>
4#include <delay.h>
5#include <device/i2c_simple.h>
6#include <edid.h>
7#include <gpio.h>
Jitao Shi542919f2019-09-26 10:22:08 +08008#include <string.h>
9
10#include "anx7625.h"
11
12#define ANXERROR(format, ...) \
13 printk(BIOS_ERR, "ERROR: %s: " format, __func__, ##__VA_ARGS__)
14#define ANXINFO(format, ...) \
15 printk(BIOS_INFO, "%s: " format, __func__, ##__VA_ARGS__)
16#define ANXDEBUG(format, ...) \
17 printk(BIOS_DEBUG, "%s: " format, __func__, ##__VA_ARGS__)
18
19/*
20 * There is a sync issue while accessing I2C register between AP(CPU) and
21 * internal firmware(OCM). To avoid the race condition, AP should access the
22 * reserved slave address before slave address changes.
23 */
24static int i2c_access_workaround(uint8_t bus, uint8_t saddr)
25{
26 uint8_t offset;
27 static uint8_t saddr_backup = 0;
28 int ret = 0;
29
30 if (saddr == saddr_backup)
31 return ret;
32
33 saddr_backup = saddr;
34
35 switch (saddr) {
36 case TCPC_INTERFACE_ADDR:
37 offset = RSVD_00_ADDR;
38 break;
39 case TX_P0_ADDR:
40 offset = RSVD_D1_ADDR;
41 break;
42 case TX_P1_ADDR:
43 offset = RSVD_60_ADDR;
44 break;
45 case RX_P0_ADDR:
46 offset = RSVD_39_ADDR;
47 break;
48 case RX_P1_ADDR:
49 offset = RSVD_7F_ADDR;
50 break;
51 default:
52 offset = RSVD_00_ADDR;
53 break;
54 }
55
56 ret = i2c_writeb(bus, saddr, offset, 0x00);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080057 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +080058 ANXERROR("Failed to access %#x:%#x\n", saddr, offset);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080059 return ret;
60 }
61 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +080062}
63
64static int anx7625_reg_read(uint8_t bus, uint8_t saddr, uint8_t offset,
65 uint8_t *val)
66{
67 int ret;
68
69 i2c_access_workaround(bus, saddr);
70 ret = i2c_readb(bus, saddr, offset, val);
71 if (ret < 0) {
72 ANXERROR("Failed to read i2c reg=%#x:%#x\n", saddr, offset);
73 return ret;
74 }
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080075 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +080076}
77
78static int anx7625_reg_block_read(uint8_t bus, uint8_t saddr, uint8_t reg_addr,
79 uint8_t len, uint8_t *buf)
80{
81 int ret;
82
83 i2c_access_workaround(bus, saddr);
84 ret = i2c_read_bytes(bus, saddr, reg_addr, buf, len);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080085 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +080086 ANXERROR("Failed to read i2c block=%#x:%#x[len=%#x]\n", saddr,
87 reg_addr, len);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080088 return ret;
89 }
90 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +080091}
92
93static int anx7625_reg_write(uint8_t bus, uint8_t saddr, uint8_t reg_addr,
94 uint8_t reg_val)
95{
96 int ret;
97
98 i2c_access_workaround(bus, saddr);
99 ret = i2c_writeb(bus, saddr, reg_addr, reg_val);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800100 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800101 ANXERROR("Failed to write i2c id=%#x:%#x\n", saddr, reg_addr);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800102 return ret;
103 }
104 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800105}
106
107static int anx7625_write_or(uint8_t bus, uint8_t saddr, uint8_t offset,
108 uint8_t mask)
109{
110 uint8_t val;
111 int ret;
112
113 ret = anx7625_reg_read(bus, saddr, offset, &val);
114 if (ret < 0)
115 return ret;
116
117 return anx7625_reg_write(bus, saddr, offset, val | mask);
118}
119
120static int anx7625_write_and(uint8_t bus, uint8_t saddr, uint8_t offset,
121 uint8_t mask)
122{
123 int ret;
124 uint8_t val;
125
126 ret = anx7625_reg_read(bus, saddr, offset, &val);
127 if (ret < 0)
128 return ret;
129
130 return anx7625_reg_write(bus, saddr, offset, val & mask);
131}
132
133static int wait_aux_op_finish(uint8_t bus)
134{
135 uint8_t val;
Jitao Shi542919f2019-09-26 10:22:08 +0800136 int loop;
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800137 int success = 0;
138 int ret;
Jitao Shi542919f2019-09-26 10:22:08 +0800139
140 for (loop = 0; loop < 150; loop++) {
141 mdelay(2);
142 anx7625_reg_read(bus, RX_P0_ADDR, AP_AUX_CTRL_STATUS, &val);
143 if (!(val & AP_AUX_CTRL_OP_EN)) {
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800144 success = 1;
Jitao Shi542919f2019-09-26 10:22:08 +0800145 break;
146 }
147 }
148
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800149 if (!success) {
Jitao Shi542919f2019-09-26 10:22:08 +0800150 ANXERROR("Timed out waiting aux operation.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800151 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800152 }
153
154 ret = anx7625_reg_read(bus, RX_P0_ADDR, AP_AUX_CTRL_STATUS, &val);
155 if (ret < 0 || val & 0x0F) {
156 ANXDEBUG("aux status %02x\n", val);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800157 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800158 }
159
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800160 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800161}
162
163static unsigned long gcd(unsigned long a, unsigned long b)
164{
165 if (a == 0)
166 return b;
167
168 while (b != 0) {
169 if (a > b)
170 a = a - b;
171 else
172 b = b - a;
173 }
174
175 return a;
176}
177
178/* Reduce fraction a/b */
179static void anx7625_reduction_of_a_fraction(unsigned long *_a,
180 unsigned long *_b)
181{
182 unsigned long gcd_num;
183 unsigned long a = *_a, b = *_b, old_a, old_b;
184 u32 denom = 1;
185
186 gcd_num = gcd(a, b);
187 a /= gcd_num;
188 b /= gcd_num;
189
190 old_a = a;
191 old_b = b;
192
193 while (a > MAX_UNSIGNED_24BIT || b > MAX_UNSIGNED_24BIT) {
194 denom++;
195 a = old_a / denom;
196 b = old_b / denom;
197 }
198
199 /* Increase a, b to have higher ODFC PLL output frequency accuracy. */
200 while ((a << 1) < MAX_UNSIGNED_24BIT && (b << 1) < MAX_UNSIGNED_24BIT) {
201 a <<= 1;
202 b <<= 1;
203 }
204
205 *_a = a;
206 *_b = b;
207}
208
209static int anx7625_calculate_m_n(u32 pixelclock,
210 unsigned long *m, unsigned long *n,
211 uint8_t *pd)
212{
213 uint8_t post_divider = *pd;
214 if (pixelclock > PLL_OUT_FREQ_ABS_MAX / POST_DIVIDER_MIN) {
215 /* pixel clock frequency is too high */
216 ANXERROR("pixelclock %u higher than %lu, "
217 "output may be unstable\n",
218 pixelclock, PLL_OUT_FREQ_ABS_MAX / POST_DIVIDER_MIN);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800219 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800220 }
221
222 if (pixelclock < PLL_OUT_FREQ_ABS_MIN / POST_DIVIDER_MAX) {
223 /* pixel clock frequency is too low */
224 ANXERROR("pixelclock %u lower than %lu, "
225 "output may be unstable\n",
226 pixelclock, PLL_OUT_FREQ_ABS_MIN / POST_DIVIDER_MAX);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800227 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800228 }
229
230 post_divider = 1;
231
232 for (post_divider = 1;
233 pixelclock < PLL_OUT_FREQ_MIN / post_divider;
234 post_divider++)
235 ;
236
237 if (post_divider > POST_DIVIDER_MAX) {
238 for (post_divider = 1;
239 pixelclock < PLL_OUT_FREQ_ABS_MIN / post_divider;
240 post_divider++)
241 ;
242
243 if (post_divider > POST_DIVIDER_MAX) {
244 ANXERROR("cannot find property post_divider(%d)\n",
245 post_divider);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800246 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800247 }
248 }
249
250 /* Patch to improve the accuracy */
251 if (post_divider == 7) {
252 /* 27,000,000 is not divisible by 7 */
253 post_divider = 8;
254 } else if (post_divider == 11) {
255 /* 27,000,000 is not divisible by 11 */
256 post_divider = 12;
257 } else if (post_divider == 13 || post_divider == 14) {
258 /*27,000,000 is not divisible by 13 or 14*/
259 post_divider = 15;
260 }
261
262 if (pixelclock * post_divider > PLL_OUT_FREQ_ABS_MAX) {
263 ANXINFO("act clock(%u) large than maximum(%lu)\n",
264 pixelclock * post_divider, PLL_OUT_FREQ_ABS_MAX);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800265 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800266 }
267
Jitao Shif68cc812020-01-14 21:23:44 +0800268 *m = pixelclock;
Jitao Shi542919f2019-09-26 10:22:08 +0800269 *n = XTAL_FRQ / post_divider;
270 *pd = post_divider;
271
272 anx7625_reduction_of_a_fraction(m, n);
273
274 return 0;
275}
276
277static int anx7625_odfc_config(uint8_t bus, uint8_t post_divider)
278{
279 int ret;
280
281 /* config input reference clock frequency 27MHz/19.2MHz */
282 ret = anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_16,
283 ~(REF_CLK_27000kHz << MIPI_FREF_D_IND));
284 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_16,
285 (REF_CLK_27000kHz << MIPI_FREF_D_IND));
286 /* post divider */
287 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_8, 0x0f);
288 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_8,
289 post_divider << 4);
290
291 /* add patch for MIS2-125 (5pcs ANX7625 fail ATE MBIST test) */
292 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_7,
293 ~MIPI_PLL_VCO_TUNE_REG_VAL);
294
295 /* reset ODFC PLL */
296 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_7,
297 ~MIPI_PLL_RESET_N);
298 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_7,
299 MIPI_PLL_RESET_N);
300
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800301 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800302 ANXERROR("IO error.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800303 return ret;
304 }
Jitao Shi542919f2019-09-26 10:22:08 +0800305
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800306 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800307}
308
309static int anx7625_dsi_video_config(uint8_t bus, struct display_timing *dt)
310{
311 unsigned long m, n;
312 u16 htotal;
313 int ret;
314 uint8_t post_divider = 0;
315
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800316 if (anx7625_calculate_m_n(dt->pixelclock * 1000, &m, &n,
317 &post_divider) < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800318 ANXERROR("cannot get property m n value.\n");
319 return -1;
320 }
321
322 ANXINFO("compute M(%lu), N(%lu), divider(%d).\n", m, n, post_divider);
323
324 /* configure pixel clock */
325 ret = anx7625_reg_write(bus, RX_P0_ADDR, PIXEL_CLOCK_L,
326 (dt->pixelclock / 1000) & 0xFF);
327 ret |= anx7625_reg_write(bus, RX_P0_ADDR, PIXEL_CLOCK_H,
328 (dt->pixelclock / 1000) >> 8);
329 /* lane count */
330 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_LANE_CTRL_0, 0xfc);
331
332 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_LANE_CTRL_0, 3);
333
334 /* Htotal */
335 htotal = dt->hactive + dt->hfront_porch +
336 dt->hback_porch + dt->hsync_len;
337 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
338 HORIZONTAL_TOTAL_PIXELS_L, htotal & 0xFF);
339 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
340 HORIZONTAL_TOTAL_PIXELS_H, htotal >> 8);
341 /* Hactive */
342 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
343 HORIZONTAL_ACTIVE_PIXELS_L, dt->hactive & 0xFF);
344 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
345 HORIZONTAL_ACTIVE_PIXELS_H, dt->hactive >> 8);
346 /* HFP */
347 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
348 HORIZONTAL_FRONT_PORCH_L, dt->hfront_porch);
349 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
350 HORIZONTAL_FRONT_PORCH_H,
351 dt->hfront_porch >> 8);
352 /* HWS */
353 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
354 HORIZONTAL_SYNC_WIDTH_L, dt->hsync_len);
355 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
356 HORIZONTAL_SYNC_WIDTH_H, dt->hsync_len >> 8);
357 /* HBP */
358 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
359 HORIZONTAL_BACK_PORCH_L, dt->hback_porch);
360 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
361 HORIZONTAL_BACK_PORCH_H, dt->hback_porch >> 8);
362 /* Vactive */
363 ret |= anx7625_reg_write(bus, RX_P2_ADDR, ACTIVE_LINES_L, dt->vactive);
364 ret |= anx7625_reg_write(bus, RX_P2_ADDR, ACTIVE_LINES_H,
365 dt->vactive >> 8);
366 /* VFP */
367 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
368 VERTICAL_FRONT_PORCH, dt->vfront_porch);
369 /* VWS */
370 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
371 VERTICAL_SYNC_WIDTH, dt->vsync_len);
372 /* VBP */
373 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
374 VERTICAL_BACK_PORCH, dt->vback_porch);
375 /* M value */
376 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
377 MIPI_PLL_M_NUM_23_16, (m >> 16) & 0xff);
378 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
379 MIPI_PLL_M_NUM_15_8, (m >> 8) & 0xff);
380 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
381 MIPI_PLL_M_NUM_7_0, (m & 0xff));
382 /* N value */
383 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
384 MIPI_PLL_N_NUM_23_16, (n >> 16) & 0xff);
385 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
386 MIPI_PLL_N_NUM_15_8, (n >> 8) & 0xff);
387 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_PLL_N_NUM_7_0,
388 (n & 0xff));
389 /* diff */
Xin Ji48ae1112021-09-03 11:11:44 +0800390 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_DIGITAL_ADJ_1, dt->k_val);
Jitao Shi542919f2019-09-26 10:22:08 +0800391
392 ret |= anx7625_odfc_config(bus, post_divider - 1);
393
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800394 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800395 ANXERROR("mipi dsi setup IO error.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800396 return ret;
397 }
Jitao Shi542919f2019-09-26 10:22:08 +0800398
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800399 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800400}
401
402static int anx7625_swap_dsi_lane3(uint8_t bus)
403{
404 int ret;
405 uint8_t val;
406
407 /* swap MIPI-DSI data lane 3 P and N */
408 ret = anx7625_reg_read(bus, RX_P1_ADDR, MIPI_SWAP, &val);
409 if (ret < 0) {
410 ANXERROR("IO error: access MIPI_SWAP.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800411 return ret;
Jitao Shi542919f2019-09-26 10:22:08 +0800412 }
413
414 val |= (1 << MIPI_SWAP_CH3);
415 return anx7625_reg_write(bus, RX_P1_ADDR, MIPI_SWAP, val);
416}
417
418static int anx7625_api_dsi_config(uint8_t bus, struct display_timing *dt)
419
420{
421 int val, ret;
422
423 /* swap MIPI-DSI data lane 3 P and N */
424 ret = anx7625_swap_dsi_lane3(bus);
425 if (ret < 0) {
426 ANXERROR("IO error: swap dsi lane 3 failed.\n");
427 return ret;
428 }
429
430 /* DSI clock settings */
431 val = (0 << MIPI_HS_PWD_CLK) |
432 (0 << MIPI_HS_RT_CLK) |
433 (0 << MIPI_PD_CLK) |
434 (1 << MIPI_CLK_RT_MANUAL_PD_EN) |
435 (1 << MIPI_CLK_HS_MANUAL_PD_EN) |
436 (0 << MIPI_CLK_DET_DET_BYPASS) |
437 (0 << MIPI_CLK_MISS_CTRL) |
438 (0 << MIPI_PD_LPTX_CH_MANUAL_PD_EN);
439 ret = anx7625_reg_write(bus, RX_P1_ADDR, MIPI_PHY_CONTROL_3, val);
440
441 /*
442 * Decreased HS prepare tg delay from 160ns to 80ns work with
443 * a) Dragon board 810 series (Qualcomm AP)
444 * b) Moving Pixel DSI source (PG3A pattern generator +
445 * P332 D-PHY Probe) default D-PHY tg 5ns/step
446 */
447 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_TIME_HS_PRPR, 0x10);
448
449 /* enable DSI mode */
450 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_18,
451 SELECT_DSI << MIPI_DPI_SELECT);
452
453 ret |= anx7625_dsi_video_config(bus, dt);
454 if (ret < 0) {
455 ANXERROR("dsi video tg config failed\n");
456 return ret;
457 }
458
459 /* toggle m, n ready */
460 ret = anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_6,
461 ~(MIPI_M_NUM_READY | MIPI_N_NUM_READY));
462 mdelay(1);
463 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_6,
464 MIPI_M_NUM_READY | MIPI_N_NUM_READY);
465
466 /* configure integer stable register */
467 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_VIDEO_STABLE_CNT, 0x02);
468 /* power on MIPI RX */
469 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_LANE_CTRL_10, 0x00);
470 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_LANE_CTRL_10, 0x80);
471
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800472 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800473 ANXERROR("IO error: mipi dsi enable init failed.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800474 return ret;
475 }
Jitao Shi542919f2019-09-26 10:22:08 +0800476
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800477 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800478}
479
480static int anx7625_dsi_config(uint8_t bus, struct display_timing *dt)
481{
482 int ret;
483
484 ANXINFO("config dsi.\n");
485
486 /* DSC disable */
487 ret = anx7625_write_and(bus, RX_P0_ADDR, R_DSC_CTRL_0, ~DSC_EN);
488 ret |= anx7625_api_dsi_config(bus, dt);
489
490 if (ret < 0) {
491 ANXERROR("IO error: api dsi config error.\n");
492 return ret;
493 }
494
495 /* set MIPI RX EN */
496 ret = anx7625_write_or(bus, RX_P0_ADDR, AP_AV_STATUS, AP_MIPI_RX_EN);
497 /* clear mute flag */
498 ret |= anx7625_write_and(bus, RX_P0_ADDR, AP_AV_STATUS, ~AP_MIPI_MUTE);
499
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800500 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800501 ANXERROR("IO error: enable mipi rx failed.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800502 return ret;
503 }
Jitao Shi542919f2019-09-26 10:22:08 +0800504
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800505 ANXINFO("success to config DSI\n");
506 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800507}
508
509static int sp_tx_rst_aux(uint8_t bus)
510{
511 int ret;
512
513 ret = anx7625_write_or(bus, TX_P2_ADDR, RST_CTRL2, AUX_RST);
514 ret |= anx7625_write_and(bus, TX_P2_ADDR, RST_CTRL2, ~AUX_RST);
515 return ret;
516}
517
518static int sp_tx_aux_wr(uint8_t bus, uint8_t offset)
519{
520 int ret;
521
522 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_BUFF_START, offset);
523 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_COMMAND, 0x04);
524 ret |= anx7625_write_or(bus, RX_P0_ADDR,
525 AP_AUX_CTRL_STATUS, AP_AUX_CTRL_OP_EN);
526 return ret | wait_aux_op_finish(bus);
527}
528
529static int sp_tx_aux_rd(uint8_t bus, uint8_t len_cmd)
530{
531 int ret;
532
533 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_COMMAND, len_cmd);
534 ret |= anx7625_write_or(bus, RX_P0_ADDR,
535 AP_AUX_CTRL_STATUS, AP_AUX_CTRL_OP_EN);
536 return ret | wait_aux_op_finish(bus);
537}
538
539static int sp_tx_get_edid_block(uint8_t bus)
540{
541 int ret;
542 uint8_t val = 0;
543
544 sp_tx_aux_wr(bus, 0x7e);
545 sp_tx_aux_rd(bus, 0x01);
546 ret = anx7625_reg_read(bus, RX_P0_ADDR, AP_AUX_BUFF_START, &val);
547
548 if (ret < 0) {
549 ANXERROR("IO error: access AUX BUFF.\n");
550 return -1;
551 }
552
553 ANXINFO("EDID Block = %d\n", val + 1);
554
555 if (val > 3)
556 val = 1;
557
558 return val;
559}
560
561static int edid_read(uint8_t bus, uint8_t offset, uint8_t *pblock_buf)
562{
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800563 int ret, cnt;
Jitao Shi542919f2019-09-26 10:22:08 +0800564
Jitao Shi542919f2019-09-26 10:22:08 +0800565 for (cnt = 0; cnt < 3; cnt++) {
566 sp_tx_aux_wr(bus, offset);
567 /* set I2C read com 0x01 mot = 0 and read 16 bytes */
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800568 ret = sp_tx_aux_rd(bus, 0xf1);
Jitao Shi542919f2019-09-26 10:22:08 +0800569
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800570 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800571 sp_tx_rst_aux(bus);
572 ANXERROR("edid read failed, reset!\n");
Jitao Shi542919f2019-09-26 10:22:08 +0800573 } else {
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800574 if (anx7625_reg_block_read(bus, RX_P0_ADDR,
575 AP_AUX_BUFF_START,
576 MAX_DPCD_BUFFER_SIZE,
577 pblock_buf) >= 0)
578 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800579 }
580 }
581
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800582 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800583}
584
585static int segments_edid_read(uint8_t bus, uint8_t segment, uint8_t *buf,
586 uint8_t offset)
587{
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800588 int ret, cnt;
Jitao Shi542919f2019-09-26 10:22:08 +0800589
590 /* write address only */
591 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_7_0, 0x30);
592 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_COMMAND, 0x04);
593 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_CTRL_STATUS,
594 AP_AUX_CTRL_ADDRONLY | AP_AUX_CTRL_OP_EN);
595
596 ret |= wait_aux_op_finish(bus);
597 /* write segment address */
598 ret |= sp_tx_aux_wr(bus, segment);
599 /* data read */
600 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_7_0, 0x50);
601
602 if (ret < 0) {
603 ANXERROR("IO error: aux initial failed.\n");
604 return ret;
605 }
606
607 for (cnt = 0; cnt < 3; cnt++) {
608 sp_tx_aux_wr(bus, offset);
609 /* set I2C read com 0x01 mot = 0 and read 16 bytes */
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800610 ret = sp_tx_aux_rd(bus, 0xf1);
Jitao Shi542919f2019-09-26 10:22:08 +0800611
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800612 if (ret < 0) {
613 sp_tx_rst_aux(bus);
Jitao Shi542919f2019-09-26 10:22:08 +0800614 ANXERROR("segment read failed, reset!\n");
Jitao Shi542919f2019-09-26 10:22:08 +0800615 } else {
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800616 if (anx7625_reg_block_read(bus, RX_P0_ADDR,
617 AP_AUX_BUFF_START,
618 MAX_DPCD_BUFFER_SIZE,
619 buf) >= 0)
620 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800621 }
622 }
623
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800624 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800625}
626
627static int sp_tx_edid_read(uint8_t bus, uint8_t *pedid_blocks_buf,
628 uint32_t size)
629{
630 uint8_t offset, edid_pos;
631 int count, blocks_num;
632 uint8_t pblock_buf[MAX_DPCD_BUFFER_SIZE];
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800633 int i, ret, g_edid_break = 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800634
635 /* address initial */
636 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_7_0, 0x50);
637 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_15_8, 0);
638 ret |= anx7625_write_and(bus, RX_P0_ADDR, AP_AUX_ADDR_19_16, 0xf0);
639
640 if (ret < 0) {
641 ANXERROR("access aux channel IO error.\n");
642 return -1;
643 }
644
645 blocks_num = sp_tx_get_edid_block(bus);
646 if (blocks_num < 0)
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800647 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800648
649 count = 0;
650
651 do {
652 switch (count) {
653 case 0:
654 case 1:
655 for (i = 0; i < 8; i++) {
656 offset = (i + count * 8) * MAX_DPCD_BUFFER_SIZE;
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800657 g_edid_break = !!edid_read(bus, offset,
658 pblock_buf);
Jitao Shi542919f2019-09-26 10:22:08 +0800659
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800660 if (g_edid_break)
Jitao Shi542919f2019-09-26 10:22:08 +0800661 break;
662
663 if (offset <= size - MAX_DPCD_BUFFER_SIZE)
664 memcpy(&pedid_blocks_buf[offset],
665 pblock_buf,
666 MAX_DPCD_BUFFER_SIZE);
667 }
668
669 break;
670 case 2:
671 case 3:
672 offset = (count == 2) ? 0x00 : 0x80;
673
674 for (i = 0; i < 8; i++) {
675 edid_pos = (i + count * 8) *
676 MAX_DPCD_BUFFER_SIZE;
677
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800678 if (g_edid_break)
Jitao Shi542919f2019-09-26 10:22:08 +0800679 break;
680
681 segments_edid_read(bus, count / 2,
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800682 pblock_buf, offset);
Jitao Shi542919f2019-09-26 10:22:08 +0800683 if (edid_pos <= size - MAX_DPCD_BUFFER_SIZE)
684 memcpy(&pedid_blocks_buf[edid_pos],
685 pblock_buf,
686 MAX_DPCD_BUFFER_SIZE);
687 offset = offset + 0x10;
688 }
689
690 break;
691 default:
692 die("%s: count should be <= 3", __func__);
693 break;
694 }
695
696 count++;
697
698 } while (blocks_num >= count);
699
700 /* reset aux channel */
701 sp_tx_rst_aux(bus);
702
703 return blocks_num;
704}
705
706static void anx7625_disable_pd_protocol(uint8_t bus)
707{
708 int ret;
709
710 /* reset main ocm */
711 ret = anx7625_reg_write(bus, RX_P0_ADDR, 0x88, 0x40);
712 /* Disable PD */
713 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AV_STATUS, AP_DISABLE_PD);
714 /* release main ocm */
715 ret |= anx7625_reg_write(bus, RX_P0_ADDR, 0x88, 0x00);
716
717 if (ret < 0)
718 ANXERROR("Failed to disable PD feature.\n");
719 else
720 ANXINFO("Disabled PD feature.\n");
721}
722
723#define FLASH_LOAD_STA 0x05
724#define FLASH_LOAD_STA_CHK (1 << 7)
725
726static int anx7625_power_on_init(uint8_t bus)
727{
728 int i, ret;
729 uint8_t val, version, revision;
730
731 anx7625_reg_write(bus, RX_P0_ADDR, XTAL_FRQ_SEL, XTAL_FRQ_27M);
732
733 for (i = 0; i < OCM_LOADING_TIME; i++) {
734 /* check interface */
735 ret = anx7625_reg_read(bus, RX_P0_ADDR, FLASH_LOAD_STA, &val);
736 if (ret < 0) {
737 ANXERROR("Failed to load flash\n");
738 return ret;
739 }
740
741 if ((val & FLASH_LOAD_STA_CHK) != FLASH_LOAD_STA_CHK) {
742 mdelay(1);
743 continue;
744 }
745 ANXINFO("Init interface.\n");
746
Jitao Shi542919f2019-09-26 10:22:08 +0800747 anx7625_disable_pd_protocol(bus);
748 anx7625_reg_read(bus, RX_P0_ADDR, OCM_FW_VERSION, &version);
749 anx7625_reg_read(bus, RX_P0_ADDR, OCM_FW_REVERSION, &revision);
750 ANXINFO("Firmware: ver %#02x, rev %#02x.\n", version, revision);
751 return 0;
752 }
753 return -1;
754}
755
756static void anx7625_start_dp_work(uint8_t bus)
757{
758 int ret;
759 uint8_t val;
760
761 /* not support HDCP */
762 ret = anx7625_write_and(bus, RX_P1_ADDR, 0xee, 0x9f);
763
764 /* try auth flag */
765 ret |= anx7625_write_or(bus, RX_P1_ADDR, 0xec, 0x10);
766 /* interrupt for DRM */
767 ret |= anx7625_write_or(bus, RX_P1_ADDR, 0xff, 0x01);
768 if (ret < 0)
769 return;
770
771 ret = anx7625_reg_read(bus, RX_P1_ADDR, 0x86, &val);
772 if (ret < 0)
773 return;
774
775 ANXINFO("Secure OCM version=%02x\n", val);
776}
777
778static int anx7625_hpd_change_detect(uint8_t bus)
779{
780 int ret;
781 uint8_t status;
782
783 ret = anx7625_reg_read(bus, RX_P0_ADDR, SYSTEM_STSTUS, &status);
784 if (ret < 0) {
785 ANXERROR("IO error: Failed to clear interrupt status.\n");
786 return ret;
787 }
788
789 if (status & HPD_STATUS) {
790 anx7625_start_dp_work(bus);
791 ANXINFO("HPD received 0x7e:0x45=%#x\n", status);
792 return 1;
793 }
794 return 0;
795}
796
797static void anx7625_parse_edid(const struct edid *edid,
798 struct display_timing *dt)
799{
800 dt->pixelclock = edid->mode.pixel_clock;
801
802 dt->hactive = edid->mode.ha;
803 dt->hsync_len = edid->mode.hspw;
804 dt->hback_porch = (edid->mode.hbl - edid->mode.hso -
805 edid->mode.hborder - edid->mode.hspw);
806 dt->hfront_porch = edid->mode.hso - edid->mode.hborder;
807
808 dt->vactive = edid->mode.va;
809 dt->vsync_len = edid->mode.vspw;
810 dt->vfront_porch = edid->mode.vso - edid->mode.vborder;
811 dt->vback_porch = (edid->mode.vbl - edid->mode.vso -
812 edid->mode.vspw - edid->mode.vborder);
813
Xin Ji48ae1112021-09-03 11:11:44 +0800814 /*
815 * The k_val is a ratio to match MIPI input and DP output video clocks.
816 * Most panels can follow the default value (0x3d).
817 * IVO panels have smaller variation than DP CTS spec and need smaller
818 * k_val (0x3b).
819 */
820 if (!strncmp(edid->manufacturer_name, "IVO", 3)) {
821 dt->k_val = 0x3b;
822 ANXINFO("detected IVO panel, use k value 0x3b\n");
823 } else {
824 dt->k_val = 0x3d;
825 ANXINFO("set default k value to 0x3d for panel\n");
826 }
827
Jitao Shi542919f2019-09-26 10:22:08 +0800828 ANXINFO("pixelclock(%d).\n"
829 " hactive(%d), hsync(%d), hfp(%d), hbp(%d)\n"
830 " vactive(%d), vsync(%d), vfp(%d), vbp(%d)\n",
831 dt->pixelclock,
832 dt->hactive, dt->hsync_len, dt->hfront_porch, dt->hback_porch,
833 dt->vactive, dt->vsync_len, dt->vfront_porch, dt->vback_porch);
834}
835
836int anx7625_dp_start(uint8_t bus, const struct edid *edid)
837{
838 int ret;
839 struct display_timing dt;
840
841 anx7625_parse_edid(edid, &dt);
842
843 ret = anx7625_dsi_config(bus, &dt);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800844 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800845 ANXERROR("MIPI phy setup error.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800846 return ret;
847 }
Jitao Shi542919f2019-09-26 10:22:08 +0800848
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800849 ANXINFO("MIPI phy setup OK.\n");
850 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800851}
852
853int anx7625_dp_get_edid(uint8_t bus, struct edid *out)
854{
855 int block_num;
856 int ret;
857 u8 edid[FOUR_BLOCK_SIZE];
858
859 block_num = sp_tx_edid_read(bus, edid, FOUR_BLOCK_SIZE);
860 if (block_num < 0) {
861 ANXERROR("Failed to get eDP EDID.\n");
862 return -1;
863 }
864
865 ret = decode_edid(edid, (block_num + 1) * ONE_BLOCK_SIZE, out);
866 if (ret != EDID_CONFORMANT) {
867 ANXERROR("Failed to decode EDID.\n");
868 return -1;
869 }
870
871 return 0;
872}
873
874int anx7625_init(uint8_t bus)
875{
876 int retry_hpd_change = 50;
877 int retry_power_on = 3;
878
879 while (--retry_power_on) {
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800880 if (anx7625_power_on_init(bus) >= 0)
Jitao Shi542919f2019-09-26 10:22:08 +0800881 break;
882 }
883 if (!retry_power_on) {
884 ANXERROR("Failed to power on.\n");
885 return -1;
886 }
887
888 while (--retry_hpd_change) {
889 mdelay(10);
890 int detected = anx7625_hpd_change_detect(bus);
891 if (detected < 0)
892 return -1;
893 if (detected > 0)
894 return 0;
895 }
896
897 ANXERROR("Timed out to detect HPD change on bus %d.\n", bus);
898 return -1;
899}