1 /*
2 * Copyright (C) 2019 Allwinnertech Co.Ltd
3 * Authors: zhengwanyu
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 */
11
12 #include <linux/dma-mapping.h>
13 #include <drm/drmP.h>
14 #include <linux/clk.h>
15 #include "sunxi_tcon.h"
16
17 #ifdef CONFIG_AW_DRM_DE_V33X
18 #include "tcon_feat.h"
19 #include "disp_al_tcon.h"
20 #else
21 #include "de_feat.h"
22 #include "de_hal.h"
23 #include "de_clock.h"
24 #include "de_lcd.h"
25 #include "de_dsi.h"
26 #endif
27
28 #include "disp_al.h"
29
30 #include "sunxi_common.h"
31 #include "sunxi_lcd.h"
32
33
34 static struct sunxi_tcon_drv *tcon_drv;
35
36 struct sunxi_dispdev_name sunxi_output_type_name[] = {
37 {DISP_OUTPUT_TYPE_LCD, "LCD"},
38 {DISP_OUTPUT_TYPE_TV, "TVE"},
39 {DISP_OUTPUT_TYPE_HDMI, "HDMI"},
40 {DISP_OUTPUT_TYPE_VGA, "VGA"},
41 {DISP_OUTPUT_TYPE_VDPO, "VDPO"},
42 {DISP_OUTPUT_TYPE_EDP, "EDP"},
43 };
44
sunxi_tcon_get_output_type_name(int output_type)45 static char *sunxi_tcon_get_output_type_name(int output_type)
46 {
47 int i, cnt;
48
49 cnt = sizeof(sunxi_output_type_name)
50 / sizeof(struct sunxi_dispdev_name);
51
52 for (i = 0; i < cnt; i++)
53 if (sunxi_output_type_name[i].device == output_type)
54 return sunxi_output_type_name[i].name;
55
56 return NULL;
57 }
58
sunxi_tcon_get_tcon(int nr)59 struct sunxi_tcon *sunxi_tcon_get_tcon(int nr)
60 {
61 return &tcon_drv->hwtcon[nr];
62 }
63
sunxi_tcon_is_use_irq(unsigned int enc_id)64 static bool sunxi_tcon_is_use_irq(unsigned int enc_id)
65 {
66 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
67
68 return hwtcon->irq_no ? true : false;
69 }
70
sunxi_tcon_get_irq_no(unsigned int enc_id)71 static unsigned int sunxi_tcon_get_irq_no(unsigned int enc_id)
72 {
73 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
74
75 return hwtcon->irq_no;
76 }
77
78 #ifdef CONFIG_AW_DRM_LCD
sunxi_tcon_lcd_enable(int nr,int lcd_id)79 int sunxi_tcon_lcd_enable(int nr, int lcd_id)
80 {
81 enum disp_lcd_if lcd_if;
82 struct disp_panel_para *panel
83 = sunxi_lcd_get_panel_para(lcd_id);
84 int type_id = 0;
85
86 type_id = sunxi_lcd_get_type_id(lcd_id);
87
88 lcd_if = sunxi_lcd_get_lcd_if(lcd_id);
89
90 tcon0_open(nr, panel);
91 if (panel->lcd_if == LCD_IF_LVDS) {
92 lvds_open(type_id, panel);
93 } else if (panel->lcd_if == LCD_IF_DSI) {
94 dsi_open(type_id, panel);
95 if (panel->lcd_tcon_mode == DISP_TCON_DUAL_DSI
96 && nr + 1 < DEVICE_DSI_NUM)
97 dsi_open(type_id + 1, panel);
98 }
99
100 return 0;
101 }
102
sunxi_tcon_lcd_disable(int nr,int lcd_id)103 int sunxi_tcon_lcd_disable(int nr, int lcd_id)
104 {
105 enum disp_lcd_if lcd_if;
106 struct disp_panel_para *panel
107 = sunxi_lcd_get_panel_para(lcd_id);
108 int type_id = 0;
109
110 type_id = sunxi_lcd_get_type_id(lcd_id);
111
112 lcd_if = sunxi_lcd_get_lcd_if(lcd_id);
113
114 if (panel->lcd_if == LCD_IF_LVDS) {
115 lvds_close(nr);
116 } else if (panel->lcd_if == LCD_IF_DSI) {
117 dsi_close(type_id);
118 if (panel->lcd_tcon_mode == DISP_TCON_DUAL_DSI &&
119 nr + 1 < DEVICE_DSI_NUM)
120 dsi_close(type_id + 1);
121 }
122 tcon0_close(nr);
123
124 return 0;
125 }
126
sunxi_tcon_lcd_query_irq(int nr)127 static int sunxi_tcon_lcd_query_irq(int nr)
128 {
129 int lcd_id, type_id = 0;
130 enum disp_lcd_if lcd_if;
131 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
132
133 if (hwtcon->type != TCON_LCD) {
134 DRM_ERROR("wrong TCON type\n");
135 return -1;
136 }
137
138 lcd_id = hwtcon->conn_type_id;
139
140 type_id = sunxi_lcd_get_type_id(lcd_id);
141
142 lcd_if = sunxi_lcd_get_lcd_if(lcd_id);
143 if (sunxi_lcd_is_use_irq(lcd_id)) {
144 if (lcd_if == LCD_IF_DSI)
145 return dsi_irq_query(type_id,
146 DSI_IRQ_VIDEO_VBLK);
147 } else if (sunxi_tcon_is_use_irq(nr)) {
148 /*DRM_INFO("tcon%d irq query\n", nr);*/
149 return tcon_irq_query(nr,
150 LCD_IRQ_TCON0_VBLK);
151 }
152
153 return 0;
154 }
155 #endif
156
157 /**
158 * @name :sunxi_tcon_device_query_irq
159 * @brief :query irq status(tconlcd or tcontv)
160 * @param[IN] :nr:index of tcon
161 * @return :irq status
162 */
sunxi_tcon_device_query_irq(int nr)163 static int sunxi_tcon_device_query_irq(int nr)
164 {
165 int ret = 0;
166 int irq_id = 0;
167 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
168
169 irq_id = (hwtcon->type == TCON_LCD) ? LCD_IRQ_TCON0_VBLK
170 : LCD_IRQ_TCON1_VBLK;
171 ret = tcon_irq_query(nr, irq_id);
172
173 return ret;
174 }
175
sunxi_tcon_query_irq(int nr)176 int sunxi_tcon_query_irq(int nr)
177 {
178 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
179
180 if (hwtcon->type == TCON_LCD)
181 #ifdef CONFIG_AW_DRM_LCD
182 return sunxi_tcon_lcd_query_irq(nr);
183 #else
184 ;
185 #endif
186 else if (hwtcon->type == TCON_TV)
187 return sunxi_tcon_device_query_irq(nr);
188
189 DRM_ERROR("WRONG TCON Type\n");
190 return -1;
191 }
192
193 /* return the tcon count*/
sunxi_tcon_get_count(void)194 int sunxi_tcon_get_count(void)
195 {
196 return tcon_drv->tcon_cnt;
197 }
198
199 /*Check if this tcon support that con_type*/
sunxi_tcon_type_is_support(int nr,int con_type)200 static bool sunxi_tcon_type_is_support(int nr, int con_type)
201 {
202 struct sunxi_tcon *hwtcon;
203
204 hwtcon = &tcon_drv->hwtcon[nr];
205
206 if ((con_type == DISP_OUTPUT_TYPE_LCD)
207 || (con_type == DISP_OUTPUT_TYPE_EDP)) {
208 if (hwtcon->type == TCON_LCD)
209 return true;
210 else
211 return false;
212 } else if ((con_type == DISP_OUTPUT_TYPE_HDMI)
213 || (con_type == DISP_OUTPUT_TYPE_TV)) {
214 if (hwtcon->type == TCON_TV)
215 return true;
216 else
217 return false;
218 }
219
220 return false;
221 }
222
tcon_info_show(struct device * dev,struct device_attribute * attr,char * buf)223 static ssize_t tcon_info_show(struct device *dev,
224 struct device_attribute *attr, char *buf)
225 {
226 ssize_t i, n = 0;
227 struct sunxi_tcon *hwtcon;
228
229 #if defined(HAVE_DEVICE_COMMON_MODULE)
230 n += sprintf(buf + n, "%s\n", "disp_if_top info:");
231 n += sprintf(buf + n, "virt_reg_base: 0x%lx\n",
232 tcon_drv->hwtopif.reg_base);
233 n += sprintf(buf + n, "clk_name:%s clk_rate: %lu enable count:%u\n\n",
234 __clk_get_name(tcon_drv->hwtopif.mclk),
235 clk_get_rate(tcon_drv->hwtopif.mclk),
236 __clk_get_enable_count(tcon_drv->hwtopif.mclk));
237 #endif
238
239 n += sprintf(buf + n, "%s\n", "sunxi-tcon info:");
240 n += sprintf(buf + n, "tcon count:%u tcon_lcd_count:%u tcon_tv_count:%u\n\n",
241 tcon_drv->tcon_cnt, tcon_drv->tcon_lcd_cnt, tcon_drv->tcon_tv_cnt);
242
243 for (i = 0; i < tcon_drv->tcon_cnt; i++) {
244 hwtcon = &tcon_drv->hwtcon[i];
245 n += sprintf(buf + n, "id: %d\n", hwtcon->id);
246
247 n += sprintf(buf + n, "virt_reg_base: 0x%lx\n",
248 hwtcon->reg_base);
249
250 if (hwtcon->type == TCON_LCD)
251 n += sprintf(buf + n, "TCON_LCD%d\n", hwtcon->type_id);
252 else if (hwtcon->type == TCON_TV)
253 n += sprintf(buf + n, "TCON_TV%d\n", hwtcon->type_id);
254 else
255 n += sprintf(buf + n, "ERROR: Unknowed TCON_TYPE:%d",
256 hwtcon->type);
257
258 n += sprintf(buf + n, "clk_name:%s clk_rate: %lu enable count:%d\n",
259 __clk_get_name(hwtcon->mclk),
260 clk_get_rate(hwtcon->mclk),
261 __clk_get_enable_count(hwtcon->mclk));
262
263 n += sprintf(buf + n, "irq no: %u\n", hwtcon->irq_no);
264
265 if (hwtcon->is_assigned)
266 n += sprintf(buf + n, "%s\n", "Has been assigned");
267 else
268 n += sprintf(buf + n, "%s\n", "Has NOT been assigned");
269 n += sprintf(buf + n, "%s", "\n");
270 }
271
272 return n;
273 }
274
tcon_info_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)275 static ssize_t tcon_info_store(struct device *dev,
276 struct device_attribute *attr,
277 const char *buf, size_t count)
278 {
279 return count;
280 }
281
282 static DEVICE_ATTR(info, 0660, tcon_info_show, tcon_info_store);
283
tcon_reg_show(struct device * dev,struct device_attribute * attr,char * buf)284 static ssize_t tcon_reg_show(struct device *dev,
285 struct device_attribute *attr, char *buf)
286 {
287 ssize_t n = 0;
288 unsigned int *reg, i, j;
289 struct sunxi_tcon *hwtcon;
290
291 for (i = 0; i < tcon_drv->tcon_cnt; i++) {
292 hwtcon = &tcon_drv->hwtcon[i];
293 if (!__clk_is_enabled(hwtcon->mclk)) {
294 n += sprintf(buf + n, "tcon(id = %d) clk is NOT enabled,"
295 "can NOT dump reg\n", hwtcon->id);
296 if (i >= tcon_drv->tcon_cnt)
297 return n;
298 continue;
299 }
300
301 n += sprintf(buf + n, "tcon(id = %d):\n", hwtcon->id);
302 reg = (unsigned int *)hwtcon->reg_base;
303 for (j = 0; j < 0x80; j++) {
304 /*print address info*/
305 if (j == 0)
306 n += sprintf(buf + n, "0x%04x:", j * 4);
307 if ((j % 4 == 0) && (j != 0)) {
308 n += sprintf(buf + n, "%s", "\n");
309 n += sprintf(buf + n, "0x%04x:", j * 4);
310 }
311
312 /*print reg value*/
313 n += sprintf(buf + n, "0x%08x ", reg[j]);
314 }
315 n += sprintf(buf + n, "%s", "\n");
316 }
317
318 return n;
319 }
320
tcon_reg_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)321 static ssize_t tcon_reg_store(struct device *dev,
322 struct device_attribute *attr,
323 const char *buf, size_t count)
324 {
325 return count;
326 }
327
328 static DEVICE_ATTR(tcon_reg, 0660, tcon_reg_show, tcon_reg_store);
329
330 #if defined(HAVE_DEVICE_COMMON_MODULE)
topif_reg_show(struct device * dev,struct device_attribute * attr,char * buf)331 static ssize_t topif_reg_show(struct device *dev,
332 struct device_attribute *attr, char *buf)
333 {
334 ssize_t n = 0;
335 unsigned int *reg, j;
336 struct sunxi_top_if *topif;
337
338 topif = &tcon_drv->hwtopif;
339 if (!__clk_is_enabled(topif->mclk)) {
340 n += sprintf(buf + n, "disp_top_if clk is NOT enabled,"
341 "can NOT dump reg\n");
342 return n;
343 }
344
345 n += sprintf(buf + n, "disp_top_if:\n");
346 reg = (unsigned int *)topif->reg_base;
347 for (j = 0; j < 0x40; j++) {
348 /*print address info*/
349 if (j == 0)
350 n += sprintf(buf + n, "0x%04x:", j * 4);
351 if ((j % 4 == 0) && (j != 0)) {
352 n += sprintf(buf + n, "%s", "\n");
353 n += sprintf(buf + n, "0x%04x:", j * 4);
354 }
355
356 /*print reg value*/
357 n += sprintf(buf + n, "0x%08x ", reg[j]);
358 }
359 n += sprintf(buf + n, "%s", "\n");
360
361 return n;
362 }
363
topif_reg_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)364 static ssize_t topif_reg_store(struct device *dev,
365 struct device_attribute *attr,
366 const char *buf, size_t count)
367 {
368 return count;
369 }
370
371 static DEVICE_ATTR(topif_reg, 0660, topif_reg_show, topif_reg_store);
372 #endif
373
374 static struct attribute *tcon_attributes[] = {
375 &dev_attr_info.attr,
376 &dev_attr_tcon_reg.attr,
377 #if defined(HAVE_DEVICE_COMMON_MODULE)
378 &dev_attr_topif_reg.attr,
379 #endif
380 NULL
381 };
382
383 static struct attribute_group tcon_attribute_group = {
384 .name = "attr",
385 .attrs = tcon_attributes,
386 };
387
388 /*judge if this encoder support this connector
389 * @encoder: id of the encoder
390 * @output_type: connector output type
391 */
sunxi_drm_encoder_support(int encoder,enum disp_output_type output_type)392 int sunxi_drm_encoder_support(int encoder, enum disp_output_type output_type)
393 {
394 return de_feat_is_supported_output_types(encoder, output_type);
395 }
396
397 /*
398 * NOTE:if you want to use abnormal dsi working mode,
399 * you have to implement this function
400 */
sunxi_tcon_mode(unsigned int id)401 unsigned int sunxi_tcon_mode(unsigned int id)
402 {
403 return 0;
404 }
405
406 /*
407 * NOTE:if you want to use abnormal dsi working mode,
408 * you have to implement this function
409 */
sunxi_tcon_slave_num(unsigned int id)410 unsigned int sunxi_tcon_slave_num(unsigned int id)
411 {
412 return 0;
413 }
414
415 /*
416 * NOTE:if you want to use abnormal dsi working mode,
417 * you have to implement this function
418 */
sunxi_tcon_port_num(unsigned int id)419 unsigned int sunxi_tcon_port_num(unsigned int id)
420 {
421 return 0;
422 }
423
424 #ifdef CONFIG_AW_DRM_LCD
sunxi_tcon_lcd_is_support(int nr,int lcd_id)425 static bool sunxi_tcon_lcd_is_support(int nr, int lcd_id)
426 {
427 int lcd_if;
428
429 if (!sunxi_tcon_type_is_support(nr, DISP_OUTPUT_TYPE_LCD))
430 return false;
431
432 lcd_if = sunxi_lcd_get_lcd_if(lcd_id);
433
434 #ifdef CONFIG_AW_DRM_DE_V2X
435 if (lcd_if == LCD_IF_DSI)
436 //return (nr == 1) ? 1 : 0;
437 return 1;
438 #endif
439 return 1; /*tcon_lcd always support*/
440 }
441
sunxi_tcon_lcd_get_tcon_id(unsigned int lcd_id)442 int sunxi_tcon_lcd_get_tcon_id(unsigned int lcd_id)
443 {
444 unsigned int tcon_cnt, i;
445 struct sunxi_tcon *hwtcon;
446
447 tcon_cnt = sunxi_tcon_get_count();
448 for (i = 0; i < tcon_cnt; i++) {
449 hwtcon = sunxi_tcon_get_tcon(i);
450 if (hwtcon->type != TCON_LCD)
451 continue;
452 if (!hwtcon->is_assigned)
453 continue;
454 if (hwtcon->conn_type_id == lcd_id)
455 break;
456 }
457
458 if (i >= tcon_cnt) {
459 DRM_ERROR("can NOT get a attached tcon\n");
460 return -1;
461 }
462
463 return i;
464 }
465
466 #ifdef CONFIG_FPGA_V4_PLATFORM
467 static struct lcd_clk_info clk_tbl[] = {
468 {LCD_IF_HV, 0x12, 1, 1, 0},
469 {LCD_IF_CPU, 12, 1, 1, 0},
470 {LCD_IF_LVDS, 7, 1, 1, 0},
471 {LCD_IF_DSI, 4, 1, 4, 0},
472 };
473 #else
474 static struct lcd_clk_info clk_tbl[] = {
475 {LCD_IF_HV, 6, 1, 1, 0},
476 {LCD_IF_CPU, 12, 1, 1, 0},
477 {LCD_IF_LVDS, 7, 1, 1, 0},
478 #if defined (DSI_VERSION_40)
479 {LCD_IF_DSI, 4, 1, 4, 150000000},
480 #else
481 {LCD_IF_DSI, 4, 1, 4, 0},
482 #endif /*endif DSI_VERSION_40*/
483 {LCD_IF_VDPO, 4, 1, 1, 0},
484 };
485 #endif
486
sunxi_tcon_get_clk_info(int lcd_id,struct lcd_clk_info * info)487 int sunxi_tcon_get_clk_info(int lcd_id, struct lcd_clk_info *info)
488 {
489 int tcon_div = 6;
490 int lcd_div = 1;
491 int dsi_div = 4;
492 int dsi_rate = 0;
493 int i;
494 int find = 0;
495 struct disp_panel_para *panel = sunxi_lcd_get_panel_para(lcd_id);
496
497 if (panel == NULL) {
498 DRM_ERROR("panel is NULL\n");
499 return 1;
500 }
501
502 for (i = 0; i < sizeof(clk_tbl) / sizeof(struct lcd_clk_info); i++) {
503 if (clk_tbl[i].lcd_if == panel->lcd_if) {
504 tcon_div = clk_tbl[i].tcon_div;
505 lcd_div = clk_tbl[i].lcd_div;
506 dsi_div = clk_tbl[i].dsi_div;
507 dsi_rate = clk_tbl[i].dsi_rate;
508 find = 1;
509 break;
510 }
511 }
512 if (find == 0) {
513 DRM_ERROR("cant find clk info for lcd_if %d\n", panel->lcd_if);
514 return 1;
515 }
516
517 #if defined(DSI_VERSION_40)
518 if (panel->lcd_if == LCD_IF_DSI) {
519 u32 lane = panel->lcd_dsi_lane;
520 u32 bitwidth = 0;
521
522 switch (panel->lcd_dsi_format) {
523 case LCD_DSI_FORMAT_RGB888:
524 bitwidth = 24;
525 break;
526 case LCD_DSI_FORMAT_RGB666:
527 bitwidth = 24;
528 break;
529 case LCD_DSI_FORMAT_RGB565:
530 bitwidth = 16;
531 break;
532 case LCD_DSI_FORMAT_RGB666P:
533 bitwidth = 18;
534 break;
535 }
536
537 dsi_div = bitwidth / lane;
538 if (panel->lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) {
539 tcon_div = dsi_div;
540 }
541 }
542 #endif
543
544 if (panel->lcd_if == LCD_IF_HV &&
545 panel->lcd_hv_if == LCD_HV_IF_CCIR656_2CYC &&
546 panel->ccir_clk_div > 0)
547 tcon_div = panel->ccir_clk_div;
548 else if (panel->lcd_tcon_mode == DISP_TCON_DUAL_DSI &&
549 panel->lcd_if == LCD_IF_DSI) {
550 tcon_div = tcon_div / 2;
551 dsi_div /= 2;
552 }
553
554 #if defined(DSI_VERSION_28)
555 if (panel->lcd_if == LCD_IF_DSI &&
556 panel->lcd_dsi_if == LCD_DSI_IF_COMMAND_MODE) {
557 tcon_div = 6;
558 dsi_div = 6;
559 }
560 #endif
561
562 info->tcon_div = tcon_div;
563 info->lcd_div = lcd_div;
564 info->dsi_div = dsi_div;
565 info->dsi_rate = dsi_rate;
566
567 return 0;
568 }
569
sunxi_tcon_set_clk(unsigned int enc_id,unsigned int lcd_id)570 static int sunxi_tcon_set_clk(unsigned int enc_id, unsigned int lcd_id)
571 {
572 int ret = 0;
573 unsigned long pll_rate = 297000000, lcd_rate = 33000000;
574 unsigned long dclk_rate = 33000000;
575 unsigned long pll_rate_set = 297000000, lcd_rate_set = 33000000;
576 struct clk *parent_clk;
577 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
578 struct lcd_clk_info clk_info;
579 struct disp_panel_para *p_panel = NULL;
580
581 if (!hwtcon->mclk) {
582 DRM_ERROR("TCON%d has NO clk\n", enc_id);
583 return -1;
584 }
585
586 p_panel = sunxi_lcd_get_panel_para(lcd_id);
587 memset(&clk_info, 0, sizeof(struct lcd_clk_info));
588
589 ret = sunxi_tcon_get_clk_info(lcd_id, &clk_info);
590 if (ret) {
591 DRM_WARN("Get clk_info fail!\n");
592 return ret;
593 }
594 dclk_rate = ((unsigned long long)sunxi_lcd_get_dclk(lcd_id)) * 1000000;
595
596 if (p_panel->lcd_if == LCD_IF_DSI) {
597 lcd_rate = dclk_rate * clk_info.dsi_div;
598 pll_rate = lcd_rate * clk_info.lcd_div;
599 } else {
600 lcd_rate = dclk_rate * clk_info.tcon_div;
601 pll_rate = lcd_rate * clk_info.lcd_div;
602 }
603
604 parent_clk = clk_get_parent(hwtcon->mclk);
605 if (parent_clk) {
606 clk_set_rate(parent_clk, pll_rate);
607 pll_rate_set = clk_get_rate(parent_clk);
608 }
609
610 if (clk_info.lcd_div)
611 lcd_rate_set = pll_rate_set / clk_info.lcd_div;
612 else
613 lcd_rate_set = pll_rate_set;
614
615 clk_set_rate(hwtcon->mclk, lcd_rate_set);
616 lcd_rate_set = clk_get_rate(hwtcon->mclk);
617
618 if (lcd_rate_set != lcd_rate)
619 DRM_WARN("Rate to be set:%lu, real clk rate:%lu\n", lcd_rate,
620 lcd_rate_set);
621
622 ret = clk_prepare_enable(hwtcon->mclk);
623 if (ret != 0) {
624 DRM_ERROR("fail enable TCON%d's clock!\n", enc_id);
625 return -1;
626 }
627
628 ret = clk_prepare_enable(hwtcon->mclk_bus);
629 if (ret != 0) {
630 DRM_ERROR("fail enable TCON%d's bus clock!\n", enc_id);
631 return -1;
632 }
633
634 hwtcon->tcon_div = clk_info.tcon_div;
635
636 return 0;
637 }
638
639 /*
640 * referred from sunxi display
641 */
sunxi_tcon_lcd_calc_judge_line(int enc_id,int lcd_id)642 static void sunxi_tcon_lcd_calc_judge_line(int enc_id, int lcd_id)
643 {
644 unsigned int usec_per_line, start_delay;
645 unsigned int usec_start_delay, usec_judge_point;
646 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
647
648 usec_per_line = sunxi_lcd_get_usec_per_line(lcd_id);
649 start_delay = tcon_get_start_delay(enc_id, hwtcon->type);
650 usec_start_delay = start_delay * usec_per_line;
651
652 if (usec_start_delay <= 200)
653 usec_judge_point = usec_start_delay * 3 / 7;
654 else if (usec_start_delay <= 400)
655 usec_judge_point = usec_start_delay / 2;
656 else
657 usec_judge_point = 200;
658
659 hwtcon->judge_line = usec_judge_point / usec_per_line;
660
661 DRM_INFO("[SUNXI-TCON]tcon%d judge_line:%u\n",
662 enc_id, hwtcon->judge_line);
663 }
664
665
666
sunxi_tcon_lcd_prepare(unsigned int enc_id,unsigned int lcd_id)667 static int sunxi_tcon_lcd_prepare(unsigned int enc_id, unsigned int lcd_id)
668 {
669 int ret = 0;
670 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
671 #if defined(HAVE_DEVICE_COMMON_MODULE)
672 struct sunxi_top_if *topif = &tcon_drv->hwtopif;
673 #endif
674
675 hwtcon->is_assigned = true;
676 #if defined(HAVE_DEVICE_COMMON_MODULE)
677 if (topif->mclk) {
678 ret = clk_prepare_enable(topif->mclk);
679 if (ret != 0) {
680 DRM_ERROR("fail enable topif's clock!\n");
681 return -1;
682 }
683 }
684 #endif
685
686 ret = sunxi_tcon_set_clk(enc_id, lcd_id);
687 if (ret < 0) {
688 DRM_ERROR("sunxi_tcon_set_clk failed\n");
689 return -1;
690 }
691
692 return 0;
693 }
694
sunxi_tcon_lcd_unprepare(unsigned int enc_id)695 static void sunxi_tcon_lcd_unprepare(unsigned int enc_id)
696 {
697 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
698 #if defined(HAVE_DEVICE_COMMON_MODULE)
699 struct sunxi_top_if *topif = &tcon_drv->hwtopif;
700 #endif
701
702 clk_disable_unprepare(hwtcon->mclk);
703 #if defined(HAVE_DEVICE_COMMON_MODULE)
704 clk_disable_unprepare(topif->mclk);
705 #endif
706 hwtcon->is_assigned = false;
707 }
708
sunxi_tcon_lcd_sw_set(unsigned int enc_id,unsigned int lcd_id)709 static int sunxi_tcon_lcd_sw_set(unsigned int enc_id, unsigned int lcd_id)
710 {
711 int ret = 0;
712 #if defined(HAVE_DEVICE_COMMON_MODULE)
713 struct sunxi_top_if *topif = &tcon_drv->hwtopif;
714 #endif
715 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
716
717 hwtcon->is_assigned = true;
718 #if defined(HAVE_DEVICE_COMMON_MODULE)
719 if (topif->mclk) {
720 ret = clk_prepare_enable(topif->mclk);
721 if (ret != 0) {
722 DRM_ERROR("fail enable topif's clock!\n");
723 return -1;
724 }
725 }
726 #endif
727
728 ret = clk_prepare_enable(hwtcon->mclk);
729 if (ret != 0) {
730 DRM_ERROR("fail enable TCON%d's clock!\n", enc_id);
731 return -1;
732 }
733
734 hwtcon->is_enabled = true;
735
736 return 0;
737 }
738
739 static int
sunxi_tcon_set_lcd(unsigned int de_id,unsigned int enc_id,unsigned int lcd_id,struct disp_video_timings * video_info,struct sunxi_connector_work_mode * conn_work_mode)740 sunxi_tcon_set_lcd(unsigned int de_id, unsigned int enc_id, unsigned int lcd_id,
741 struct disp_video_timings *video_info,
742 struct sunxi_connector_work_mode *conn_work_mode)
743 {
744 int ret = 0;
745 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
746
747 ret = sunxi_tcon_lcd_prepare(enc_id, lcd_id);
748 if (ret < 0) {
749 DRM_ERROR("sunxi_tcon_lcd_prepare failed\n");
750 return ret;
751 }
752
753 hwtcon->conn_type_id = lcd_id;
754 tcon_init(enc_id);
755 tcon0_set_dclk_div(enc_id, hwtcon->tcon_div);
756 if (tcon0_cfg(enc_id, sunxi_lcd_get_panel_para(lcd_id)) != 0) {
757 DRM_ERROR("lcd cfg fail!\n");
758 return -1;
759 }
760
761 tcon0_cfg_ext(enc_id, sunxi_lcd_get_panel_ext_para(lcd_id));
762
763 tcon0_src_select(enc_id, LCD_SRC_DE, de_id);
764
765 sunxi_tcon_lcd_calc_judge_line(enc_id, lcd_id);
766
767 hwtcon->is_enabled = true;
768
769 return 0;
770 }
771
sunxi_tcon_unset_lcd(unsigned int enc_id)772 static void sunxi_tcon_unset_lcd(unsigned int enc_id)
773 {
774 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
775
776 hwtcon->is_enabled = false;
777 hwtcon->conn_type_id = 0;
778 hwtcon->judge_line = 0;
779
780 tcon_exit(enc_id);
781 sunxi_tcon_lcd_unprepare(enc_id);
782
783 return;
784 }
785 #endif
786
787
788 #if defined(CONFIG_AW_DRM_TV) \
789 || defined(CONFIG_AW_DRM_HDMI14) \
790 || defined(CONFIG_AW_DRM_HDMI20)
sunxi_tcon_set_clk_rate(struct clk * mclk,struct disp_video_timings * timing,struct sunxi_connector_work_mode * conn_work_mode)791 static int sunxi_tcon_set_clk_rate(struct clk *mclk,
792 struct disp_video_timings *timing,
793 struct sunxi_connector_work_mode *conn_work_mode)
794 {
795 struct clk *parent_clk;
796 unsigned long rate = 0, round_rate = 0;
797 long rate_diff = 0;
798 unsigned long parent_rate = 0, parent_round_rate = 0;
799 long parent_rate_diff = 0;
800 unsigned int div = 1;
801
802 parent_clk = clk_get_parent(mclk);
803 if (!parent_clk) {
804 DRM_ERROR("can not get tcon parent clk!\n");
805 return -1;
806 }
807
808 /*calculate rate*/
809 rate = timing->pixel_clk * (timing->pixel_repeat + 1);
810 if (conn_work_mode->color_fmt == COLOR_FMT_YUV420)
811 rate /= 2;
812
813 round_rate = clk_round_rate(mclk, rate);
814 rate_diff = (long)(round_rate - rate);
815 if ((rate_diff > 5000000) || (rate_diff < -5000000)) {
816 for (div = 1; (rate * div) <= 600000000; div++) {
817 parent_rate = rate * div;
818 parent_round_rate = clk_round_rate(parent_clk,
819 parent_rate);
820 parent_rate_diff = (long)(parent_round_rate - parent_rate);
821 if ((parent_rate_diff < 5000000)
822 && (parent_rate_diff > -5000000)) {
823 clk_set_rate(parent_clk, parent_rate);
824 clk_set_rate(mclk, rate);
825 break;
826 }
827 }
828
829 if ((rate * div) > 600000000)
830 clk_set_rate(mclk, rate);
831 } else {
832 clk_set_rate(mclk, rate);
833 }
834
835 return 0;
836
837 }
838
sunxi_tcon_tv_is_support(int nr,int tv_id)839 static bool sunxi_tcon_tv_is_support(int nr, int tv_id)
840 {
841 return sunxi_tcon_type_is_support(nr, DISP_OUTPUT_TYPE_TV);
842 }
843
844 /**
845 * @name :sunxi_tcon_tv_prepare
846 * @brief :config and enable tcon's clks
847 * @param[IN] :enc_id:index of tcon(encoder)
848 * @param[IN] :p_info:video timing of current resolution
849 * @return :0 if success, -1 else
850 */
sunxi_tcon_tv_prepare(unsigned int enc_id,struct disp_video_timings * p_info,struct sunxi_connector_work_mode * conn_work_mode)851 static int sunxi_tcon_tv_prepare(unsigned int enc_id,
852 struct disp_video_timings *p_info,
853 struct sunxi_connector_work_mode *conn_work_mode)
854 {
855 int ret = 0;
856 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
857 #if defined(HAVE_DEVICE_COMMON_MODULE)
858 struct sunxi_top_if *topif = &tcon_drv->hwtopif;
859
860 if (!topif) {
861 DRM_ERROR("Null pointer: topif\n");
862 return -1;
863 }
864 #endif
865
866 if (!hwtcon || !p_info) {
867 DRM_ERROR("Null pointer: hwtcon:%p p_info:%p\n",
868 hwtcon, p_info);
869 return -1;
870 }
871
872 hwtcon->is_assigned = true;
873 #if defined(HAVE_DEVICE_COMMON_MODULE)
874 if (topif->mclk) {
875 ret = clk_prepare_enable(topif->mclk);
876 if (ret != 0) {
877 DRM_ERROR("fail enable topif's clock!\n");
878 return -1;
879 }
880 }
881 #endif
882
883 sunxi_tcon_set_clk_rate(hwtcon->mclk, p_info, conn_work_mode);
884 //clk_set_rate(hwtcon->mclk, p_info->pixel_clk);
885
886 ret = clk_prepare_enable(hwtcon->mclk);
887 if (ret != 0) {
888 DRM_ERROR("fail enable tcon's clock!\n");
889 return -1;
890 }
891
892 return 0;
893 }
894
sunxi_tcon_tv_unprepare(unsigned int enc_id)895 static void sunxi_tcon_tv_unprepare(unsigned int enc_id)
896 {
897 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
898 #if defined(HAVE_DEVICE_COMMON_MODULE)
899 struct sunxi_top_if *topif = &tcon_drv->hwtopif;
900
901 if (!topif) {
902 DRM_ERROR("Null pointer!\n");
903 return;
904 }
905 #endif
906
907 hwtcon->is_assigned = false;
908 clk_disable_unprepare(hwtcon->mclk);
909 #if defined(HAVE_DEVICE_COMMON_MODULE)
910 clk_disable_unprepare(topif->mclk);
911 #endif
912 }
913
sunxi_tcon_sw_set_tv(unsigned int enc_id,unsigned int lcd_id)914 static int sunxi_tcon_sw_set_tv(unsigned int enc_id, unsigned int lcd_id)
915 {
916 int ret = 0;
917 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
918 #if defined(HAVE_DEVICE_COMMON_MODULE)
919 struct sunxi_top_if *topif = &tcon_drv->hwtopif;
920
921 if (!topif) {
922 DRM_ERROR("Null pointer!\n");
923 return -1;
924 }
925 #endif
926
927 if (!hwtcon) {
928 DRM_ERROR("Null pointer!\n");
929 return -1;
930 }
931
932 hwtcon->is_assigned = true;
933 #if defined(HAVE_DEVICE_COMMON_MODULE)
934 if (topif->mclk) {
935 ret = clk_prepare_enable(topif->mclk);
936 if (ret != 0) {
937 DRM_ERROR("fail enable topif's clock!\n");
938 return -1;
939 }
940 }
941 #endif
942
943 ret = clk_prepare_enable(hwtcon->mclk);
944 if (ret != 0) {
945 DRM_ERROR("fail enable tcon's clock!\n");
946 return -1;
947 }
948
949 return 0;
950 }
951
952 /**
953 * @name :sunxi_tcon_unset_tv
954 * @brief :disable specified tcon_tv
955 * @param[IN] :enc_id:index of tcon
956 * @return :NONE
957 */
sunxi_tcon_unset_tv(unsigned int enc_id)958 static void sunxi_tcon_unset_tv(unsigned int enc_id)
959 {
960 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
961
962 hwtcon->is_enabled = false;
963 hwtcon->conn_type_id = 0;
964 hwtcon->judge_line = 0;
965 tcon1_close(enc_id);
966 tcon_irq_disable(enc_id, LCD_IRQ_TCON1_VBLK);
967 tcon_exit(enc_id);
968 #ifndef CONFIG_AW_DRM_DE_SUN50IW1
969 tcon1_hdmi_clk_enable(enc_id, 0);
970 #endif
971
972 sunxi_tcon_tv_unprepare(enc_id);
973 }
974 #endif
975
976 #if defined(CONFIG_AW_DRM_TV)
977 /**
978 * @name :sunxi_tcon_set_tv
979 * @brief :set tcon_tv according disp_video_timings
980 * @param[IN] :enc_id: index of tcon
981 * @param[IN] :tv_id: index of tv
982 * @param[IN] :de_id: index of de
983 * @param[IN] :video_info:pointer of video timing to be set
984 * @param[IN] :tv_type: see definition of disp_tv_output
985 * @return :always 0
986 */
987 static int
sunxi_tcon_set_tv(unsigned int de_id,unsigned int enc_id,unsigned int tv_id,struct disp_video_timings * video_info,struct sunxi_connector_work_mode * conn_work_mode)988 sunxi_tcon_set_tv(unsigned int de_id, unsigned int enc_id, unsigned int tv_id,
989 struct disp_video_timings *video_info,
990 struct sunxi_connector_work_mode *conn_work_mode)
991 {
992 int ret;
993 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
994 enum disp_tv_output tv_type = sunxi_tv_get_interface_type(tv_id);
995
996 ret = sunxi_tcon_tv_prepare(enc_id, video_info, conn_work_mode);
997 if (ret < 0) {
998 DRM_ERROR("sunxi_tcon_tv_prepare failed\n");
999 return ret;
1000 }
1001
1002 hwtcon->conn_type_id = tv_id;
1003
1004 tcon_init(enc_id);
1005 tcon1_set_timming(enc_id, video_info);
1006 if (tv_type != DISP_VGA)
1007 tcon1_yuv_range(enc_id, 1);
1008 tcon1_src_select(enc_id, LCD_SRC_DE, de_id);
1009
1010 hwtcon->judge_line = 4;
1011
1012 hwtcon->is_enabled = true;
1013
1014 tcon1_tv_clk_enable(enc_id, 1);
1015 tcon1_open(enc_id);
1016 if (tv_type == DISP_VGA)
1017 tcon1_out_to_gpio(enc_id);
1018
1019 return 0;
1020 }
1021
1022 #elif defined(CONFIG_AW_DRM_HDMI14) || defined(CONFIG_AW_DRM_HDMI20)
1023 /**
1024 * @name :sunxi_tcon_set_hdmi
1025 * @brief :set tcon_hdmi according disp_video_timings
1026 * @param[IN] :enc_id: index of tcon
1027 * @param[IN] :hdmi_id: index of hdmi
1028 * @param[IN] :de_id: index of de
1029 * @param[IN] :video_info:pointer of video timing to be set
1030 * @return :always 0
1031 */
1032 static int
sunxi_tcon_set_hdmi(unsigned int de_id,unsigned int enc_id,unsigned int hdmi_id,struct disp_video_timings * video_info,struct sunxi_connector_work_mode * conn_work_mode)1033 sunxi_tcon_set_hdmi(unsigned int de_id, unsigned int enc_id, unsigned int hdmi_id,
1034 struct disp_video_timings *video_info,
1035 struct sunxi_connector_work_mode *conn_work_mode)
1036 {
1037 int ret, fps;
1038 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
1039
1040 ret = sunxi_tcon_tv_prepare(enc_id, video_info, conn_work_mode);
1041 if (ret < 0) {
1042 DRM_ERROR("sunxi_tcon_tv_prepare failed\n");
1043 return ret;
1044 }
1045
1046 fps = video_info->pixel_clk / video_info->hor_total_time
1047 /video_info->ver_total_time
1048 * (video_info->b_interlace + 1)
1049 / (video_info->trd_mode + 1);
1050 de_update_device_fps(de_id, fps);
1051
1052 tcon_init(enc_id);
1053 tcon1_set_timming(enc_id, video_info);
1054
1055 /*tcon pol issue*/
1056 #if defined(DISP2_TCON_TV_SYNC_POL_ISSUE)
1057 tcon_set_sync_pol(de_id, !video_info->ver_sync_polarity,
1058 !video_info->hor_sync_polarity);
1059 #endif
1060
1061 /*
1062 * If yuv output(cs != 0), remap yuv plane to (v y u) sequency
1063 * else disable color remap function
1064 */
1065 if (conn_work_mode->color_fmt == COLOR_FMT_RGB444) {
1066 #if defined(CONFIG_AW_DRM_DE_SUN50IW1)
1067 tcon1_hdmi_color_remap(de_id, 0);
1068 #else
1069 tcon1_hdmi_color_remap(de_id, 0, DISP_CSC_TYPE_RGB);
1070 #endif
1071 } else {
1072 #if defined(CONFIG_AW_DRM_DE_SUN50IW1) || defined(CONFIG_AW_DRM_DE_V33X)
1073 tcon1_hdmi_color_remap(de_id, 1);
1074 #else
1075 if (conn_work_mode->color_fmt == COLOR_FMT_YUV444)
1076 tcon1_hdmi_color_remap(de_id, 1, DISP_CSC_TYPE_YUV444);
1077 else if (conn_work_mode->color_fmt == COLOR_FMT_YUV422)
1078 tcon1_hdmi_color_remap(de_id, 1, DISP_CSC_TYPE_YUV422);
1079 else if (conn_work_mode->color_fmt == COLOR_FMT_YUV420)
1080 tcon1_hdmi_color_remap(de_id, 1, DISP_CSC_TYPE_YUV420);
1081 #endif
1082 }
1083
1084 #if defined(CONFIG_AW_DRM_DE_SUN50IW1)
1085 tcon1_src_select(enc_id, 0);
1086 #else
1087 tcon1_src_select(enc_id, LCD_SRC_DE, de_id);
1088 #endif
1089
1090 #if defined(CONFIG_AW_DRM_DE_V33X)
1091 tcon1_black_src(screen_id, 0, conn_work_mode->color_fmt);
1092 #endif
1093
1094 hwtcon->judge_line = 4;
1095
1096 hwtcon->is_enabled = true;
1097
1098 #if !defined(CONFIG_AW_DRM_DE_SUN50IW1)
1099 tcon1_hdmi_clk_enable(enc_id, 1);
1100 #endif
1101
1102 tcon1_open(enc_id);
1103
1104 return 0;
1105 }
1106
1107 #endif
1108
1109 /*
1110 unsigned int sunxi_tcon_get_tcon_type(unsigned int nr)
1111 {
1112 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
1113
1114 return hwtcon->type;
1115 }
1116
1117 unsigned int sunxi_tcon_get_judge_line(unsigned int nr)
1118 {
1119 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
1120
1121 return hwtcon->judge_line;
1122 }
1123
1124 unsigned int sunxi_tcon_get_cur_line(unsigned int nr)
1125 {
1126 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
1127
1128 return tcon_get_cur_line(nr, hwtcon->type);
1129 }
1130
1131 unsigned int sunxi_tcon_get_start_delay(unsigned int nr)
1132 {
1133 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
1134
1135 return tcon_get_start_delay(nr, hwtcon->type);
1136 }
1137 */
1138
1139 /*
1140 * referred from sunxi display
1141 */
sunxi_tcon_sync_time_is_enough(unsigned int nr)1142 bool sunxi_tcon_sync_time_is_enough(unsigned int nr)
1143 {
1144 int cur_line, judge_line, start_delay;
1145 unsigned int tcon_type;
1146 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(nr);
1147
1148 tcon_type = hwtcon->type;
1149 judge_line = hwtcon->judge_line;
1150
1151 cur_line = tcon_get_cur_line(nr, tcon_type);
1152 start_delay = tcon_get_start_delay(nr, tcon_type);
1153
1154 /*
1155 DRM_INFO("cur_line:%d start_delay:%d judge_line:%d\n",
1156 cur_line, start_delay, judge_line);
1157 */
1158
1159 if (cur_line <= (start_delay - judge_line))
1160 return true;
1161
1162 return false;
1163 }
1164
sunxi_tcon_init_al(void)1165 static int sunxi_tcon_init_al(void)
1166 {
1167 int i;
1168 struct disp_bsp_init_para *para;
1169
1170 para = sunxi_disp_get_bsp_init_para();
1171 if (!para) {
1172 DRM_ERROR("sunxi_disp_get_bsp_init_para failed\n");
1173 return -1;
1174 }
1175
1176 for (i = 0; i < tcon_drv->tcon_cnt; i++) {
1177 para->reg_base[DISP_MOD_LCD0 + i]
1178 = tcon_drv->hwtcon[i].reg_base;
1179 tcon_set_reg_base(i, tcon_drv->hwtcon[i].reg_base);
1180 }
1181
1182 #if defined(HAVE_DEVICE_COMMON_MODULE)
1183 para->reg_base[DISP_MOD_DEVICE] = tcon_drv->hwtopif.reg_base;
1184 tcon_top_set_reg_base(0, para->reg_base[DISP_MOD_DEVICE]);
1185 #endif
1186
1187 return 0;
1188 }
1189
1190 static struct sunxi_tcon_funcs lcd_tcon = {
1191 .is_use_irq = sunxi_tcon_is_use_irq,
1192 .get_irq_no = sunxi_tcon_get_irq_no,
1193
1194 #ifdef CONFIG_AW_DRM_LCD
1195 .conn_is_support = sunxi_tcon_lcd_is_support,
1196 .set = sunxi_tcon_set_lcd,
1197 .sw_set = sunxi_tcon_lcd_sw_set,
1198 .unset = sunxi_tcon_unset_lcd,
1199 #endif
1200 };
1201
1202
1203 static struct sunxi_tcon_funcs tv_tcon = {
1204 .is_use_irq = sunxi_tcon_is_use_irq,
1205 .get_irq_no = sunxi_tcon_get_irq_no,
1206 #ifdef CONFIG_AW_DRM_TV
1207 .conn_is_support = sunxi_tcon_tv_is_support,
1208 .set = sunxi_tcon_set_tv,
1209 .sw_set = sunxi_tcon_sw_set_tv,
1210 .unset = sunxi_tcon_unset_tv,
1211 #endif
1212 };
1213
1214 static struct sunxi_tcon_funcs hdmi_tcon = {
1215 .is_use_irq = sunxi_tcon_is_use_irq,
1216 .get_irq_no = sunxi_tcon_get_irq_no,
1217 #if defined(CONFIG_AW_DRM_HDMI14) || defined(CONFIG_AW_DRM_HDMI20)
1218 .conn_is_support = sunxi_tcon_tv_is_support,
1219 .set = sunxi_tcon_set_hdmi,
1220 .sw_set = sunxi_tcon_sw_set_tv,
1221 .unset = sunxi_tcon_unset_tv,
1222 #endif
1223 };
1224
1225 struct sunxi_tcon_funcs *
sunxi_tcon_attach_connector_type(int enc_id,int conn_type)1226 sunxi_tcon_attach_connector_type(int enc_id, int conn_type)
1227 {
1228 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
1229
1230 if (conn_type == DISP_OUTPUT_TYPE_LCD) {
1231 hwtcon->funcs = &lcd_tcon;
1232 } else if (conn_type == DISP_OUTPUT_TYPE_TV) {
1233 hwtcon->funcs = &tv_tcon;
1234 } else if (conn_type == DISP_OUTPUT_TYPE_HDMI) {
1235 hwtcon->funcs = &hdmi_tcon;
1236 } else {
1237 hwtcon->funcs = NULL;
1238 DRM_ERROR("NOT support connector type:%d\n", conn_type);
1239 }
1240
1241 return hwtcon->funcs;
1242 }
1243
sunxi_tcon_unattach_connector_type(int enc_id)1244 void sunxi_tcon_unattach_connector_type(int enc_id)
1245 {
1246 struct sunxi_tcon *hwtcon = sunxi_tcon_get_tcon(enc_id);
1247
1248 hwtcon->funcs = NULL;
1249 }
1250
sunxi_tcon_parse_dts(struct platform_device * pdev)1251 static int sunxi_tcon_parse_dts(struct platform_device *pdev)
1252 {
1253 struct device_node *node;
1254 struct sunxi_tcon *hwtcon;
1255 int i = 0, index = 0;
1256 char id[32];
1257 char id2[32];
1258
1259 node = pdev->dev.of_node;
1260 if (!node) {
1261 DRM_ERROR("get sunxi-tcon node err.\n ");
1262 return -EINVAL;
1263 }
1264
1265 /*parse disp_if_top*/
1266 index = 0;
1267 #if defined(HAVE_DEVICE_COMMON_MODULE)
1268 tcon_drv->hwtopif.reg_base =
1269 (uintptr_t __force)of_iomap(node, index++);
1270 if (!tcon_drv->hwtopif.reg_base) {
1271 DRM_ERROR("unable to map device common module registers\n");
1272 return -EINVAL;
1273 }
1274 DRM_DEBUG_DRIVER("[SUNXI-TCON]get disp_if_top reg_base:0x%lx\n",
1275 tcon_drv->hwtopif.reg_base);
1276
1277 tcon_drv->hwtopif.mclk = devm_clk_get(&pdev->dev, "clk_bus_dpss_top0");
1278 if (IS_ERR(tcon_drv->hwtopif.mclk)) {
1279 DRM_ERROR("fail to get clk for device common module\n");
1280 return -EINVAL;
1281 }
1282 #endif
1283
1284 for (i = 0; i < tcon_drv->tcon_cnt; i++) {
1285 sprintf(id, "clk_tcon%d", i);
1286 sprintf(id2, "clk_bus_tcon%d", i);
1287
1288 hwtcon = &tcon_drv->hwtcon[i];
1289
1290 hwtcon->reg_base
1291 = (uintptr_t __force)of_iomap(node, index);
1292 if (!hwtcon->reg_base) {
1293 DRM_ERROR("unable to map tcon:%d registers\n", i);
1294 return -1;
1295 }
1296
1297 hwtcon->irq_no = irq_of_parse_and_map(node, i);
1298 if (!hwtcon->irq_no) {
1299 DRM_ERROR("get irq no of tcon%d failed\n", i);
1300 return -1;
1301 }
1302 DRM_DEBUG_DRIVER("[SUNXI-TCON]TCON%d get irq_no:%d\n",
1303 i, hwtcon->irq_no);
1304
1305 hwtcon->mclk = devm_clk_get(&pdev->dev, id);
1306 if (IS_ERR(hwtcon->mclk)) {
1307 DRM_ERROR("fail to get clk for tcon %d\n", i);
1308 return -1;
1309 }
1310
1311 hwtcon->mclk_bus = devm_clk_get(&pdev->dev, id2);
1312 if (IS_ERR(hwtcon->mclk_bus)) {
1313 DRM_ERROR("fail to get clk bus for tcon %d\n", i);
1314 return -1;
1315 }
1316 index++;
1317 }
1318
1319 return 0;
1320 }
1321
sunxi_tcon_probe(struct platform_device * pdev)1322 static int sunxi_tcon_probe(struct platform_device *pdev)
1323 {
1324 int i;
1325 struct sunxi_tcon *hwtcon;
1326 unsigned int output_type; /*type outputdata from tcon and connector*/
1327
1328 DRM_INFO("[SUNXI-TCON] sunxi_tcon_probe start\n");
1329
1330 tcon_drv->pdev = pdev;
1331
1332 #ifndef CONFIG_AW_DRM_DE_V33X
1333 if (de_feat_init()) {
1334 DRM_ERROR("de_feat_init failed\n");
1335 goto tcon_err;
1336 }
1337 #endif
1338
1339 tcon_drv->tcon_cnt = de_feat_get_num_devices();
1340 if ((tcon_drv->tcon_cnt <= 0) || (tcon_drv->tcon_cnt > TCON_NUM_MAX)) {
1341 DRM_ERROR("get wrong tcon count:%d\n",
1342 tcon_drv->tcon_cnt);
1343 goto tcon_err;
1344 }
1345
1346 for (i = 0; i < tcon_drv->tcon_cnt; i++) {
1347 hwtcon = &tcon_drv->hwtcon[i];
1348 hwtcon->id = i;
1349
1350 /*judge the type of tcon*/
1351 #ifdef CONFIG_AW_DRM_DE_V33X
1352 output_type = de_feat_get_supported_output_types(i);
1353 #else
1354 output_type = DISP_OUTPUT_TYPE_LCD; /* Fix me */
1355 #endif
1356 if ((output_type & DISP_OUTPUT_TYPE_LCD)
1357 || (output_type & DISP_OUTPUT_TYPE_EDP)) {
1358 tcon_drv->tcon_lcd_cnt++;
1359 hwtcon->type_id = tcon_drv->tcon_lcd_cnt - 1;
1360 hwtcon->type = TCON_LCD;
1361 DRM_DEBUG_DRIVER("[SUNXI-TCON]tcon%d output_type:%s"
1362 " is TCON_LCD%d\n",
1363 hwtcon->id, sunxi_tcon_get_output_type_name(output_type),
1364 hwtcon->type_id);
1365 } else if ((output_type & DISP_OUTPUT_TYPE_HDMI)
1366 || (output_type & DISP_OUTPUT_TYPE_TV)
1367 || (output_type & DISP_OUTPUT_TYPE_VGA)) {
1368 tcon_drv->tcon_tv_cnt++;
1369 hwtcon->type_id = tcon_drv->tcon_tv_cnt - 1;
1370 hwtcon->type = TCON_TV;
1371 DRM_DEBUG_DRIVER("[SUNXI-TCON]tcon%d output_type:%s"
1372 " is TCON_TV%d\n",
1373 hwtcon->id, sunxi_tcon_get_output_type_name(output_type),
1374 hwtcon->type_id);
1375 } else {
1376 DRM_ERROR("tcon%d has wrong type:%s\n",
1377 i,
1378 sunxi_tcon_get_output_type_name(output_type));
1379 return -1;
1380 }
1381 }
1382
1383 if (sunxi_tcon_parse_dts(pdev) < 0) {
1384 DRM_ERROR("sunxi_tcon_parse_dts failed\n");
1385 goto tcon_err;
1386 }
1387
1388 if (sunxi_tcon_init_al() < 0) {
1389 DRM_ERROR("sunxi_tcon_init_al failed\n");
1390 goto tcon_err;
1391 }
1392
1393 return 0;
1394
1395 tcon_err:
1396 DRM_ERROR("[SUNXI-DE] sunxi_tcon_probe FAILED\n");
1397 return -EINVAL;
1398 }
1399
sunxi_tcon_remove(struct platform_device * pdev)1400 static int sunxi_tcon_remove(struct platform_device *pdev)
1401 {
1402 return 0;
1403 }
1404
1405 /*Note: sunxi-lcd is represented of sunxi tcon,
1406 *using lcd is order to be same with sunxi display driver
1407 */
1408 static const struct of_device_id sunxi_tcon_match[] = {
1409
1410 { .compatible = "allwinner,sunxi-tcon", },
1411 {},
1412 };
1413
1414 struct platform_driver sunxi_tcon_platform_driver = {
1415 .probe = sunxi_tcon_probe,
1416 .remove = sunxi_tcon_remove,
1417 .driver = {
1418 .name = "tcon",
1419 .owner = THIS_MODULE,
1420 .of_match_table = sunxi_tcon_match,
1421 },
1422 };
1423
sunxi_tcon_module_init(void)1424 int sunxi_tcon_module_init(void)
1425 {
1426 int ret = 0, err;
1427 struct drv_model_info *drv_model;
1428
1429 DRM_INFO("[SUNXI-TCON]sunxi_tcon_module_init\n");
1430
1431 tcon_drv = kzalloc(sizeof(struct sunxi_tcon_drv), GFP_KERNEL);
1432 if (!tcon_drv) {
1433 DRM_ERROR("can NOT allocate memory for tcon_drv\n");
1434 goto tcon_err;
1435 }
1436
1437 drv_model = &tcon_drv->drv_model;
1438
1439 if (alloc_chrdev_region(&drv_model->devid, 0, 1, "tcon") < 0) {
1440 DRM_ERROR("alloc_chrdev_region failed\n");
1441 goto tcon_err;
1442 }
1443
1444 drv_model->cdev = cdev_alloc();
1445 if (!drv_model->cdev) {
1446 DRM_ERROR("cdev_alloc failed\n");
1447 goto tcon_err;
1448 }
1449
1450 cdev_init(drv_model->cdev, NULL);
1451 drv_model->cdev->owner = THIS_MODULE;
1452 err = cdev_add(drv_model->cdev, drv_model->devid, 1);
1453 if (err) {
1454 DRM_ERROR("cdev_add major number:%d failed\n",
1455 MAJOR(drv_model->devid));
1456 goto tcon_err;
1457 }
1458
1459 drv_model->sysclass = class_create(THIS_MODULE, "tcon");
1460 if (IS_ERR(drv_model->sysclass)) {
1461 DRM_ERROR("create class error\n");
1462 goto tcon_err;
1463 }
1464
1465 drv_model->dev = device_create(drv_model->sysclass, NULL,
1466 drv_model->devid, NULL, "tcon");
1467 if (!drv_model->dev) {
1468 DRM_ERROR("device_create failed\n");
1469 goto tcon_err;
1470 }
1471
1472 ret = platform_driver_register(&sunxi_tcon_platform_driver);
1473 if (ret) {
1474 DRM_ERROR("platform_driver_register failed\n");
1475 goto tcon_err;
1476 }
1477
1478 ret = sysfs_create_group(&drv_model->dev->kobj,
1479 &tcon_attribute_group);
1480 if (ret < 0) {
1481 DRM_ERROR("sysfs_create_file fail!\n");
1482 goto tcon_err;
1483 }
1484
1485 DRM_INFO("[SUNXI-TCON]sunxi_tcon_module_init end\n\n");
1486 return 0;
1487
1488 tcon_err:
1489 kfree(tcon_drv);
1490 DRM_ERROR("sunxi_tcon_module_init FAILED\n");
1491 return -1;
1492 }
1493
sunxi_tcon_module_exit(void)1494 void sunxi_tcon_module_exit(void)
1495 {
1496 struct drv_model_info *drv_model;
1497
1498 DRM_INFO("[SUNXI-TCON]sunxi_tcon_module_exit\n");
1499
1500 drv_model = &tcon_drv->drv_model;
1501 platform_driver_unregister(&sunxi_tcon_platform_driver);
1502 device_destroy(drv_model->sysclass, drv_model->devid);
1503 class_destroy(drv_model->sysclass);
1504
1505 cdev_del(drv_model->cdev);
1506 kfree(tcon_drv);
1507 }
1508
1509