blob: be9c03ac66241f73e53b3fa2188d41ca808d1682 [file] [log] [blame]
nealiliade1eb002025-06-09 16:50:16 +09001/*
2 * LT8619C HDMI to LVDS converter + TFT-LCD 080-QC4011
3 *
4 * Copyright (C) 2020 Hardkernel Co., Ltd.
5 *
6 * Author : Joy Cho <joy.cho@hardkernel.com>
7 *
8 * The base initial code of this driver is from Lotium semiconductor
9 * and output signal of LT8619C is fixed as following.
10 * - resolution 1024x768
11 * - 1 port LVDS
12 * - 8bit, DE timing mode
13 *
14 * FIXME for copyright notification and driver license level
15 */
16
17#include <linux/delay.h>
18#include <linux/gpio.h>
19#include <linux/i2c.h>
20#include <linux/regmap.h>
21#include <linux/module.h>
22
23#include <drm/drm_panel.h>
24
nealiliade1eb002025-06-09 16:50:16 +090025#include <drm/drm_atomic_helper.h>
26#include <drm/drm_crtc_helper.h>
27#include <drm/drm_bridge.h>
nealiliade1eb002025-06-09 16:50:16 +090028
29#include "lt8619c.h"
30
31static struct _LT8619C_RXStatus RXStat, *pRXStat;
32
33static uint16_t h_active, v_active;
34static uint16_t h_syncwidth, v_syncwidth;
35static uint16_t h_bkporch, v_bkporch;
36static uint16_t h_total, v_total;
37static uint8_t h_syncpol, v_syncpol;
38static uint32_t frame_counter;
39
40/* onchip EDID */
41uint8_t onchip_edid[256] = {
42 /* 1024*768 */
43 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
44 0x21, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45 0x08, 0x20, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
46 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
47 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
48 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
49 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
50 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
51 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
52 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
53 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
54 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
55 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
56 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
57 0x00, 0x48, 0x4b, 0x5f, 0x56, 0x55, 0x37, 0x43,
nealilia9ab775d2025-06-17 12:16:33 +090058 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x6a,
nealiliade1eb002025-06-09 16:50:16 +090059
60 0x02, 0x03, 0x12, 0xf1, 0x23, 0x09, 0x07, 0x07,
61 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00,
62 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6
76};
77
78/* lcd timing */
79struct video_timing lcd_timing = {
80 /* pix_clk/kHz, hfp, hs, hbp,hact,htotal,vfp, vs, vbp,vact, vtotal */
81 /* 1024x600p60 */
82 51200, 8, 144, 168, 1024, 1344, 3, 6, 26, 600, 635
83};
84
85struct lt8619c {
86 struct device *dev;
87 struct regmap *regmap;
88
89 struct drm_bridge bridge;
90 struct drm_connector connector;
91 struct drm_panel *panel;
92
93 struct i2c_client *client;
94 struct gpio_desc *gpiod_reset;
95};
96
97static struct lt8619c *lt8619c;
98
99static struct regmap_config lt8619c_regmap_config = {
100 .reg_bits = 8,
101 .val_bits = 8,
102 .name = "lt8619c",
103};
104
105#define LT8619C_ADDR 0x32 /* 0x64 >> 1 */
106
107static uint8_t lt8619c_reg_read(char reg)
108{
109 uint8_t out_buf[2];
110 uint8_t in_buf[2];
111 uint8_t val;
112
113 struct i2c_msg msgs[] = {
114 {
115 .addr = LT8619C_ADDR,
116 .flags = 0,
117 .len = 1,
118 .buf = out_buf,
119 },
120 {
121 .addr = LT8619C_ADDR,
122 .flags = I2C_M_RD,
123 .len = 1,
124 .buf = in_buf,
125 }
126 };
127
128 out_buf[0] = reg;
129 out_buf[1] = 0;
130
131 if (i2c_transfer(lt8619c->client->adapter, msgs, 2) == 2) {
132 val = in_buf[0];
133 } else {
134 dev_info(lt8619c->dev, "i2c 0x%02x 0x%02x read failed\n",
135 reg, val);
136 }
137
138 return val;
139}
140
141static int lt8619c_reg_write(uint8_t reg, uint8_t data)
142{
143 uint8_t outbuf[2];
144 struct i2c_msg msg = {
145 .addr = LT8619C_ADDR,
146 .flags = 0,
147 .len = 2,
148 .buf = outbuf,
149 };
150
151 outbuf[0] = reg;
152 outbuf[1] = data;
153
154 if (i2c_transfer(lt8619c->client->adapter, &msg, 1) != 1)
155 dev_info(lt8619c->dev, "i2c 0x%02x 0x%02x write failed\n",
156 reg, data);
157
158 return 0;
159}
160
161static int lt8619c_reg_write_bytes(uint8_t reg, uint8_t *data, uint16_t length)
162{
163 //- uint8_t outbuf[length + 1];
164 uint8_t outbuf[256 + 1];
165 struct i2c_msg msg = {
166 .addr = LT8619C_ADDR,
167 .flags = 0,
168 .len = length + 1,
169 .buf = outbuf,
170 };
171
172 outbuf[0] = reg;
173 memcpy(outbuf + 1, data, length);
174
175 if (i2c_transfer(lt8619c->client->adapter, &msg, 1) != 1)
176 dev_info(lt8619c->dev, "i2c write failed\n");
177
178 return 0;
179}
180
181static bool lt8619c_check_chipid(void)
182{
183 unsigned int chip_id[3];
184
185 lt8619c_reg_write(0xff, 0x60);
186
187 chip_id[0] = lt8619c_reg_read(0x00);
188 chip_id[1] = lt8619c_reg_read(0x01);
189 chip_id[2] = lt8619c_reg_read(0x02);
190 dev_dbg(lt8619c->dev, "Read Chip : 0x%x, 0x%x, 0x%x\n",
191 chip_id[0], chip_id[1], chip_id[2]);
192
193 if ((chip_id[0] == 0x16) && chip_id[1] == 0x04)
194 return true;
195 else
196 return false;
197}
198
199static void lt8619c_setHPD(uint8_t level)
200{
201 lt8619c_reg_write(0xFF, 0x80);
202
203 if (level)
204 lt8619c_reg_write(0x06, (lt8619c_reg_read(0x06)|0x08));
205 else
206 lt8619c_reg_write(0x06, (lt8619c_reg_read(0x06)&0xF7));
207}
208
209static void lt8619c_edid_calc(uint8_t *pbuf, struct video_timing *timing)
210{
211 uint16_t hblanking = 0;
212 uint16_t vblanking = 0;
213 uint16_t pixel_clk;
214
215 pixel_clk = timing->pixel_clk / 10;
216
217 if (pbuf == NULL || timing == NULL)
218 return;
219
220 hblanking = timing->hfp + timing->hs + timing->hbp; /* H blanking */
221 vblanking = timing->vfp + timing->vs + timing->vbp; /* V blanking */
222
223 pbuf[0] = pixel_clk % 256;
224 pbuf[1] = pixel_clk / 256;
225 pbuf[2] = timing->hact % 256;
226 pbuf[3] = hblanking % 256;
227 pbuf[4] = ((timing->hact / 256) << 4) + hblanking / 256;
228 pbuf[5] = timing->vact % 256;
229 pbuf[6] = vblanking % 256;
230 pbuf[7] = ((timing->vact / 256) << 4) + vblanking / 256;
231 pbuf[8] = timing->hfp % 256;
232 pbuf[9] = timing->hs % 256;
233 pbuf[10] = ((timing->vfp % 256) << 4) + timing->vs % 256;
234 pbuf[11] = ((timing->hfp/256) << 6) + ((timing->hs / 256) << 4)+
235 ((timing->vfp / 256) << 2) + (timing->vs / 256);
236 pbuf[17] = 0x1c; /* progress negative vsync, negative hsync */
237}
238
239static uint8_t lt8619c_edid_checksum(uint8_t block, uint8_t *buf)
240{
241 uint8_t i = 0;
242 uint8_t checksum = 0;
243 uint8_t *pbuf = buf + 128 * block;
244
245 //- if (pbuf == NULL || (pbuf + 127) == NULL)
246 //- return 0;
247
248 for (i = 0, checksum = 0; i < 127 ; i++) {
249 checksum += pbuf[i];
250 checksum %= 256;
251 }
252 checksum = 256 - checksum;
253
254 dev_info(lt8619c->dev, "EDID checksum 0x%x\n", checksum);
255
256 return checksum;
257}
258
259static void lt8619c_set_edid(uint8_t *edid_buf)
260{
261 int ret = 0;
262
263 lt8619c_reg_write(0xFF, 0x80);
264 lt8619c_reg_write(0x8E, 0x07);
265 lt8619c_reg_write(0x8F, 0x00);
266
267 if (edid_buf == NULL)
268 ret = lt8619c_reg_write_bytes(0x90, &onchip_edid[0], 256);
269 else
270 ret = lt8619c_reg_write_bytes(0x90, edid_buf, 256);
271
272 if (ret)
273 dev_info(lt8619c->dev, "EDID Set write error\n");
274
275 lt8619c_reg_write(0x8E, 0x02);
276}
277
278static void lt8619c_rx_init(void)
279{
280 lt8619c_reg_write(0xFF, 0x80);
281 /* RGD_CLK_STABLE_OPT[1:0] */
282 lt8619c_reg_write(0x2c, (lt8619c_reg_read(0x2C)|0x30));
283
284 lt8619c_reg_write(0xFF, 0x60);
285 lt8619c_reg_write(0x04, 0xF2);
286 lt8619c_reg_write(0x83, 0x3F);
287 lt8619c_reg_write(0x80, 0x08); /* use xtal_clk as sys_clk */
288#ifdef DDR_CLK
289 lt8619c_reg_write(0xa4, 0x14); /* 0x10:SDR clk,0x14: DDR clk */
290#else
291 lt8619c_reg_write(0xa4, 0x10); /* 0x10:SDR clk,0x14: DDR clk */
292#endif
293 dev_dbg(lt8619c->dev, "LT8619C output mode : %d\n", LT8619C_OUTPUTMODE);
294
295 dev_info(lt8619c->dev, "\nLT8619C set to OUTPUT_RGB888");
296 lt8619c_reg_write(0xFF, 0x60);
297 lt8619c_reg_write(0x07, 0xff);
298 lt8619c_reg_write(0xa8, 0x0f);
299 lt8619c_reg_write(0x60, 0x00);
300 lt8619c_reg_write(0x96, 0x71);
301 lt8619c_reg_write(0xa0, 0x50);
302 // 0x60A3=0x44:Phase adjust enable;0x60A3=0x30:PIN68
303 // switch to output PCLK(only use for U5);
304 lt8619c_reg_write(0xa3, 0x74);
305 //Phase code value: 0x20,0x28,0x21,0x29,0x22,0x2a,0x23,0x2b,0x24,0x2c
306 lt8619c_reg_write(0xa2, 0x29);
307 //RGB mapping control
308 lt8619c_reg_write(0x6d, 0x00);//0x07//00
309 //RGB high/low bit swap control
310 lt8619c_reg_write(0x6e, 0x00);
311 /* LT8619C_OUTPUTMODE == OUTPUT_RGB888 */
312
313 lt8619c_reg_write(0xff, 0x60);
314 lt8619c_reg_write(0x0e, 0xfd);
315 lt8619c_reg_write(0x0e, 0xff);
316 lt8619c_reg_write(0x0d, 0xfc);
317 lt8619c_reg_write(0x0d, 0xff);
318}
319
320void lt8619c_audio_init(uint8_t audio_input)
321{
322 if (audio_input == I2S_2CH) {
323 dev_dbg(lt8619c->dev, "Audio Init : Audio set to I2S_2CH\n");
324 lt8619c_reg_write(0xff, 0x60);
325 lt8619c_reg_write(0x4c, 0x00);
326 } else if (audio_input == SPDIF) {
327 dev_dbg(lt8619c->dev, "Audio Init : Audio set to SPDIF\n");
328 lt8619c_reg_write(0xff, 0x60);
329 lt8619c_reg_write(0x4c, 0x80);
330 } else {
331 dev_dbg(lt8619c->dev, "Audio Init : Error! Check Audio Mode\n");
332 }
333
334 lt8619c_reg_write(0xff, 0x80);
335 lt8619c_reg_write(0x5d, 0xc9);
336 lt8619c_reg_write(0x07, 0x16);
337 lt8619c_reg_write(0x08, 0x80);
338}
339
340static void lt8619c_poweron(void)
341{
342 /* set HPD as low */
343 lt8619c_setHPD(0);
344
345 /* calculate timing & set edid */
346 lt8619c_edid_calc(onchip_edid + 0x36, &lcd_timing);
347 onchip_edid[127] = lt8619c_edid_checksum(0, &onchip_edid[0]);
nealilia9ab775d2025-06-17 12:16:33 +0900348 onchip_edid[255] = lt8619c_edid_checksum(1, &onchip_edid[0]);
nealiliade1eb002025-06-09 16:50:16 +0900349 lt8619c_set_edid(onchip_edid);
350
351 mdelay(100);
352
353 /* set HPD as low */
354 lt8619c_setHPD(1);
355
356 /* rx init */
357 lt8619c_rx_init();
358
359 /* audio init */
360 lt8619c_audio_init(I2S_2CH);
361
362 mdelay(1);
363}
364
365static void lt8619c_rx_reset(void)
366{
367 lt8619c_reg_write(0xFF, 0x60);
368 lt8619c_reg_write(0x0E, 0xBF); /* reset RXPLL */
369 lt8619c_reg_write(0x09, 0xFD); /* reset RXPLL Lock det */
370 mdelay(5);
371
372 lt8619c_reg_write(0x0E, 0xFF); /* release RXPLL */
373 lt8619c_reg_write(0x09, 0xFF);
374
375 lt8619c_reg_write(0xFF, 0x60);
376 lt8619c_reg_write(0x0e, 0xC7); /* reset PI */
377 lt8619c_reg_write(0x09, 0x0F); /* reset RX,CDR */
378 mdelay(10);
379
380 lt8619c_reg_write(0x0e, 0xFF); /* release PI */
381 mdelay(10);
382 lt8619c_reg_write(0x09, 0x8F); /* release RX */
383 mdelay(10);
384 lt8619c_reg_write(0x09, 0xFF); /* release CDR */
385 mdelay(50);
386}
387
388static bool lt8619c_detect_clk(void)
389{
390 uint8_t read_data;
391 bool ret;
392
393 ret = true;
394 lt8619c_reg_write(0xFF, 0x80);
395 if (lt8619c_reg_read(0x44) & 0x08) {
396 if (!pRXStat->flag_RXClkStable) {
397 pRXStat->flag_RXClkStable =
398 !pRXStat->flag_RXClkStable;
399 dev_dbg(lt8619c->dev, "[detect_clk] rx clock stable %d\n",
400 pRXStat->flag_RXClkStable);
401 lt8619c_reg_write(0xFF, 0x60);
402 read_data = lt8619c_reg_read(0x97);
403 lt8619c_reg_write(0x97, (read_data & 0x3f));
404 lt8619c_reg_write(0xFF, 0x80);
405 lt8619c_reg_write(0x1b, 0x00);
406
407 lt8619c_rx_reset();
408 mdelay(5);
409
410 lt8619c_reg_write(0xFF, 0x80);
411 read_data = (lt8619c_reg_read(0x87) & 0x10);
412 if (read_data) {
413 pRXStat->flag_RXPLLLocked = true;
414 dev_dbg(lt8619c->dev, "[detect_clk] clock detected & pll lock ok\n");
415 ret = true;
416 } else {
417 pRXStat->flag_RXPLLLocked = false;
418 memset(pRXStat, 0, sizeof(RXStat));
419 dev_dbg(lt8619c->dev, "[detect_clk] clock detected & pll unlock\n");
420 ret = false;
421 }
422 } else {
423 lt8619c_reg_write(0xFF, 0x80);
424 read_data = lt8619c_reg_read(0x87) & 0x10;
425
426 if (read_data) {
427 pRXStat->flag_RXPLLLocked = true;
428 dev_dbg(lt8619c->dev, "[detect_clk] pll lock ok\n");
429 ret = true;
430 } else {
431 pRXStat->flag_RXPLLLocked = false;
432 memset(pRXStat, 0, sizeof(RXStat));
433 dev_dbg(lt8619c->dev, "[detect_clk] pll unlock\n");
434 ret = false;
435 }
436 }
437 } else {
438 if (pRXStat->flag_RXClkStable)
439 dev_dbg(lt8619c->dev, "[detect_clk] lt8619c clock disappeared\n");
440 memset(pRXStat, 0, sizeof(RXStat));
441 ret = false;
442 }
443 return ret;
444}
445
446static void lt8619c_input_info(void)
447{
448 uint8_t loop_num, read_data;
449
450 lt8619c_reg_write(0xFF, 0x80);
451
452 if (pRXStat->flag_RXClkStable && pRXStat->flag_RXPLLLocked) {
453 dev_dbg(lt8619c->dev, "[input info] check hsync start:\n");
454
455 if (lt8619c_reg_read(0x13) & 0x01) {
456 if (!pRXStat->Flag_HsyncStable) {
457 pRXStat->Flag_HsyncStable = true;
458 for (loop_num = 0; loop_num < 8; loop_num++) {
459 dev_dbg(lt8619c->dev,
460 "[input info] check hsync : %d\n",
461 loop_num);
462 mdelay(20);
463
464 if (!(lt8619c_reg_read(0x13) & 0x01)) {
465 dev_dbg(lt8619c->dev, "[input info] lt8619c 8013[0]=0\n");
466 pRXStat->Flag_HsyncStable
467 = false;
468 dev_dbg(lt8619c->dev, "[input info] hsync stable FAIL\n");
469 break;
470 }
471 }
472
473 if (pRXStat->Flag_HsyncStable) {
474 lt8619c_reg_write(0xFF, 0x60);
475 read_data = lt8619c_reg_read(0x0D);
476 /* reset LVDS/BT fifo */
477 lt8619c_reg_write(0x0D,
478 read_data & 0xf8);
479 lt8619c_reg_write(0x0D,
480 read_data | 0x06);
481 lt8619c_reg_write(0x0D,
482 read_data | 0x01);
483 dev_dbg(lt8619c->dev, "[input info] hsync stable ok\n");
484 }
485 }
486 } else {
487 if (pRXStat->Flag_HsyncStable)
488 dev_dbg(lt8619c->dev, "[input info] hsync stable -> unstable\n");
489
490 pRXStat->Flag_HsyncStable = false;
491 dev_dbg(lt8619c->dev, "[input info] hsync keep unstable\n");
492 //memset(pRXStat, 0, sizeof(_RXStat));
493 }
494 }
495
496 if (pRXStat->Flag_HsyncStable) {
497 read_data = lt8619c_reg_read(0x13);
498 pRXStat->input_hdmimode =
499 (read_data & 0x02)?(true):(false);
500 if (pRXStat->input_hdmimode) {
501 pRXStat->input_vic = lt8619c_reg_read(0x74) & 0x7f;
502 pRXStat->input_colorspace =
503 lt8619c_reg_read(0x71) & 0x60;
504 pRXStat->input_colordepth =
505 lt8619c_reg_read(0x16) & 0xf0;
506 pRXStat->input_colorimetry =
507 lt8619c_reg_read(0x72) & 0xc0;
508 pRXStat->input_ex_colorimetry =
509 lt8619c_reg_read(0x73) & 0x70;
510 pRXStat->input_QuantRange =
511 lt8619c_reg_read(0x73) & 0x0c;
512 pRXStat->input_PRfactor =
513 lt8619c_reg_read(0x75) & 0x0f;
514
515 if (pRXStat->input_PRfactor == 1) {
516 lt8619c_reg_write(0xFF, 0x60);
517 read_data = lt8619c_reg_read(0x97);
518 lt8619c_reg_write(0x97, read_data | 0x40);
519 lt8619c_reg_write(0xFF, 0x80);
520 lt8619c_reg_write(0x1b, 0x20);
521 } else if (pRXStat->input_PRfactor == 3) {
522 lt8619c_reg_write(0xFF, 0x60);
523 read_data = lt8619c_reg_read(0x97);
524 lt8619c_reg_write(0x97, read_data | 0x80);
525 lt8619c_reg_write(0xFF, 0x80);
526 lt8619c_reg_write(0x1b, 0x60);
527 } else {
528 lt8619c_reg_write(0xFF, 0x60);
529 read_data = lt8619c_reg_read(0x97);
530 lt8619c_reg_write(0x97, read_data & 0x3f);
531 lt8619c_reg_write(0xFF, 0x80);
532 lt8619c_reg_write(0x1b, 0x00);
533 }
534 } else {
535 pRXStat->input_vic = 0;
536 pRXStat->input_colorspace = COLOR_RGB;
537 pRXStat->input_colordepth = 0;
538 pRXStat->input_colorimetry = ITU_709;
539 pRXStat->input_ex_colorimetry = 0;
540 pRXStat->input_QuantRange = FULL_RANGE;
541 pRXStat->input_PRfactor = 0;
542 lt8619c_reg_write(0xFF, 0x60);
543 read_data = lt8619c_reg_read(0x97);
544 lt8619c_reg_write(0x97, read_data & 0x3f);
545 lt8619c_reg_write(0xFF, 0x80);
546 lt8619c_reg_write(0x1b, 0x00);
547 }
548 }
549}
550
551static void lt8619c_csc_conversion(void)
552{
553 dev_dbg(lt8619c->dev, "[csc conv] output color 0x%x\n",
554 LT8619C_OUTPUTCOLOR);
555
556 lt8619c_reg_write(0xFF, 0x60);
557 lt8619c_reg_write(0x07, 0xfe);
558
559 if (LT8619C_OUTPUTCOLOR == COLOR_RGB) {
560 if (pRXStat->input_colorspace == COLOR_RGB) {
561 lt8619c_reg_write(0x52, 0x00);
562 if (pRXStat->input_QuantRange == LIMIT_RANGE)
563 lt8619c_reg_write(0x53, 0x08);
564 else
565 lt8619c_reg_write(0x53, 0x00);
566 } else {
567 if (pRXStat->input_colorspace == COLOR_YCBCR422)
568 lt8619c_reg_write(0x52, 0x01);
569 else
570 lt8619c_reg_write(0x52, 0x00);
571
572 if (pRXStat->input_QuantRange == LIMIT_RANGE) {
573 if (pRXStat->input_colorimetry == ITU_601)
574 lt8619c_reg_write(0x53, 0x50);
575 else if (pRXStat->input_colorimetry == ITU_709)
576 lt8619c_reg_write(0x53, 0x70);
577 else /* NO_DATA or EXTENDED_COLORIETRY */
578 lt8619c_reg_write(0x53, 0x70);
579 } else if (pRXStat->input_QuantRange == FULL_RANGE) {
580 if (pRXStat->input_colorimetry == ITU_601)
581 lt8619c_reg_write(0x53, 0x40);
582 else if (pRXStat->input_colorimetry == ITU_709)
583 lt8619c_reg_write(0x53, 0x60);
584 else /* NO_DATA or EXTENDED_COLORIETRY */
585 lt8619c_reg_write(0x53, 0x60);
586 } else { /* DEFAULT_RANGE or RESERVED_VAL */
587 lt8619c_reg_write(0x53, 0x60);
588 }
589 }
590 } else if (LT8619C_OUTPUTCOLOR == COLOR_YCBCR444) {
591 if (pRXStat->input_colorspace == COLOR_RGB) {
592 lt8619c_reg_write(0x53, 0x00);
593 if (pRXStat->input_QuantRange == LIMIT_RANGE) {
594 if (pRXStat->input_colorimetry == ITU_601)
595 lt8619c_reg_write(0x52, 0x08);
596 else if (pRXStat->input_colorimetry == ITU_709)
597 lt8619c_reg_write(0x52, 0x28);
598 else /* NO_DATA or EXTENDED_COLORIETRY */
599 lt8619c_reg_write(0x52, 0x28);
600 } else if (pRXStat->input_QuantRange == FULL_RANGE) {
601 if (pRXStat->input_colorimetry == ITU_601)
602 lt8619c_reg_write(0x52, 0x18);
603 else if (pRXStat->input_colorimetry == ITU_709)
604 lt8619c_reg_write(0x52, 0x38);
605 else /* NO_DATA or EXTENDED_COLORIETRY */
606 lt8619c_reg_write(0x52, 0x38);
607 } else { /* DEFAULT_RANGE or RESERVED_VAL */
608 lt8619c_reg_write(0x52, 0x38);
609 }
610 } else if (pRXStat->input_colorspace == COLOR_YCBCR444) {
611 lt8619c_reg_write(0x52, 0x00);
612 lt8619c_reg_write(0x53, 0x00);
613 } else if (pRXStat->input_colorspace == COLOR_YCBCR422) {
614 lt8619c_reg_write(0x52, 0x01);
615 lt8619c_reg_write(0x53, 0x00);
616 }
617 } else if (LT8619C_OUTPUTCOLOR == COLOR_YCBCR422) {
618 if (pRXStat->input_colorspace == COLOR_RGB) {
619 lt8619c_reg_write(0x53, 0x00);
620
621 if (pRXStat->input_QuantRange == LIMIT_RANGE) {
622 if (pRXStat->input_colorimetry == ITU_601)
623 lt8619c_reg_write(0x52, 0x0a);
624 else if (pRXStat->input_colorimetry == ITU_709)
625 lt8619c_reg_write(0x52, 0x2a);
626 else /* NO_DATA or EXTENDED_COLORIETRY */
627 lt8619c_reg_write(0x52, 0x2a);
628 } else if (pRXStat->input_QuantRange == FULL_RANGE) {
629 if (pRXStat->input_colorimetry == ITU_601)
630 lt8619c_reg_write(0x52, 0x1a);
631 else if (pRXStat->input_colorimetry == ITU_709)
632 lt8619c_reg_write(0x52, 0x3a);
633 else /* NO_DATA or EXTENDED_COLORIETRY */
634 lt8619c_reg_write(0x52, 0x3a);
635 } else { /* DEFAULT_RANGE or RESERVED_VAL */
636 lt8619c_reg_write(0x52, 0x3a);
637 }
638 } else if (pRXStat->input_colorspace == COLOR_YCBCR444) {
639 lt8619c_reg_write(0x52, 0x02);
640 lt8619c_reg_write(0x53, 0x00);
641 } else if (pRXStat->input_colorspace == COLOR_YCBCR422) {
642 lt8619c_reg_write(0x52, 0x00);
643 lt8619c_reg_write(0x53, 0x00);
644 }
645 }
646}
647
648static void lt8619c_check_video(void)
649{
650 uint8_t tmp_read;
651
652 if (!pRXStat->Flag_HsyncStable) {
653 h_total = 0;
654 v_total = 0;
655 return;
656 }
657
658 lt8619c_reg_write(0xFF, 0x60);
659
660 h_active = ((uint16_t)lt8619c_reg_read(0x22)) << 8;
661 h_active += lt8619c_reg_read(0x23);
662 v_active = ((uint16_t)(lt8619c_reg_read(0x20) & 0x0f)) << 8;
663 v_active += lt8619c_reg_read(0x21);
664
665 frame_counter = ((uint32_t)lt8619c_reg_read(0x10)) << 16;
666 frame_counter += ((uint32_t)lt8619c_reg_read(0x11)) << 8;
667 frame_counter += lt8619c_reg_read(0x12);
668
669 h_syncwidth = ((uint16_t)(lt8619c_reg_read(0x14) & 0x0f)) << 8;
670 h_syncwidth += lt8619c_reg_read(0x15);
671 v_syncwidth = lt8619c_reg_read(0x13);
672
673 h_bkporch = ((uint16_t)(lt8619c_reg_read(0x18) & 0x0f)) << 8;
674 h_bkporch += lt8619c_reg_read(0x19);
675 v_bkporch = lt8619c_reg_read(0x16);
676
677 h_total = ((uint16_t)lt8619c_reg_read(0x1e)) << 8;
678 h_total += lt8619c_reg_read(0x1f);
679 v_total = ((uint16_t)(lt8619c_reg_read(0x1c) & 0x0f)) << 8;
680 v_total += lt8619c_reg_read(0x1d);
681
682 tmp_read = lt8619c_reg_read(0x24);
683 h_syncpol = tmp_read & 0x01;
684 v_syncpol = (tmp_read & 0x02) >> 1;
685}
686
687static void detect_lvdspll_lock(void)
688{
689 uint8_t read_data_1;
690 uint8_t check_num = 0;
691
692 lt8619c_reg_write(0xFF, 0x60);
693
694 if ((lt8619c_reg_read(0xA3) & 0x40) == 0x40) {
695 lt8619c_reg_write(0xFF, 0x80);
696 while ((lt8619c_reg_read(0x87) & 0x20) == 0x00) {
697 lt8619c_reg_write(0xFF, 0x60);
698 read_data_1 = lt8619c_reg_read(0x0e);
699 lt8619c_reg_write(0x0e, read_data_1 & 0xFD);
700
701 mdelay(5);
702
703 lt8619c_reg_write(0x0e, 0xFF);
704 lt8619c_reg_write(0xFF, 0x80);
705
706 check_num++;
707 if (check_num > 10)
708 break;
709 }
710 }
711}
712
713static void lt8619c_bt_setting(void)
714{
715 uint8_t val_6060;
716 uint16_t tmp_data;
717
718 if (!pRXStat->Flag_HsyncStable)
719 return;
720
721 detect_lvdspll_lock();
722
723 lt8619c_reg_write(0xFF, 0x60);
724 val_6060 = lt8619c_reg_read(0x60) & 0xc7;
725
726 /* set BT TX h/vsync polarity */
727 if (h_syncpol)
728 val_6060 |= 0x20;
729
730 if (v_syncpol)
731 val_6060 |= 0x10;
732
733 /*
734 * double the value of v_active&v_total
735 * when input is interlace resolution.
736 * if user needs to support interlace format not listed here,
737 * please add that interlace format info here.
738 */
739 if (pRXStat->input_hdmimode) {
740 switch (pRXStat->input_vic) {
741 case 5: /* 1080i */
742 case 6: /* 480i */
743 case 7: /* 480iH */
744 case 10: /* 480i4x */
745 case 11: /* 480i4xH */
746 case 20: /* 1080i25 */
747 dev_dbg(lt8619c->dev, "[bt setting] VIC20\n");
748 val_6060 |= 0x08;
749 v_active <<= 1;
750
751 if (v_total % 2 == 1)
752 v_total = (v_total << 1) - 1;
753 else
754 v_total = (v_total << 1) + 1;
755
756 lt8619c_reg_write(0x68, 23);
757 break;
758 case 21: /* 576i */
759 case 22: /* 576iH */
760 case 25: /* 576i4x */
761 case 26: /* 576i4xH */
762 dev_dbg(lt8619c->dev, "[bt setting] VIC26\n");
763 val_6060 |= 0x08;
764 v_active <<= 1;
765
766 if (v_total % 2 == 1)
767 v_total = (v_total << 1) - 1;
768 else
769 v_total = (v_total << 1) + 1;
770
771 lt8619c_reg_write(0x68, 25);
772 break;
773 default:
774 lt8619c_reg_write(0x68, 0x00);
775 break;
776 }
777 /* DVI Input */
778 } else {
779 if ((h_active == 1920) && (v_active == 540)) {
780 val_6060 |= 0x08;
781 v_active <<= 1;
782 if (v_total % 2 == 1)
783 v_total = (v_total << 1) - 1;
784 else
785 v_total = (v_total << 1) + 1;
786 lt8619c_reg_write(0x68, 23);
787 } else if ((h_active == 1440) && (v_active == 240)) {
788 val_6060 |= 0x08;
789 v_active <<= 1;
790 if (v_total % 2 == 1)
791 v_total = (v_total << 1) - 1;
792 else
793 v_total = (v_total << 1) + 1;
794 lt8619c_reg_write(0x68, 23);
795 } else if ((h_active == 1440) && (v_active == 288)) {
796 val_6060 |= 0x08;
797 v_active <<= 1;
798 if (v_total % 2 == 1)
799 v_total = (v_total << 1) - 1;
800 else
801 v_total = (v_total << 1) + 1;
802 lt8619c_reg_write(0x68, 25);
803 }
804 }
805
806 lt8619c_reg_write(0x60, val_6060);
807 tmp_data = h_syncwidth + h_bkporch;
808 lt8619c_reg_write(0x61, (uint8_t)(tmp_data >> 8));
809 lt8619c_reg_write(0x62, (uint8_t)tmp_data);
810 tmp_data = h_active;
811 lt8619c_reg_write(0x63, (uint8_t)(tmp_data >> 8));
812 lt8619c_reg_write(0x64, (uint8_t)tmp_data);
813 tmp_data = h_total;
814 lt8619c_reg_write(0x65, (uint8_t)(tmp_data >> 8));
815 lt8619c_reg_write(0x66, (uint8_t)tmp_data);
816 tmp_data = v_syncwidth + v_bkporch;
817 lt8619c_reg_write(0x67, (uint8_t)tmp_data);
818 tmp_data = v_active;
819 lt8619c_reg_write(0x69, (uint8_t)(tmp_data >> 8));
820 lt8619c_reg_write(0x6a, (uint8_t)tmp_data);
821 tmp_data = v_total;
822 lt8619c_reg_write(0x6b, (uint8_t)(tmp_data >> 8));
823 lt8619c_reg_write(0x6c, (uint8_t)tmp_data);
824}
825
826static void lt8619c_print_rxinfo(void)
827{
828 uint32_t clk = 0;
829 static uint16_t print_en;
830 uint32_t audio_rate = 0;
831
832 print_en = 0;
833
834 if (!pRXStat->Flag_HsyncStable) {
835 print_en = 0;
836 return;
837 }
838 if (print_en != 3)
839 print_en++;
840
841 if (print_en == 3) {
842 print_en = 0;
843
844 if (pRXStat->input_hdmimode) {
845 dev_dbg(lt8619c->dev, "[rxinfo] input is HDMI signal\n");
846 dev_dbg(lt8619c->dev, "[rxinfo] input vid %d\n",
847 pRXStat->input_vic);
848
849 if (pRXStat->input_colorspace == COLOR_RGB)
850 dev_dbg(lt8619c->dev,
851 "[rxinfo] input colorspace : COLOR_RGB\n");
852 else if (pRXStat->input_colorspace == COLOR_YCBCR444)
853 dev_dbg(lt8619c->dev, "[rxinfo] input colorspace : COLOR_YCBCR444\n");
854 else if (pRXStat->input_colorspace == COLOR_YCBCR422)
855 dev_dbg(lt8619c->dev, "[rxinfo] input colorspace : COLOR_YCBCR422\n");
856 else
857 dev_dbg(lt8619c->dev, "[rxinfo] input colorspace UNKNOWN\n");
858
859 dev_info(lt8619c->dev, "[rxinfo] input colordepth %d\n",
860 pRXStat->input_colordepth);
861
862 if (pRXStat->input_colorimetry == NO_DATA)
863 dev_dbg(lt8619c->dev, "[rxinfo] input colorimetry : NO DATA\n");
864 else if (pRXStat->input_colorimetry == ITU_601)
865 dev_dbg(lt8619c->dev, "[rxinfo] input colorimetry : ITU 601\n");
866 else if (pRXStat->input_colorimetry == ITU_709)
867 dev_dbg(lt8619c->dev, "[rxinfo] input colorimetry : ITU 709\n");
868 else
869 dev_dbg(lt8619c->dev, "[rxinfo] input colorimetry : extended colorimetry\n");
870
871 if (pRXStat->input_colorimetry == EXTENDED_COLORIETRY) {
872 if (pRXStat->input_ex_colorimetry == xvYCC601)
873 dev_dbg(lt8619c->dev, "[rxinfo] input ex colorimetry : xvYCC601\n");
874 else if
875 (pRXStat->input_ex_colorimetry ==
876 xvYCC709)
877 dev_dbg(lt8619c->dev,
878 "[rxinfo] input ex colorimetry : xvYCC709\n");
879 else
880 dev_dbg(lt8619c->dev,
881 "[rxinfo] input ex colorimetry : FUTURE COLORIMETRY\n");
882 }
883
884 if (pRXStat->input_QuantRange == DEFAULT_RANGE)
885 dev_dbg(lt8619c->dev,
886 "[rxinfo] input quant range : DEFAULT\n");
887 else if (pRXStat->input_QuantRange == LIMIT_RANGE)
888 dev_dbg(lt8619c->dev,
889 "[rxinfo] input quant range : LIMIT\n");
890 else if (pRXStat->input_QuantRange == FULL_RANGE)
891 dev_dbg(lt8619c->dev,
892 "[rxinfo] input quant range : FULL\n");
893 else
894 dev_dbg(lt8619c->dev,
895 "[rxinfo] input quant range : RESERVED\n");
896
897 dev_info(lt8619c->dev, "[rxinfo] input PRfactor %d\n",
898 pRXStat->input_PRfactor);
899 } else {
900 dev_dbg(lt8619c->dev, "[rxinfo] input : DVI signal\n");
901 }
902
903 dev_dbg(lt8619c->dev, "[rxinfo] input timing info\n");
904
905 lt8619c_reg_write(0xFF, 0x80);
906 clk = lt8619c_reg_read(0x44)&0x07;
907 clk <<= 8;
908 clk += lt8619c_reg_read(0x45);
909 clk <<= 8;
910 clk += lt8619c_reg_read(0x46);
911
912 dev_dbg(lt8619c->dev,
913 "[rxinfo] TMDS clock freq : %d kHz\n", clk);
914
915 dev_dbg(lt8619c->dev, "[rxinfo] h_active %d\n",
916 h_active);
917 dev_dbg(lt8619c->dev, "[rxinfo] v_active %d\n",
918 v_active);
919 dev_dbg(lt8619c->dev, "[rxinfo] h_syncwidth %d\n",
920 h_syncwidth);
921 dev_dbg(lt8619c->dev, "[rxinfo] v_syncwidth %d\n",
922 v_syncwidth);
923 dev_dbg(lt8619c->dev, "[rxinfo] h_bkporch %d\n",
924 h_bkporch);
925 dev_dbg(lt8619c->dev, "[rxinfo] v_bkporch %d\n",
926 v_bkporch);
927 dev_dbg(lt8619c->dev, "[rxinfo] h_total %d\n", h_total);
928 dev_dbg(lt8619c->dev, "[rxinfo] v_total %d\n", v_total);
929 dev_dbg(lt8619c->dev, "[rxinfo] frame_counter %d\n",
930 frame_counter);
931
932 if (h_syncpol)
933 dev_dbg(lt8619c->dev, "[rxinfo] h_syncpol is positive\n");
934 else
935 dev_dbg(lt8619c->dev, "[rxinfo] h_syncpol is negative\n");
936
937 if (v_syncpol)
938 dev_dbg(lt8619c->dev, "[rxinfo] v_syncpol is positive\n");
939 else
940 dev_dbg(lt8619c->dev, "[rxinfo] v_syncpol is negative\n");
941
942 lt8619c_reg_write(0xFF, 0x80);
943 audio_rate = ((lt8619c_reg_read(0x11)&0x03) * 0x100) +
944 lt8619c_reg_read(0x12);
945 dev_dbg(lt8619c->dev, "[rxinfo] audio rate %d kHz\n",
946 audio_rate);
947 }
948}
949
950static int lt8619c_probe(struct i2c_client *client,
951 const struct i2c_device_id *id)
952{
953 struct device *dev = &client->dev;
954
955 lt8619c = devm_kzalloc(dev, sizeof(*lt8619c), GFP_KERNEL);
956 if (!lt8619c)
957 return -ENOMEM;
958
959 lt8619c->dev = dev;
960 lt8619c->client = client;
961
962 lt8619c->regmap = devm_regmap_init_i2c(client, &lt8619c_regmap_config);
963 if (IS_ERR(lt8619c->regmap))
964 return PTR_ERR(lt8619c->regmap);
965
966 /* Reset GPIO */
967 lt8619c->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
968 if (IS_ERR(lt8619c->gpiod_reset))
969 return PTR_ERR(lt8619c->gpiod_reset);
970
971 /* init variable */
972 pRXStat = &RXStat;
973 memset(pRXStat, 0, sizeof(RXStat));
974
975 /* reset */
976 if (lt8619c->gpiod_reset) {
977 gpiod_set_value_cansleep(lt8619c->gpiod_reset, 0);
978 usleep_range(5000, 10000);
979 }
980
981 /* check chip ID */
982 if (!lt8619c_check_chipid()) {
983 dev_err(dev, "failed to read LT8619C chip id\n");
984 return 0;
985 }
986
987 lt8619c_poweron();
988
989 if (lt8619c_detect_clk()) {
990 lt8619c_input_info();
991 lt8619c_csc_conversion();
992 lt8619c_check_video();
993 lt8619c_bt_setting();
994 lt8619c_print_rxinfo();
995 }
996
997 dev_info(lt8619c->dev, "LT8619C init done\n");
998
999 return 0;
1000}
1001
1002static int lt8619c_remove(struct i2c_client *client)
1003{
1004 return 0;
1005}
1006
1007static const struct i2c_device_id lt8619c_ids[] = {
1008 { "lt8619c", 0 },
1009 { }
1010};
1011MODULE_DEVICE_TABLE(i2c, lt8619c_ids);
1012
1013static struct i2c_driver lt8619c_driver = {
1014 .driver = {
1015 .name = "lt8619c",
1016 },
1017 .probe = lt8619c_probe,
1018 .remove = lt8619c_remove,
1019 .id_table = lt8619c_ids,
1020};
1021
1022static int __init lt8619c_init(void)
1023{
1024 return i2c_add_driver(&lt8619c_driver);
1025}
1026/* must be loaded after rk817 mfd driver */
1027late_initcall(lt8619c_init);
1028
1029static void __exit lt8619c_exit(void)
1030{
1031 i2c_del_driver(&lt8619c_driver);
1032}
1033module_exit(lt8619c_exit);
1034
1035MODULE_DESCRIPTION("LT8619C HDMI to LVDS Driver for TFT LCD QC4011");
1036MODULE_AUTHOR("Joy Cho <joy.cho@hardkernel.com>");
1037MODULE_LICENSE("GPL");