blob: 8726ac025482ebd2f86c452575b58c51a8e01c68 [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>
Yidi Lin2751d292023-10-31 17:15:50 +08004#include <commonlib/bsd/gcd.h>
Jitao Shi542919f2019-09-26 10:22:08 +08005#include <delay.h>
6#include <device/i2c_simple.h>
7#include <edid.h>
8#include <gpio.h>
Jitao Shi542919f2019-09-26 10:22:08 +08009#include <string.h>
Yu-Ping Wu734a7772021-11-25 16:28:13 +080010#include <types.h>
Jitao Shi542919f2019-09-26 10:22:08 +080011
12#include "anx7625.h"
13
14#define ANXERROR(format, ...) \
Julius Wernere9665952022-01-21 17:06:20 -080015 printk(BIOS_ERR, "%s: " format, __func__, ##__VA_ARGS__)
Jitao Shi542919f2019-09-26 10:22:08 +080016#define ANXINFO(format, ...) \
17 printk(BIOS_INFO, "%s: " format, __func__, ##__VA_ARGS__)
18#define ANXDEBUG(format, ...) \
19 printk(BIOS_DEBUG, "%s: " format, __func__, ##__VA_ARGS__)
20
21/*
22 * There is a sync issue while accessing I2C register between AP(CPU) and
23 * internal firmware(OCM). To avoid the race condition, AP should access the
24 * reserved slave address before slave address changes.
25 */
26static int i2c_access_workaround(uint8_t bus, uint8_t saddr)
27{
28 uint8_t offset;
29 static uint8_t saddr_backup = 0;
30 int ret = 0;
31
32 if (saddr == saddr_backup)
33 return ret;
34
35 saddr_backup = saddr;
36
37 switch (saddr) {
38 case TCPC_INTERFACE_ADDR:
39 offset = RSVD_00_ADDR;
40 break;
41 case TX_P0_ADDR:
42 offset = RSVD_D1_ADDR;
43 break;
44 case TX_P1_ADDR:
45 offset = RSVD_60_ADDR;
46 break;
47 case RX_P0_ADDR:
48 offset = RSVD_39_ADDR;
49 break;
50 case RX_P1_ADDR:
51 offset = RSVD_7F_ADDR;
52 break;
53 default:
54 offset = RSVD_00_ADDR;
55 break;
56 }
57
58 ret = i2c_writeb(bus, saddr, offset, 0x00);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080059 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +080060 ANXERROR("Failed to access %#x:%#x\n", saddr, offset);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080061 return ret;
62 }
63 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +080064}
65
66static int anx7625_reg_read(uint8_t bus, uint8_t saddr, uint8_t offset,
67 uint8_t *val)
68{
69 int ret;
70
71 i2c_access_workaround(bus, saddr);
72 ret = i2c_readb(bus, saddr, offset, val);
73 if (ret < 0) {
74 ANXERROR("Failed to read i2c reg=%#x:%#x\n", saddr, offset);
75 return ret;
76 }
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080077 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +080078}
79
80static int anx7625_reg_block_read(uint8_t bus, uint8_t saddr, uint8_t reg_addr,
81 uint8_t len, uint8_t *buf)
82{
83 int ret;
84
85 i2c_access_workaround(bus, saddr);
86 ret = i2c_read_bytes(bus, saddr, reg_addr, buf, len);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080087 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +080088 ANXERROR("Failed to read i2c block=%#x:%#x[len=%#x]\n", saddr,
89 reg_addr, len);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +080090 return ret;
91 }
92 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +080093}
94
95static int anx7625_reg_write(uint8_t bus, uint8_t saddr, uint8_t reg_addr,
96 uint8_t reg_val)
97{
98 int ret;
99
100 i2c_access_workaround(bus, saddr);
101 ret = i2c_writeb(bus, saddr, reg_addr, reg_val);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800102 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800103 ANXERROR("Failed to write i2c id=%#x:%#x\n", saddr, reg_addr);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800104 return ret;
105 }
106 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800107}
108
109static int anx7625_write_or(uint8_t bus, uint8_t saddr, uint8_t offset,
110 uint8_t mask)
111{
112 uint8_t val;
113 int ret;
114
115 ret = anx7625_reg_read(bus, saddr, offset, &val);
116 if (ret < 0)
117 return ret;
118
119 return anx7625_reg_write(bus, saddr, offset, val | mask);
120}
121
122static int anx7625_write_and(uint8_t bus, uint8_t saddr, uint8_t offset,
123 uint8_t mask)
124{
125 int ret;
126 uint8_t val;
127
128 ret = anx7625_reg_read(bus, saddr, offset, &val);
129 if (ret < 0)
130 return ret;
131
132 return anx7625_reg_write(bus, saddr, offset, val & mask);
133}
134
135static int wait_aux_op_finish(uint8_t bus)
136{
137 uint8_t val;
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800138 int ret;
Jitao Shi542919f2019-09-26 10:22:08 +0800139
Yu-Ping Wu734a7772021-11-25 16:28:13 +0800140 if (!retry(150,
141 (anx7625_reg_read(bus, RX_P0_ADDR, AP_AUX_CTRL_STATUS, &val),
142 !(val & AP_AUX_CTRL_OP_EN)), mdelay(2))) {
Jitao Shi542919f2019-09-26 10:22:08 +0800143 ANXERROR("Timed out waiting aux operation.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800144 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800145 }
146
147 ret = anx7625_reg_read(bus, RX_P0_ADDR, AP_AUX_CTRL_STATUS, &val);
148 if (ret < 0 || val & 0x0F) {
149 ANXDEBUG("aux status %02x\n", val);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800150 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800151 }
152
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800153 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800154}
155
Jitao Shi542919f2019-09-26 10:22:08 +0800156/* Reduce fraction a/b */
Yidi Lin2751d292023-10-31 17:15:50 +0800157static void anx7625_reduction_of_a_fraction(u32 *_a, u32 *_b)
Jitao Shi542919f2019-09-26 10:22:08 +0800158{
Yidi Lin2751d292023-10-31 17:15:50 +0800159 u32 gcd_num;
160 u32 a = *_a, b = *_b, old_a, old_b;
Jitao Shi542919f2019-09-26 10:22:08 +0800161 u32 denom = 1;
162
Yidi Lin2751d292023-10-31 17:15:50 +0800163 gcd_num = gcd32(a, b);
Jitao Shi542919f2019-09-26 10:22:08 +0800164 a /= gcd_num;
165 b /= gcd_num;
166
167 old_a = a;
168 old_b = b;
169
170 while (a > MAX_UNSIGNED_24BIT || b > MAX_UNSIGNED_24BIT) {
171 denom++;
172 a = old_a / denom;
173 b = old_b / denom;
174 }
175
176 /* Increase a, b to have higher ODFC PLL output frequency accuracy. */
177 while ((a << 1) < MAX_UNSIGNED_24BIT && (b << 1) < MAX_UNSIGNED_24BIT) {
178 a <<= 1;
179 b <<= 1;
180 }
181
182 *_a = a;
183 *_b = b;
184}
185
Yidi Lin2751d292023-10-31 17:15:50 +0800186static int anx7625_calculate_m_n(u32 pixelclock, u32 *m, u32 *n, uint8_t *pd)
Jitao Shi542919f2019-09-26 10:22:08 +0800187{
188 uint8_t post_divider = *pd;
189 if (pixelclock > PLL_OUT_FREQ_ABS_MAX / POST_DIVIDER_MIN) {
190 /* pixel clock frequency is too high */
191 ANXERROR("pixelclock %u higher than %lu, "
192 "output may be unstable\n",
193 pixelclock, PLL_OUT_FREQ_ABS_MAX / POST_DIVIDER_MIN);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800194 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800195 }
196
197 if (pixelclock < PLL_OUT_FREQ_ABS_MIN / POST_DIVIDER_MAX) {
198 /* pixel clock frequency is too low */
199 ANXERROR("pixelclock %u lower than %lu, "
200 "output may be unstable\n",
201 pixelclock, PLL_OUT_FREQ_ABS_MIN / POST_DIVIDER_MAX);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800202 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800203 }
204
205 post_divider = 1;
206
207 for (post_divider = 1;
208 pixelclock < PLL_OUT_FREQ_MIN / post_divider;
209 post_divider++)
210 ;
211
212 if (post_divider > POST_DIVIDER_MAX) {
213 for (post_divider = 1;
214 pixelclock < PLL_OUT_FREQ_ABS_MIN / post_divider;
215 post_divider++)
216 ;
217
218 if (post_divider > POST_DIVIDER_MAX) {
219 ANXERROR("cannot find property post_divider(%d)\n",
220 post_divider);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800221 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800222 }
223 }
224
225 /* Patch to improve the accuracy */
226 if (post_divider == 7) {
227 /* 27,000,000 is not divisible by 7 */
228 post_divider = 8;
229 } else if (post_divider == 11) {
230 /* 27,000,000 is not divisible by 11 */
231 post_divider = 12;
232 } else if (post_divider == 13 || post_divider == 14) {
233 /*27,000,000 is not divisible by 13 or 14*/
234 post_divider = 15;
235 }
236
237 if (pixelclock * post_divider > PLL_OUT_FREQ_ABS_MAX) {
238 ANXINFO("act clock(%u) large than maximum(%lu)\n",
239 pixelclock * post_divider, PLL_OUT_FREQ_ABS_MAX);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800240 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800241 }
242
Jitao Shif68cc812020-01-14 21:23:44 +0800243 *m = pixelclock;
Jitao Shi542919f2019-09-26 10:22:08 +0800244 *n = XTAL_FRQ / post_divider;
245 *pd = post_divider;
246
247 anx7625_reduction_of_a_fraction(m, n);
248
249 return 0;
250}
251
252static int anx7625_odfc_config(uint8_t bus, uint8_t post_divider)
253{
254 int ret;
255
256 /* config input reference clock frequency 27MHz/19.2MHz */
257 ret = anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_16,
258 ~(REF_CLK_27000kHz << MIPI_FREF_D_IND));
259 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_16,
260 (REF_CLK_27000kHz << MIPI_FREF_D_IND));
261 /* post divider */
262 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_8, 0x0f);
263 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_8,
264 post_divider << 4);
265
266 /* add patch for MIS2-125 (5pcs ANX7625 fail ATE MBIST test) */
267 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_7,
268 ~MIPI_PLL_VCO_TUNE_REG_VAL);
269
270 /* reset ODFC PLL */
271 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_7,
272 ~MIPI_PLL_RESET_N);
273 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_7,
274 MIPI_PLL_RESET_N);
275
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800276 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800277 ANXERROR("IO error.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800278 return ret;
279 }
Jitao Shi542919f2019-09-26 10:22:08 +0800280
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800281 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800282}
283
284static int anx7625_dsi_video_config(uint8_t bus, struct display_timing *dt)
285{
Yidi Lin2751d292023-10-31 17:15:50 +0800286 u32 m, n;
Jitao Shi542919f2019-09-26 10:22:08 +0800287 u16 htotal;
288 int ret;
289 uint8_t post_divider = 0;
290
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800291 if (anx7625_calculate_m_n(dt->pixelclock * 1000, &m, &n,
292 &post_divider) < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800293 ANXERROR("cannot get property m n value.\n");
294 return -1;
295 }
296
Yidi Lin2751d292023-10-31 17:15:50 +0800297 ANXINFO("compute M(%u), N(%u), divider(%d).\n", m, n, post_divider);
Jitao Shi542919f2019-09-26 10:22:08 +0800298
299 /* configure pixel clock */
300 ret = anx7625_reg_write(bus, RX_P0_ADDR, PIXEL_CLOCK_L,
301 (dt->pixelclock / 1000) & 0xFF);
302 ret |= anx7625_reg_write(bus, RX_P0_ADDR, PIXEL_CLOCK_H,
303 (dt->pixelclock / 1000) >> 8);
304 /* lane count */
305 ret |= anx7625_write_and(bus, RX_P1_ADDR, MIPI_LANE_CTRL_0, 0xfc);
306
307 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_LANE_CTRL_0, 3);
308
309 /* Htotal */
310 htotal = dt->hactive + dt->hfront_porch +
311 dt->hback_porch + dt->hsync_len;
312 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
313 HORIZONTAL_TOTAL_PIXELS_L, htotal & 0xFF);
314 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
315 HORIZONTAL_TOTAL_PIXELS_H, htotal >> 8);
316 /* Hactive */
317 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
318 HORIZONTAL_ACTIVE_PIXELS_L, dt->hactive & 0xFF);
319 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
320 HORIZONTAL_ACTIVE_PIXELS_H, dt->hactive >> 8);
321 /* HFP */
322 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
323 HORIZONTAL_FRONT_PORCH_L, dt->hfront_porch);
324 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
325 HORIZONTAL_FRONT_PORCH_H,
326 dt->hfront_porch >> 8);
327 /* HWS */
328 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
329 HORIZONTAL_SYNC_WIDTH_L, dt->hsync_len);
330 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
331 HORIZONTAL_SYNC_WIDTH_H, dt->hsync_len >> 8);
332 /* HBP */
333 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
334 HORIZONTAL_BACK_PORCH_L, dt->hback_porch);
335 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
336 HORIZONTAL_BACK_PORCH_H, dt->hback_porch >> 8);
337 /* Vactive */
338 ret |= anx7625_reg_write(bus, RX_P2_ADDR, ACTIVE_LINES_L, dt->vactive);
339 ret |= anx7625_reg_write(bus, RX_P2_ADDR, ACTIVE_LINES_H,
340 dt->vactive >> 8);
341 /* VFP */
342 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
343 VERTICAL_FRONT_PORCH, dt->vfront_porch);
344 /* VWS */
345 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
346 VERTICAL_SYNC_WIDTH, dt->vsync_len);
347 /* VBP */
348 ret |= anx7625_reg_write(bus, RX_P2_ADDR,
349 VERTICAL_BACK_PORCH, dt->vback_porch);
350 /* M value */
351 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
352 MIPI_PLL_M_NUM_23_16, (m >> 16) & 0xff);
353 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
354 MIPI_PLL_M_NUM_15_8, (m >> 8) & 0xff);
355 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
356 MIPI_PLL_M_NUM_7_0, (m & 0xff));
357 /* N value */
358 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
359 MIPI_PLL_N_NUM_23_16, (n >> 16) & 0xff);
360 ret |= anx7625_reg_write(bus, RX_P1_ADDR,
361 MIPI_PLL_N_NUM_15_8, (n >> 8) & 0xff);
362 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_PLL_N_NUM_7_0,
363 (n & 0xff));
364 /* diff */
Xin Ji48ae1112021-09-03 11:11:44 +0800365 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_DIGITAL_ADJ_1, dt->k_val);
Jitao Shi542919f2019-09-26 10:22:08 +0800366
367 ret |= anx7625_odfc_config(bus, post_divider - 1);
368
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800369 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800370 ANXERROR("mipi dsi setup IO error.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800371 return ret;
372 }
Jitao Shi542919f2019-09-26 10:22:08 +0800373
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800374 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800375}
376
377static int anx7625_swap_dsi_lane3(uint8_t bus)
378{
379 int ret;
380 uint8_t val;
381
382 /* swap MIPI-DSI data lane 3 P and N */
383 ret = anx7625_reg_read(bus, RX_P1_ADDR, MIPI_SWAP, &val);
384 if (ret < 0) {
385 ANXERROR("IO error: access MIPI_SWAP.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800386 return ret;
Jitao Shi542919f2019-09-26 10:22:08 +0800387 }
388
389 val |= (1 << MIPI_SWAP_CH3);
390 return anx7625_reg_write(bus, RX_P1_ADDR, MIPI_SWAP, val);
391}
392
393static int anx7625_api_dsi_config(uint8_t bus, struct display_timing *dt)
394
395{
396 int val, ret;
397
398 /* swap MIPI-DSI data lane 3 P and N */
399 ret = anx7625_swap_dsi_lane3(bus);
400 if (ret < 0) {
401 ANXERROR("IO error: swap dsi lane 3 failed.\n");
402 return ret;
403 }
404
405 /* DSI clock settings */
406 val = (0 << MIPI_HS_PWD_CLK) |
407 (0 << MIPI_HS_RT_CLK) |
408 (0 << MIPI_PD_CLK) |
409 (1 << MIPI_CLK_RT_MANUAL_PD_EN) |
410 (1 << MIPI_CLK_HS_MANUAL_PD_EN) |
411 (0 << MIPI_CLK_DET_DET_BYPASS) |
412 (0 << MIPI_CLK_MISS_CTRL) |
413 (0 << MIPI_PD_LPTX_CH_MANUAL_PD_EN);
414 ret = anx7625_reg_write(bus, RX_P1_ADDR, MIPI_PHY_CONTROL_3, val);
415
416 /*
417 * Decreased HS prepare tg delay from 160ns to 80ns work with
418 * a) Dragon board 810 series (Qualcomm AP)
419 * b) Moving Pixel DSI source (PG3A pattern generator +
420 * P332 D-PHY Probe) default D-PHY tg 5ns/step
421 */
422 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_TIME_HS_PRPR, 0x10);
423
424 /* enable DSI mode */
425 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_18,
426 SELECT_DSI << MIPI_DPI_SELECT);
427
428 ret |= anx7625_dsi_video_config(bus, dt);
429 if (ret < 0) {
430 ANXERROR("dsi video tg config failed\n");
431 return ret;
432 }
433
434 /* toggle m, n ready */
435 ret = anx7625_write_and(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_6,
436 ~(MIPI_M_NUM_READY | MIPI_N_NUM_READY));
437 mdelay(1);
438 ret |= anx7625_write_or(bus, RX_P1_ADDR, MIPI_DIGITAL_PLL_6,
439 MIPI_M_NUM_READY | MIPI_N_NUM_READY);
440
441 /* configure integer stable register */
442 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_VIDEO_STABLE_CNT, 0x02);
443 /* power on MIPI RX */
444 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_LANE_CTRL_10, 0x00);
445 ret |= anx7625_reg_write(bus, RX_P1_ADDR, MIPI_LANE_CTRL_10, 0x80);
446
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800447 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800448 ANXERROR("IO error: mipi dsi enable init failed.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800449 return ret;
450 }
Jitao Shi542919f2019-09-26 10:22:08 +0800451
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800452 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800453}
454
455static int anx7625_dsi_config(uint8_t bus, struct display_timing *dt)
456{
457 int ret;
458
459 ANXINFO("config dsi.\n");
460
461 /* DSC disable */
462 ret = anx7625_write_and(bus, RX_P0_ADDR, R_DSC_CTRL_0, ~DSC_EN);
463 ret |= anx7625_api_dsi_config(bus, dt);
464
465 if (ret < 0) {
466 ANXERROR("IO error: api dsi config error.\n");
467 return ret;
468 }
469
470 /* set MIPI RX EN */
471 ret = anx7625_write_or(bus, RX_P0_ADDR, AP_AV_STATUS, AP_MIPI_RX_EN);
472 /* clear mute flag */
473 ret |= anx7625_write_and(bus, RX_P0_ADDR, AP_AV_STATUS, ~AP_MIPI_MUTE);
474
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800475 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800476 ANXERROR("IO error: enable mipi rx failed.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800477 return ret;
478 }
Jitao Shi542919f2019-09-26 10:22:08 +0800479
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800480 ANXINFO("success to config DSI\n");
481 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800482}
483
484static int sp_tx_rst_aux(uint8_t bus)
485{
486 int ret;
487
488 ret = anx7625_write_or(bus, TX_P2_ADDR, RST_CTRL2, AUX_RST);
489 ret |= anx7625_write_and(bus, TX_P2_ADDR, RST_CTRL2, ~AUX_RST);
490 return ret;
491}
492
493static int sp_tx_aux_wr(uint8_t bus, uint8_t offset)
494{
495 int ret;
496
497 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_BUFF_START, offset);
498 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_COMMAND, 0x04);
499 ret |= anx7625_write_or(bus, RX_P0_ADDR,
500 AP_AUX_CTRL_STATUS, AP_AUX_CTRL_OP_EN);
501 return ret | wait_aux_op_finish(bus);
502}
503
504static int sp_tx_aux_rd(uint8_t bus, uint8_t len_cmd)
505{
506 int ret;
507
508 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_COMMAND, len_cmd);
509 ret |= anx7625_write_or(bus, RX_P0_ADDR,
510 AP_AUX_CTRL_STATUS, AP_AUX_CTRL_OP_EN);
511 return ret | wait_aux_op_finish(bus);
512}
513
514static int sp_tx_get_edid_block(uint8_t bus)
515{
516 int ret;
517 uint8_t val = 0;
518
519 sp_tx_aux_wr(bus, 0x7e);
520 sp_tx_aux_rd(bus, 0x01);
521 ret = anx7625_reg_read(bus, RX_P0_ADDR, AP_AUX_BUFF_START, &val);
522
523 if (ret < 0) {
524 ANXERROR("IO error: access AUX BUFF.\n");
525 return -1;
526 }
527
528 ANXINFO("EDID Block = %d\n", val + 1);
529
530 if (val > 3)
531 val = 1;
532
533 return val;
534}
535
536static int edid_read(uint8_t bus, uint8_t offset, uint8_t *pblock_buf)
537{
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800538 int ret, cnt;
Jitao Shi542919f2019-09-26 10:22:08 +0800539
Jitao Shi542919f2019-09-26 10:22:08 +0800540 for (cnt = 0; cnt < 3; cnt++) {
541 sp_tx_aux_wr(bus, offset);
542 /* set I2C read com 0x01 mot = 0 and read 16 bytes */
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800543 ret = sp_tx_aux_rd(bus, 0xf1);
Jitao Shi542919f2019-09-26 10:22:08 +0800544
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800545 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800546 sp_tx_rst_aux(bus);
547 ANXERROR("edid read failed, reset!\n");
Jitao Shi542919f2019-09-26 10:22:08 +0800548 } else {
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800549 if (anx7625_reg_block_read(bus, RX_P0_ADDR,
550 AP_AUX_BUFF_START,
551 MAX_DPCD_BUFFER_SIZE,
552 pblock_buf) >= 0)
553 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800554 }
555 }
556
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800557 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800558}
559
560static int segments_edid_read(uint8_t bus, uint8_t segment, uint8_t *buf,
561 uint8_t offset)
562{
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800563 int ret, cnt;
Jitao Shi542919f2019-09-26 10:22:08 +0800564
565 /* write address only */
566 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_7_0, 0x30);
567 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_COMMAND, 0x04);
568 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_CTRL_STATUS,
569 AP_AUX_CTRL_ADDRONLY | AP_AUX_CTRL_OP_EN);
570
571 ret |= wait_aux_op_finish(bus);
572 /* write segment address */
573 ret |= sp_tx_aux_wr(bus, segment);
574 /* data read */
575 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_7_0, 0x50);
576
577 if (ret < 0) {
578 ANXERROR("IO error: aux initial failed.\n");
579 return ret;
580 }
581
582 for (cnt = 0; cnt < 3; cnt++) {
583 sp_tx_aux_wr(bus, offset);
584 /* set I2C read com 0x01 mot = 0 and read 16 bytes */
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800585 ret = sp_tx_aux_rd(bus, 0xf1);
Jitao Shi542919f2019-09-26 10:22:08 +0800586
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800587 if (ret < 0) {
588 sp_tx_rst_aux(bus);
Jitao Shi542919f2019-09-26 10:22:08 +0800589 ANXERROR("segment read failed, reset!\n");
Jitao Shi542919f2019-09-26 10:22:08 +0800590 } else {
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800591 if (anx7625_reg_block_read(bus, RX_P0_ADDR,
592 AP_AUX_BUFF_START,
593 MAX_DPCD_BUFFER_SIZE,
594 buf) >= 0)
595 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800596 }
597 }
598
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800599 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800600}
601
602static int sp_tx_edid_read(uint8_t bus, uint8_t *pedid_blocks_buf,
603 uint32_t size)
604{
605 uint8_t offset, edid_pos;
606 int count, blocks_num;
607 uint8_t pblock_buf[MAX_DPCD_BUFFER_SIZE];
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800608 int i, ret, g_edid_break = 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800609
610 /* address initial */
611 ret = anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_7_0, 0x50);
612 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AUX_ADDR_15_8, 0);
613 ret |= anx7625_write_and(bus, RX_P0_ADDR, AP_AUX_ADDR_19_16, 0xf0);
614
615 if (ret < 0) {
616 ANXERROR("access aux channel IO error.\n");
617 return -1;
618 }
619
620 blocks_num = sp_tx_get_edid_block(bus);
621 if (blocks_num < 0)
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800622 return -1;
Jitao Shi542919f2019-09-26 10:22:08 +0800623
624 count = 0;
625
626 do {
627 switch (count) {
628 case 0:
629 case 1:
630 for (i = 0; i < 8; i++) {
631 offset = (i + count * 8) * MAX_DPCD_BUFFER_SIZE;
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800632 g_edid_break = !!edid_read(bus, offset,
633 pblock_buf);
Jitao Shi542919f2019-09-26 10:22:08 +0800634
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800635 if (g_edid_break)
Jitao Shi542919f2019-09-26 10:22:08 +0800636 break;
637
638 if (offset <= size - MAX_DPCD_BUFFER_SIZE)
639 memcpy(&pedid_blocks_buf[offset],
640 pblock_buf,
641 MAX_DPCD_BUFFER_SIZE);
642 }
643
644 break;
645 case 2:
646 case 3:
647 offset = (count == 2) ? 0x00 : 0x80;
648
649 for (i = 0; i < 8; i++) {
650 edid_pos = (i + count * 8) *
651 MAX_DPCD_BUFFER_SIZE;
652
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800653 if (g_edid_break)
Jitao Shi542919f2019-09-26 10:22:08 +0800654 break;
655
656 segments_edid_read(bus, count / 2,
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800657 pblock_buf, offset);
Jitao Shi542919f2019-09-26 10:22:08 +0800658 if (edid_pos <= size - MAX_DPCD_BUFFER_SIZE)
659 memcpy(&pedid_blocks_buf[edid_pos],
660 pblock_buf,
661 MAX_DPCD_BUFFER_SIZE);
662 offset = offset + 0x10;
663 }
664
665 break;
666 default:
667 die("%s: count should be <= 3", __func__);
668 break;
669 }
670
671 count++;
672
673 } while (blocks_num >= count);
674
675 /* reset aux channel */
676 sp_tx_rst_aux(bus);
677
678 return blocks_num;
679}
680
681static void anx7625_disable_pd_protocol(uint8_t bus)
682{
683 int ret;
684
685 /* reset main ocm */
686 ret = anx7625_reg_write(bus, RX_P0_ADDR, 0x88, 0x40);
687 /* Disable PD */
688 ret |= anx7625_reg_write(bus, RX_P0_ADDR, AP_AV_STATUS, AP_DISABLE_PD);
689 /* release main ocm */
690 ret |= anx7625_reg_write(bus, RX_P0_ADDR, 0x88, 0x00);
691
692 if (ret < 0)
693 ANXERROR("Failed to disable PD feature.\n");
694 else
695 ANXINFO("Disabled PD feature.\n");
696}
697
698#define FLASH_LOAD_STA 0x05
699#define FLASH_LOAD_STA_CHK (1 << 7)
700
701static int anx7625_power_on_init(uint8_t bus)
702{
703 int i, ret;
704 uint8_t val, version, revision;
705
706 anx7625_reg_write(bus, RX_P0_ADDR, XTAL_FRQ_SEL, XTAL_FRQ_27M);
707
708 for (i = 0; i < OCM_LOADING_TIME; i++) {
709 /* check interface */
710 ret = anx7625_reg_read(bus, RX_P0_ADDR, FLASH_LOAD_STA, &val);
711 if (ret < 0) {
712 ANXERROR("Failed to load flash\n");
713 return ret;
714 }
715
716 if ((val & FLASH_LOAD_STA_CHK) != FLASH_LOAD_STA_CHK) {
717 mdelay(1);
718 continue;
719 }
720 ANXINFO("Init interface.\n");
721
Jitao Shi542919f2019-09-26 10:22:08 +0800722 anx7625_disable_pd_protocol(bus);
723 anx7625_reg_read(bus, RX_P0_ADDR, OCM_FW_VERSION, &version);
724 anx7625_reg_read(bus, RX_P0_ADDR, OCM_FW_REVERSION, &revision);
725 ANXINFO("Firmware: ver %#02x, rev %#02x.\n", version, revision);
726 return 0;
727 }
728 return -1;
729}
730
731static void anx7625_start_dp_work(uint8_t bus)
732{
733 int ret;
734 uint8_t val;
735
736 /* not support HDCP */
737 ret = anx7625_write_and(bus, RX_P1_ADDR, 0xee, 0x9f);
738
739 /* try auth flag */
740 ret |= anx7625_write_or(bus, RX_P1_ADDR, 0xec, 0x10);
741 /* interrupt for DRM */
742 ret |= anx7625_write_or(bus, RX_P1_ADDR, 0xff, 0x01);
743 if (ret < 0)
744 return;
745
746 ret = anx7625_reg_read(bus, RX_P1_ADDR, 0x86, &val);
747 if (ret < 0)
748 return;
749
750 ANXINFO("Secure OCM version=%02x\n", val);
751}
752
753static int anx7625_hpd_change_detect(uint8_t bus)
754{
755 int ret;
756 uint8_t status;
757
758 ret = anx7625_reg_read(bus, RX_P0_ADDR, SYSTEM_STSTUS, &status);
759 if (ret < 0) {
760 ANXERROR("IO error: Failed to clear interrupt status.\n");
761 return ret;
762 }
763
764 if (status & HPD_STATUS) {
765 anx7625_start_dp_work(bus);
766 ANXINFO("HPD received 0x7e:0x45=%#x\n", status);
767 return 1;
768 }
769 return 0;
770}
771
772static void anx7625_parse_edid(const struct edid *edid,
773 struct display_timing *dt)
774{
775 dt->pixelclock = edid->mode.pixel_clock;
776
777 dt->hactive = edid->mode.ha;
778 dt->hsync_len = edid->mode.hspw;
779 dt->hback_porch = (edid->mode.hbl - edid->mode.hso -
780 edid->mode.hborder - edid->mode.hspw);
781 dt->hfront_porch = edid->mode.hso - edid->mode.hborder;
782
783 dt->vactive = edid->mode.va;
784 dt->vsync_len = edid->mode.vspw;
785 dt->vfront_porch = edid->mode.vso - edid->mode.vborder;
786 dt->vback_porch = (edid->mode.vbl - edid->mode.vso -
787 edid->mode.vspw - edid->mode.vborder);
788
Xin Ji48ae1112021-09-03 11:11:44 +0800789 /*
790 * The k_val is a ratio to match MIPI input and DP output video clocks.
791 * Most panels can follow the default value (0x3d).
792 * IVO panels have smaller variation than DP CTS spec and need smaller
793 * k_val (0x3b).
794 */
795 if (!strncmp(edid->manufacturer_name, "IVO", 3)) {
796 dt->k_val = 0x3b;
797 ANXINFO("detected IVO panel, use k value 0x3b\n");
798 } else {
799 dt->k_val = 0x3d;
800 ANXINFO("set default k value to 0x3d for panel\n");
801 }
802
Jitao Shi542919f2019-09-26 10:22:08 +0800803 ANXINFO("pixelclock(%d).\n"
804 " hactive(%d), hsync(%d), hfp(%d), hbp(%d)\n"
805 " vactive(%d), vsync(%d), vfp(%d), vbp(%d)\n",
806 dt->pixelclock,
807 dt->hactive, dt->hsync_len, dt->hfront_porch, dt->hback_porch,
808 dt->vactive, dt->vsync_len, dt->vfront_porch, dt->vback_porch);
809}
810
811int anx7625_dp_start(uint8_t bus, const struct edid *edid)
812{
813 int ret;
814 struct display_timing dt;
815
816 anx7625_parse_edid(edid, &dt);
817
818 ret = anx7625_dsi_config(bus, &dt);
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800819 if (ret < 0) {
Jitao Shi542919f2019-09-26 10:22:08 +0800820 ANXERROR("MIPI phy setup error.\n");
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800821 return ret;
822 }
Jitao Shi542919f2019-09-26 10:22:08 +0800823
Yu-Ping Wu0c9b1de2021-11-25 14:31:21 +0800824 ANXINFO("MIPI phy setup OK.\n");
825 return 0;
Jitao Shi542919f2019-09-26 10:22:08 +0800826}
827
828int anx7625_dp_get_edid(uint8_t bus, struct edid *out)
829{
830 int block_num;
831 int ret;
832 u8 edid[FOUR_BLOCK_SIZE];
833
834 block_num = sp_tx_edid_read(bus, edid, FOUR_BLOCK_SIZE);
835 if (block_num < 0) {
836 ANXERROR("Failed to get eDP EDID.\n");
837 return -1;
838 }
839
840 ret = decode_edid(edid, (block_num + 1) * ONE_BLOCK_SIZE, out);
841 if (ret != EDID_CONFORMANT) {
842 ANXERROR("Failed to decode EDID.\n");
843 return -1;
844 }
845
846 return 0;
847}
848
849int anx7625_init(uint8_t bus)
850{
851 int retry_hpd_change = 50;
Jitao Shi542919f2019-09-26 10:22:08 +0800852
Yu-Ping Wu734a7772021-11-25 16:28:13 +0800853 if (!retry(3, anx7625_power_on_init(bus) >= 0)) {
Jitao Shi542919f2019-09-26 10:22:08 +0800854 ANXERROR("Failed to power on.\n");
855 return -1;
856 }
857
858 while (--retry_hpd_change) {
859 mdelay(10);
860 int detected = anx7625_hpd_change_detect(bus);
861 if (detected < 0)
862 return -1;
863 if (detected > 0)
864 return 0;
865 }
866
867 ANXERROR("Timed out to detect HPD change on bus %d.\n", bus);
868 return -1;
869}