| // 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> |
| #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_reg.h" |
| #include "lcd_common.h" |
| |
| static int vx1_fsm_acq_st; |
| |
| #define VX1_TRAINING_TIMEOUT 60 /* vsync cnt */ |
| static int vx1_training_wait_cnt; |
| static int vx1_training_stable_cnt; |
| static int vx1_timeout_reset_flag; |
| static int lcd_vx1_intr_request; |
| static int lcd_vx1_vsync_isr_en; |
| static int lcd_vx1_isr_flag; |
| |
| #define VX1_LOCKN_WAIT_TIMEOUT 5000 /* *50us */ |
| #define VX1_HPD_WAIT_TIMEOUT 10000 /* *50us */ |
| |
| #define VX1_HPLL_INTERVAL (HZ) |
| /* enable htpdn_fail,lockn_fail,acq_hold */ |
| #define VBYONE_INTR_UNMASK 0x2bff /* 0x2a00 */ |
| |
| static void lcd_vbyone_sync_pol(struct aml_lcd_drv_s *pdrv, |
| int hsync_pol, int vsync_pol) |
| { |
| unsigned int offset; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, hsync_pol, 4, 1); |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, vsync_pol, 5, 1); |
| |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, hsync_pol, 6, 1); |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, vsync_pol, 7, 1); |
| } else { |
| 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 int lcd_vbyone_lanes_set(struct aml_lcd_drv_s *pdrv, int lane_num, |
| int byte_mode, int region_num, |
| int hsize, int vsize) |
| { |
| unsigned int offset; |
| 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 & LCD_DBG_PR_NORMAL) { |
| LCDPR("byte_mode=%d, lane_num=%d, region_num=%d\n", |
| byte_mode, lane_num, region_num); |
| } |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| sublane_num = lane_num / region_num; /* lane num in each region */ |
| lcd_vcbus_setb(VBO_LANES_T7 + offset, (lane_num - 1), 0, 3); |
| lcd_vcbus_setb(VBO_LANES_T7 + offset, (region_num - 1), 4, 2); |
| lcd_vcbus_setb(VBO_LANES_T7 + offset, (sublane_num - 1), 8, 3); |
| lcd_vcbus_setb(VBO_LANES_T7 + offset, (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_T7 + offset, region_size[0]); |
| lcd_vcbus_write(VBO_REGION_01_T7 + offset, region_size[1]); |
| lcd_vcbus_write(VBO_REGION_02_T7 + offset, region_size[2]); |
| lcd_vcbus_write(VBO_REGION_03_T7 + offset, region_size[3]); |
| } |
| lcd_vcbus_write(VBO_ACT_VSIZE_T7 + offset, vsize); |
| /* different from FBC code!!! */ |
| /* lcd_vcbus_setb(VBO_CTRL_H_T7 + offset,0x80,11,5); */ |
| /* different from simulation code!!! */ |
| lcd_vcbus_setb(VBO_CTRL_H_T7 + offset, 0x0, 0, 4); |
| lcd_vcbus_setb(VBO_CTRL_H_T7 + offset, 0x1, 9, 1); |
| /* lcd_vcbus_setb(VBO_CTRL_L_T7 + offset,enable,0,1); */ |
| } else { |
| 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; |
| } |
| |
| void lcd_vbyone_enable_dft(struct aml_lcd_drv_s *pdrv) |
| { |
| int lane_count, byte_mode, region_num, hsize, vsize; |
| /* int color_fmt; */ |
| int vin_color, vin_bpp; |
| |
| hsize = pdrv->config.basic.h_active; |
| vsize = pdrv->config.basic.v_active; |
| lane_count = pdrv->config.control.vbyone_cfg.lane_count; /* 8 */ |
| region_num = pdrv->config.control.vbyone_cfg.region_num; /* 2 */ |
| byte_mode = pdrv->config.control.vbyone_cfg.byte_mode; /* 4 */ |
| /* color_fmt = pdrv->config.control.vbyone_cfg.color_fmt; // 4 */ |
| |
| vin_color = 4; /* fixed RGB */ |
| switch (pdrv->config.basic.lcd_bits) { |
| case 6: |
| vin_bpp = 2; /* 18bbp 4:4:4 */ |
| break; |
| case 8: |
| vin_bpp = 1; /* 24bbp 4:4:4 */ |
| break; |
| case 10: |
| default: |
| vin_bpp = 0; /* 30bbp 4:4:4 */ |
| break; |
| } |
| |
| /* 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(pdrv, 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(pdrv, 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); |
| */ |
| /* VBO_RGN_GEN clk always on */ |
| lcd_vcbus_setb(VBO_GCLK_MAIN, 2, 2, 2); |
| |
| /* 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 */ |
| |
| lcd_vbyone_hw_filter(pdrv, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 2, 2); |
| |
| lcd_vcbus_setb(VBO_CTRL_L, 1, 0, 1); |
| |
| lcd_vbyone_wait_timing_stable(pdrv); |
| lcd_vbyone_sw_reset(pdrv); |
| |
| /* training hold */ |
| if (pdrv->config.control.vbyone_cfg.ctrl_flag & 0x4) |
| lcd_vbyone_cdr_training_hold(pdrv, 1); |
| } |
| |
| void lcd_vbyone_disable_dft(struct aml_lcd_drv_s *pdrv) |
| { |
| lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1); |
| /* clear insig setting */ |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 2, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 0, 1); |
| } |
| |
| void lcd_vbyone_enable_t7(struct aml_lcd_drv_s *pdrv) |
| { |
| int lane_count, byte_mode, region_num, hsize, vsize; |
| /* int color_fmt; */ |
| int vin_color, vin_bpp; |
| unsigned int offset; |
| |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| |
| hsize = pdrv->config.basic.h_active; |
| vsize = pdrv->config.basic.v_active; |
| lane_count = pdrv->config.control.vbyone_cfg.lane_count; /* 8 */ |
| region_num = pdrv->config.control.vbyone_cfg.region_num; /* 2 */ |
| byte_mode = pdrv->config.control.vbyone_cfg.byte_mode; /* 4 */ |
| /* color_fmt = pdrv->config.control.vbyone_cfg.color_fmt; // 4 */ |
| |
| vin_color = 4; /* fixed RGB */ |
| switch (pdrv->config.basic.lcd_bits) { |
| case 6: |
| vin_bpp = 2; /* 18bbp 4:4:4 */ |
| break; |
| case 8: |
| vin_bpp = 1; /* 24bbp 4:4:4 */ |
| break; |
| case 10: |
| default: |
| vin_bpp = 0; /* 30bbp 4:4:4 */ |
| break; |
| } |
| |
| /* set Vbyone vin color format */ |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, vin_color, 8, 3); |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, vin_bpp, 11, 2); |
| |
| lcd_vbyone_lanes_set(pdrv, 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(pdrv, 0, 0); |
| |
| /* below line copy from simulation */ |
| /* gate the input when vsync asserted */ |
| lcd_vcbus_setb(VBO_VIN_CTRL_T7 + offset, 1, 0, 2); |
| /* lcd_vcbus_write(VBO_VBK_CTRL_0_T7 + offset,0x13); |
| * lcd_vcbus_write(VBO_VBK_CTRL_1_T7 + offset,0x56); |
| * lcd_vcbus_write(VBO_HBK_CTRL_T7 + offset,0x3478); |
| * lcd_vcbus_setb(VBO_PXL_CTRL_T7 + offset,0x2,0,4); |
| * lcd_vcbus_setb(VBO_PXL_CTRL_T7 + offset,0x3,VBO_PXL_CTR1_BIT,VBO_PXL_CTR1_WID); |
| * set_vbyone_ctlbits(1,0,0); |
| */ |
| /* VBO_RGN_GEN clk always on */ |
| lcd_vcbus_setb(VBO_GCLK_MAIN_T7 + offset, 2, 2, 2); |
| |
| /* PAD select: */ |
| if (lane_count == 1 || lane_count == 2) |
| lcd_vcbus_setb(LCD_PORT_SWAP_T7 + offset, 1, 9, 2); |
| else if (lane_count == 4) |
| lcd_vcbus_setb(LCD_PORT_SWAP_T7 + offset, 2, 9, 2); |
| else |
| lcd_vcbus_setb(LCD_PORT_SWAP_T7 + offset, 0, 9, 2); |
| /* lcd_vcbus_setb(LCD_PORT_SWAP + offset, 1, 8, 1);//reverse lane output order */ |
| |
| lcd_vbyone_hw_filter(pdrv, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL_T7 + offset, 0, 2, 2); |
| |
| lcd_vcbus_setb(VBO_CTRL_L_T7 + offset, 1, 0, 1); |
| |
| lcd_vbyone_wait_timing_stable(pdrv); |
| lcd_vbyone_sw_reset(pdrv); |
| |
| /* training hold */ |
| if (pdrv->config.control.vbyone_cfg.ctrl_flag & 0x4) |
| lcd_vbyone_cdr_training_hold(pdrv, 1); |
| } |
| |
| void lcd_vbyone_disable_t7(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int offset; |
| |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| |
| lcd_vcbus_setb(VBO_CTRL_L_T7 + offset, 0, 0, 1); |
| /* clear insig setting */ |
| lcd_vcbus_setb(VBO_INSGN_CTRL_T7 + offset, 0, 2, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL_T7 + offset, 0, 0, 1); |
| } |
| |
| void lcd_vbyone_link_maintain_clear(void) |
| { |
| vx1_training_wait_cnt = 0; |
| vx1_timeout_reset_flag = 0; |
| } |
| |
| void lcd_vbyone_sw_reset(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_phy_tx_ctrl0, reg_rst, offset; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7) { |
| switch (pdrv->index) { |
| case 0: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY0_CNTL0; |
| break; |
| case 1: |
| reg_phy_tx_ctrl0 = COMBO_DPHY_EDP_LVDS_TX_PHY1_CNTL0; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", pdrv->index, __func__); |
| return; |
| } |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_rst = VBO_SOFT_RST_T7 + offset; |
| |
| /* force PHY to 0 */ |
| lcd_combo_dphy_setb(pdrv, reg_phy_tx_ctrl0, 3, 8, 2); |
| lcd_vcbus_write(reg_rst, 0x1ff); |
| udelay(5); |
| /* realease PHY */ |
| lcd_combo_dphy_setb(pdrv, reg_phy_tx_ctrl0, 0, 8, 2); |
| lcd_vcbus_write(reg_rst, 0); |
| } else if (pdrv->data->chip_type == LCD_CHIP_T3) { |
| switch (pdrv->index) { |
| case 0: |
| reg_phy_tx_ctrl0 = ANACTRL_LVDS_TX_PHY_CNTL0; |
| break; |
| case 1: |
| reg_phy_tx_ctrl0 = ANACTRL_LVDS_TX_PHY_CNTL2; |
| break; |
| default: |
| LCDERR("[%d]: %s: invalid drv_index\n", pdrv->index, __func__); |
| return; |
| } |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_rst = VBO_SOFT_RST_T7 + offset; |
| |
| /* force PHY to 0 */ |
| lcd_ana_setb(reg_phy_tx_ctrl0, 3, 8, 2); |
| lcd_vcbus_write(reg_rst, 0x1ff); |
| udelay(5); |
| /* realease PHY */ |
| lcd_ana_setb(reg_phy_tx_ctrl0, 0, 8, 2); |
| lcd_vcbus_write(reg_rst, 0); |
| } else { |
| /* force PHY to 0 */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0x1ff); |
| udelay(5); |
| /* realease PHY */ |
| lcd_ana_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0); |
| } |
| } |
| |
| void lcd_vbyone_wait_timing_stable(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int offset, timing_state; |
| int i = 200; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc[pdrv->index]; |
| |
| timing_state = lcd_vcbus_read(VBO_INTR_STATE_T7 + offset) & 0x1ff; |
| while ((timing_state) && (i > 0)) { |
| /* clear video timing error intr */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL_T7 + offset, 0x7, 0, 3); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL_T7 + offset, 0, 0, 3); |
| lcd_delay_ms(2); |
| timing_state = lcd_vcbus_read(VBO_INTR_STATE_T7 + offset) & 0x1ff; |
| i--; |
| }; |
| } else { |
| 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); |
| lcd_delay_ms(2); |
| timing_state = lcd_vcbus_read(VBO_INTR_STATE) & 0x1ff; |
| i--; |
| }; |
| } |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) { |
| LCDPR("[%d]: vbyone timing state: 0x%03x, i=%d\n", |
| pdrv->index, timing_state, (200 - i)); |
| } |
| lcd_delay_ms(2); |
| } |
| |
| void lcd_vbyone_cdr_training_hold(struct aml_lcd_drv_s *pdrv, int flag) |
| { |
| unsigned int offset, reg; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc[pdrv->index]; |
| reg = VBO_FSM_HOLDER_H_T7 + offset; |
| } else { |
| reg = VBO_FSM_HOLDER_H; |
| } |
| if (flag) { |
| LCDPR("[%d]: ctrl_flag for cdr_training_hold\n", pdrv->index); |
| lcd_vcbus_setb(reg, 0xffff, 0, 16); |
| } else { |
| lcd_delay_ms(pdrv->config.control.vbyone_cfg.cdr_training_hold); |
| lcd_vcbus_setb(reg, 0, 0, 16); |
| } |
| } |
| |
| void lcd_vbyone_wait_hpd(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_status, reg_ctrl, offset, val; |
| int i = 0; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_status = VBO_STATUS_L_T7 + offset; |
| reg_ctrl = VBO_INSGN_CTRL_T7 + offset; |
| } else { |
| reg_status = VBO_STATUS_L; |
| reg_ctrl = VBO_INSGN_CTRL; |
| } |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s ...\n", pdrv->index, __func__); |
| while (i++ < VX1_HPD_WAIT_TIMEOUT) { |
| if (lcd_vcbus_getb(reg_status, 6, 1) == 0) |
| break; |
| lcd_delay_us(50); |
| } |
| |
| val = lcd_vcbus_getb(reg_status, 6, 1); |
| if (val) { |
| LCDPR("[%d]: %s: hpd=%d\n", pdrv->index, __func__, val); |
| } else { |
| LCDPR("[%d]: %s: hpd=%d, i=%d\n", pdrv->index, __func__, val, i); |
| /* force low only activated for actual hpd is low */ |
| lcd_vcbus_setb(reg_ctrl, 1, 2, 2); |
| } |
| |
| if (pdrv->config.control.vbyone_cfg.ctrl_flag & 0x2) { |
| LCDPR("[%d]: ctrl_flag for hpd_data delay\n", pdrv->index); |
| lcd_delay_ms(pdrv->config.control.vbyone_cfg.hpd_data_delay); |
| } else { |
| usleep_range(10000, 10500); |
| /* add 10ms delay for compatibility */ |
| } |
| } |
| |
| static void lcd_vbyone_power_on_wait_lockn(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_status, offset; |
| int i = 0; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_status = VBO_STATUS_L_T7 + offset; |
| } else { |
| reg_status = VBO_STATUS_L; |
| } |
| |
| /* training hold release */ |
| if (pdrv->config.control.vbyone_cfg.ctrl_flag & 0x4) |
| lcd_vbyone_cdr_training_hold(pdrv, 0); |
| while (i++ < VX1_LOCKN_WAIT_TIMEOUT) { |
| if ((lcd_vcbus_read(reg_status) & 0x3f) == 0x20) |
| break; |
| lcd_delay_us(50); |
| } |
| LCDPR("[%d]: %s status: 0x%x, i=%d\n", |
| pdrv->index, __func__, lcd_vcbus_read(reg_status), i); |
| /* power on reset */ |
| if (pdrv->config.control.vbyone_cfg.ctrl_flag & 0x1) { |
| LCDPR("[%d]: ctrl_flag for power on reset\n", pdrv->index); |
| lcd_delay_ms(pdrv->config.control.vbyone_cfg.power_on_reset_delay); |
| lcd_vbyone_sw_reset(pdrv); |
| i = 0; |
| while (i++ < VX1_LOCKN_WAIT_TIMEOUT) { |
| if ((lcd_vcbus_read(reg_status) & 0x3f) == 0x20) |
| break; |
| lcd_delay_us(50); |
| } |
| LCDPR("[%d]: %s status: 0x%x, i=%d\n", |
| pdrv->index, __func__, lcd_vcbus_read(reg_status), i); |
| } |
| |
| vx1_training_wait_cnt = 0; |
| vx1_training_stable_cnt = 0; |
| vx1_fsm_acq_st = 0; |
| lcd_vbyone_interrupt_enable(pdrv, 1); |
| } |
| |
| #define LCD_VX1_WAIT_STABLE_POWER_ON_DELAY 300 /* ms */ |
| void lcd_vbyone_power_on_wait_stable(struct aml_lcd_drv_s *pdrv) |
| { |
| lcd_vx1_intr_request = 1; |
| |
| lcd_delay_ms(LCD_VX1_WAIT_STABLE_POWER_ON_DELAY); |
| if (lcd_vx1_intr_request == 0) |
| return; |
| |
| lcd_vbyone_power_on_wait_lockn(pdrv); |
| } |
| |
| void lcd_vbyone_wait_stable(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_status, offset; |
| int i = 0; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_status = VBO_STATUS_L_T7 + offset; |
| } else { |
| reg_status = VBO_STATUS_L; |
| } |
| |
| while (i++ < VX1_LOCKN_WAIT_TIMEOUT) { |
| if ((lcd_vcbus_read(reg_status) & 0x3f) == 0x20) |
| break; |
| lcd_delay_us(50); |
| } |
| LCDPR("[%d]: %s status: 0x%x, i=%d\n", |
| pdrv->index, __func__, lcd_vcbus_read(reg_status), i); |
| |
| vx1_training_wait_cnt = 0; |
| vx1_training_stable_cnt = 0; |
| vx1_fsm_acq_st = 0; |
| lcd_vbyone_interrupt_enable(pdrv, 1); |
| } |
| |
| void lcd_vbyone_hw_filter(struct aml_lcd_drv_s *pdrv, int flag) |
| { |
| struct vbyone_config_s *vx1_conf; |
| unsigned int reg_filter_l, reg_filter_h, reg_ctrl; |
| unsigned int temp, period, offset; |
| unsigned int tick_period[] = { |
| 0xfff, |
| 0xff, /* 1: 0.8us */ |
| 0x1ff, /* 2: 1.7us */ |
| 0x3ff, /* 3: 3.4us */ |
| 0x7ff, /* 4: 6.9us */ |
| 0xfff, /* 5: 13.8us */ |
| 0x1fff, /* 6: 27us */ |
| 0x3fff, /* 7: 55us */ |
| 0x7fff, /* 8: 110us */ |
| 0xffff, /* 9: 221us */ |
| 0x1ffff, /* 10: 441us */ |
| 0x3ffff, /* 11: 883us */ |
| 0x7ffff, /* 12: 1.76ms */ |
| 0xfffff, /* 13: 3.53ms */ |
| }; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_filter_l = VBO_INFILTER_CTRL_T7 + offset; |
| reg_filter_h = VBO_INFILTER_CTRL_H_T7 + offset; |
| reg_ctrl = VBO_INSGN_CTRL_T7 + offset; |
| } else { |
| reg_filter_l = VBO_INFILTER_TICK_PERIOD_L; |
| reg_filter_h = VBO_INFILTER_TICK_PERIOD_H; |
| reg_ctrl = VBO_INSGN_CTRL; |
| } |
| |
| vx1_conf = &pdrv->config.control.vbyone_cfg; |
| if (flag) { |
| period = vx1_conf->hw_filter_time & 0xff; |
| if (period >= |
| (sizeof(tick_period) / sizeof(unsigned int))) |
| period = tick_period[0]; |
| else |
| period = tick_period[period]; |
| temp = period & 0xffff; |
| lcd_vcbus_write(reg_filter_l, temp); |
| temp = (period >> 16) & 0xf; |
| lcd_vcbus_write(reg_filter_h, temp); |
| /* hpd */ |
| temp = vx1_conf->hw_filter_cnt & 0xff; |
| if (temp == 0xff) { |
| lcd_vcbus_setb(reg_ctrl, 0, 8, 4); |
| } else { |
| temp = (temp == 0) ? 0x7 : temp; |
| lcd_vcbus_setb(reg_ctrl, temp, 8, 4); |
| } |
| /* lockn */ |
| temp = (vx1_conf->hw_filter_cnt >> 8) & 0xff; |
| if (temp == 0xff) { |
| lcd_vcbus_setb(reg_ctrl, 0, 12, 4); |
| } else { |
| temp = (temp == 0) ? 0x7 : temp; |
| lcd_vcbus_setb(reg_ctrl, temp, 12, 4); |
| } |
| } else { |
| temp = (vx1_conf->hw_filter_time >> 8) & 0x1; |
| if (temp) { |
| lcd_vcbus_write(reg_filter_l, 0xff); |
| lcd_vcbus_write(reg_filter_h, 0x0); |
| lcd_vcbus_setb(reg_ctrl, 0, 8, 4); |
| lcd_vcbus_setb(reg_ctrl, 0, 12, 4); |
| LCDPR("[%d]: %s: %d disable for debug\n", |
| pdrv->index, __func__, flag); |
| } |
| } |
| } |
| |
| void lcd_vbyone_interrupt_enable(struct aml_lcd_drv_s *pdrv, int flag) |
| { |
| struct vbyone_config_s *vx1_conf; |
| unsigned int reg_ctrl, reg_holder_l, reg_unmask; |
| unsigned int offset; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s: %d\n", pdrv->index, __func__, flag); |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_ctrl = VBO_INTR_STATE_CTRL_T7 + offset; |
| reg_holder_l = VBO_FSM_HOLDER_L_T7 + offset; |
| reg_unmask = VBO_INTR_UNMASK_T7 + offset; |
| } else { |
| reg_ctrl = VBO_INTR_STATE_CTRL; |
| reg_holder_l = VBO_FSM_HOLDER_L; |
| reg_unmask = VBO_INTR_UNMASK; |
| } |
| |
| vx1_conf = &pdrv->config.control.vbyone_cfg; |
| if (flag) { |
| lcd_vbyone_hw_filter(pdrv, 1); |
| if (vx1_conf->intr_en) { |
| vx1_fsm_acq_st = 0; |
| /* clear interrupt */ |
| lcd_vcbus_setb(reg_ctrl, 0x01ff, 0, 9); |
| lcd_vcbus_setb(reg_ctrl, 0, 0, 9); |
| |
| /* set hold in FSM_ACQ */ |
| if (vx1_conf->vsync_intr_en == 3 || |
| vx1_conf->vsync_intr_en == 4) |
| lcd_vcbus_setb(reg_holder_l, 0, 0, 16); |
| else |
| lcd_vcbus_setb(reg_holder_l, 0xffff, 0, 16); |
| /* enable interrupt */ |
| lcd_vcbus_setb(reg_unmask, VBYONE_INTR_UNMASK, 0, 15); |
| } else { |
| /* mask interrupt */ |
| lcd_vcbus_write(reg_unmask, 0x0); |
| if (vx1_conf->vsync_intr_en) { |
| /* keep holder for vsync monitor enabled */ |
| /* set hold in FSM_ACQ */ |
| if (vx1_conf->vsync_intr_en == 3 || |
| vx1_conf->vsync_intr_en == 4) |
| lcd_vcbus_setb(reg_holder_l, 0, 0, 16); |
| else |
| lcd_vcbus_setb(reg_holder_l, 0xffff, 0, 16); |
| } else { |
| /* release holder for vsync monitor disabled */ |
| /* release hold in FSM_ACQ */ |
| lcd_vcbus_setb(reg_holder_l, 0, 0, 16); |
| } |
| |
| vx1_fsm_acq_st = 0; |
| /* clear interrupt */ |
| lcd_vcbus_setb(reg_ctrl, 0x01ff, 0, 9); |
| lcd_vcbus_setb(reg_ctrl, 0, 0, 9); |
| } |
| lcd_vx1_isr_flag = 1; |
| } else { |
| lcd_vx1_isr_flag = 0; |
| /* mask interrupt */ |
| lcd_vcbus_write(reg_unmask, 0x0); |
| /* release hold in FSM_ACQ */ |
| lcd_vcbus_setb(reg_holder_l, 0, 0, 16); |
| lcd_vbyone_hw_filter(pdrv, 0); |
| lcd_vx1_intr_request = 0; |
| } |
| } |
| |
| #define LCD_PCLK_TOLERANCE 2000000 /* 2M */ |
| #define LCD_ENCL_CLK_ERR_CNT_MAX 3 |
| static unsigned char lcd_encl_clk_err_cnt; |
| static void lcd_pll_monitor_timer_handler(struct timer_list *timer) |
| { |
| struct aml_lcd_drv_s *pdrv = from_timer(pdrv, timer, pll_mnt_timer); |
| int encl_clk; |
| |
| if ((pdrv->status & LCD_STATUS_ENCL_ON) == 0) |
| goto vx1_hpll_timer_end; |
| |
| encl_clk = lcd_encl_clk_msr(pdrv); |
| if (encl_clk == 0) |
| lcd_encl_clk_err_cnt++; |
| else |
| lcd_encl_clk_err_cnt = 0; |
| if (lcd_encl_clk_err_cnt >= LCD_ENCL_CLK_ERR_CNT_MAX) { |
| LCDPR("[%d]: %s: pll frequency error: %d\n", |
| pdrv->index, __func__, encl_clk); |
| lcd_pll_reset(pdrv); |
| lcd_encl_clk_err_cnt = 0; |
| } |
| |
| vx1_hpll_timer_end: |
| pdrv->pll_mnt_timer.expires = jiffies + VX1_HPLL_INTERVAL; |
| add_timer(&pdrv->pll_mnt_timer); |
| } |
| |
| static void lcd_vx1_hold_reset(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int reg_ctrl, reg_unmask; |
| unsigned int offset; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_ctrl = VBO_INTR_STATE_CTRL_T7 + offset; |
| reg_unmask = VBO_INTR_UNMASK_T7 + offset; |
| } else { |
| reg_ctrl = VBO_INTR_STATE_CTRL; |
| reg_unmask = VBO_INTR_UNMASK; |
| } |
| |
| vx1_fsm_acq_st = 0; |
| lcd_vcbus_write(reg_unmask, 0x0); /* mask interrupt */ |
| /* clear interrupt */ |
| lcd_vcbus_setb(reg_ctrl, 0x01ff, 0, 9); |
| lcd_vcbus_setb(reg_ctrl, 0, 0, 9); |
| |
| /* clear FSM_continue */ |
| lcd_vcbus_setb(reg_ctrl, 0, 15, 1); |
| |
| lcd_vbyone_sw_reset(pdrv); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(reg_ctrl, 1, 7, 1); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(reg_ctrl, 0, 7, 1); |
| |
| /* enable interrupt */ |
| lcd_vcbus_setb(reg_unmask, VBYONE_INTR_UNMASK, 0, 15); |
| } |
| |
| static void lcd_vx1_timeout_reset(struct work_struct *work) |
| { |
| struct aml_lcd_drv_s *pdrv; |
| |
| pdrv = container_of(work, struct aml_lcd_drv_s, vx1_reset_work); |
| |
| if (vx1_timeout_reset_flag == 0) |
| return; |
| |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| pdrv->module_reset(pdrv); |
| if (pdrv->config.control.vbyone_cfg.intr_en) |
| lcd_vx1_hold_reset(pdrv); |
| vx1_timeout_reset_flag = 0; |
| } |
| |
| #define VSYNC_CNT_VX1_RESET 5 |
| #define VSYNC_CNT_VX1_STABLE 20 |
| static unsigned short vsync_cnt = VSYNC_CNT_VX1_STABLE; |
| static int lcd_vbyone_vsync_handler(struct aml_lcd_drv_s *pdrv) |
| { |
| struct vbyone_config_s *vx1_conf; |
| unsigned int reg_status, reg_intr_ctrl, reg_intr_state, offset; |
| |
| if (lcd_vx1_vsync_isr_en == 0) |
| return 0; |
| |
| vx1_conf = &pdrv->config.control.vbyone_cfg; |
| if ((pdrv->status & LCD_STATUS_IF_ON) == 0) |
| return 0; |
| if (lcd_vx1_isr_flag == 0) |
| return 0; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_status = VBO_STATUS_L_T7 + offset; |
| reg_intr_ctrl = VBO_INTR_STATE_CTRL_T7 + offset; |
| reg_intr_state = VBO_INTR_STATE_T7 + offset; |
| } else { |
| reg_status = VBO_STATUS_L; |
| reg_intr_ctrl = VBO_INTR_STATE_CTRL; |
| reg_intr_state = VBO_INTR_STATE; |
| } |
| |
| if (lcd_vcbus_read(reg_status) & 0x40) /* hpd detect */ |
| return 0; |
| |
| lcd_vcbus_setb(reg_intr_ctrl, 1, 0, 1); |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 0, 1); |
| |
| if (vx1_conf->vsync_intr_en == 0) { |
| vx1_training_wait_cnt = 0; |
| return 0; |
| } |
| |
| if (vx1_conf->vsync_intr_en == 4) { |
| if (vsync_cnt == 3) { |
| lcd_vcbus_setb(reg_intr_ctrl, 0x3ff, 0, 10); |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 0, 10); |
| vsync_cnt++; |
| } else if (vsync_cnt >= 5) { |
| vsync_cnt = 0; |
| if ((lcd_vcbus_read(reg_intr_state) & 0x40)) { |
| lcd_vbyone_hw_filter(pdrv, 0); |
| lcd_vbyone_sw_reset(pdrv); |
| LCDPR("[%d]: vx1 sw_reset 4\n", pdrv->index); |
| while (lcd_vcbus_read(reg_status) & 0x4) |
| break; |
| lcd_vcbus_setb(reg_intr_ctrl, 0x3ff, 0, 10); |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 0, 10); |
| lcd_vbyone_hw_filter(pdrv, 1); |
| } |
| } else { |
| vsync_cnt++; |
| } |
| } else if (vx1_conf->vsync_intr_en == 3) { |
| if (vsync_cnt < VSYNC_CNT_VX1_RESET) { |
| vsync_cnt++; |
| } else if (vsync_cnt == VSYNC_CNT_VX1_RESET) { |
| lcd_vbyone_hw_filter(pdrv, 0); |
| lcd_vbyone_sw_reset(pdrv); |
| vsync_cnt++; |
| } else if ((vsync_cnt > VSYNC_CNT_VX1_RESET) && |
| (vsync_cnt < VSYNC_CNT_VX1_STABLE)) { |
| if (lcd_vcbus_read(reg_status) & 0x20) { |
| vsync_cnt = VSYNC_CNT_VX1_STABLE; |
| lcd_vbyone_hw_filter(pdrv, 1); |
| } else { |
| vsync_cnt++; |
| } |
| } |
| } else if (vx1_conf->vsync_intr_en == 2) { |
| if (vsync_cnt >= 5) { |
| vsync_cnt = 0; |
| if (!(lcd_vcbus_read(reg_status) & 0x20)) { |
| lcd_vbyone_hw_filter(pdrv, 0); |
| lcd_vbyone_sw_reset(pdrv); |
| LCDPR("[%d]: vx1 sw_reset 2\n", pdrv->index); |
| while (lcd_vcbus_read(reg_status) & 0x4) |
| break; |
| |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 15, 1); |
| lcd_vcbus_setb(reg_intr_ctrl, 1, 15, 1); |
| } else { |
| lcd_vbyone_hw_filter(pdrv, 1); |
| } |
| } else { |
| vsync_cnt++; |
| } |
| } else { |
| if (vx1_training_wait_cnt >= VX1_TRAINING_TIMEOUT) { |
| if ((lcd_vcbus_read(reg_status) & 0x3f) != 0x20) { |
| if (vx1_timeout_reset_flag == 0) { |
| vx1_timeout_reset_flag = 1; |
| lcd_queue_work(&pdrv->vx1_reset_work); |
| } |
| } else { |
| vx1_training_stable_cnt++; |
| if (vx1_training_stable_cnt >= 5) { |
| vx1_training_wait_cnt = 0; |
| vx1_training_stable_cnt = 0; |
| } |
| } |
| } else { |
| vx1_training_wait_cnt++; |
| } |
| } |
| |
| return 0; |
| } |
| |
| #define VX1_LOCKN_WAIT_CNT_MAX 50 |
| static int vx1_lockn_wait_cnt; |
| |
| #define VX1_FSM_ACQ_NEXT_STEP_CONTINUE 0 |
| #define VX1_FSM_ACQ_NEXT_RELEASE_HOLDER 1 |
| #define VX1_FSM_ACQ_NEXT VX1_FSM_ACQ_NEXT_STEP_CONTINUE |
| static irqreturn_t lcd_vbyone_interrupt_handler(int irq, void *data) |
| { |
| struct aml_lcd_drv_s *pdrv = (struct aml_lcd_drv_s *)data; |
| struct vbyone_config_s *vx1_conf; |
| unsigned int reg_status, reg_intr_ctrl, reg_intr_state, reg_unmask; |
| #if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER) |
| unsigned int reg_holder_l; |
| #endif |
| unsigned int data32, data32_1, offset; |
| int encl_clk; |
| |
| if ((pdrv->status & LCD_STATUS_IF_ON) == 0) |
| return IRQ_HANDLED; |
| if (lcd_vx1_isr_flag == 0) |
| return IRQ_HANDLED; |
| |
| vx1_conf = &pdrv->config.control.vbyone_cfg; |
| if (vx1_conf->vsync_intr_en == 2 || |
| vx1_conf->vsync_intr_en == 4) |
| return IRQ_HANDLED; |
| |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_status = VBO_STATUS_L_T7 + offset; |
| reg_intr_ctrl = VBO_INTR_STATE_CTRL_T7 + offset; |
| reg_intr_state = VBO_INTR_STATE_T7 + offset; |
| reg_unmask = VBO_INTR_UNMASK_T7 + offset; |
| #if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER) |
| reg_holder_l = VBO_FSM_HOLDER_L_T7 + offset; |
| #endif |
| } else { |
| reg_status = VBO_STATUS_L; |
| reg_intr_ctrl = VBO_INTR_STATE_CTRL; |
| reg_intr_state = VBO_INTR_STATE; |
| reg_unmask = VBO_INTR_UNMASK; |
| #if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER) |
| reg_holder_l = VBO_FSM_HOLDER_L; |
| #endif |
| } |
| |
| lcd_vcbus_write(reg_unmask, 0x0); /* mask interrupt */ |
| |
| encl_clk = lcd_encl_clk_msr(pdrv); |
| data32 = (lcd_vcbus_read(reg_intr_state) & 0x7fff); |
| /* clear the interrupt */ |
| data32_1 = ((data32 >> 9) << 3); |
| if (data32 & 0x1c0) |
| data32_1 |= (1 << 2); |
| if (data32 & 0x38) |
| data32_1 |= (1 << 1); |
| if (data32 & 0x7) |
| data32_1 |= (1 << 0); |
| lcd_vcbus_setb(reg_intr_ctrl, data32_1, 0, 9); |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 0, 9); |
| if (lcd_debug_print_flag & LCD_DBG_PR_ISR) { |
| LCDPR("[%d]: vx1 intr status = 0x%04x, encl_clkmsr = %d", |
| pdrv->index, data32, encl_clk); |
| } |
| |
| if (vx1_conf->vsync_intr_en == 3) { |
| if (data32 & 0x1000) { |
| if (vsync_cnt >= VSYNC_CNT_VX1_STABLE) { |
| vsync_cnt = 0; |
| LCDPR("[%d]: vx1 lockn rise edge occurred\n", |
| pdrv->index); |
| } |
| } |
| } else { |
| if (data32 & 0x200) { |
| LCDPR("[%d]: vx1 htpdn fall occurred\n", pdrv->index); |
| vx1_fsm_acq_st = 0; |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 15, 1); |
| } |
| if (data32 & 0x800) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_ISR) { |
| LCDPR("[%d]: vx1 lockn fall occurred\n", |
| pdrv->index); |
| } |
| vx1_fsm_acq_st = 0; |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 15, 1); |
| if (vx1_lockn_wait_cnt++ > VX1_LOCKN_WAIT_CNT_MAX) { |
| if (vx1_timeout_reset_flag == 0) { |
| vx1_timeout_reset_flag = 1; |
| lcd_queue_work(&pdrv->vx1_reset_work); |
| vx1_lockn_wait_cnt = 0; |
| return IRQ_HANDLED; |
| } |
| } |
| } |
| if (data32 & 0x2000) { |
| /* LCDPR("vx1 fsm_acq wait end\n"); */ |
| if (lcd_debug_print_flag & LCD_DBG_PR_ISR) { |
| LCDPR("[%d]: vx1 status 0: 0x%x, fsm_acq_st: %d\n", |
| pdrv->index, lcd_vcbus_read(reg_status), |
| vx1_fsm_acq_st); |
| } |
| if (vx1_fsm_acq_st == 0) { |
| /* clear FSM_continue */ |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 15, 1); |
| LCDPR("[%d]: vx1 sw reset\n", pdrv->index); |
| lcd_vbyone_sw_reset(pdrv); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(reg_intr_ctrl, 1, 7, 1); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 7, 1); |
| vx1_fsm_acq_st = 1; |
| } else { |
| vx1_fsm_acq_st = 2; |
| /* set FSM_continue */ |
| #if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER) |
| lcd_vcbus_setb(reg_holder_l, 0, 0, 16); |
| #else |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 15, 1); |
| lcd_vcbus_setb(reg_intr_ctrl, 1, 15, 1); |
| #endif |
| } |
| if (lcd_debug_print_flag & LCD_DBG_PR_ISR) { |
| LCDPR("[%d]: vx1 status 1: 0x%x, fsm_acq_st: %d\n", |
| pdrv->index, lcd_vcbus_read(reg_status), |
| vx1_fsm_acq_st); |
| } |
| } |
| |
| if (data32 & 0x1ff) { |
| if (lcd_debug_print_flag & LCD_DBG_PR_ISR) { |
| LCDPR("[%d]: vx1 reset for timing err\n", |
| pdrv->index); |
| } |
| vx1_fsm_acq_st = 0; |
| lcd_vbyone_sw_reset(pdrv); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(reg_intr_ctrl, 1, 7, 1); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 7, 1); |
| } |
| |
| lcd_delay_us(20); |
| if ((lcd_vcbus_read(reg_status) & 0x3f) == 0x20) { |
| vx1_lockn_wait_cnt = 0; |
| /* vx1_training_wait_cnt = 0; */ |
| #if (VX1_FSM_ACQ_NEXT == VX1_FSM_ACQ_NEXT_RELEASE_HOLDER) |
| lcd_vcbus_setb(reg_holder_l, 0xffff, 0, 16); |
| #endif |
| lcd_vbyone_hw_filter(pdrv, 1); |
| LCDPR("[%d]: vx1 fsm stable\n", pdrv->index); |
| } |
| } |
| /* enable interrupt */ |
| lcd_vcbus_setb(reg_unmask, VBYONE_INTR_UNMASK, 0, 15); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void lcd_vbyone_interrupt_init(struct aml_lcd_drv_s *pdrv) |
| { |
| struct vbyone_config_s *vx1_conf; |
| unsigned int reg_insig_ctrl, reg_holder_l, reg_holder_h, reg_ctrl_l; |
| unsigned int reg_intr_ctrl, offset; |
| |
| vx1_conf = &pdrv->config.control.vbyone_cfg; |
| if (pdrv->data->chip_type == LCD_CHIP_T7 || |
| pdrv->data->chip_type == LCD_CHIP_T3) { |
| offset = pdrv->data->offset_venc_if[pdrv->index]; |
| reg_insig_ctrl = VBO_INSGN_CTRL_T7 + offset; |
| reg_holder_l = VBO_FSM_HOLDER_L_T7 + offset; |
| reg_holder_h = VBO_FSM_HOLDER_H_T7 + offset; |
| reg_ctrl_l = VBO_CTRL_L_T7 + offset; |
| reg_intr_ctrl = VBO_INTR_STATE_CTRL_T7 + offset; |
| } else { |
| reg_insig_ctrl = VBO_INSGN_CTRL; |
| reg_holder_l = VBO_FSM_HOLDER_L; |
| reg_holder_h = VBO_FSM_HOLDER_H; |
| reg_ctrl_l = VBO_CTRL_L; |
| reg_intr_ctrl = VBO_INTR_STATE_CTRL; |
| } |
| |
| /* release sw filter ctrl in uboot */ |
| lcd_vcbus_setb(reg_insig_ctrl, 0, 0, 1); |
| lcd_vbyone_hw_filter(pdrv, 1); |
| |
| /* set hold in FSM_ACQ */ |
| if (vx1_conf->vsync_intr_en == 3 || |
| vx1_conf->vsync_intr_en == 4) |
| lcd_vcbus_setb(reg_holder_l, 0, 0, 16); |
| else |
| lcd_vcbus_setb(reg_holder_l, 0xffff, 0, 16); |
| /* set hold in FSM_CDR */ |
| lcd_vcbus_setb(reg_holder_h, 0, 0, 16); |
| /* not wait lockn to 1 in FSM_ACQ */ |
| lcd_vcbus_setb(reg_ctrl_l, 1, 10, 1); |
| /* lcd_vcbus_setb(VBO_CTRL_L, 0, 9, 1);*/ /*use sw pll_lock */ |
| /* reg_pll_lock = 1 to realease force to FSM_ACQ*/ |
| /*lcd_vcbus_setb(VBO_CTRL_H, 1, 13, 1); */ |
| |
| /* vx1 interrupt setting */ |
| lcd_vcbus_setb(reg_intr_ctrl, 1, 12, 1); /* intr pulse width */ |
| lcd_vcbus_setb(reg_intr_ctrl, 0x01ff, 0, 9); /* clear interrupt */ |
| lcd_vcbus_setb(reg_intr_ctrl, 0, 0, 9); |
| |
| vx1_fsm_acq_st = 0; |
| vx1_lockn_wait_cnt = 0; |
| vx1_training_wait_cnt = 0; |
| vx1_timeout_reset_flag = 0; |
| vx1_training_stable_cnt = 0; |
| lcd_encl_clk_err_cnt = 0; |
| lcd_vx1_isr_flag = 0; |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: %s\n", pdrv->index, __func__); |
| } |
| |
| #define VBYONE_IRQF IRQF_SHARED /* IRQF_DISABLED */ /* IRQF_SHARED */ |
| |
| int lcd_vbyone_interrupt_up(struct aml_lcd_drv_s *pdrv) |
| { |
| unsigned int venc_vx1_irq = 0; |
| |
| lcd_vbyone_interrupt_init(pdrv); |
| |
| INIT_WORK(&pdrv->vx1_reset_work, lcd_vx1_timeout_reset); |
| |
| if (!pdrv->res_vx1_irq) { |
| LCDERR("[%d]: res_vx1_irq is null\n", pdrv->index); |
| return -1; |
| } |
| venc_vx1_irq = pdrv->res_vx1_irq->start; |
| LCDPR("[%d]: venc_vx1_irq: %d\n", pdrv->index, venc_vx1_irq); |
| |
| snprintf(pdrv->vbyone_isr_name, 10, "vbyone%d", pdrv->index); |
| if (request_irq(venc_vx1_irq, lcd_vbyone_interrupt_handler, 0, |
| pdrv->vbyone_isr_name, (void *)pdrv)) { |
| LCDERR("[%d]: can't request %s\n", |
| pdrv->index, pdrv->vbyone_isr_name); |
| } else { |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) { |
| LCDPR("[%d]: request %s successful\n", |
| pdrv->index, pdrv->vbyone_isr_name); |
| } |
| } |
| pdrv->vbyone_vsync_handler = lcd_vbyone_vsync_handler; |
| |
| lcd_vx1_intr_request = 1; |
| lcd_vx1_vsync_isr_en = 1; |
| lcd_vbyone_interrupt_enable(pdrv, 1); |
| |
| /* add timer to monitor pll frequency */ |
| timer_setup(&pdrv->pll_mnt_timer, &lcd_pll_monitor_timer_handler, 0); |
| /* vx1_hpll_timer.data = NULL; */ |
| pdrv->pll_mnt_timer.expires = jiffies + VX1_HPLL_INTERVAL; |
| /*add_timer(&pdrv->pll_mnt_timer);*/ |
| /*LCDPR("[%d]: add vbyone hpll timer handler\n", pdrv->index);*/ |
| |
| return 0; |
| } |
| |
| void lcd_vbyone_interrupt_down(struct aml_lcd_drv_s *pdrv) |
| { |
| del_timer_sync(&pdrv->pll_mnt_timer); |
| |
| lcd_vx1_vsync_isr_en = 0; |
| lcd_vbyone_interrupt_enable(pdrv, 0); |
| free_irq(pdrv->res_vx1_irq->start, (void *)pdrv); |
| cancel_work_sync(&pdrv->vx1_reset_work); |
| |
| if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) |
| LCDPR("[%d]: free vbyone irq\n", pdrv->index); |
| } |