| /* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ |
| /* |
| * drivers/display/vout/vdac.c |
| * |
| * Copyright (C) 2020 Amlogic, Inc. All rights reserved. |
| * |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <asm/arch/register.h> |
| #include <asm/arch/secure_apb.h> |
| #include <amlogic/aml_efuse.h> |
| #include <asm/arch/cpu.h> |
| #include <asm/cpu_id.h> |
| #include "vdac.h" |
| |
| static struct meson_vdac_ctrl_s *vdac_ctrl; |
| static unsigned int pri_flag; |
| |
| #define REG_ADDR_HIU(reg) (reg + 0L) |
| |
| static void vdac_write_hiu(unsigned int addr, unsigned int val) |
| { |
| *(volatile unsigned int *)REG_ADDR_HIU(addr) = (val); |
| } |
| |
| static unsigned int vdac_read_hiu(unsigned int addr) |
| { |
| return *(volatile unsigned int *)(REG_ADDR_HIU(addr)); |
| } |
| |
| static void vdac_set_hiu_bits(unsigned int addr, unsigned int val, |
| unsigned int start, unsigned int len) |
| { |
| vdac_write_hiu(addr, ((vdac_read_hiu(addr) & |
| ~(((1L << (len))-1) << (start))) | |
| (((val)&((1L<<(len))-1)) << (start)))); |
| } |
| |
| static int vdac_ctrl_config(bool on, unsigned int reg, unsigned int bit) |
| { |
| unsigned int val; |
| int i = 0; |
| int ret = -1; |
| |
| if (!vdac_ctrl) { |
| printf("%s: vdac_ctrl data is NULL\n", __func__); |
| return ret; |
| } |
| |
| while (i < VDAC_CTRL_MAX) { |
| if (vdac_ctrl[i].reg == VDAC_REG_MAX) |
| break; |
| if ((vdac_ctrl[i].reg == reg) && (vdac_ctrl[i].bit == bit)) { |
| if (on) |
| val = vdac_ctrl[i].val; |
| else |
| val = vdac_ctrl[i].val ? 0 : 1; |
| vdac_set_hiu_bits(reg, val, bit, vdac_ctrl[i].len); |
| //if (vdac_debug_print) { |
| // printf("vdac: reg=0x%02x set bit%d=%d, readback=0x%08x\n", |
| // reg, bit, val, vdac_hiu_reg_read(reg)); |
| //} |
| ret = 0; |
| break; |
| } |
| i++; |
| } |
| |
| return ret; |
| } |
| |
| static void vdac_enable_cvbs_out(bool on) |
| { |
| unsigned int reg_cntl0 = HHI_VDAC_CNTL0; |
| unsigned int reg_cntl1 = HHI_VDAC_CNTL1; |
| |
| if (on) { |
| if (get_cpu_id().family_id <= MESON_CPU_MAJOR_ID_GXLX) |
| vdac_set_hiu_bits(reg_cntl0, 0, 12, 4); |
| else |
| vdac_set_hiu_bits(reg_cntl0, 0x6, 12, 4); |
| vdac_ctrl_config(1, reg_cntl1, 3); |
| vdac_ctrl_config(1, reg_cntl0, 0); |
| vdac_ctrl_config(1, reg_cntl0, 9); |
| if (get_cpu_id().family_id == MESON_CPU_MAJOR_ID_TXLX) { |
| vdac_ctrl_config(0, reg_cntl0, 13); |
| udelay(5); |
| vdac_ctrl_config(1, reg_cntl0, 13); |
| } |
| if (get_cpu_id().family_id < MESON_CPU_MAJOR_ID_G12A) |
| vdac_ctrl_config(0, reg_cntl0, 10); |
| } else { |
| vdac_ctrl_config(0, reg_cntl0, 9); |
| vdac_ctrl_config(0, reg_cntl0, 0); |
| vdac_ctrl_config(0, reg_cntl1, 3); |
| } |
| } |
| |
| void vdac_enable(bool on, unsigned int module_sel) |
| { |
| if (!vdac_ctrl) { |
| printf("%s: vdac_ctrl data is NULL\n", __func__); |
| return; |
| } |
| |
| printf("%s: %d, module_sel:0x%x\n", __func__, on, module_sel); |
| |
| switch (module_sel) { |
| case VDAC_MODULE_CVBS_OUT: |
| if (on) { |
| pri_flag |= VDAC_MODULE_CVBS_OUT; |
| vdac_enable_cvbs_out(1); |
| } else { |
| pri_flag &= ~VDAC_MODULE_CVBS_OUT; |
| vdac_enable_cvbs_out(0); |
| } |
| break; |
| default: |
| printf("%s:module_sel: 0x%x wrong module index !! ", |
| __func__, module_sel); |
| break; |
| } |
| } |
| |
| int vdac_vref_adj(unsigned int value) |
| { |
| unsigned int reg = HHI_VDAC_CNTL0; |
| unsigned int bit = 16; |
| int i = 0; |
| int ret = -1; |
| |
| if (!vdac_ctrl) { |
| printf("%s: vdac_ctrl data is NULL\n", __func__); |
| return ret; |
| } |
| |
| while (i < VDAC_CTRL_MAX) { |
| if (vdac_ctrl[i].reg == VDAC_REG_MAX) |
| break; |
| if ((vdac_ctrl[i].reg == reg) && (vdac_ctrl[i].bit == bit)) { |
| vdac_set_hiu_bits(reg, value, bit, vdac_ctrl[i].len); |
| //if (vdac_debug_print) { |
| // printf("vdac: reg=0x%x set bit%d=0x%x, readback=0x%08x\n", |
| // reg, bit, value, vdac_hiu_reg_read(reg)); |
| //} |
| ret = 0; |
| break; |
| } |
| i++; |
| } |
| |
| return ret; |
| } |
| |
| static void vdac_gsw_init(void) |
| { |
| unsigned int reg = HHI_VDAC_CNTL1; |
| unsigned int bit = 0; |
| int i = 0; |
| int ret; |
| |
| if (!vdac_ctrl) { |
| printf("%s: vdac_ctrl is NULL\n", __func__); |
| return; |
| } |
| |
| ret = efuse_get_cali_cvbs(); |
| if (ret == -1) |
| return; |
| |
| printf("%s: 0x%x\n", __func__, ret); |
| while (i < VDAC_CTRL_MAX) { |
| if (vdac_ctrl[i].reg == VDAC_REG_MAX) |
| break; |
| if (vdac_ctrl[i].reg == reg && vdac_ctrl[i].bit == bit) { |
| vdac_set_hiu_bits(reg, ret, bit, vdac_ctrl[i].len); |
| break; |
| } |
| i++; |
| } |
| } |
| |
| static struct meson_vdac_ctrl_s vdac_ctrl_enable_gxl[] = { |
| {HHI_VDAC_CNTL0, 0, 9, 1}, |
| {HHI_VDAC_CNTL0, 1, 10, 1}, |
| {HHI_VDAC_CNTL0, 1, 0, 1}, |
| {HHI_VDAC_CNTL0, 0, 16, 5}, /* vref adj */ |
| {HHI_VDAC_CNTL1, 0, 0, 3}, /*gsw */ |
| {HHI_VDAC_CNTL1, 0, 3, 1}, |
| {VDAC_REG_MAX, 0, 0, 0}, |
| }; |
| |
| static struct meson_vdac_ctrl_s vdac_ctrl_enable_txl[] = { |
| {HHI_VDAC_CNTL0, 1, 9, 1}, |
| {HHI_VDAC_CNTL0, 1, 10, 1}, |
| {HHI_VDAC_CNTL0, 1, 0, 1}, |
| {HHI_VDAC_CNTL0, 0, 16, 5}, /* vref adj */ |
| {HHI_VDAC_CNTL1, 0, 0, 3}, /*gsw */ |
| {HHI_VDAC_CNTL1, 1, 3, 1}, |
| {VDAC_REG_MAX, 0, 0, 0}, |
| }; |
| |
| static struct meson_vdac_ctrl_s vdac_ctrl_enable_txlx[] = { |
| {HHI_VDAC_CNTL0, 1, 9, 1}, |
| {HHI_VDAC_CNTL0, 1, 10, 1}, |
| {HHI_VDAC_CNTL0, 1, 0, 1}, |
| {HHI_VDAC_CNTL0, 0, 13, 1}, /* bandgap */ |
| {HHI_VDAC_CNTL0, 0, 16, 5}, /* vref adj */ |
| {HHI_VDAC_CNTL1, 0, 0, 3}, /*gsw */ |
| {HHI_VDAC_CNTL1, 1, 3, 1}, |
| {VDAC_REG_MAX, 0, 0, 0}, |
| }; |
| |
| static struct meson_vdac_ctrl_s vdac_ctrl_enable_g12ab[] = { |
| {HHI_VDAC_CNTL0, 0, 9, 1}, |
| {HHI_VDAC_CNTL0, 1, 0, 1}, |
| {HHI_VDAC_CNTL0, 0, 16, 5}, /* vref adj */ |
| {HHI_VDAC_CNTL1, 0, 0, 3}, /*gsw */ |
| {HHI_VDAC_CNTL1, 0, 3, 1}, |
| {VDAC_REG_MAX, 0, 0, 0}, |
| }; |
| |
| static struct meson_vdac_ctrl_s vdac_ctrl_enable_tl1[] = { |
| {HHI_VDAC_CNTL0, 0, 9, 1}, |
| {HHI_VDAC_CNTL0, 1, 0, 1}, |
| {HHI_VDAC_CNTL0, 0, 16, 5}, /* vref adj */ |
| {HHI_VDAC_CNTL1, 0, 0, 3}, /*gsw */ |
| {HHI_VDAC_CNTL1, 0, 3, 1}, |
| {HHI_VDAC_CNTL1, 0, 7, 1}, /* bandgap */ |
| {VDAC_REG_MAX, 0, 0, 0}, |
| }; |
| |
| static struct meson_vdac_ctrl_s vdac_ctrl_enable_t5w[] = { |
| {HHI_VDAC_CNTL1, 0, 0, 7}, /*gsw */ |
| {VDAC_REG_MAX, 0, 0, 0}, |
| }; |
| |
| void vdac_ctrl_config_probe(void) |
| { |
| pri_flag = 0; |
| |
| switch (get_cpu_id().family_id) { |
| case MESON_CPU_MAJOR_ID_GXL: |
| case MESON_CPU_MAJOR_ID_GXM: |
| case MESON_CPU_MAJOR_ID_GXLX: |
| vdac_ctrl = vdac_ctrl_enable_gxl; |
| break; |
| case MESON_CPU_MAJOR_ID_TXL: |
| case MESON_CPU_MAJOR_ID_TXHD: |
| vdac_ctrl = vdac_ctrl_enable_txl; |
| break; |
| case MESON_CPU_MAJOR_ID_TXLX: |
| vdac_ctrl = vdac_ctrl_enable_txlx; |
| break; |
| case MESON_CPU_MAJOR_ID_G12A: |
| case MESON_CPU_MAJOR_ID_G12B: |
| case MESON_CPU_MAJOR_ID_SM1: |
| vdac_ctrl = vdac_ctrl_enable_g12ab; |
| break; |
| case MESON_CPU_MAJOR_ID_TL1: |
| case MESON_CPU_MAJOR_ID_TM2: |
| vdac_ctrl = vdac_ctrl_enable_tl1; |
| break; |
| case MESON_CPU_MAJOR_ID_T5W: |
| vdac_ctrl = vdac_ctrl_enable_t5w; |
| break; |
| default: |
| break; |
| } |
| vdac_gsw_init(); |
| |
| return; |
| } |