• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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