| /* |
| * drivers/display/lcd/lcd_tablet/lcd_drv.c |
| * |
| * Copyright (C) 2015 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <vpp.h> |
| #include <asm/arch/io.h> |
| #include <amlogic/aml_lcd.h> |
| #include "../aml_lcd_reg.h" |
| #include "../aml_lcd_common.h" |
| #include "lcd_tablet.h" |
| #include "mipi_dsi_util.h" |
| |
| |
| static int lcd_type_supported(struct lcd_config_s *pconf) |
| { |
| int lcd_type = pconf->lcd_basic.lcd_type; |
| int ret = -1; |
| |
| switch (lcd_type) { |
| case LCD_TTL: |
| case LCD_LVDS: |
| case LCD_VBYONE: |
| case LCD_MIPI: |
| ret = 0; |
| break; |
| default: |
| LCDERR("invalid lcd type: %s(%d)\n", |
| lcd_type_type_to_str(lcd_type), lcd_type); |
| break; |
| } |
| return ret; |
| } |
| |
| #define STV2_SEL 5 |
| #define STV1_SEL 4 |
| static void lcd_encl_tcon_set(struct lcd_config_s *pconf) |
| { |
| struct lcd_timing_s *tcon_adr = &pconf->lcd_timing; |
| |
| lcd_vcbus_write(L_RGB_BASE_ADDR, 0); |
| lcd_vcbus_write(L_RGB_COEFF_ADDR, 0x400); |
| |
| switch (pconf->lcd_basic.lcd_bits) { |
| case 6: |
| lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x600); |
| break; |
| case 8: |
| lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x400); |
| break; |
| case 10: |
| default: |
| lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x0); |
| break; |
| } |
| |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_LVDS: |
| lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1); |
| if (pconf->lcd_timing.vsync_pol) |
| lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1); |
| break; |
| case LCD_VBYONE: |
| if (pconf->lcd_timing.hsync_pol) |
| lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 0, 1); |
| if (pconf->lcd_timing.vsync_pol) |
| lcd_vcbus_setb(L_POL_CNTL_ADDR, 1, 1, 1); |
| break; |
| case LCD_MIPI: |
| //lcd_vcbus_setb(L_POL_CNTL_ADDR, 0x3, 0, 2); |
| /*lcd_vcbus_write(L_POL_CNTL_ADDR, (lcd_vcbus_read(L_POL_CNTL_ADDR) | ((0 << 2) | (vs_pol_adj << 1) | (hs_pol_adj << 0))));*/ |
| /*lcd_vcbus_write(L_POL_CNTL_ADDR, (lcd_vcbus_read(L_POL_CNTL_ADDR) | ((1 << LCD_TCON_DE_SEL) | (1 << LCD_TCON_VS_SEL) | (1 << LCD_TCON_HS_SEL))));*/ |
| break; |
| default: |
| break; |
| } |
| |
| /* DE signal for TTL m8,m8m2 */ |
| lcd_vcbus_write(L_OEH_HS_ADDR, tcon_adr->de_hs_addr); |
| lcd_vcbus_write(L_OEH_HE_ADDR, tcon_adr->de_he_addr); |
| lcd_vcbus_write(L_OEH_VS_ADDR, tcon_adr->de_vs_addr); |
| lcd_vcbus_write(L_OEH_VE_ADDR, tcon_adr->de_ve_addr); |
| /* DE signal for TTL m8b */ |
| lcd_vcbus_write(L_OEV1_HS_ADDR, tcon_adr->de_hs_addr); |
| lcd_vcbus_write(L_OEV1_HE_ADDR, tcon_adr->de_he_addr); |
| lcd_vcbus_write(L_OEV1_VS_ADDR, tcon_adr->de_vs_addr); |
| lcd_vcbus_write(L_OEV1_VE_ADDR, tcon_adr->de_ve_addr); |
| |
| /* Hsync signal for TTL m8,m8m2 */ |
| if (tcon_adr->hsync_pol == 0) { |
| lcd_vcbus_write(L_STH1_HS_ADDR, tcon_adr->hs_he_addr); |
| lcd_vcbus_write(L_STH1_HE_ADDR, tcon_adr->hs_hs_addr); |
| } else { |
| lcd_vcbus_write(L_STH1_HS_ADDR, tcon_adr->hs_hs_addr); |
| lcd_vcbus_write(L_STH1_HE_ADDR, tcon_adr->hs_he_addr); |
| } |
| lcd_vcbus_write(L_STH1_VS_ADDR, tcon_adr->hs_vs_addr); |
| lcd_vcbus_write(L_STH1_VE_ADDR, tcon_adr->hs_ve_addr); |
| |
| /* Vsync signal for TTL m8,m8m2 */ |
| lcd_vcbus_write(L_STV1_HS_ADDR, tcon_adr->vs_hs_addr); |
| lcd_vcbus_write(L_STV1_HE_ADDR, tcon_adr->vs_he_addr); |
| if (tcon_adr->vsync_pol == 0) { |
| lcd_vcbus_write(L_STV1_VS_ADDR, tcon_adr->vs_ve_addr); |
| lcd_vcbus_write(L_STV1_VE_ADDR, tcon_adr->vs_vs_addr); |
| } else { |
| lcd_vcbus_write(L_STV1_VS_ADDR, tcon_adr->vs_vs_addr); |
| lcd_vcbus_write(L_STV1_VE_ADDR, tcon_adr->vs_ve_addr); |
| } |
| |
| /* DE signal */ |
| lcd_vcbus_write(L_DE_HS_ADDR, tcon_adr->de_hs_addr); |
| lcd_vcbus_write(L_DE_HE_ADDR, tcon_adr->de_he_addr); |
| lcd_vcbus_write(L_DE_VS_ADDR, tcon_adr->de_vs_addr); |
| lcd_vcbus_write(L_DE_VE_ADDR, tcon_adr->de_ve_addr); |
| |
| /* Hsync signal */ |
| lcd_vcbus_write(L_HSYNC_HS_ADDR, tcon_adr->hs_hs_addr); |
| lcd_vcbus_write(L_HSYNC_HE_ADDR, tcon_adr->hs_he_addr); |
| lcd_vcbus_write(L_HSYNC_VS_ADDR, tcon_adr->hs_vs_addr); |
| lcd_vcbus_write(L_HSYNC_VE_ADDR, tcon_adr->hs_ve_addr); |
| |
| /* Vsync signal */ |
| lcd_vcbus_write(L_VSYNC_HS_ADDR, tcon_adr->vs_hs_addr); |
| lcd_vcbus_write(L_VSYNC_HE_ADDR, tcon_adr->vs_he_addr); |
| lcd_vcbus_write(L_VSYNC_VS_ADDR, tcon_adr->vs_vs_addr); |
| lcd_vcbus_write(L_VSYNC_VE_ADDR, tcon_adr->vs_ve_addr); |
| |
| lcd_vcbus_write(L_INV_CNT_ADDR, 0); |
| lcd_vcbus_write(L_TCON_MISC_SEL_ADDR, ((1 << STV1_SEL) | (1 << STV2_SEL))); |
| |
| lcd_vcbus_write(VPP_MISC, lcd_vcbus_read(VPP_MISC) & ~(VPP_OUT_SATURATE)); |
| } |
| |
| static void lcd_venc_set(struct lcd_config_s *pconf) |
| { |
| unsigned int h_active, v_active; |
| unsigned int video_on_pixel, video_on_line; |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| h_active = pconf->lcd_basic.h_active; |
| v_active = pconf->lcd_basic.v_active; |
| video_on_pixel = pconf->lcd_timing.video_on_pixel; |
| video_on_line = pconf->lcd_timing.video_on_line; |
| |
| lcd_vcbus_write(ENCL_VIDEO_EN, 0); |
| |
| lcd_vcbus_write(ENCL_VIDEO_MODE, 0x8000); // bit[15] shadown en |
| lcd_vcbus_write(ENCL_VIDEO_MODE_ADV, 0x0418); // Sampling rate: 1 |
| |
| // bypass filter |
| lcd_vcbus_write(ENCL_VIDEO_FILT_CTRL, 0x1000); |
| lcd_vcbus_write(ENCL_VIDEO_MAX_PXCNT, pconf->lcd_basic.h_period - 1); |
| lcd_vcbus_write(ENCL_VIDEO_MAX_LNCNT, pconf->lcd_basic.v_period - 1); |
| lcd_vcbus_write(ENCL_VIDEO_HAVON_BEGIN, video_on_pixel); |
| lcd_vcbus_write(ENCL_VIDEO_HAVON_END, h_active - 1 + video_on_pixel); |
| lcd_vcbus_write(ENCL_VIDEO_VAVON_BLINE, video_on_line); |
| lcd_vcbus_write(ENCL_VIDEO_VAVON_ELINE, v_active - 1 + video_on_line); |
| |
| lcd_vcbus_write(ENCL_VIDEO_HSO_BEGIN, pconf->lcd_timing.hs_hs_addr); |
| lcd_vcbus_write(ENCL_VIDEO_HSO_END, pconf->lcd_timing.hs_he_addr); |
| lcd_vcbus_write(ENCL_VIDEO_VSO_BEGIN, pconf->lcd_timing.vs_hs_addr); |
| lcd_vcbus_write(ENCL_VIDEO_VSO_END, pconf->lcd_timing.vs_he_addr); |
| lcd_vcbus_write(ENCL_VIDEO_VSO_BLINE, pconf->lcd_timing.vs_vs_addr); |
| lcd_vcbus_write(ENCL_VIDEO_VSO_ELINE, pconf->lcd_timing.vs_ve_addr); |
| lcd_vcbus_write(ENCL_VIDEO_RGBIN_CTRL, 3); |
| |
| switch (lcd_drv->chip_type) { |
| case LCD_CHIP_TL1: |
| case LCD_CHIP_TM2: |
| /*[15:14]: 2'b10 or 2'b01*/ |
| lcd_vcbus_write(ENCL_INBUF_CNTL1, (2 << 14) | (h_active - 1)); |
| lcd_vcbus_write(ENCL_INBUF_CNTL0, 0x200); |
| break; |
| default: |
| break; |
| } |
| |
| /* default black pattern */ |
| lcd_vcbus_write(ENCL_TST_MDSEL, 0); |
| lcd_vcbus_write(ENCL_TST_Y, 0); |
| lcd_vcbus_write(ENCL_TST_CB, 0); |
| lcd_vcbus_write(ENCL_TST_CR, 0); |
| lcd_vcbus_write(ENCL_TST_EN, 1); |
| lcd_vcbus_setb(ENCL_VIDEO_MODE_ADV, 0, 3, 1); |
| |
| lcd_vcbus_write(ENCL_VIDEO_EN, 1); |
| } |
| |
| static void lcd_ttl_control_set(struct lcd_config_s *pconf) |
| { |
| unsigned int clk_pol, rb_swap, bit_swap; |
| |
| clk_pol = pconf->lcd_control.ttl_config->clk_pol; |
| rb_swap = (pconf->lcd_control.ttl_config->swap_ctrl >> 1) & 1; |
| bit_swap = (pconf->lcd_control.ttl_config->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_lvds_clk_util_set(struct lcd_config_s *pconf) |
| { |
| unsigned int phy_div; |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| if (pconf->lcd_control.lvds_config->dual_port) |
| phy_div = 2; |
| else |
| phy_div = 1; |
| |
| /* set fifo_clk_sel: div 7 */ |
| lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (1 << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12); |
| switch (lcd_drv->chip_type) { /* pn swap */ |
| case LCD_CHIP_TL1: |
| case LCD_CHIP_TM2: |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 1, 2, 1); |
| break; |
| default: |
| break; |
| } |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1, |
| (1 << 30) | ((phy_div - 1) << 25) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1); |
| } |
| |
| static void lcd_lvds_control_set(struct lcd_config_s *pconf) |
| { |
| unsigned int bit_num = 1; |
| unsigned int pn_swap, port_swap, lane_reverse; |
| unsigned int dual_port, fifo_mode; |
| unsigned int lvds_repack = 1; |
| unsigned int ch_swap0, ch_swap1, ch_swap2, temp0, temp1; |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| lcd_lvds_clk_util_set(pconf); |
| |
| lvds_repack = (pconf->lcd_control.lvds_config->lvds_repack) & 0x3; |
| pn_swap = (pconf->lcd_control.lvds_config->pn_swap) & 0x1; |
| dual_port = (pconf->lcd_control.lvds_config->dual_port) & 0x1; |
| port_swap = (pconf->lcd_control.lvds_config->port_swap) & 0x1; |
| lane_reverse = (pconf->lcd_control.lvds_config->lane_reverse) & 0x1; |
| |
| switch (pconf->lcd_basic.lcd_bits) { |
| case 10: |
| bit_num=0; |
| break; |
| case 8: |
| bit_num=1; |
| break; |
| case 6: |
| bit_num=2; |
| break; |
| case 4: |
| bit_num=3; |
| break; |
| default: |
| bit_num=1; |
| break; |
| } |
| if (dual_port) |
| fifo_mode = 0x3; |
| else |
| fifo_mode = 0x1; |
| |
| lcd_vcbus_write(LVDS_PACK_CNTL_ADDR, |
| (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 |
| (bit_num << 8) | // 0:10bits, 1:8bits, 2:6bits, 3:4bits. |
| (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 (lcd_drv->chip_type) { |
| case LCD_CHIP_TXHD: |
| /* lvds channel: //tx 10 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 */ |
| ch_swap0 = 0x3210; |
| ch_swap1 = 0x8764; |
| ch_swap2 = 0x00a9; |
| if (port_swap) { |
| temp0 = ((ch_swap1 << 16) | ch_swap0) & 0xfffff; |
| temp1 = ((ch_swap2 << 12) | (ch_swap1 >> 4)) & 0xfffff; |
| /* ch_swap0: temp1[15:0] |
| * ch_swap1: temp0[11:0], temp1[19:16] |
| * ch_swap2: temp0[19:12] */ |
| ch_swap0 = temp1 & 0xffff; |
| ch_swap1 = ((temp0 << 4) | ((temp1 >> 16) & 0xf)) & 0xffff; |
| ch_swap2 = (temp0 >> 12) & 0xff; |
| } |
| if (lane_reverse) { |
| temp0 = ((ch_swap1 << 16) | ch_swap0) & 0xfffff; |
| temp1 = ((ch_swap2 << 12) | (ch_swap1 >> 4)) & 0xfffff; |
| /* ch_swap0: temp0[7:4], temp0[11:8], temp0[15:12], temp0[19:16] |
| * ch_swap1: temp1[11:8], temp1[15:12], temp1[19:16], temp0[3:0] |
| * ch_swap2: temp1[3:0], temp1[7:4] */ |
| ch_swap0 = (((temp0 >> 4) & 0xf) << 12) | |
| (((temp0 >> 8) & 0xf) << 8) | |
| (((temp0 >> 12) & 0xf) << 4) | |
| (((temp0 >> 16) & 0xf) << 0); |
| ch_swap1 = (((temp1 >> 8) & 0xf) << 12) | |
| (((temp1 >> 12) & 0xf) << 8) | |
| (((temp1 >> 16) & 0xf) << 4) | |
| (((temp0 >> 0) & 0xf) << 0); |
| ch_swap2 = (((temp1 >> 0) & 0xf) << 4) | |
| (((temp1 >> 4) & 0xf) << 0); |
| } |
| lcd_vcbus_write(LVDS_CH_SWAP0, ch_swap0); |
| lcd_vcbus_write(LVDS_CH_SWAP1, ch_swap1); |
| lcd_vcbus_write(LVDS_CH_SWAP2, ch_swap2); |
| break; |
| 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; |
| default: |
| lcd_vcbus_setb(LCD_PORT_SWAP, port_swap, 12, 1); |
| if (lane_reverse) |
| lcd_vcbus_setb(LVDS_GEN_CNTL, 0x03, 13, 2); |
| break; |
| } |
| |
| lcd_vcbus_write(LVDS_GEN_CNTL, (lcd_vcbus_read(LVDS_GEN_CNTL) | (1 << 4) | (fifo_mode << 0))); |
| lcd_vcbus_setb(LVDS_GEN_CNTL, 1, 3, 1); |
| } |
| |
| static void lcd_lvds_disable(void) |
| { |
| lcd_vcbus_setb(LVDS_GEN_CNTL, 0, 3, 1); /* disable lvds fifo */ |
| } |
| |
| static void lcd_vbyone_sync_pol(int hsync_pol, int vsync_pol) |
| { |
| lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 4, 1); |
| lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 5, 1); |
| |
| lcd_vcbus_setb(VBO_VIN_CTRL, hsync_pol, 6, 1); |
| lcd_vcbus_setb(VBO_VIN_CTRL, vsync_pol, 7, 1); |
| } |
| |
| static void lcd_vbyone_clk_util_set(struct lcd_config_s *pconf) |
| { |
| unsigned int lcd_bits; |
| unsigned int div_sel, phy_div; |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| phy_div = pconf->lcd_control.vbyone_config->phy_div; |
| lcd_bits = pconf->lcd_basic.lcd_bits; |
| |
| switch (lcd_bits) { |
| case 6: |
| div_sel = 0; |
| break; |
| case 8: |
| div_sel = 2; |
| break; |
| case 10: |
| div_sel = 3; |
| break; |
| default: |
| div_sel = 3; |
| break; |
| } |
| /* set fifo_clk_sel */ |
| lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0, (div_sel << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0xfff, 16, 12); |
| switch (lcd_drv->chip_type) { /* pn swap */ |
| case LCD_CHIP_TL1: |
| case LCD_CHIP_TM2: |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 1, 2, 1); |
| break; |
| default: |
| break; |
| } |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1, |
| (1 << 30) | ((phy_div - 1) << 25) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1, 1, 31, 1); |
| } |
| |
| static int lcd_vbyone_lanes_set(int lane_num, int byte_mode, int region_num, |
| int hsize, int vsize) |
| { |
| int sublane_num; |
| int region_size[4]; |
| int tmp; |
| |
| switch (lane_num) { |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| break; |
| default: |
| return -1; |
| } |
| switch (region_num) { |
| case 1: |
| case 2: |
| case 4: |
| break; |
| default: |
| return -1; |
| } |
| if (lane_num % region_num) |
| return -1; |
| switch (byte_mode) { |
| case 3: |
| case 4: |
| break; |
| default: |
| return -1; |
| } |
| if (lcd_debug_print_flag) { |
| LCDPR("byte_mode=%d, lane_num=%d, region_num=%d\n", |
| byte_mode, lane_num, region_num); |
| } |
| |
| sublane_num = lane_num / region_num; /* lane num in each region */ |
| lcd_vcbus_setb(VBO_LANES, (lane_num - 1), 0, 3); |
| lcd_vcbus_setb(VBO_LANES, (region_num - 1), 4, 2); |
| lcd_vcbus_setb(VBO_LANES, (sublane_num - 1), 8, 3); |
| lcd_vcbus_setb(VBO_LANES, (byte_mode - 1), 11, 2); |
| |
| if (region_num > 1) { |
| region_size[3] = (hsize / lane_num) * sublane_num; |
| tmp = (hsize % lane_num); |
| region_size[0] = region_size[3] + (((tmp / sublane_num) > 0) ? |
| sublane_num : (tmp % sublane_num)); |
| region_size[1] = region_size[3] + (((tmp / sublane_num) > 1) ? |
| sublane_num : (tmp % sublane_num)); |
| region_size[2] = region_size[3] + (((tmp / sublane_num) > 2) ? |
| sublane_num : (tmp % sublane_num)); |
| lcd_vcbus_write(VBO_REGION_00, region_size[0]); |
| lcd_vcbus_write(VBO_REGION_01, region_size[1]); |
| lcd_vcbus_write(VBO_REGION_02, region_size[2]); |
| lcd_vcbus_write(VBO_REGION_03, region_size[3]); |
| } |
| lcd_vcbus_write(VBO_ACT_VSIZE, vsize); |
| /* different from FBC code!!! */ |
| /* lcd_vcbus_setb(VBO_CTRL_H,0x80,11,5); */ |
| /* different from simulation code!!! */ |
| lcd_vcbus_setb(VBO_CTRL_H, 0x0, 0, 4); |
| lcd_vcbus_setb(VBO_CTRL_H, 0x1, 9, 1); |
| /* lcd_vcbus_setb(VBO_CTRL_L,enable,0,1); */ |
| |
| return 0; |
| } |
| |
| static void lcd_vbyone_sw_reset(void) |
| { |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| /* force PHY to 0 */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0x1ff); |
| udelay(5); |
| /* realease PHY */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0); |
| } |
| |
| static void lcd_vbyone_wait_timing_stable(void) |
| { |
| unsigned int timing_state; |
| int i = 200; |
| |
| timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff; |
| while ((timing_state) && (i > 0)) { |
| /* clear video timing error intr */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x7, 0, 3); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 3); |
| mdelay(2); |
| timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff; |
| i--; |
| }; |
| if (lcd_debug_print_flag) { |
| LCDPR("vbyone timing state: 0x%03x, i=%d\n", |
| timing_state, (200 - i)); |
| } |
| mdelay(2); |
| } |
| |
| static void lcd_vbyone_control_set(struct lcd_config_s *pconf) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| int lane_count, byte_mode, region_num, hsize, vsize; |
| /* int color_fmt; */ |
| int vin_color, vin_bpp; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| hsize = pconf->lcd_basic.h_active; |
| vsize = pconf->lcd_basic.v_active; |
| lane_count = pconf->lcd_control.vbyone_config->lane_count; /* 8 */ |
| region_num = pconf->lcd_control.vbyone_config->region_num; /* 2 */ |
| byte_mode = pconf->lcd_control.vbyone_config->byte_mode; /* 4 */ |
| /* color_fmt = pconf->lcd_control.vbyone_config->color_fmt; // 4 */ |
| |
| lcd_vbyone_clk_util_set(pconf); |
| #if 0 |
| switch (color_fmt) { |
| case 0:/* SDVT_VBYONE_18BPP_RGB */ |
| vin_color = 4; |
| vin_bpp = 2; |
| break; |
| case 1:/* SDVT_VBYONE_18BPP_YCBCR444 */ |
| vin_color = 0; |
| vin_bpp = 2; |
| break; |
| case 2:/* SDVT_VBYONE_24BPP_RGB */ |
| vin_color = 4; |
| vin_bpp = 1; |
| break; |
| case 3:/* SDVT_VBYONE_24BPP_YCBCR444 */ |
| vin_color = 0; |
| vin_bpp = 1; |
| break; |
| case 4:/* SDVT_VBYONE_30BPP_RGB */ |
| vin_color = 4; |
| vin_bpp = 0; |
| break; |
| case 5:/* SDVT_VBYONE_30BPP_YCBCR444 */ |
| vin_color = 0; |
| vin_bpp = 0; |
| break; |
| default: |
| LCDERR("vbyone COLOR_FORMAT unsupport\n"); |
| return; |
| } |
| #else |
| vin_color = 4; /* fixed RGB */ |
| vin_bpp = 0; /* fixed 30bbp 4:4:4 */ |
| #endif |
| |
| /* set Vbyone vin color format */ |
| lcd_vcbus_setb(VBO_VIN_CTRL, vin_color, 8, 3); |
| lcd_vcbus_setb(VBO_VIN_CTRL, vin_bpp, 11, 2); |
| |
| lcd_vbyone_lanes_set(lane_count, byte_mode, region_num, hsize, vsize); |
| /*set hsync/vsync polarity to let the polarity is low active |
| inside the VbyOne */ |
| lcd_vbyone_sync_pol(0, 0); |
| |
| /* below line copy from simulation */ |
| /* gate the input when vsync asserted */ |
| lcd_vcbus_setb(VBO_VIN_CTRL, 1, 0, 2); |
| /* lcd_vcbus_write(VBO_VBK_CTRL_0,0x13); |
| //lcd_vcbus_write(VBO_VBK_CTRL_1,0x56); |
| //lcd_vcbus_write(VBO_HBK_CTRL,0x3478); |
| //lcd_vcbus_setb(VBO_PXL_CTRL,0x2,0,4); |
| //lcd_vcbus_setb(VBO_PXL_CTRL,0x3,VBO_PXL_CTR1_BIT,VBO_PXL_CTR1_WID); |
| //set_vbyone_ctlbits(1,0,0); */ |
| |
| /* PAD select: */ |
| if ((lane_count == 1) || (lane_count == 2)) |
| lcd_vcbus_setb(LCD_PORT_SWAP, 1, 9, 2); |
| else if (lane_count == 4) |
| lcd_vcbus_setb(LCD_PORT_SWAP, 2, 9, 2); |
| else |
| lcd_vcbus_setb(LCD_PORT_SWAP, 0, 9, 2); |
| /* lcd_vcbus_setb(LCD_PORT_SWAP, 1, 8, 1);//reverse lane output order */ |
| |
| /* Mux pads in combo-phy: 0 for dsi; 1 for lvds or vbyone; 2 for edp */ |
| //lcd_hiu_write(HHI_DSI_LVDS_EDP_CNTL0, 0x1); |
| |
| /* set hpd & lockn hw filter */ |
| switch (lcd_drv->chip_type) { |
| case LCD_CHIP_TL1: |
| case LCD_CHIP_TM2: |
| lcd_vcbus_write(VBO_INFILTER_TICK_PERIOD_L, 0xff); |
| lcd_vcbus_write(VBO_INFILTER_TICK_PERIOD_H, 0x0); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0x7, 8, 4); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0x7, 12, 4); |
| break; |
| default: |
| lcd_vcbus_write(VBO_INFILTER_CTRL, 0xff77); |
| break; |
| } |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 2, 2); |
| |
| lcd_vcbus_setb(VBO_CTRL_L, 1, 0, 1); |
| |
| /*force vencl clk enable, otherwise, it might auto turn off by mipi DSI |
| //lcd_vcbus_setb(VPU_MISC_CTRL, 1, 0, 1); */ |
| |
| lcd_vbyone_wait_timing_stable(); |
| lcd_vbyone_sw_reset(); |
| } |
| |
| static void lcd_vbyone_disable(void) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1); |
| switch (lcd_drv->chip_type) { |
| case LCD_CHIP_TXL: |
| case LCD_CHIP_TXLX: |
| case LCD_CHIP_TL1: |
| case LCD_CHIP_TM2: |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 2, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 0, 1); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void lcd_vbyone_wait_stable(void) |
| { |
| int i = 500; |
| |
| while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) { |
| mdelay(2); |
| i--; |
| } |
| LCDPR("%s status: 0x%x, i=%d\n", __func__, lcd_vcbus_read(VBO_STATUS_L), (500 - i)); |
| } |
| |
| static void lcd_vbyone_wait_hpd(void) |
| { |
| int i = 0; |
| while (lcd_vcbus_read(VBO_STATUS_L) & 0x40) { |
| if (i++ >= 10000) |
| break; |
| udelay(50); |
| } |
| if (lcd_vcbus_read(VBO_STATUS_L) & 0x40) |
| LCDPR("%s: hpd=%d\n", __func__, |
| ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1)); |
| else |
| LCDPR("%s: hpd=%d, i=%d\n", __func__, |
| ((lcd_vcbus_read(VBO_STATUS_L) >> 6) & 0x1), i); |
| |
| /* musk htpd */ |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 1, 2, 2); |
| |
| mdelay(10); /* add 10ms delay for compatibility */ |
| } |
| |
| static unsigned int vbyone_lane_num[] = { |
| 1, |
| 2, |
| 4, |
| 8, |
| 8, |
| }; |
| |
| #define VBYONE_BIT_RATE_MAX 3100 //MHz |
| #define VBYONE_BIT_RATE_MIN 600 |
| static void lcd_vbyone_config_set(struct lcd_config_s *pconf) |
| { |
| unsigned int band_width, bit_rate, pclk, phy_div; |
| unsigned int byte_mode, lane_count, minlane; |
| unsigned int temp, i; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| //auto calculate bandwidth, clock |
| lane_count = pconf->lcd_control.vbyone_config->lane_count; |
| byte_mode = pconf->lcd_control.vbyone_config->byte_mode; |
| /* byte_mode * byte2bit * 8/10_encoding * pclk = |
| byte_mode * 8 * 10 / 8 * pclk */ |
| pclk = pconf->lcd_timing.lcd_clk / 1000; /* kHz */ |
| band_width = byte_mode * 10 * pclk; |
| |
| temp = VBYONE_BIT_RATE_MAX * 1000; |
| temp = (band_width + temp - 1) / temp; |
| for (i = 0; i < 4; i++) { |
| if (temp <= vbyone_lane_num[i]) |
| break; |
| } |
| minlane = vbyone_lane_num[i]; |
| if (lane_count < minlane) { |
| LCDERR("vbyone lane_num(%d) is less than min(%d)\n", |
| lane_count, minlane); |
| lane_count = minlane; |
| pconf->lcd_control.vbyone_config->lane_count = lane_count; |
| LCDPR("change to min lane_num %d\n", minlane); |
| } |
| |
| bit_rate = band_width / lane_count; |
| phy_div = lane_count / lane_count; |
| if (phy_div == 8) { |
| phy_div /= 2; |
| bit_rate /= 2; |
| } |
| if (bit_rate > (VBYONE_BIT_RATE_MAX * 1000)) { |
| LCDERR("vbyone bit rate(%dKHz) is out of max(%dKHz)\n", |
| bit_rate, (VBYONE_BIT_RATE_MAX * 1000)); |
| } |
| if (bit_rate < (VBYONE_BIT_RATE_MIN * 1000)) { |
| LCDERR("vbyone bit rate(%dKHz) is out of min(%dKHz)\n", |
| bit_rate, (VBYONE_BIT_RATE_MIN * 1000)); |
| } |
| bit_rate = bit_rate * 1000; /* Hz */ |
| |
| pconf->lcd_control.vbyone_config->phy_div = phy_div; |
| pconf->lcd_timing.bit_rate = bit_rate; |
| |
| if (lcd_debug_print_flag) { |
| LCDPR("lane_count=%u, bit_rate = %uMHz, pclk=%u.%03uMhz\n", |
| lane_count, (bit_rate / 1000000), |
| (pclk / 1000), (pclk % 1000)); |
| } |
| } |
| |
| void lcd_tablet_config_update(struct lcd_config_s *pconf) |
| { |
| /* update interface timing */ |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_config_set(pconf); |
| break; |
| case LCD_MIPI: |
| lcd_mipi_dsi_config_set(pconf); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void lcd_tablet_driver_init_pre(void) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct lcd_config_s *pconf; |
| int ret; |
| |
| pconf = lcd_drv->lcd_config; |
| LCDPR("tablet driver init(ver %s): %s\n", lcd_drv->version, |
| lcd_type_type_to_str(pconf->lcd_basic.lcd_type)); |
| ret = lcd_type_supported(pconf); |
| if (ret) |
| return; |
| |
| lcd_clk_set(pconf); |
| lcd_venc_set(pconf); |
| lcd_encl_tcon_set(pconf); |
| } |
| |
| int lcd_tablet_driver_init(void) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct lcd_config_s *pconf; |
| int ret; |
| |
| pconf = lcd_drv->lcd_config; |
| ret = lcd_type_supported(pconf); |
| if (ret) |
| return -1; |
| |
| /* init driver */ |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_TTL: |
| lcd_ttl_control_set(pconf); |
| lcd_pinmux_set(1); |
| break; |
| case LCD_LVDS: |
| lcd_lvds_control_set(pconf); |
| lcd_lvds_phy_set(pconf, 1); |
| break; |
| case LCD_VBYONE: |
| lcd_pinmux_set(1); |
| lcd_vbyone_control_set(pconf); |
| lcd_vbyone_wait_hpd(); |
| lcd_vbyone_phy_set(pconf, 1); |
| lcd_vbyone_wait_stable(); |
| break; |
| case LCD_MIPI: |
| lcd_mipi_phy_set(pconf, 1); |
| lcd_mipi_control_set(pconf, 1); |
| break; |
| default: |
| break; |
| } |
| |
| lcd_vcbus_write(VENC_INTCTRL, 0x200); |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| void lcd_tablet_driver_disable(void) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct lcd_config_s *pconf; |
| int ret; |
| |
| LCDPR("disable driver\n"); |
| pconf = lcd_drv->lcd_config; |
| ret = lcd_type_supported(pconf); |
| if (ret) |
| return; |
| |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_TTL: |
| lcd_pinmux_set(0); |
| break; |
| case LCD_LVDS: |
| lcd_lvds_phy_set(pconf, 0); |
| lcd_lvds_disable(); |
| break; |
| case LCD_VBYONE: |
| lcd_vbyone_phy_set(pconf, 0); |
| lcd_pinmux_set(0); |
| lcd_vbyone_disable(); |
| break; |
| case LCD_MIPI: |
| mipi_dsi_link_off(pconf); |
| lcd_mipi_phy_set(pconf, 0); |
| lcd_mipi_control_set(pconf, 0); |
| break; |
| default: |
| break; |
| } |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| } |
| |