1397 static int __init enable_tve_setup(char *options)
1398 {
1399 g_enable_tve = true;
1400
1401 return 1;
1402 }
1403 __setup("tve", enable_tve_setup);
一般情况,freescale会推荐在kernel命令行参数中使能TVE, 这个函数就是处理kernel传入的参数
'init=/init androidboot.console=ttymxc0 di1_primary video=mxcdi1fb:YUV444, 720x576-i@50 tve'
1198 static int tve_probe(struct platform_device *pdev)
1199 {1215 映射TVE寄存器地址到,结果保存到tve.base中
1217~1225 注册Cable Detect handler
1227 创建headphone属性文件,可以通过cat /sys/devices/platform/tve.0/headphone查看当前 cable 状态。
1231~1243 如果在kernel cmdline中指定了di1_primary,那么registered_fb[0] 的id就是"DISP3 BG - DI1",此时关闭fb_blank
1257 ~ 1266 获取di1和TVE的时钟
1267 ~ 1269 设置TVE的时钟rate,设置tve_clk1为di1_clk的父亲,这样启动di1就会为tve时钟上电;
1269 使能tve clk,这样才能操作tve的寄存器。
1271 ~ 1278 获取tve 版本;根据tve 的版本,来选择要操作的寄存器集合,
1292 ~ 1306 video_modes PAL, NTSC, 720P 是rev1和rev2都支持的video mode,rev2支持其他集中模式,XGA和SXGA仅在53上支持。
1309 初始化一个动作队列,这个工作队列是用来处理cable detect
1310 ~ 1313 设置cable detect register
1325 已经操作完TVE的寄存器,可以关闭clk了
1327 注册一个notifier callback,这样在fb event事件发生时就可以调用这个callback
1334 TVE是primary frame buffer,所以要显示logo
1343 不知道为什么要设置为 var.yres * 3 以后回来再看
1348~1356 关闭frame buffer再打开frame buffer
1179 static int _tve_get_revision(void)
1180 {
1181 u32 conf_reg;
1182 u32 rev = 0;
1183
1184 /* find out TVE rev based on the base addr default value
1185 * can be used at the init/probe ONLY */
1186 conf_reg = __raw_readl(tve.base);
1187 switch (conf_reg) {
1188 case 0x00842000:
1189 rev = 1;
1190 break;
1191 case 0x00100000:
1192 rev = 2;
1193 break;
1194 }
1195 return rev;
1196 }
通过读取0xBASE_0000寄存器的初始缺省值来获取TVE的版本号
1147 static ssize_t show_headphone(struct device *dev,
1148 struct device_attribute *attr, char *buf)
1149 {
1150 int detect;
1151
1152 if (!enabled) {
1153 strcpy(buf, "tve power off\n");
1154 return strlen(buf);
1155 }
1156
1157 detect = tve_update_detect_status();
1158
1159 if (detect == 0)
1160 strcpy(buf, "none\n");
1161 else if (detect == 1)
1162 strcpy(buf, "cvbs\n");
1163 else if (detect == 2)
1164 strcpy(buf, "headset\n");
1165 else if (detect == 3)
1166 strcpy(buf, "component\n");
1167 else
1168 strcpy(buf, "svideo\n");
1169
1170 return strlen(buf);
1171 }
这个函数是一个sys接口的show函数,用来显示当前cable的插入状态,cat /sys/devices/platform/tve.0/headphone将显示相应状态
1157是真正的cable检测函数
643 ~ 647 TVE当前是disable的,所以不会做检测,直接返回tve.detect=0
650 ~655 应该使能CD_EN
657 ~ 669 每2ms检测一次tve_stat_reg,直到CD_MON_END_INT发生或者timeout
670 ~ 673 无CD_MON_END_INT中断发生,返回
675~710 根据tve_state_reg中的CD_CH_x_LM_ST和CD_CH_x_SM_ST来决定cable的插入状态
712 tve_set_tvout_mode是根据当前的cable 状态来调整tve out 模式
715 ~ 717 清空tve_stat_reg的cable detect状态位,写1清空
718 ~ 719 使能CD_SM_INT和CD_LM_INT,检测 cable的插拔
721 ~ 722 上报headphone事件给poll这个headphone文件描述符的进程
918 ~ 919 因为tve_fb_event被调用时不知道是不是tve的事件,如果不是,不做处理
922 ~ 943 FB_EVENT_FB_REGISTERED,那么要把NTSC PAL 720P加入到mode_list中,如果是rev2再加入一些mode,如果是mx53还支持XGA SXGA模式
944 ~ 1038 处理模式发生变化的情况
949 ~ 958 先检查新mode是否在tve_modelist中,不在的话则tve_disable
963 ~ 1032 1. 调用tve_set_di_fmt设置display input format; 2. disable tve; 3. tve_setup设置TVE 输出模式; 4.如果tve_fbi是UNBLANK的,那么还要调用tve_enable使能TVE
1033 ~ 1036 disable TVE,设置TVE
1128 ~ 1131 unblank操作,dsiable tve设置tve.blank为FB_BLANK_POWERDOWN
1043 ~ 1127 FB_BLANK_UNBLANK并且TVE当前状态不是FB_BLANK_UNBLANK,如果当前mode不是ffbi的当前模式,那么设置为当前模式,最后还要使能TVE
该函数调用fbi->fbops->fb_ioctl设置 display format,由于fmt是内核空间,而fb_ioctl系统调用会确定&fmt是用户空间,
所以需要把当前的fs设置为内核空间,避开这个检查。参见http://blog.chinaunix.net/space.php?uid=20564848&do=blog&cuid=2097853
771 static irqreturn_t tve_detect_handler(int irq, void *data)
772 {
773 u32 int_ctl = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
774
775 /* disable INT first */
776 int_ctl &= ~(CD_SM_INT | CD_LM_INT | CD_MON_END_INT);
777 __raw_writel(int_ctl, tve.base + tve_regs->tve_int_cont_reg);
778
779 __raw_writel(CD_MON_END_INT | CD_LM_INT | CD_SM_INT,
780 tve.base + tve_regs->tve_stat_reg);
781
782 schedule_delayed_work(&tve.cd_work, msecs_to_jiffies(1000));
783
784 return IRQ_HANDLED;
785 }
中断处理函数,首先disable INT清除中断状态位,然后清除掉tve_stat_reg中相应的状态位,这里我奇怪为什么要清除CD_MON_END_INT,因为tve_update_detect_status要使用这个标志呀
615 static void tve_disable(void)
616 {
617 u32 reg;
618 unsigned long lock_flags;
619
620 spin_lock_irqsave(&tve_lock, lock_flags);
621 if (enabled) {
622 enabled = 0;
623 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
624 __raw_writel(reg & ~TVE_ENABLE & ~TVE_IPU_CLK_ENABLE,
625 tve.base + tve_regs->tve_com_conf_reg);
626 clk_disable(tve.clk);
627 pr_debug("TVE power off.\n");
628 }
629 spin_unlock_irqrestore(&tve_lock, lock_flags);
630 }
关闭TVE和TVE IPU clock,关闭tve.clk时钟,置位enable为0
578 static void tve_enable(void)
579 {
580 u32 reg;
581 unsigned long lock_flags;
582
583 spin_lock_irqsave(&tve_lock, lock_flags);
584 if (!enabled) {
585 enabled = 1;
586 clk_enable(tve.clk);
587 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
588 __raw_writel(reg | TVE_IPU_CLK_ENABLE | TVE_ENABLE,
589 tve.base + tve_regs->tve_com_conf_reg);
590 pr_debug("TVE power on.\n");
591 }
592
593 if (is_vga_mode()) {
594 /* disable interrupt */
595 pr_debug("TVE VGA disable cable detect.\n");
596 __raw_writel(0xffffffff, tve.base + tve_regs->tve_stat_reg);
597 __raw_writel(0, tve.base + tve_regs->tve_int_cont_reg);
598 } else {
599 /* enable interrupt */
600 pr_debug("TVE TVE enable cable detect.\n");
601 __raw_writel(0xffffffff, tve.base + tve_regs->tve_stat_reg);
602 __raw_writel(CD_SM_INT | CD_LM_INT | CD_MON_END_INT,
603 tve.base + tve_regs->tve_int_cont_reg);
604 }
605
606 spin_unlock_irqrestore(&tve_lock, lock_flags);
607
608 tve_dump_regs();
609 }
584 ~ 591 使能tve.clk,使能TVE以及TVE IPU clock
593 ~ 598 对于TVE VGA模式关闭cable detect
598 ~ 604 对于其他的模式使能cable detect
415 static int tve_setup(int mode)
416 {
417 u32 reg;
418 struct clk *tve_parent_clk;
419 unsigned long parent_clock_rate = 216000000, di1_clock_rate = 27000000;
420 unsigned long tve_clock_rate = 216000000;
421 unsigned long lock_flags;
422
423 if (tve.cur_mode == mode)
424 return 0;
425
426 spin_lock_irqsave(&tve_lock, lock_flags);
427
428 switch (mode) {
429 case TVOUT_FMT_PAL:
430 case TVOUT_FMT_NTSC:
431 parent_clock_rate = 216000000;
432 di1_clock_rate = 27000000;
433 break;
434 case TVOUT_FMT_720P60:
435 case TVOUT_FMT_1080I60:
436 case TVOUT_FMT_1080I50:
437 case TVOUT_FMT_720P30:
438 case TVOUT_FMT_1080P30:
439 case TVOUT_FMT_1080P25:
440 case TVOUT_FMT_1080P24:
441 parent_clock_rate = 297000000;
442 tve_clock_rate = 297000000;
443 di1_clock_rate = 74250000;
444 break;
445 case TVOUT_FMT_VGA_XGA:
446 parent_clock_rate = 260000000;
447 tve_clock_rate = 130000000;
448 di1_clock_rate = 65000000;
449 break;
450 case TVOUT_FMT_VGA_SXGA:
451 parent_clock_rate = 216000000;
452 di1_clock_rate = 108000000;
453 break;
454 }
455 if (enabled)
456 clk_disable(tve.clk);
457
458 tve_parent_clk = clk_get_parent(tve.clk);
459
460 clk_set_rate(tve_parent_clk, parent_clock_rate);
461
462 tve_clock_rate = clk_round_rate(tve.clk, tve_clock_rate);
463 clk_set_rate(tve.clk, tve_clock_rate);
464
465 clk_enable(tve.clk);
466 di1_clock_rate = clk_round_rate(tve.di_clk, di1_clock_rate);
467 clk_set_rate(tve.di_clk, di1_clock_rate);
468
469 tve.cur_mode = mode;
470
471 /* select output video format */
472 if (mode == TVOUT_FMT_PAL) {
473 tve_disable_vga_mode();
474 tve_set_tvout_mode(YPBPR);
475 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
476 reg = (reg & ~TVE_STAND_MASK) | TVE_PAL_STAND;
477 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
478 pr_debug("TVE: change to PAL video\n");
479 } else if (mode == TVOUT_FMT_NTSC) {
480 tve_disable_vga_mode();
481 tve_set_tvout_mode(YPBPR);
482 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
483 reg = (reg & ~TVE_STAND_MASK) | TVE_NTSC_STAND;
484 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
485 pr_debug("TVE: change to NTSC video\n");
486 } else if (mode == TVOUT_FMT_720P60) {
487 tve_disable_vga_mode();
488 if (!_is_tvout_mode_hd_compatible()) {
489 tve_set_tvout_mode(YPBPR);
490 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
491 }
492 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
493 reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P60_STAND;
494 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
495 pr_debug("TVE: change to 720P60 video\n");
496 } else if (mode == TVOUT_FMT_720P30) {
497 tve_disable_vga_mode();
498 if (!_is_tvout_mode_hd_compatible()) {
499 tve_set_tvout_mode(YPBPR);
500 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
501 }
502 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
503 reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P30_STAND;
504 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
505 pr_debug("TVE: change to 720P30 video\n");
506 } else if (mode == TVOUT_FMT_1080I60) {
507 tve_disable_vga_mode();
508 if (!_is_tvout_mode_hd_compatible()) {
509 tve_set_tvout_mode(YPBPR);
510 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
511 }
512 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
513 reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080I60_STAND;
514 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
515 pr_debug("TVE: change to 1080I60 video\n");
516 } else if (mode == TVOUT_FMT_1080I50) {
517 tve_disable_vga_mode();
518 if (!_is_tvout_mode_hd_compatible()) {
519 tve_set_tvout_mode(YPBPR);
520 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
521 }
522 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
523 reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080I50_STAND;
524 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
525 pr_debug("TVE: change to 1080I50 video\n");
526 } else if (mode == TVOUT_FMT_1080P30) {
527 tve_disable_vga_mode();
528 if (!_is_tvout_mode_hd_compatible()) {
529 tve_set_tvout_mode(YPBPR);
530 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
531 }
532 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
533 reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P30_STAND;
534 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
535 pr_debug("TVE: change to 1080P30 video\n");
536 } else if (mode == TVOUT_FMT_1080P25) {
537 tve_disable_vga_mode();
538 if (!_is_tvout_mode_hd_compatible()) {
539 tve_set_tvout_mode(YPBPR);
540 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
541 }
542 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
543 reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P25_STAND;
544 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
545 pr_debug("TVE: change to 1080P25 video\n");
546 } else if (mode == TVOUT_FMT_1080P24) {
547 tve_disable_vga_mode();
548 if (!_is_tvout_mode_hd_compatible()) {
549 tve_set_tvout_mode(YPBPR);
550 pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
551 }
552 reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
553 reg = (reg & ~TVE_STAND_MASK) | TVE_HD1080P24_STAND;
554 __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
555 pr_debug("TVE: change to 1080P24 video\n");
556 } else if ((mode == TVOUT_FMT_VGA_XGA) || (mode == TVOUT_FMT_VGA_SXGA)) {
557 /* do not need cable detect */
558 tve_setup_vga();
559 pr_debug("TVE: change to VGA video\n");
560 } else if (mode == TVOUT_FMT_OFF) {
561 __raw_writel(0x0, tve.base + tve_regs->tve_com_conf_reg);
562 pr_debug("TVE: change to OFF video\n");
563 } else {
564 pr_debug("TVE: no such video format.\n");
565 }
566
567 if (!enabled)
568 clk_disable(tve.clk);
569
570 spin_unlock_irqrestore(&tve_lock, lock_flags);
571 return 0;
572 }
428 ~ 454根据输出模式,设置时钟屏率,这里没搞明白这几个时钟的关系,以后再分析
455和465 先关闭再使能,为了保证所有情况下都打开tve.clk,以便下面对tve的操作。
458 ~ 467 设置parent clock, tve clk以及di1 clock的时钟频率
472 ~565 根据mode设置TVE的输出格式,这里没看明白的就是474行,传入的 mode为什么是YPBPR? 不是应该传入CVBS0吗,看不懂,觉得这里有问题,或者这里需要根据情况hard code;然后把输出模式设置到tve_com_conf_reg中
567 ~ 568 已经用完tve.clk,关闭它
333 static void tve_set_tvout_mode(int mode)
334 {
335 u32 conf_reg;
336
337 /* clear sync_ch and tvout_mode fields */
338 conf_reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
339 conf_reg &= ~(tve_reg_fields->sync_ch_mask |
340 tve_reg_fields->tvout_mode_mask);
341
342 conf_reg = conf_reg & ~TVE_DAC_SAMPRATE_MASK;
343 if (tve.revision == 2) {
344 conf_reg = (conf_reg & ~TVEV2_DATA_SRC_MASK) |
345 TVEV2_DATA_SRC_BUS_1;
346 conf_reg = conf_reg & ~TVEV2_INP_VIDEO_FORM;
347 conf_reg = conf_reg & ~TVEV2_P2I_CONV_EN;
348 }
349
350 conf_reg |=
351 mode << tve_reg_fields->
352 tvout_mode_offset | tvout_mode_to_channel_map[mode] <<
353 tve_reg_fields->sync_ch_offset;
354 __raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
355 }
344 ~ 345 设置TVEV2_DATA_SRC 为BUS_1,这里还是hard code,其实我觉得也可以设置为BUS_2,那么数据就从IPU data bus2过来
346 设置input video的格式为YCbCr422
347 禁止progressive 到interlace的转换,我觉得如果是要得到NTSC PAL输出,最好使能它
350 ~ 355 根据给定的模式设置TV_OUT_MODE, 以及SYNC_CH_X_EN
小结:
分析后,如果想要实现自己的LCD+cvbs双屏输入,还需要定制一些参数,也许没看懂,做freescale的东西,真累。