| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * |
| * Copyright (C) 2019 Amlogic, Inc. All rights reserved. |
| * |
| */ |
| |
| #include <linux/init.h> |
| #include <linux/version.h> |
| #include <linux/types.h> |
| #include <linux/slab.h> |
| #include <linux/module.h> |
| #include <linux/platform_device.h> |
| #include <linux/string.h> |
| #include <linux/kernel.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/notifier.h> |
| #include <linux/reboot.h> |
| #include <linux/of.h> |
| #include <linux/reset.h> |
| #ifdef CONFIG_AMLOGIC_VPU |
| #include <linux/amlogic/media/vpu/vpu.h> |
| #endif |
| #include <linux/amlogic/media/vout/vinfo.h> |
| #include <linux/amlogic/media/vout/lcd/lcd_vout.h> |
| #include <linux/amlogic/media/vout/lcd/lcd_notify.h> |
| #include "lcd_tablet.h" |
| #include "mipi_dsi_util.h" |
| #include "../lcd_reg.h" |
| #include "../lcd_common.h" |
| |
| static int lcd_type_supported(struct lcd_config_s *pconf) |
| { |
| int lcd_type = pconf->basic.lcd_type; |
| int ret = -1; |
| |
| switch (lcd_type) { |
| case LCD_TTL: |
| case LCD_LVDS: |
| case LCD_VBYONE: |
| case LCD_MIPI: |
| case LCD_EDP: |
| ret = 0; |
| break; |
| default: |
| LCDERR("invalid lcd type: %s(%d)\n", |
| lcd_type_type_to_str(lcd_type), lcd_type); |
| break; |
| } |
| return ret; |
| } |
| |
| static void lcd_ttl_control_set(struct aml_lcd_drv_s *pdrv) |
| { |
| struct lcd_config_s *pconf = &pdrv->config; |
| unsigned int clk_pol, rb_swap, bit_swap; |
| |
| clk_pol = pconf->control.ttl_cfg.clk_pol; |
| rb_swap = (pconf->control.ttl_cfg.swap_ctrl >> 1) & 1; |
| bit_swap = (pconf->control.ttl_cfg.swap_ctrl >> 0) & 1; |
| |
| lcd_vcbus_setb(L_POL_CNTL_ADDR, clk_pol, 6, 1); |
| lcd_vcbus_setb(L_DUAL_PORT_CNTL_ADDR, rb_swap, 1, 1); |
| lcd_vcbus_setb(L_DUAL_PORT_CNTL_ADDR, bit_swap, 0, 1); |
| } |
| |
| static void lcd_mipi_control_set(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int bit_lane_sel; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| switch (pdrv->index) { |
| case 0: |
| bit_lane_sel = 0; |
| break; |
| case 1: |
| bit_lane_sel = 8; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7) { |
| // sel dphy lane |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL1, |
| 0x0, bit_lane_sel, 10); |
| } |
| |
| mipi_dsi_tx_ctrl(pdrv, 1); |
| } |
| |
| static void lcd_mipi_disable(struct aml_lcd_drv_s *pdrv) |
| { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| mipi_dsi_tx_ctrl(pdrv, 0); |
| } |
| |
| static void lcd_edp_control_set(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_dphy_tx_ctrl0, reg_dphy_tx_ctrl1; |
| unsigned int bit_data_in_lvds, bit_data_in_edp, bit_lane_sel; |
| |
| switch (pdrv->index) { |
| case 0: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL1; |
| bit_data_in_lvds = 0; |
| bit_data_in_edp = 1; |
| bit_lane_sel = 0; |
| break; |
| case 1: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL1; |
| bit_data_in_lvds = 2; |
| bit_data_in_edp = 3; |
| bit_lane_sel = 8; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| // sel dphy data_in |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL0, 0, bit_data_in_lvds, 1); |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL0, 1, bit_data_in_edp, 1); |
| // sel dphy lane |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL1, 0x155, bit_lane_sel, 10); |
| |
| // sel edp fifo clkdiv 20, enable lane |
| lcd_combo_dphy_write(pdrv, reg_dphy_tx_ctrl0, |
| ((0x4 << 5) | (0x1f << 16))); |
| |
| // fifo enable |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl1, 1, 6, 10); |
| // fifo wr enable |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl1, 1, 7, 10); |
| |
| edp_tx_ctrl(pdrv, 1); |
| } |
| |
| static void lcd_edp_disable(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_dphy_tx_ctrl0, reg_dphy_tx_ctrl1; |
| |
| switch (pdrv->index) { |
| case 0: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL1; |
| break; |
| case 1: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL1; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| edp_tx_ctrl(pdrv, 0); |
| |
| // fifo wr disable |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl1, 0, 7, 10); |
| // fifo disable |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl1, 0, 6, 10); |
| // lane disable |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl0, 0, 16, 8); |
| } |
| |
| static void lcd_lvds_clk_util_set(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_phy_tx_ctrl0, reg_phy_tx_ctrl1; |
| unsigned int bit_data_in_lvds, bit_data_in_edp, bit_lane_sel; |
| unsigned int phy_div, val_lane_sel, len_lane_sel; |
| |
| if (pdrv->config.control.lvds_cfg.dual_port) |
| phy_div = 2; |
| else |
| phy_div = 1; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7) { |
| switch (pdrv->index) { |
| case 0: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| reg_phy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL1; |
| bit_data_in_lvds = 0; |
| bit_data_in_edp = 1; |
| bit_lane_sel = 0; |
| val_lane_sel = 0x155; |
| len_lane_sel = 10; |
| break; |
| case 1: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| reg_phy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL1; |
| bit_data_in_lvds = 2; |
| bit_data_in_edp = 3; |
| bit_lane_sel = 10; |
| val_lane_sel = 0x155; |
| len_lane_sel = 10; |
| break; |
| case 2: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY2_CNTL0; |
| reg_phy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY2_CNTL1; |
| bit_data_in_lvds = 4; |
| bit_data_in_edp = 0xff; |
| if (pdrv->config.control.lvds_cfg.dual_port) { |
| bit_lane_sel = 10; |
| val_lane_sel = 0xaaaaa; |
| len_lane_sel = 20; |
| } else { |
| bit_lane_sel = 20; |
| val_lane_sel = 0x2aa; |
| len_lane_sel = 10; |
| } |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| // sel dphy data_in |
| if (bit_data_in_edp < 0xff) { |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL0, 0, |
| bit_data_in_edp, 1); |
| } |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL0, 1, |
| bit_data_in_lvds, 1); |
| // sel dphy lane |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL1, val_lane_sel, |
| bit_lane_sel, len_lane_sel); |
| |
| /* set fifo_clk_sel: div 7 */ |
| lcd_combo_dphy_write(pdrv, reg_phy_tx_ctrl0, (1 << 5)); |
| /* set cntl_ser_en: 8-channel */ |
| lcd_combo_dphy_setb(pdrv, reg_phy_tx_ctrl0, 0x3ff, 16, 10); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_combo_dphy_write(pdrv, reg_phy_tx_ctrl1, |
| (1 << 6) | (1 << 0)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_combo_dphy_setb(pdrv, reg_phy_tx_ctrl1, 1, 7, 1); |
| } else if (pdrv->data->chip_type == LCD_CHIP_T3) { |
| /* set fifo_clk_sel: div 7 */ |
| lcd_ana_write(ANACTRL_LVDS_TX_PHY_CNTL0, (1 << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_ana_setb(ANACTRL_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12); |
| /* pn swap */ |
| lcd_ana_setb(ANACTRL_LVDS_TX_PHY_CNTL0, 1, 2, 1); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_ana_write(ANACTRL_LVDS_TX_PHY_CNTL1, (1 << 30) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_ana_setb(ANACTRL_LVDS_TX_PHY_CNTL1, 1, 31, 1); |
| } else { |
| /* set fifo_clk_sel: div 7 */ |
| lcd_ana_write(HHI_LVDS_TX_PHY_CNTL0, (1 << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12); |
| /* pn swap */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 1, 2, 1); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_ana_write(HHI_LVDS_TX_PHY_CNTL1, |
| (1 << 30) | ((phy_div - 1) << 25) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1); |
| } |
| } |
| |
| static void lcd_lvds_control_set(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_lvds_pack_ctrl, reg_lvds_gen_ctrl; |
| unsigned int bit_num, pn_swap, port_swap, lane_reverse; |
| unsigned int dual_port, fifo_mode, lvds_repack; |
| unsigned int offset; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| lcd_lvds_clk_util_set(pdrv); |
| |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| lvds_repack = (pdrv->config.control.lvds_cfg.lvds_repack) & 0x3; |
| pn_swap = (pdrv->config.control.lvds_cfg.pn_swap) & 0x1; |
| dual_port = (pdrv->config.control.lvds_cfg.dual_port) & 0x1; |
| port_swap = (pdrv->config.control.lvds_cfg.port_swap) & 0x1; |
| lane_reverse = (pdrv->config.control.lvds_cfg.lane_reverse) & 0x1; |
| |
| switch (pdrv->config.basic.lcd_bits) { |
| case 10: |
| bit_num = 0; |
| break; |
| case 6: |
| bit_num = 2; |
| break; |
| case 8: |
| default: |
| bit_num = 1; |
| break; |
| } |
| if (dual_port) |
| fifo_mode = 0x3; |
| else |
| fifo_mode = 0x1; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| reg_lvds_pack_ctrl = LVDS_PACK_CNTL_ADDR_T7 + offset; |
| reg_lvds_gen_ctrl = LVDS_GEN_CNTL_T7 + offset; |
| lcd_vcbus_write(LVDS_SER_EN_T7 + offset, 0xfff); |
| } else { |
| reg_lvds_pack_ctrl = LVDS_PACK_CNTL_ADDR; |
| reg_lvds_gen_ctrl = LVDS_GEN_CNTL; |
| } |
| |
| lcd_vcbus_write(reg_lvds_pack_ctrl, |
| (lvds_repack << 0) | /* repack //[1:0] */ |
| (0 << 3) | /* reserve */ |
| (0 << 4) | /* lsb first */ |
| (pn_swap << 5) | /* pn swap */ |
| (dual_port << 6) | /* dual port */ |
| (0 << 7) | /* use tcon control */ |
| /* 0:10bits, 1:8bits, 2:6bits, 3:4bits */ |
| (bit_num << 8) | |
| (0 << 10) | /* r_select //0:R, 1:G, 2:B, 3:0 */ |
| (1 << 12) | /* g_select //0:R, 1:G, 2:B, 3:0 */ |
| (2 << 14)); /* b_select //0:R, 1:G, 2:B, 3:0 */ |
| |
| /* lvsd swap */ |
| switch (pdrv->data->chip_type) { |
| case LCD_CHIP_TL1: |
| case LCD_CHIP_TM2: |
| /* lvds channel: //tx 12 channels |
| * 0: d0_a |
| * 1: d1_a |
| * 2: d2_a |
| * 3: clk_a |
| * 4: d3_a |
| * 5: d4_a |
| * 6: d0_b |
| * 7: d1_b |
| * 8: d2_b |
| * 9: clk_b |
| * a: d3_b |
| * b: d4_b |
| */ |
| if (port_swap) { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0x456789ab); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0x0123); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0x10ba9876); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0x5432); |
| } |
| } else { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0xab012345); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0x6789); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0x76543210); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0xba98); |
| } |
| } |
| break; |
| case LCD_CHIP_T5: |
| case LCD_CHIP_T5D: |
| case LCD_CHIP_T5W: |
| /* lvds channel: //tx 12 channels |
| * 0: d0_a |
| * 1: d1_a |
| * 2: d2_a |
| * 3: clk_a |
| * 4: d3_a |
| * 5: d4_a |
| * 6: d0_b |
| * 7: d1_b |
| * 8: d2_b |
| * 9: clk_b |
| * a: d3_b |
| * b: d4_b |
| */ |
| if (port_swap) { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0x345789ab); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0x0612); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0x210a9876); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0x5b43); |
| } |
| } else { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0xab12345); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0x60789); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0, 0x87643210); |
| lcd_vcbus_write(P2P_CH_SWAP1, 0xb5a9); |
| } |
| } |
| break; |
| case LCD_CHIP_T7: |
| /* lvds channel: //tx 12 channels |
| * 0: d0_a |
| * 1: d1_a |
| * 2: d2_a |
| * 3: clk_a |
| * 4: d3_a |
| * 5: d4_a |
| * 6: d0_b |
| * 7: d1_b |
| * 8: d2_b |
| * 9: clk_b |
| * a: d3_b |
| * b: d4_b |
| */ |
| if (port_swap) { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x345789ab); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0x0612); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x210a9876); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0x5b43); |
| } |
| } else { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x9ab12345); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0x6078); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x87643210); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0xb5a9); |
| } |
| } |
| lcd_vcbus_write(P2P_BIT_REV_T7 + offset, 2); |
| break; |
| case LCD_CHIP_T3: |
| /* lvds channel: //tx 12 channels |
| * 0: d0_a |
| * 1: d1_a |
| * 2: d2_a |
| * 3: clk_a |
| * 4: d3_a |
| * 5: d4_a |
| * 6: d0_b |
| * 7: d1_b |
| * 8: d2_b |
| * 9: clk_b |
| * a: d3_b |
| * b: d4_b |
| */ |
| if (port_swap) { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x456789ab); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0x0123); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x10ba9876); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0x5432); |
| } |
| } else { |
| if (lane_reverse) { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0xab012345); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0x6789); |
| } else { |
| lcd_vcbus_write(P2P_CH_SWAP0_T7 + offset, 0x76543210); |
| lcd_vcbus_write(P2P_CH_SWAP1_T7 + offset, 0xba98); |
| } |
| } |
| lcd_vcbus_write(P2P_BIT_REV_T7 + offset, 2); |
| break; |
| default: |
| break; |
| } |
| |
| lcd_vcbus_setb(reg_lvds_gen_ctrl, 1, 4, 1); |
| lcd_vcbus_setb(reg_lvds_gen_ctrl, fifo_mode, 0, 2); |
| lcd_vcbus_setb(reg_lvds_gen_ctrl, 1, 3, 1); |
| } |
| |
| static void lcd_lvds_disable(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_dphy_tx_ctrl0, reg_dphy_tx_ctrl1, offset = 0; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7) { |
| switch (pdrv->index) { |
| case 0: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL1; |
| break; |
| case 1: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL1; |
| break; |
| case 2: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY2_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY2_CNTL1; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| |
| /* disable lvds fifo */ |
| lcd_vcbus_setb(LVDS_GEN_CNTL_T7 + offset, 0, 3, 1); |
| lcd_vcbus_setb(LVDS_GEN_CNTL_T7 + offset, 0, 0, 2); |
| /* disable fifo */ |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl1, 0, 6, 2); |
| /* disable lane */ |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl0, 0, 16, 10); |
| } else if (pdrv->data->chip_type == LCD_CHIP_T3) { |
| /* disable lvds fifo */ |
| lcd_vcbus_setb(LVDS_GEN_CNTL_T7, 0, 3, 1); |
| lcd_vcbus_setb(LVDS_GEN_CNTL_T7, 0, 0, 2); |
| /* disable fifo */ |
| lcd_ana_setb(ANACTRL_LVDS_TX_PHY_CNTL1, 0, 30, 2); |
| /* disable lane */ |
| lcd_ana_setb(ANACTRL_LVDS_TX_PHY_CNTL0, 0, 16, 12); |
| } else { |
| /* disable lvds fifo */ |
| lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 3, 1); |
| lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 0, 2); |
| /* disable fifo */ |
| lcd_clk_setb(HHI_LVDS_TX_PHY_CNTL1, 0, 30, 2); |
| /* disable lane */ |
| lcd_clk_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 16, 12); |
| } |
| } |
| |
| static void lcd_vbyone_clk_util_set(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int lcd_bits, div_sel, phy_div; |
| unsigned int reg_phy_tx_ctrl0, reg_phy_tx_ctrl1; |
| unsigned int bit_data_in_lvds, bit_data_in_edp, bit_lane_sel; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| phy_div = pdrv->config.control.vbyone_cfg.phy_div; |
| lcd_bits = pdrv->config.basic.lcd_bits; |
| switch (lcd_bits) { |
| case 6: |
| div_sel = 0; |
| break; |
| case 8: |
| div_sel = 2; |
| break; |
| case 10: |
| default: |
| div_sel = 3; |
| break; |
| } |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7) { |
| switch (pdrv->index) { |
| case 0: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| reg_phy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL1; |
| bit_data_in_lvds = 0; |
| bit_data_in_edp = 1; |
| bit_lane_sel = 0; |
| break; |
| case 1: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| reg_phy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL1; |
| bit_data_in_lvds = 2; |
| bit_data_in_edp = 3; |
| bit_lane_sel = 16; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| // sel dphy data_in |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL0, |
| 0, bit_data_in_edp, 1); |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL0, |
| 1, bit_data_in_lvds, 1); |
| // sel dphy lane |
| lcd_combo_dphy_setb(pdrv, COMBO_DPHY_CNTL1, |
| 0x5555, bit_lane_sel, 16); |
| |
| /* set fifo_clk_sel: div 10 */ |
| lcd_combo_dphy_write(pdrv, reg_phy_tx_ctrl0, (div_sel << 5)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_combo_dphy_setb(pdrv, reg_phy_tx_ctrl0, 0xff, 16, 8); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_combo_dphy_write(pdrv, reg_phy_tx_ctrl1, |
| (1 << 6) | (1 << 0)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_combo_dphy_setb(pdrv, reg_phy_tx_ctrl1, 1, 7, 1); |
| } else if (pdrv->data->chip_type == LCD_CHIP_T3) { |
| switch (pdrv->index) { |
| case 0: |
| reg_phy_tx_ctrl0 = ANACTRL_LVDS_TX_PHY_CNTL0; |
| reg_phy_tx_ctrl1 = ANACTRL_LVDS_TX_PHY_CNTL1; |
| /* set fifo_clk_sel: div 10 */ |
| lcd_ana_write(reg_phy_tx_ctrl0, (div_sel << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_ana_setb(reg_phy_tx_ctrl0, 0xfff, 16, 12); |
| /* pn swap */ |
| lcd_ana_setb(reg_phy_tx_ctrl0, 1, 2, 1); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_ana_write(reg_phy_tx_ctrl1, (1 << 30) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_ana_setb(reg_phy_tx_ctrl1, 1, 31, 1); |
| break; |
| case 1: |
| reg_phy_tx_ctrl0 = ANACTRL_LVDS_TX_PHY_CNTL2; |
| reg_phy_tx_ctrl1 = ANACTRL_LVDS_TX_PHY_CNTL3; |
| /* set fifo_clk_sel: div 10 */ |
| lcd_ana_write(reg_phy_tx_ctrl0, (div_sel << 6)); |
| /* set cntl_ser_en: 4-channel to 1 */ |
| lcd_ana_setb(reg_phy_tx_ctrl0, 0xf, 16, 4); |
| /* pn swap */ |
| lcd_ana_setb(reg_phy_tx_ctrl0, 1, 2, 1); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_ana_write(reg_phy_tx_ctrl1, (1 << 30) | (3 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_ana_setb(reg_phy_tx_ctrl1, 1, 31, 1); |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| } else { |
| /* set fifo_clk_sel: div 10 */ |
| lcd_ana_write(HHI_LVDS_TX_PHY_CNTL0, (div_sel << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12); |
| /* pn swap */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 1, 2, 1); |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_ana_write(HHI_LVDS_TX_PHY_CNTL1, |
| (1 << 30) | ((phy_div - 1) << 25) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1); |
| } |
| } |
| |
| static void lcd_vbyone_control_set(struct aml_lcd_drv_s *pdrv) |
| { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| lcd_vbyone_clk_util_set(pdrv); |
| |
| switch (pdrv->data->chip_type) { |
| case LCD_CHIP_T7: |
| case LCD_CHIP_T3: |
| lcd_vbyone_enable_t7(pdrv); |
| break; |
| default: |
| lcd_vbyone_enable_dft(pdrv); |
| break; |
| } |
| } |
| |
| static void lcd_vbyone_control_off(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_dphy_tx_ctrl0, reg_dphy_tx_ctrl1; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| switch (pdrv->data->chip_type) { |
| case LCD_CHIP_T7: |
| switch (pdrv->index) { |
| case 0: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL1; |
| break; |
| case 1: |
| reg_dphy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| reg_dphy_tx_ctrl1 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL1; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| lcd_vbyone_disable_t7(pdrv); |
| /* disable fifo */ |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl1, 0, 6, 2); |
| /* disable lane */ |
| lcd_combo_dphy_setb(pdrv, reg_dphy_tx_ctrl0, 0, 16, 8); |
| break; |
| case LCD_CHIP_T3: |
| switch (pdrv->index) { |
| case 0: |
| reg_dphy_tx_ctrl0 = ANACTRL_LVDS_TX_PHY_CNTL0; |
| reg_dphy_tx_ctrl1 = ANACTRL_LVDS_TX_PHY_CNTL1; |
| break; |
| case 1: |
| reg_dphy_tx_ctrl0 = ANACTRL_LVDS_TX_PHY_CNTL2; |
| reg_dphy_tx_ctrl1 = ANACTRL_LVDS_TX_PHY_CNTL3; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", |
| pdrv->index, __func__); |
| return; |
| } |
| |
| lcd_vbyone_disable_t7(pdrv); |
| /* disable fifo */ |
| lcd_ana_setb(reg_dphy_tx_ctrl1, 0, 30, 2); |
| /* disable lane */ |
| lcd_ana_setb(reg_dphy_tx_ctrl0, 0, 16, 12); |
| break; |
| default: |
| lcd_vbyone_disable_dft(pdrv); |
| /* disable fifo */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL1, 0, 30, 2); |
| /* disable lane */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 16, 12); |
| break; |
| } |
| } |
| |
| void lcd_tablet_clk_config_change(struct aml_lcd_drv_s *pdrv) |
| { |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_clk_request(pdrv->lcd_vpu_dev, pdrv->config.timing.lcd_clk); |
| #endif |
| |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_config_set(pdrv); |
| break; |
| case LCD_MIPI: |
| lcd_mipi_dsi_config_set(pdrv); |
| break; |
| default: |
| break; |
| } |
| |
| lcd_clk_generate_parameter(pdrv); |
| } |
| |
| void lcd_tablet_clk_update(struct aml_lcd_drv_s *pdrv) |
| { |
| lcd_tablet_clk_config_change(pdrv); |
| |
| if (pdrv->config.basic.lcd_type == LCD_VBYONE) |
| lcd_vbyone_interrupt_enable(pdrv, 0); |
| lcd_set_clk(pdrv); |
| if (pdrv->config.basic.lcd_type == LCD_VBYONE) |
| lcd_vbyone_wait_stable(pdrv); |
| } |
| |
| void lcd_tablet_config_update(struct aml_lcd_drv_s *pdrv) |
| { |
| struct vinfo_s *info; |
| |
| /* update lcd config sync_duration */ |
| info = &pdrv->vinfo; |
| pdrv->config.timing.sync_duration_num = info->sync_duration_num; |
| pdrv->config.timing.sync_duration_den = info->sync_duration_den; |
| |
| /* update clk & timing config */ |
| lcd_vmode_change(pdrv); |
| info->video_clk = pdrv->config.timing.lcd_clk; |
| info->htotal = pdrv->config.basic.h_period; |
| info->vtotal = pdrv->config.basic.v_period; |
| /* update interface timing */ |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_config_set(pdrv); |
| break; |
| case LCD_MIPI: |
| lcd_mipi_dsi_config_set(pdrv); |
| break; |
| case LCD_EDP: |
| lcd_edp_config_set(pdrv); |
| default: |
| break; |
| } |
| } |
| |
| void lcd_tablet_config_post_update(struct aml_lcd_drv_s *pdrv) |
| { |
| /* update interface timing */ |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_MIPI: |
| lcd_mipi_dsi_config_post(pdrv); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void lcd_tablet_driver_init_pre(struct aml_lcd_drv_s *pdrv) |
| { |
| int ret; |
| |
| LCDPR("[%d]: tablet driver init(ver %s): %s\n", |
| pdrv->index, LCD_DRV_VERSION, |
| lcd_type_type_to_str(pdrv->config.basic.lcd_type)); |
| ret = lcd_type_supported(&pdrv->config); |
| if (ret) |
| return; |
| |
| lcd_tablet_config_update(pdrv); |
| lcd_tablet_config_post_update(pdrv); |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_clk_request(pdrv->lcd_vpu_dev, pdrv->config.timing.lcd_clk); |
| vpu_dev_mem_power_on(pdrv->lcd_vpu_dev); |
| #endif |
| lcd_clk_gate_switch(pdrv, 1); |
| |
| /* init driver */ |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_interrupt_enable(pdrv, 0); |
| break; |
| default: |
| break; |
| } |
| |
| lcd_set_clk(pdrv); |
| lcd_set_venc(pdrv); |
| pdrv->mute_state = 1; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s finished\n", pdrv->index, __func__); |
| } |
| |
| void lcd_tablet_driver_disable_post(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int offset; |
| |
| offset = pdrv->data->offset_venc[pdrv->index]; |
| lcd_vcbus_write(ENCL_VIDEO_EN + offset, 0); /* disable encl */ |
| |
| lcd_disable_clk(pdrv); |
| lcd_clk_gate_switch(pdrv, 0); |
| #ifdef CONFIG_AMLOGIC_VPU |
| vpu_dev_mem_power_down(pdrv->lcd_vpu_dev); |
| vpu_dev_clk_release(pdrv->lcd_vpu_dev); |
| #endif |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s finished\n", pdrv->index, __func__); |
| } |
| |
| int lcd_tablet_driver_init(struct aml_lcd_drv_s *pdrv) |
| { |
| int ret; |
| |
| ret = lcd_type_supported(&pdrv->config); |
| if (ret) |
| return -1; |
| |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_TTL: |
| lcd_ttl_control_set(pdrv); |
| lcd_ttl_pinmux_set(pdrv, 1); |
| break; |
| case LCD_LVDS: |
| lcd_lvds_control_set(pdrv); |
| lcd_phy_set(pdrv, 1); |
| break; |
| case LCD_VBYONE: |
| lcd_vbyone_pinmux_set(pdrv, 1); |
| lcd_vbyone_control_set(pdrv); |
| lcd_vbyone_wait_hpd(pdrv); |
| lcd_phy_set(pdrv, 1); |
| lcd_vbyone_power_on_wait_stable(pdrv); |
| break; |
| case LCD_MIPI: |
| lcd_phy_set(pdrv, 1); |
| lcd_mipi_control_set(pdrv); |
| break; |
| case LCD_EDP: |
| lcd_edp_pinmux_set(pdrv, 1); |
| lcd_phy_set(pdrv, 1); |
| lcd_edp_control_set(pdrv); |
| break; |
| default: |
| break; |
| } |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s finished\n", pdrv->index, __func__); |
| return 0; |
| } |
| |
| void lcd_tablet_driver_disable(struct aml_lcd_drv_s *pdrv) |
| { |
| int ret; |
| |
| LCDPR("[%d]: disable driver\n", pdrv->index); |
| ret = lcd_type_supported(&pdrv->config); |
| if (ret) |
| return; |
| |
| switch (pdrv->config.basic.lcd_type) { |
| case LCD_TTL: |
| lcd_ttl_pinmux_set(pdrv, 0); |
| break; |
| case LCD_LVDS: |
| lcd_phy_set(pdrv, 0); |
| lcd_lvds_disable(pdrv); |
| break; |
| case LCD_VBYONE: |
| lcd_vbyone_link_maintain_clear(); |
| lcd_vbyone_interrupt_enable(pdrv, 0); |
| lcd_phy_set(pdrv, 0); |
| lcd_vbyone_pinmux_set(pdrv, 0); |
| lcd_vbyone_control_off(pdrv); |
| break; |
| case LCD_MIPI: |
| mipi_dsi_link_off(pdrv); |
| lcd_phy_set(pdrv, 0); |
| lcd_mipi_disable(pdrv); |
| break; |
| case LCD_EDP: |
| lcd_edp_disable(pdrv); |
| lcd_phy_set(pdrv, 0); |
| lcd_edp_pinmux_set(pdrv, 0); |
| break; |
| default: |
| break; |
| } |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s finished\n", pdrv->index, __func__); |
| } |