/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/display/vout/cvbs.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include #include #include #include #include #include #include "cvbs_regs.h" #include "cvbs_config.h" #include "cvbs.h" #include "vdac.h" /*----------------------------------------------------------------------------*/ static struct cvbs_drv_s cvbs_drv = { .vdac_data = NULL, }; static struct cvbs_vdac_data_s vdac_data_default = { .vref_adj = 0x0, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_gxl = { .vref_adj = 0xb, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_txl = { .vref_adj = 0x2, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_g12a = { .vref_adj = 0x10, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_g12b = { .vref_adj = 0xf, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_sm1 = { .vref_adj = 0xf, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_tl1 = { .vref_adj = 0x10, .gsw = 0x0, }; static struct cvbs_vdac_data_s vdac_data_tm2 = { .vref_adj = 0x10, .gsw = 0x0, }; unsigned int cvbs_mode = VMODE_MAX; /*bit[0]: 0=vid_pll, 1=gp0_pll*/ /*bit[1]: 0=vid2_clk, 1=vid1_clk*/ /*path 0:vid_pll vid2_clk*/ /*path 1:gp0_pll vid2_clk*/ /*path 2:vid_pll vid1_clk*/ /*path 3:gp0_pll vid1_clk*/ static unsigned int s_enci_clk_path = 0; /*----------------------------------------------------------------------------*/ // interface for registers of soc #define REG_OFFSET_VCBUS(reg) (((reg) << 2)) #define REG_OFFSET_CBUS(reg) (((reg) << 2)) /* memory mapping */ #define REG_ADDR_HIU(reg) (reg + 0L) #define REG_ADDR_VCBUS(reg) (REG_BASE_VCBUS + REG_OFFSET_VCBUS(reg)) #define REG_ADDR_CBUS(reg) (REG_BASE_CBUS + REG_OFFSET_CBUS(reg)) static unsigned int cvbs_get_hiu_logic_addr(unsigned int addr_offset) { return (REG_BASE_HIU + ((addr_offset&0xff)<<2)); } static int cvbs_write_cbus(unsigned int addr_offset, unsigned int value) { *(volatile unsigned int *)REG_ADDR_CBUS(addr_offset) = (value); return 0; } static int cvbs_read_cbus(unsigned int addr_offset) { unsigned int val = 0; val = *(volatile unsigned int *)(REG_ADDR_CBUS(addr_offset)); return val; } static int cvbs_write_hiu(unsigned int addr, unsigned int value) { *(volatile unsigned int *)REG_ADDR_HIU(addr) = (value); return 0; } static int cvbs_read_hiu(unsigned int addr) { unsigned int val = 0; val = *(volatile unsigned int *)(REG_ADDR_HIU(addr)); return val; } static int cvbs_set_hiu_bits(unsigned int addr, unsigned int value, unsigned int start, unsigned int len) { cvbs_write_hiu(addr, ((cvbs_read_hiu(addr) & ~(((1L << (len))-1) << (start))) | (((value)&((1L<<(len))-1)) << (start)))); return 0; } static int cvbs_get_hiu_bits(unsigned int addr, unsigned int start, unsigned int len) { return (cvbs_read_hiu(addr) >> (start)) & ((1L << (len)) - 1); } static unsigned int cvbs_read_vcbus(unsigned int addr_offset) { unsigned int val = 0; val = *(volatile unsigned int *)(REG_ADDR_VCBUS(addr_offset)); return val; } static int cvbs_write_vcbus(unsigned int addr_offset, unsigned int value) { *(volatile unsigned int *)REG_ADDR_VCBUS(addr_offset) = (value); return 0; } static int cvbs_set_vcbus_bits(unsigned int addr_offset, unsigned int value, unsigned int start, unsigned int len) { cvbs_write_vcbus(addr_offset, ((cvbs_read_vcbus(addr_offset) & ~(((1L << (len))-1) << (start))) | (((value)&((1L<<(len))-1)) << (start)))); return 0; } /*----------------------------------------------------------------------------*/ // interface for cpu id checking static int check_cpu_type(unsigned int cpu_type) { return (cvbs_read_cbus(ASSIST_HW_REV)==cpu_type); } static bool inline is_equal_after_meson_cpu(unsigned int id) { return (get_cpu_id().family_id >= id)?1:0; } static bool inline is_meson_gxl_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_GXL)? 1:0; } static bool inline is_meson_gxlx_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_GXLX)? 1:0; } static bool inline is_meson_g12a_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_G12A) ? 1 : 0; } static inline bool is_meson_g12b_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_G12B) ? 1 : 0; } static inline bool is_meson_sm1_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_SM1) ? 1 : 0; } static inline bool is_meson_tl1_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_TL1) ? 1 : 0; } static inline bool is_meson_tm2_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_TM2) ? 1 : 0; } static bool inline is_meson_txl_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_TXL)? 1:0; } static bool inline is_meson_gxm_cpu(void) { return (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_GXM)? 1:0; } static bool inline is_meson_gxl_package_905X(void) { return ((get_cpu_id().family_id == MESON_CPU_MAJOR_ID_GXL) && (get_cpu_id().package_id == MESON_CPU_PACKAGE_ID_905X))? 1:0; } static bool inline is_meson_gxl_package_905L(void) { return ((get_cpu_id().family_id == MESON_CPU_MAJOR_ID_GXL) && (get_cpu_id().package_id == MESON_CPU_PACKAGE_ID_905L))? 1:0; } /*----------------------------------------------------------------------------*/ // configuration for enci bist int cvbs_set_bist(char* bist_mode) { if (!strcmp(bist_mode, "off")) { cvbs_write_vcbus(ENCI_VIDEO_MODE_ADV, 0x26); cvbs_write_vcbus(ENCI_TST_EN, 0x0); } else { unsigned int mode = 0; if (!strcmp(bist_mode, "fixval") || !strcmp(bist_mode, "0")) mode = 0; else if (!strcmp(bist_mode, "colorbar") || !strcmp(bist_mode, "1")) mode = 1; else if (!strcmp(bist_mode, "thinline") || !strcmp(bist_mode, "2")) mode = 2; else if (!strcmp(bist_mode, "dotgrid") || !strcmp(bist_mode, "3")) mode = 3; cvbs_write_vcbus(ENCI_VIDEO_MODE_ADV, 0x2); cvbs_write_vcbus(ENCI_TST_MDSEL, mode); cvbs_write_vcbus(ENCI_TST_CLRBAR_STRT, 0x112); cvbs_write_vcbus(ENCI_TST_CLRBAR_WIDTH, 0xb4); cvbs_write_vcbus(ENCI_TST_EN, 0x1); } return 0; } /*----------------------------------------------------------------------------*/ int cvbs_set_vdac(int status) { switch (status) { case 0:// close vdac if (cvbs_drv.vdac_data) vdac_enable(0, VDAC_MODULE_CVBS_OUT); else printf("cvbs ERROR:need run cvbs init.\n"); break; case 1:// from enci to vdac cvbs_set_vcbus_bits(VENC_VDAC_DACSEL0, 0, 5, 1); if (cvbs_drv.vdac_data) { vdac_vref_adj(cvbs_drv.vdac_data->vref_adj); vdac_enable(1, VDAC_MODULE_CVBS_OUT); } else { printf("cvbs ERROR:need run cvbs init.\n"); } break; default: break; } return 0; } /*----------------------------------------------------------------------------*/ // interface for debug static void cvbs_dump_cvbs_regs(void) { struct reg_s *p = NULL; if (VMODE_PAL == cvbs_mode) { // 576cvbs p = (struct reg_s*)&tvregs_576cvbs_enc[0]; } else if (VMODE_NTSC == cvbs_mode) { // 480cvbs p = (struct reg_s*)&tvregs_480cvbs_enc[0]; } if (NULL == p) { printf("it's not in cvbs mode!\n"); return; } if (MREG_END_MARKER != p->reg) printf("cvbs enci registers:\n"); while (MREG_END_MARKER != p->reg) { printf(" vcbus[0x%.2x] = 0x%.4x\n", p->reg, cvbs_read_vcbus(p->reg)); p ++; } return; } unsigned int cvbs_clk_regs[] = { #if (defined(CONFIG_AML_MESON_G12A)) HHI_HDMI_PLL_CNTL0, #else HHI_HDMI_PLL_CNTL, #endif #if (!defined(CONFIG_CHIP_AML_GXB) && \ !defined(CONFIG_AML_MESON_GXTVBB)) HHI_HDMI_PLL_CNTL1, #endif HHI_HDMI_PLL_CNTL2, HHI_HDMI_PLL_CNTL3, HHI_HDMI_PLL_CNTL4, HHI_HDMI_PLL_CNTL5, #if (defined(CONFIG_CHIP_AML_GXB) || \ defined(CONFIG_AML_MESON_GXTVBB) || \ defined(CONFIG_AML_MESON_G12A)) HHI_HDMI_PLL_CNTL6, #endif HHI_VID_PLL_CLK_DIV, HHI_VIID_CLK_DIV, HHI_VIID_CLK_CNTL, HHI_VID_CLK_DIV, HHI_VID_CLK_CNTL2, MREG_END_MARKER }; static void cvbs_dump_clock_regs(void) { unsigned int *p = &cvbs_clk_regs[0]; if (MREG_END_MARKER != *p) printf("cvbs clock registers:\n"); while (MREG_END_MARKER != *p) { printf(" hiu[0x%.2x] = 0x%.4x\n", *p, cvbs_read_hiu(*p)); p ++; } return; } int cvbs_reg_debug(int argc, char* const argv[]) { unsigned int addr, start, end, value; if (!strcmp(argv[1], "r")) { if (argc != 4) goto fail_cmd; addr = simple_strtoul(argv[3], NULL, 16); if (!strcmp(argv[2], "c")) printf("cvbs read cbus[0x%.2x] = 0x%.4x\n", addr, cvbs_read_cbus(addr)); else if (!strcmp(argv[2], "h")) printf("cvbs read hiu[0x%.2x] = 0x%.4x\n", addr, cvbs_read_hiu(cvbs_get_hiu_logic_addr(addr))); else if (!strcmp(argv[2], "v")) printf("cvbs read vcbus[0x%.2x] = 0x%.4x\n", addr, cvbs_read_vcbus(addr)); } else if (!strcmp(argv[1], "w")) { if (argc != 5) goto fail_cmd; addr = simple_strtoul(argv[4], NULL, 16); value = simple_strtoul(argv[2], NULL, 16); if (!strcmp(argv[3], "c")) { cvbs_write_cbus(addr, value); printf("cvbs write cbus[0x%.2x] = 0x%.4x\n", addr, cvbs_read_cbus(addr)); } else if (!strcmp(argv[3], "h")) { cvbs_write_hiu(cvbs_get_hiu_logic_addr(addr), value); printf("cvbs write hiu[0x%.2x] = 0x%.4x\n", addr, cvbs_read_hiu(cvbs_get_hiu_logic_addr(addr))); } else if (!strcmp(argv[3], "v")) { cvbs_write_vcbus(addr, value); printf("cvbs write hiu[0x%.2x] = 0x%.4x\n", addr, cvbs_read_vcbus(addr)); } } else if (!strcmp(argv[1], "dump")) { unsigned int i = 0; unsigned int type = 0xff; if (argc != 5) goto fail_cmd; if (!strcmp(argv[2], "h")) type = BUS_TYPE_CBUS; if (!strcmp(argv[2], "h")) type = BUS_TYPE_HIU; else if (!strcmp(argv[2], "v")) type = BUS_TYPE_VCBUS; if (type == 0xff) goto fail_cmd; start = simple_strtoul(argv[3], NULL, 16); end = simple_strtoul(argv[4], NULL, 16); if (type == BUS_TYPE_CBUS) { for (i=start; i<=end; i++) printf("cvbs read cbus[0x%.2x] = 0x%.4x\n", i, cvbs_read_cbus(i)); } if (type == BUS_TYPE_HIU) { for (i=start; i<=end; i++) printf("cvbs read hiu[0x%.2x] = 0x%.4x\n", i, cvbs_read_hiu(cvbs_get_hiu_logic_addr(i))); } else if (type == BUS_TYPE_VCBUS) { for (i=start; i<=end; i++) printf("cvbs read vcbus[0x%.2x] = 0x%.4x\n", i, cvbs_read_vcbus(i)); } } else if (!strcmp(argv[1], "clock")) { if (argc != 2) goto fail_cmd; cvbs_dump_clock_regs(); } else if (!strcmp(argv[1], "enci")) { if (argc != 2) goto fail_cmd; cvbs_dump_cvbs_regs(); } else if (!strcmp(argv[1], "set_clkpath")) { if (argc != 3) goto fail_cmd; value = simple_strtoul(argv[2], NULL, 0); if (check_cpu_type(MESON_CPU_MAJOR_ID_G12A) || check_cpu_type(MESON_CPU_MAJOR_ID_G12B) || check_cpu_type(MESON_CPU_MAJOR_ID_SM1) || is_meson_tl1_cpu() || is_meson_tm2_cpu()) { if (value == 1 || value == 2 || value == 3 || value == 0) { s_enci_clk_path = value; printf("path 0:vid_pll vid2_clk\n"); printf("path 1:gp0_pll vid2_clk\n"); printf("path 2:vid_pll vid1_clk\n"); printf("path 3:gp0_pll vid1_clk\n"); printf("you select path %d\n", s_enci_clk_path); } else { printf("invalid value, only 0/1/2/3\n"); printf("bit[0]: 0=vid_pll, 1=gp0_pll\n"); printf("bit[1]: 0=vid2_clk, 1=vid1_clk\n"); } } else printf("only support G12A chip"); } return 0; fail_cmd: return 1; } /*----------------------------------------------------------------------------*/ // configuration for clock #define WAIT_FOR_PLL_LOCKED(reg) \ do { \ unsigned int pll_lock; \ unsigned int time_out = 0; \ do { \ udelay(20); \ pll_lock = cvbs_get_hiu_bits(reg, 31, 1); \ time_out ++; \ } while ((pll_lock == 0) && (time_out < 10000)); \ if (pll_lock == 0) \ printf("[error]: cvbs pll lock failed\n"); \ } while(0); static void cvbs_config_hdmipll_gxb(void) { #if (defined(CONFIG_CHIP_AML_GXB) || \ defined(CONFIG_AML_MESON_GXTVBB)) printf("%s\n", __func__); cvbs_write_hiu(HHI_HDMI_PLL_CNTL, 0x5800023d); cvbs_write_hiu(HHI_HDMI_PLL_CNTL2, 0x00404e00); cvbs_write_hiu(HHI_HDMI_PLL_CNTL3, 0x0d5c5091); cvbs_write_hiu(HHI_HDMI_PLL_CNTL4, 0x801da72c); cvbs_write_hiu(HHI_HDMI_PLL_CNTL5, 0x71486980); cvbs_write_hiu(HHI_HDMI_PLL_CNTL6, 0x00000e55); cvbs_write_hiu(HHI_HDMI_PLL_CNTL, 0x4800023d); WAIT_FOR_PLL_LOCKED(HHI_HDMI_PLL_CNTL); #endif return; } static void cvbs_config_hdmipll_gxl(void) { #if (!defined(CONFIG_AML_MESON_G12A)) printf("%s\n", __func__); cvbs_write_hiu(HHI_HDMI_PLL_CNTL, 0x4000027b); #if (!defined(CONFIG_CHIP_AML_GXB) && \ !defined(CONFIG_AML_MESON_GXTVBB)) cvbs_write_hiu(HHI_HDMI_PLL_CNTL1, 0x800cb300); cvbs_write_hiu(HHI_HDMI_PLL_CNTL2, 0xa6212844); cvbs_write_hiu(HHI_HDMI_PLL_CNTL3, 0x0c4d000c); cvbs_write_hiu(HHI_HDMI_PLL_CNTL4, 0x001fa729); cvbs_write_hiu(HHI_HDMI_PLL_CNTL5, 0x01a31500); #endif cvbs_set_hiu_bits(HHI_HDMI_PLL_CNTL, 0x1, 28, 1); cvbs_set_hiu_bits(HHI_HDMI_PLL_CNTL, 0x0, 28, 1); WAIT_FOR_PLL_LOCKED(HHI_HDMI_PLL_CNTL); #endif return; } static void cvbs_config_hdmipll_gxtvbb(void) { #if (defined(CONFIG_CHIP_AML_GXB) || \ defined(CONFIG_AML_MESON_GXTVBB)) printf("%s\n", __func__); cvbs_write_hiu(HHI_HDMI_PLL_CNTL, 0x5800023d); cvbs_write_hiu(HHI_HDMI_PLL_CNTL2, 0x00404380); cvbs_write_hiu(HHI_HDMI_PLL_CNTL3, 0x0d5c5091); cvbs_write_hiu(HHI_HDMI_PLL_CNTL4, 0x801da72c); cvbs_write_hiu(HHI_HDMI_PLL_CNTL5, 0x71486980); cvbs_write_hiu(HHI_HDMI_PLL_CNTL6, 0x00000e55); cvbs_write_hiu(HHI_HDMI_PLL_CNTL, 0x4800023d); WAIT_FOR_PLL_LOCKED(HHI_HDMI_PLL_CNTL); #endif return; } static void cvbs_config_hdmipll_g12a(void) { #if (defined(CONFIG_AML_MESON_G12A)) printf("%s\n", __func__); cvbs_write_hiu(HHI_HDMI_PLL_CNTL0, 0x1a0504f7); cvbs_write_hiu(HHI_HDMI_PLL_CNTL1, 0x00010000); cvbs_write_hiu(HHI_HDMI_PLL_CNTL2, 0x00000000); cvbs_write_hiu(HHI_HDMI_PLL_CNTL3, 0x6a28dc00); cvbs_write_hiu(HHI_HDMI_PLL_CNTL4, 0x65771290); cvbs_write_hiu(HHI_HDMI_PLL_CNTL5, 0x39272000); cvbs_write_hiu(HHI_HDMI_PLL_CNTL6, 0x56540000); cvbs_write_hiu(HHI_HDMI_PLL_CNTL0, 0x3a0504f7); udelay(100); cvbs_write_hiu(HHI_HDMI_PLL_CNTL0, 0x1a0504f7); WAIT_FOR_PLL_LOCKED(HHI_HDMI_PLL_CNTL0); #endif return; } static void cvbs_config_gp0pll_g12a(void) { #if (defined(CONFIG_AML_MESON_G12A)) printf("%s\n", __func__); cvbs_write_hiu(HHI_GP0_PLL_CNTL0, 0x180204f7); cvbs_write_hiu(HHI_GP0_PLL_CNTL1, 0x00010000); cvbs_write_hiu(HHI_GP0_PLL_CNTL2, 0x00000000); cvbs_write_hiu(HHI_GP0_PLL_CNTL3, 0x6a28dc00); cvbs_write_hiu(HHI_GP0_PLL_CNTL4, 0x65771290); cvbs_write_hiu(HHI_GP0_PLL_CNTL5, 0x39272000); cvbs_write_hiu(HHI_GP0_PLL_CNTL6, 0x56540000); cvbs_write_hiu(HHI_GP0_PLL_CNTL0, 0x380204f7); udelay(100); cvbs_write_hiu(HHI_GP0_PLL_CNTL0, 0x180204f7); WAIT_FOR_PLL_LOCKED(HHI_GP0_PLL_CNTL0); #endif return; } static void cvbs_config_tcon_pll(void) { printf("%s\n", __func__); cvbs_write_hiu(HHI_TCON_PLL_CNTL0, 0x202f04f7); udelay(100); cvbs_write_hiu(HHI_TCON_PLL_CNTL0, 0x302f04f7); udelay(100); cvbs_write_hiu(HHI_TCON_PLL_CNTL1, 0x10110000); cvbs_write_hiu(HHI_TCON_PLL_CNTL2, 0x00001108); cvbs_write_hiu(HHI_TCON_PLL_CNTL3, 0x10051400); cvbs_write_hiu(HHI_TCON_PLL_CNTL4, 0x010100c0); udelay(100); cvbs_write_hiu(HHI_TCON_PLL_CNTL4, 0x038300c0); udelay(100); cvbs_write_hiu(HHI_TCON_PLL_CNTL0, 0x342f04f7); udelay(100); cvbs_write_hiu(HHI_TCON_PLL_CNTL0, 0x142f04f7); udelay(100); cvbs_write_hiu(HHI_TCON_PLL_CNTL2, 0x00003008); udelay(100); WAIT_FOR_PLL_LOCKED(HHI_TCON_PLL_CNTL0); return; } static void cvbs_set_vid1_clk(unsigned int src_pll) { int sel = 0; printf("%s\n", __func__); if (src_pll == 0) { /* hpll */ /* divider: 1 */ /* Disable the div output clock */ cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 0, 19, 1); cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 0, 15, 1); cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 1, 18, 1); /* Enable the final output clock */ cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 1, 19, 1); sel = 0; } else { /* gp0_pll */ sel = 1; } /* xd: 55 */ /* setup the XD divider value */ cvbs_set_hiu_bits(HHI_VID_CLK_DIV, (55 - 1), VCLK_XD0, 8); //udelay(5); /*0x59[16]/0x5f[19]/0x5f[20]*/ cvbs_set_hiu_bits(HHI_VID_CLK_CNTL, sel, VCLK_CLK_IN_SEL, 3); cvbs_set_hiu_bits(HHI_VID_CLK_CNTL, 1, VCLK_EN0, 1); //udelay(2); /* vclk: 27M */ /* [31:28]=0 enci_clk_sel, select vclk_div1 */ cvbs_set_hiu_bits(HHI_VID_CLK_DIV, 0, 28, 4); cvbs_set_hiu_bits(HHI_VIID_CLK_DIV, 0, 28, 4); /* release vclk_div_reset and enable vclk_div */ cvbs_set_hiu_bits(HHI_VID_CLK_DIV, 1, VCLK_XD_EN, 2); //udelay(5); cvbs_set_hiu_bits(HHI_VID_CLK_CNTL, 1, VCLK_DIV1_EN, 1); cvbs_set_hiu_bits(HHI_VID_CLK_CNTL, 1, VCLK_SOFT_RST, 1); //udelay(10); cvbs_set_hiu_bits(HHI_VID_CLK_CNTL, 0, VCLK_SOFT_RST, 1); //udelay(5); } static void cvbs_set_vid2_clk(unsigned int src_pll) { int sel = 0; printf("%s\n", __func__); if (src_pll == 0) { /* hpll */ /* divider: 1 */ /* Disable the div output clock */ cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 0, 19, 1); cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 0, 15, 1); cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 1, 18, 1); /* Enable the final output clock */ cvbs_set_hiu_bits(HHI_VID_PLL_CLK_DIV, 1, 19, 1); sel = 0; } else { /* gp0_pll */ sel = 1; } /* xd: 55 */ /* setup the XD divider value */ cvbs_set_hiu_bits(HHI_VIID_CLK_DIV, (55 - 1), VCLK2_XD, 8); //udelay(5); /* Bit[18:16] - v2_cntl_clk_in_sel: vid_pll */ cvbs_set_hiu_bits(HHI_VIID_CLK_CNTL, sel, VCLK2_CLK_IN_SEL, 3); cvbs_set_hiu_bits(HHI_VIID_CLK_CNTL, 1, VCLK2_EN, 1); //udelay(2); /* vclk: 27M */ /* [31:28]=8 enci_clk_sel, select vclk2_div1 */ cvbs_set_hiu_bits(HHI_VID_CLK_DIV, 8, 28, 4); cvbs_set_hiu_bits(HHI_VIID_CLK_DIV, 8, 28, 4); /* release vclk2_div_reset and enable vclk2_div */ cvbs_set_hiu_bits(HHI_VIID_CLK_DIV, 1, VCLK2_XD_EN, 2); //udelay(5); cvbs_set_hiu_bits(HHI_VIID_CLK_CNTL, 1, VCLK2_DIV1_EN, 1); cvbs_set_hiu_bits(HHI_VIID_CLK_CNTL, 1, VCLK2_SOFT_RST, 1); //udelay(10); cvbs_set_hiu_bits(HHI_VIID_CLK_CNTL, 0, VCLK2_SOFT_RST, 1); //udelay(5); } static int cvbs_config_clock(void) { /* pll output 1485M */ if (check_cpu_type(MESON_CPU_MAJOR_ID_GXBB)) cvbs_config_hdmipll_gxb(); else if (check_cpu_type(MESON_CPU_MAJOR_ID_GXTVBB)) cvbs_config_hdmipll_gxtvbb(); else if (check_cpu_type(MESON_CPU_MAJOR_ID_G12A) || check_cpu_type(MESON_CPU_MAJOR_ID_G12B) || check_cpu_type(MESON_CPU_MAJOR_ID_SM1)) { if (s_enci_clk_path & 0x1) cvbs_config_gp0pll_g12a(); else cvbs_config_hdmipll_g12a(); } else if (is_meson_tl1_cpu() || is_meson_tm2_cpu()) { cvbs_config_tcon_pll(); } else if (is_equal_after_meson_cpu(MESON_CPU_MAJOR_ID_GXL)) cvbs_config_hdmipll_gxl(); if (check_cpu_type(MESON_CPU_MAJOR_ID_G12A) || check_cpu_type(MESON_CPU_MAJOR_ID_G12B) || check_cpu_type(MESON_CPU_MAJOR_ID_SM1)) { if (s_enci_clk_path & 0x2) cvbs_set_vid1_clk(s_enci_clk_path & 0x1); else cvbs_set_vid2_clk(s_enci_clk_path & 0x1); } else if (is_meson_tl1_cpu() || is_meson_tm2_cpu()) { if (s_enci_clk_path & 0x2) cvbs_set_vid1_clk(0); else cvbs_set_vid2_clk(0); } else { cvbs_set_vid2_clk(0); } cvbs_set_hiu_bits(HHI_VID_CLK_CNTL2, 1, 0, 1); cvbs_set_hiu_bits(HHI_VID_CLK_CNTL2, 1, 4, 1); return 0; } static void cvbs_disable_clock(void) { cvbs_set_hiu_bits(HHI_VID_CLK_CNTL2, 0, 4, 1); cvbs_set_hiu_bits(HHI_VID_CLK_CNTL2, 0, 0, 1); } /*----------------------------------------------------------------------------*/ // configuration for enci static void cvbs_performance_enhancement(int mode) { const struct reg_s *s = NULL; struct performance_config_s *perfconf = NULL; int i = 0; switch (mode) { case VMODE_PAL: perfconf = &cvbs_drv.perf_conf_pal; break; case VMODE_NTSC: case VMODE_NTSC_M: perfconf = &cvbs_drv.perf_conf_ntsc; break; default: break; } if (!perfconf) return; if (!perfconf->reg_table) { printf("no performance table\n"); return; } i = 0; s = perfconf->reg_table; while (i < perfconf->reg_cnt) { cvbs_write_vcbus(s->reg, s->val); //printf("vcbus reg[0x%04x] = 0x%08x\n", s->reg, s->val); s++; i++; } printf("%s\n", __func__); } static int cvbs_config_enci(int vmode) { const struct reg_s *s = NULL; switch (vmode) { case VMODE_PAL: s = &tvregs_576cvbs_enc[0]; break; case VMODE_NTSC: case VMODE_NTSC_M: s = &tvregs_480cvbs_enc[0]; break; case VMODE_PAL_M: s = &tvregs_pal_m_enc[0]; break; case VMODE_PAL_N: s = &tvregs_pal_n_enc[0]; break; default: break; } if (s == NULL) return -1; while ((s->reg != MREG_END_MARKER)) { cvbs_write_vcbus(s->reg, s->val); //printf("reg[0x%.2x] = 0x%.4x\n", s->reg, s->val); s ++; } cvbs_performance_enhancement(vmode); return 0; } /*----------------------------------------------------------------------------*/ // configuration for output // output vmode: 576cvbs, 480cvbs int cvbs_set_vmode(char* vmode_name) { if (!cvbs_drv.vdac_data->vref_adj) { printf("cvbsout not vref_adj:%d\n", cvbs_drv.vdac_data->vref_adj); return 0; } if (!strncmp(vmode_name, "576cvbs", strlen("576cvbs"))) { cvbs_mode = VMODE_PAL; cvbs_config_enci(0); cvbs_config_clock(); cvbs_set_vdac(1); return 0; } else if (!strncmp(vmode_name, "480cvbs", strlen("480cvbs"))) { cvbs_mode = VMODE_NTSC; cvbs_config_enci(1); cvbs_config_clock(); cvbs_set_vdac(1); return 0; } else if (!strncmp(vmode_name, "ntsc_m", strlen("ntsc_m"))) { cvbs_mode = VMODE_NTSC_M; cvbs_config_enci(VMODE_NTSC_M); cvbs_config_clock(); cvbs_set_vdac(1); return 0; } else if (!strncmp(vmode_name, "pal_m", strlen("pal_m"))) { cvbs_mode = VMODE_PAL_M; cvbs_config_enci(VMODE_PAL_M); cvbs_config_clock(); cvbs_set_vdac(1); return 0; } else if (!strncmp(vmode_name, "pal_n", strlen("pal_n"))) { cvbs_mode = VMODE_PAL_N; cvbs_config_enci(VMODE_PAL_N); cvbs_config_clock(); cvbs_set_vdac(1); return 0; } else if (!strncmp(vmode_name, "disable", strlen("disable"))) { cvbs_set_vdac(0); cvbs_write_vcbus(ENCI_VIDEO_EN, 0); cvbs_disable_clock(); return 0; } else { printf("[%s] is invalid for cvbs.\n", vmode_name); return -1; } return 0; } /*----------------------------------------------------------------------------*/ // check for valid video mode int cvbs_outputmode_check(char *vmode_name, unsigned int frac) { if (frac) { printf("cvbs: don't support frac\n"); return -1; } if ((!strncmp(vmode_name, "576cvbs", strlen("576cvbs"))) || (!strncmp(vmode_name, "480cvbs", strlen("480cvbs"))) || (!strncmp(vmode_name, "ntsc_m", strlen("ntsc_m"))) || (!strncmp(vmode_name, "pal_m", strlen("pal_m"))) || (!strncmp(vmode_name, "pal_n", strlen("pal_n")))) { return 0; } printf("cvbs: outputmode[%s] is invalid\n", vmode_name); return -1; } /*----------------------------------------------------------------------------*/ // list for valid video mode void cvbs_show_valid_vmode(void) { printf("576cvbs\n""480cvbs\n""ntsc_m\n""pal_m\n""pal_n\n"); return; } static char *cvbsout_performance_str[] = { "performance", /* default for pal */ "performance_pal", "performance_ntsc", }; static void cvbs_get_config(void) { #ifdef CONFIG_OF_LIBFDT char *dt_addr = NULL; int parent_offset; char *propdata; const char *str; struct reg_s *s; unsigned int i, j, temp, cnt; #ifdef CONFIG_DTB_MEM_ADDR dt_addr = (char *)CONFIG_DTB_MEM_ADDR; #else dt_addr = (char *)0x01000000; #endif if (fdt_check_header(dt_addr) < 0) return; if (!cvbs_drv.vdac_data->vref_adj) { printf("cvbsout not vref_adj:%d\n", cvbs_drv.vdac_data->vref_adj); return; } parent_offset = fdt_path_offset(dt_addr, "/cvbsout"); if (parent_offset < 0) { printf("not find /cvbsout node: %s\n", fdt_strerror(parent_offset)); return; } /* clk_path */ propdata = (char *)fdt_getprop(dt_addr, parent_offset, "clk_path", NULL); if (propdata) { s_enci_clk_path = be32_to_cpup((u32*)propdata); printf("cvbs: find clk_path: 0x%x\n", s_enci_clk_path); } /* performance */ str = cvbsout_performance_str[1]; propdata = (char *)fdt_getprop(dt_addr, parent_offset, str, NULL); if (propdata == NULL) { str = cvbsout_performance_str[0]; propdata = (char *)fdt_getprop(dt_addr, parent_offset, str, NULL); if (propdata == NULL) goto cvbs_performance_config_ntsc; } cnt = 0; while (cnt < CVBS_PERFORMANCE_CNT_MAX) { j = 2 * cnt; temp = be32_to_cpup((((u32*)propdata)+j)); if (temp == MREG_END_MARKER) /* ending */ break; cnt++; } if (cnt >= CVBS_PERFORMANCE_CNT_MAX) cnt = 0; if (cnt > 0) { printf("cvbs: find performance_pal config\n"); cvbs_drv.perf_conf_pal.reg_table = malloc(sizeof(struct reg_s) * cnt); if (cvbs_drv.perf_conf_pal.reg_table == NULL) { printf("cvbs: error: failed to alloc %s table\n", str); cnt = 0; } memset(cvbs_drv.perf_conf_pal.reg_table, 0, (sizeof(struct reg_s) * cnt)); cvbs_drv.perf_conf_pal.reg_cnt = cnt; i = 0; s = cvbs_drv.perf_conf_pal.reg_table; while (i < cvbs_drv.perf_conf_pal.reg_cnt) { j = 2 * i; s->reg = be32_to_cpup((((u32*)propdata)+j)); s->val = be32_to_cpup((((u32*)propdata)+j+1)); /* printf("%p: 0x%04x = 0x%x\n", s, s->reg, s->val); */ s++; i++; } } cvbs_performance_config_ntsc: str = cvbsout_performance_str[2]; propdata = (char *)fdt_getprop(dt_addr, parent_offset, str, NULL); if (propdata == NULL) return; cnt = 0; while (cnt < CVBS_PERFORMANCE_CNT_MAX) { j = 2 * cnt; temp = be32_to_cpup((((u32*)propdata)+j)); if (temp == MREG_END_MARKER) /* ending */ break; cnt++; } if (cnt >= CVBS_PERFORMANCE_CNT_MAX) cnt = 0; if (cnt > 0) { printf("cvbs: find performance_ntsc config\n"); cvbs_drv.perf_conf_ntsc.reg_table = malloc(sizeof(struct reg_s) * cnt); if (cvbs_drv.perf_conf_ntsc.reg_table == NULL) { printf("cvbs: error: failed to alloc %s table\n", str); cnt = 0; } memset(cvbs_drv.perf_conf_ntsc.reg_table, 0, (sizeof(struct reg_s) * cnt)); cvbs_drv.perf_conf_ntsc.reg_cnt = cnt; i = 0; s = cvbs_drv.perf_conf_ntsc.reg_table; while (i < cvbs_drv.perf_conf_ntsc.reg_cnt) { j = 2 * i; s->reg = be32_to_cpup((((u32*)propdata)+j)); s->val = be32_to_cpup((((u32*)propdata)+j+1)); /* printf("%p: 0x%04x = 0x%x\n", s, s->reg, s->val); */ s++; i++; } } #endif } void vdac_data_config(void) { printf("cvbs: cpuid:0x%x\n", get_cpu_id().family_id); switch (get_cpu_id().family_id) { case MESON_CPU_MAJOR_ID_GXL: case MESON_CPU_MAJOR_ID_GXM: case MESON_CPU_MAJOR_ID_GXLX: cvbs_drv.vdac_data = &vdac_data_gxl; break; case MESON_CPU_MAJOR_ID_TXL: case MESON_CPU_MAJOR_ID_TXHD: case MESON_CPU_MAJOR_ID_TXLX: cvbs_drv.vdac_data = &vdac_data_txl; break; case MESON_CPU_MAJOR_ID_G12A: cvbs_drv.vdac_data = &vdac_data_g12a; break; case MESON_CPU_MAJOR_ID_G12B: cvbs_drv.vdac_data = &vdac_data_g12b; break; case MESON_CPU_MAJOR_ID_SM1: cvbs_drv.vdac_data = &vdac_data_sm1; break; case MESON_CPU_MAJOR_ID_TL1: cvbs_drv.vdac_data = &vdac_data_tl1; break; case MESON_CPU_MAJOR_ID_TM2: cvbs_drv.vdac_data = &vdac_data_tm2; break; default: cvbs_drv.vdac_data = &vdac_data_default; break; } return; } void cvbs_init(void) { vdac_data_config(); vdac_ctrl_config_probe(); cvbs_get_config(); return; }