| /* |
| * drivers/amlogic/media/vout/lcd/lcd_tv/lcd_drv.c |
| * |
| * Copyright (C) 2017 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 <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_tv.h" |
| #include "../lcd_reg.h" |
| #include "../lcd_common.h" |
| |
| static struct delayed_work lcd_vx1_stable_delayed_work; |
| |
| 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 struct work_struct lcd_vx1_reset_work; |
| static int lcd_vx1_intr_request; |
| |
| #define VX1_HPLL_INTERVAL (HZ) |
| static struct timer_list vx1_hpll_timer; |
| |
| 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_LVDS: |
| case LCD_VBYONE: |
| case LCD_MLVDS: |
| case LCD_P2P: |
| 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_encl_tcon_set(struct lcd_config_s *pconf) |
| { |
| unsigned int lcd_bits; |
| |
| lcd_vcbus_write(L_RGB_BASE_ADDR, 0); |
| lcd_vcbus_write(L_RGB_COEFF_ADDR, 0x400); |
| aml_lcd_notifier_call_chain(LCD_EVENT_GAMMA_UPDATE, NULL); |
| |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_MLVDS: |
| case LCD_P2P: |
| lcd_bits = 10; |
| break; |
| default: |
| lcd_bits = pconf->lcd_basic.lcd_bits; |
| break; |
| } |
| switch (lcd_bits) { |
| case 8: |
| lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x400); |
| break; |
| case 6: |
| lcd_vcbus_write(L_DITH_CNTL_ADDR, 0x600); |
| 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; |
| default: |
| break; |
| } |
| |
| if (lcd_vcbus_read(VPP_MISC) & VPP_OUT_SATURATE) |
| 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); |
| |
| /* Enable Hsync and equalization pulse switch in center; |
| * bit[14] cfg_de_v = 1 |
| */ |
| lcd_vcbus_write(ENCL_VIDEO_MODE, 0x8000);/*bit[15] shadown en*/ |
| lcd_vcbus_write(ENCL_VIDEO_MODE_ADV, 0x18); /* 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); |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_P2P: |
| lcd_vcbus_write(ENCL_VIDEO_V_PRE_DE_BLINE, |
| video_on_line - 1 - 4); |
| lcd_vcbus_write(ENCL_VIDEO_V_PRE_DE_ELINE, |
| video_on_line - 1); |
| lcd_vcbus_write(ENCL_VIDEO_H_PRE_DE_BEGIN, |
| video_on_pixel + PRE_DE_DELAY); |
| lcd_vcbus_write(ENCL_VIDEO_H_PRE_DE_END, |
| h_active - 1 + video_on_pixel + PRE_DE_DELAY); |
| break; |
| default: |
| break; |
| } |
| |
| 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->data->chip_type) { |
| case LCD_CHIP_TL1: |
| /*[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); |
| |
| aml_lcd_notifier_call_chain(LCD_EVENT_BACKLIGHT_UPDATE, NULL); |
| } |
| |
| static void lcd_lvds_clk_util_set(struct lcd_config_s *pconf) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| unsigned int phy_div; |
| unsigned int reg_cntl0, reg_cntl1; |
| |
| switch (lcd_drv->data->chip_type) { |
| case LCD_CHIP_TL1: |
| reg_cntl0 = HHI_LVDS_TX_PHY_CNTL0_TL1; |
| reg_cntl1 = HHI_LVDS_TX_PHY_CNTL1_TL1; |
| break; |
| default: |
| reg_cntl0 = HHI_LVDS_TX_PHY_CNTL0; |
| reg_cntl1 = HHI_LVDS_TX_PHY_CNTL1; |
| break; |
| } |
| |
| if (pconf->lcd_control.lvds_config->dual_port) |
| phy_div = 2; |
| else |
| phy_div = 1; |
| |
| /* set fifo_clk_sel: div 7 */ |
| lcd_hiu_write(reg_cntl0, (1 << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_hiu_setb(reg_cntl0, 0xfff, 16, 12); |
| switch (lcd_drv->data->chip_type) { /* pn swap */ |
| case LCD_CHIP_TL1: |
| lcd_hiu_setb(reg_cntl0, 1, 2, 1); |
| break; |
| default: |
| break; |
| } |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_hiu_write(reg_cntl1, |
| (1 << 30) | ((phy_div - 1) << 25) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_hiu_setb(reg_cntl1, 1, 31, 1); |
| } |
| |
| static void lcd_lvds_control_set(struct lcd_config_s *pconf) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| unsigned int bit_num = 1; |
| unsigned int pn_swap, port_swap, lane_reverse; |
| unsigned int dual_port, fifo_mode; |
| unsigned int lvds_repack = 1; |
| |
| 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 */ |
| |
| switch (lcd_drv->data->chip_type) { |
| case LCD_CHIP_TL1: |
| 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_mlvds_control_set(struct lcd_config_s *pconf) |
| { |
| unsigned int div_sel; |
| unsigned int channel_sel0, channel_sel1; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| /* phy_div: 0=div6, 1=div 7, 2=div8, 3=div10 */ |
| switch (pconf->lcd_basic.lcd_bits) { |
| case 6: |
| div_sel = 0; |
| break; |
| case 8: |
| div_sel = 2; |
| break; |
| default: |
| div_sel = 2; |
| break; |
| } |
| |
| /* fifo_clk_sel[7:6]: 0=div6, 1=div 7, 2=div8, 3=div10 */ |
| lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL0_TL1, (div_sel << 6)); |
| /* serializer_en[27:16] */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0_TL1, 0xfff, 16, 12); |
| /* pn swap[2] */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0_TL1, 1, 2, 1); |
| |
| /* fifo enable[30], phy_clock gating[24] */ |
| lcd_hiu_write(HHI_LVDS_TX_PHY_CNTL1_TL1, (1 << 30) | (1 << 24)); |
| /* fifo write enable[31] */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL1_TL1, 1, 31, 1); |
| |
| channel_sel0 = pconf->lcd_control.mlvds_config->channel_sel0; |
| channel_sel1 = pconf->lcd_control.mlvds_config->channel_sel1; |
| lcd_vcbus_write(P2P_CH_SWAP0, channel_sel0); |
| lcd_vcbus_write(P2P_CH_SWAP1, channel_sel1); |
| |
| lcd_tcon_enable(pconf); |
| } |
| |
| static void lcd_mlvds_disable(void) |
| { |
| lcd_tcon_disable(); |
| } |
| |
| #if 0 |
| static void lcd_vbyone_ctlbits(int p3d_en, int p3d_lr, int mode) |
| { |
| if (mode == 0) { /* insert at the first pixel */ |
| lcd_vcbus_setb(VBO_PXL_CTRL, |
| (1 << p3d_en) | (p3d_lr & 0x1), 0, 4); |
| } else { |
| lcd_vcbus_setb(VBO_VBK_CTRL_0, |
| (1 << p3d_en) | (p3d_lr & 0x1), 0, 2); |
| } |
| } |
| #endif |
| |
| 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(); |
| unsigned int reg_cntl0, reg_cntl1; |
| |
| switch (lcd_drv->data->chip_type) { |
| case LCD_CHIP_TL1: |
| reg_cntl0 = HHI_LVDS_TX_PHY_CNTL0_TL1; |
| reg_cntl1 = HHI_LVDS_TX_PHY_CNTL1_TL1; |
| break; |
| default: |
| reg_cntl0 = HHI_LVDS_TX_PHY_CNTL0; |
| reg_cntl1 = HHI_LVDS_TX_PHY_CNTL1; |
| break; |
| } |
| |
| 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(reg_cntl0, (div_sel << 6)); |
| /* set cntl_ser_en: 8-channel to 1 */ |
| lcd_hiu_setb(reg_cntl0, 0xfff, 16, 12); |
| switch (lcd_drv->data->chip_type) { /* pn swap */ |
| case LCD_CHIP_TL1: |
| lcd_hiu_setb(reg_cntl0, 1, 2, 1); |
| break; |
| default: |
| break; |
| } |
| |
| /* decoupling fifo enable, gated clock enable */ |
| lcd_hiu_write(reg_cntl1, |
| (1 << 30) | ((phy_div - 1) << 25) | (1 << 24)); |
| /* decoupling fifo write enable after fifo enable */ |
| lcd_hiu_setb(reg_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_hw_filter(int flag) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| switch (lcd_drv->data->chip_type) { |
| case LCD_CHIP_TL1: |
| if (flag) { |
| lcd_vcbus_write(VBO_INFILTER_TICK_PERIOD_L, 0xffff); |
| lcd_vcbus_write(VBO_INFILTER_TICK_PERIOD_H, 0xf); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0x7, 8, 4); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0x7, 12, 4); |
| } else { |
| 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; |
| } |
| } |
| |
| 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_cdr_training_hold(struct vbyone_config_s *vx1_conf, |
| int flag) |
| { |
| if (flag) { |
| LCDPR("ctrl_flag for cdr_training_hold\n"); |
| lcd_vcbus_setb(VBO_FSM_HOLDER_H, 0xffff, 0, 16); |
| } else { |
| msleep(vx1_conf->cdr_training_hold); |
| lcd_vcbus_setb(VBO_FSM_HOLDER_H, 0, 0, 16); |
| } |
| } |
| |
| static void lcd_vbyone_control_set(struct lcd_config_s *pconf) |
| { |
| int lane_count, byte_mode, region_num, hsize, vsize, 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);*/ |
| |
| lcd_vbyone_hw_filter(0); |
| 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(); |
| |
| /* training hold */ |
| if ((pconf->lcd_control.vbyone_config->ctrl_flag) & 0x4) { |
| lcd_vbyone_cdr_training_hold( |
| pconf->lcd_control.vbyone_config, 1); |
| } |
| } |
| |
| static void lcd_vbyone_disable(void) |
| { |
| lcd_vcbus_setb(VBO_CTRL_L, 0, 0, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 2, 1); |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 0, 1); |
| } |
| |
| #define VBYONE_INTR_UNMASK 0x2bff /* 0x2a00 */ |
| /* enable htpdn_fail,lockn_fail,acq_hold */ |
| void lcd_vbyone_interrupt_enable(int flag) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct vbyone_config_s *vx1_conf; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s: %d\n", __func__, flag); |
| |
| vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config; |
| if (flag) { |
| lcd_vbyone_hw_filter(1); |
| if (vx1_conf->intr_en) { |
| vx1_fsm_acq_st = 0; |
| /* clear interrupt */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9); |
| |
| /* set hold in FSM_ACQ */ |
| if (vx1_conf->vsync_intr_en == 3) |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16); |
| else |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16); |
| /* enable interrupt */ |
| lcd_vcbus_setb(VBO_INTR_UNMASK, |
| VBYONE_INTR_UNMASK, 0, 15); |
| } else { |
| /* mask interrupt */ |
| lcd_vcbus_write(VBO_INTR_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) |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, |
| 0, 0, 16); |
| else |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, |
| 0xffff, 0, 16); |
| } else { |
| /* release holder for vsync monitor disabled */ |
| /* release hold in FSM_ACQ */ |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16); |
| } |
| |
| vx1_fsm_acq_st = 0; |
| /* clear interrupt */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9); |
| } |
| } else { |
| /* mask interrupt */ |
| lcd_vcbus_write(VBO_INTR_UNMASK, 0x0); |
| /* release hold in FSM_ACQ */ |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16); |
| lcd_vx1_intr_request = 0; |
| } |
| } |
| |
| static void lcd_vbyone_interrupt_init(struct aml_lcd_drv_s *lcd_drv) |
| { |
| struct vbyone_config_s *vx1_conf; |
| |
| vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config; |
| |
| /* release sw filter ctrl in uboot */ |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 0, 0, 1); |
| lcd_vbyone_hw_filter(1); |
| |
| /* set hold in FSM_ACQ */ |
| if (vx1_conf->vsync_intr_en == 3) |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0, 0, 16); |
| else |
| lcd_vcbus_setb(VBO_FSM_HOLDER_L, 0xffff, 0, 16); |
| /* set hold in FSM_CDR */ |
| lcd_vcbus_setb(VBO_FSM_HOLDER_H, 0, 0, 16); |
| /* not wait lockn to 1 in FSM_ACQ */ |
| lcd_vcbus_setb(VBO_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(VBO_INTR_STATE_CTRL, 1, 12, 1); /* intr pulse width */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9); /* clear interrupt */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9); |
| |
| vx1_fsm_acq_st = 0; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| } |
| |
| #define VX1_LOCKN_WAIT_TIMEOUT 5000 /* 250ms */ |
| void lcd_vbyone_wait_stable(void) |
| { |
| int i = VX1_LOCKN_WAIT_TIMEOUT; |
| |
| while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) { |
| udelay(50); |
| i--; |
| } |
| LCDPR("%s status: 0x%x, i=%d\n", |
| __func__, lcd_vcbus_read(VBO_STATUS_L), |
| (VX1_LOCKN_WAIT_TIMEOUT - i)); |
| vx1_training_wait_cnt = 0; |
| vx1_training_stable_cnt = 0; |
| vx1_fsm_acq_st = 0; |
| lcd_vbyone_interrupt_enable(1); |
| } |
| |
| static void lcd_vbyone_power_on_wait_stable(struct lcd_config_s *pconf) |
| { |
| int i = VX1_LOCKN_WAIT_TIMEOUT; |
| |
| /* training hold release */ |
| if ((pconf->lcd_control.vbyone_config->ctrl_flag) & 0x4) { |
| lcd_vbyone_cdr_training_hold( |
| pconf->lcd_control.vbyone_config, 0); |
| } |
| while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && (i > 0)) { |
| udelay(50); |
| i--; |
| } |
| LCDPR("%s status: 0x%x, i=%d\n", |
| __func__, lcd_vcbus_read(VBO_STATUS_L), |
| (VX1_LOCKN_WAIT_TIMEOUT - i)); |
| |
| /* power on reset */ |
| if ((pconf->lcd_control.vbyone_config->ctrl_flag) & 0x1) { |
| LCDPR("ctrl_flag for power on reset\n"); |
| msleep(pconf->lcd_control.vbyone_config->power_on_reset_delay); |
| lcd_vbyone_sw_reset(); |
| i = VX1_LOCKN_WAIT_TIMEOUT; |
| while (((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) && |
| (i > 0)) { |
| udelay(50); |
| i--; |
| } |
| LCDPR("%s status: 0x%x, i=%d\n", |
| __func__, lcd_vcbus_read(VBO_STATUS_L), |
| (VX1_LOCKN_WAIT_TIMEOUT - i)); |
| } |
| |
| vx1_training_wait_cnt = 0; |
| vx1_training_stable_cnt = 0; |
| vx1_fsm_acq_st = 0; |
| lcd_vbyone_interrupt_enable(1); |
| } |
| |
| #define LCD_VX1_WAIT_STABLE_DELAY 300 /* ms */ |
| static void lcd_vx1_wait_stable_delayed(struct work_struct *work) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| if (lcd_vx1_intr_request == 0) |
| return; |
| |
| lcd_vbyone_power_on_wait_stable(lcd_drv->lcd_config); |
| } |
| |
| static void lcd_vbyone_wait_hpd(struct lcd_config_s *pconf) |
| { |
| int i = 0; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("vx1 wait hpd to low ...\n"); |
| |
| 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); |
| /* force low only actived for actual hpd is low */ |
| lcd_vcbus_setb(VBO_INSGN_CTRL, 1, 2, 2); |
| } |
| |
| if ((pconf->lcd_control.vbyone_config->ctrl_flag) & 0x2) { |
| LCDPR("ctrl_flag for hpd_data delay\n"); |
| msleep(pconf->lcd_control.vbyone_config->hpd_data_delay); |
| } else |
| usleep_range(10000, 10500); |
| /* add 10ms delay for compatibility */ |
| } |
| |
| #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_vx1_hpll_timer_handler(unsigned long arg) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| int encl_clk; |
| #if 0 |
| int pclk, pclk_min, pclk_max; |
| #endif |
| |
| if ((lcd_drv->lcd_status & LCD_STATUS_ENCL_ON) == 0) |
| goto vx1_hpll_timer_end; |
| |
| #if 0 |
| pclk = lcd_drv->lcd_config->lcd_timing.lcd_clk; |
| pclk_min = pclk - LCD_PCLK_TOLERANCE; |
| pclk_max = pclk + LCD_PCLK_TOLERANCE; |
| encl_clk = lcd_encl_clk_msr(); |
| if ((encl_clk < pclk_min) || (encl_clk > pclk_max)) { |
| LCDPR("%s: pll frequency error: %d\n", __func__, encl_clk); |
| lcd_pll_reset(); |
| } |
| #else |
| encl_clk = lcd_encl_clk_msr(); |
| 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("%s: pll frequency error: %d\n", __func__, encl_clk); |
| lcd_pll_reset(); |
| lcd_encl_clk_err_cnt = 0; |
| } |
| #endif |
| |
| vx1_hpll_timer_end: |
| vx1_hpll_timer.expires = jiffies + VX1_HPLL_INTERVAL; |
| add_timer(&vx1_hpll_timer); |
| } |
| |
| static void lcd_vx1_hold_reset(void) |
| { |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| vx1_fsm_acq_st = 0; |
| lcd_vcbus_write(VBO_INTR_UNMASK, 0x0); /* mask interrupt */ |
| /* clear interrupt */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0x01ff, 0, 9); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9); |
| |
| /* clear FSM_continue */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1); |
| |
| /* force PHY to 0 */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0x1ff); |
| udelay(5); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1); |
| /* realease PHY */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1); |
| lcd_vcbus_write(VBO_SOFT_RST, 0); |
| |
| /* enable interrupt */ |
| lcd_vcbus_setb(VBO_INTR_UNMASK, VBYONE_INTR_UNMASK, 0, 15); |
| } |
| |
| static void lcd_vx1_timeout_reset(struct work_struct *p_work) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| if (vx1_timeout_reset_flag == 0) |
| return; |
| |
| LCDPR("%s\n", __func__); |
| lcd_drv->module_reset(); |
| if (lcd_drv->lcd_config->lcd_control.vbyone_config->intr_en) |
| lcd_vx1_hold_reset(); |
| 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 irqreturn_t lcd_vbyone_vsync_isr(int irq, void *dev_id) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct vbyone_config_s *vx1_conf; |
| |
| vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config; |
| if ((lcd_drv->lcd_status & LCD_STATUS_IF_ON) == 0) |
| return IRQ_HANDLED; |
| if (lcd_vcbus_read(VBO_STATUS_L) & 0x40) /* hpd detect */ |
| return IRQ_HANDLED; |
| |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 0, 1); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 1); |
| |
| if (vx1_conf->vsync_intr_en == 0) { |
| vx1_training_wait_cnt = 0; |
| return IRQ_HANDLED; |
| } |
| |
| 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(0); |
| lcd_vbyone_sw_reset(); |
| vsync_cnt++; |
| } else if ((vsync_cnt > VSYNC_CNT_VX1_RESET) && |
| (vsync_cnt < VSYNC_CNT_VX1_STABLE)) { |
| if (lcd_vcbus_read(VBO_STATUS_L) & 0x20) { |
| vsync_cnt = VSYNC_CNT_VX1_STABLE; |
| lcd_vbyone_hw_filter(1); |
| } else { |
| vsync_cnt++; |
| } |
| } |
| } else if (vx1_conf->vsync_intr_en == 2) { |
| if (vsync_cnt >= 5) { |
| vsync_cnt = 0; |
| if (!(lcd_vcbus_read(VBO_STATUS_L) & 0x20)) { |
| lcd_vbyone_hw_filter(0); |
| lcd_vbyone_sw_reset(); |
| LCDPR("vx1 sw_reset 2\n"); |
| while (lcd_vcbus_read(VBO_STATUS_L) & 0x4) |
| break; |
| |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 15, 1); |
| } else { |
| lcd_vbyone_hw_filter(1); |
| } |
| } else |
| vsync_cnt++; |
| } else { |
| if (vx1_training_wait_cnt >= VX1_TRAINING_TIMEOUT) { |
| if ((lcd_vcbus_read(VBO_STATUS_L) & 0x3f) != 0x20) { |
| if (vx1_timeout_reset_flag == 0) { |
| vx1_timeout_reset_flag = 1; |
| if (lcd_drv->workqueue) { |
| queue_work(lcd_drv->workqueue, |
| &lcd_vx1_reset_work); |
| } else { |
| schedule_work( |
| &lcd_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 IRQ_HANDLED; |
| } |
| |
| #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 *dev_id) |
| { |
| unsigned int data32, data32_1; |
| int encl_clk; |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct vbyone_config_s *vx1_conf; |
| |
| vx1_conf = lcd_drv->lcd_config->lcd_control.vbyone_config; |
| |
| lcd_vcbus_write(VBO_INTR_UNMASK, 0x0); /* mask interrupt */ |
| |
| encl_clk = lcd_encl_clk_msr(); |
| data32 = (lcd_vcbus_read(VBO_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(VBO_INTR_STATE_CTRL, data32_1, 0, 9); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 0, 9); |
| if (lcd_debug_print_flag) { |
| LCDPR("vx1 intr status = 0x%04x, encl_clkmsr = %d", |
| data32, encl_clk); |
| } |
| |
| if (vx1_conf->vsync_intr_en == 3) { |
| if (data32 & 0x1000) { |
| if (vsync_cnt >= (VSYNC_CNT_VX1_RESET + 1)) { |
| vsync_cnt = 0; |
| LCDPR("vx1 lockn rise edge occurred\n"); |
| } |
| } |
| } else { |
| if (data32 & 0x200) { |
| LCDPR("vx1 htpdn fall occurred\n"); |
| vx1_fsm_acq_st = 0; |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1); |
| } |
| if (data32 & 0x800) { |
| if (lcd_debug_print_flag) |
| LCDPR("vx1 lockn fall occurred\n"); |
| vx1_fsm_acq_st = 0; |
| lcd_vcbus_setb(VBO_INTR_STATE_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; |
| if (lcd_drv->workqueue) { |
| queue_work(lcd_drv->workqueue, |
| &lcd_vx1_reset_work); |
| } else { |
| schedule_work( |
| &lcd_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) { |
| LCDPR("vx1 status 0: 0x%x, fsm_acq_st: %d\n", |
| lcd_vcbus_read(VBO_STATUS_L), |
| vx1_fsm_acq_st); |
| } |
| if (vx1_fsm_acq_st == 0) { |
| /* clear FSM_continue */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1); |
| LCDPR("vx1 sw reset\n"); |
| /* force PHY to 0 */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0x1ff); |
| udelay(5); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1); |
| /* realease PHY */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1); |
| lcd_vcbus_write(VBO_SOFT_RST, 0); |
| 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(VBO_FSM_HOLDER_L, 0, 0, 16); |
| #else |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 15, 1); |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 15, 1); |
| #endif |
| } |
| if (lcd_debug_print_flag) { |
| LCDPR("vx1 status 1: 0x%x, fsm_acq_st: %d\n", |
| lcd_vcbus_read(VBO_STATUS_L), |
| vx1_fsm_acq_st); |
| } |
| } |
| |
| if (data32 & 0x1ff) { |
| if (lcd_debug_print_flag) |
| LCDPR("vx1 reset for timing err\n"); |
| vx1_fsm_acq_st = 0; |
| /* force PHY to 0 */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 3, 8, 2); |
| lcd_vcbus_write(VBO_SOFT_RST, 0x1ff); |
| udelay(5); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 1, 7, 1); |
| /* realease PHY */ |
| lcd_hiu_setb(HHI_LVDS_TX_PHY_CNTL0, 0, 8, 2); |
| /* clear lockn raising flag */ |
| lcd_vcbus_setb(VBO_INTR_STATE_CTRL, 0, 7, 1); |
| lcd_vcbus_write(VBO_SOFT_RST, 0); |
| } |
| |
| udelay(20); |
| if ((lcd_vcbus_read(VBO_STATUS_L) & 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(VBO_FSM_HOLDER_L, 0xffff, 0, 16); |
| #endif |
| lcd_vbyone_hw_filter(1); |
| LCDPR("vx1 fsm stable\n"); |
| } |
| } |
| /* enable interrupt */ |
| lcd_vcbus_setb(VBO_INTR_UNMASK, VBYONE_INTR_UNMASK, 0, 15); |
| |
| return IRQ_HANDLED; |
| } |
| |
| static void lcd_p2p_control_set(struct lcd_config_s *pconf) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| unsigned int reg_cntl0, reg_cntl1; |
| unsigned int phy_div; |
| unsigned int channel_sel0, channel_sel1; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| /* phy_div: 0=div6, 1=div 7, 2=div8, 3=div10 */ |
| switch (pconf->lcd_control.p2p_config->p2p_type) { |
| case P2P_CHPI: /* 8/10 coding */ |
| phy_div = 3; |
| break; |
| default: |
| phy_div = 2; |
| break; |
| } |
| |
| switch (lcd_drv->data->chip_type) { |
| case LCD_CHIP_TL1: |
| reg_cntl0 = HHI_LVDS_TX_PHY_CNTL0_TL1; |
| reg_cntl1 = HHI_LVDS_TX_PHY_CNTL1_TL1; |
| break; |
| default: |
| reg_cntl0 = HHI_LVDS_TX_PHY_CNTL0; |
| reg_cntl1 = HHI_LVDS_TX_PHY_CNTL1; |
| break; |
| } |
| |
| /* fifo_clk_sel[7:6]: 0=div6, 1=div 7, 2=div8, 3=div10 */ |
| lcd_hiu_write(reg_cntl0, (phy_div << 6)); |
| /* serializer_en[27:16] */ |
| lcd_hiu_setb(reg_cntl0, 0xfff, 16, 12); |
| /* pn swap[2] */ |
| lcd_hiu_setb(reg_cntl0, 1, 2, 1); |
| |
| /* fifo enable[30], phy_clock gating[24] */ |
| lcd_hiu_write(reg_cntl1, (1 << 30) | (1 << 24)); |
| /* fifo write enable[31] */ |
| lcd_hiu_setb(reg_cntl1, 1, 31, 1); |
| |
| channel_sel0 = pconf->lcd_control.p2p_config->channel_sel0; |
| channel_sel1 = pconf->lcd_control.p2p_config->channel_sel1; |
| lcd_vcbus_write(P2P_CH_SWAP0, channel_sel0); |
| lcd_vcbus_write(P2P_CH_SWAP1, channel_sel1); |
| |
| lcd_tcon_enable(pconf); |
| } |
| |
| static void lcd_p2p_disable(void) |
| { |
| lcd_tcon_disable(); |
| } |
| |
| 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)); |
| } |
| } |
| |
| static void lcd_mlvds_config_set(struct lcd_config_s *pconf) |
| { |
| unsigned int bit_rate, pclk; |
| unsigned int lcd_bits, channel_num; |
| unsigned int channel_sel0, channel_sel1, pi_clk_sel = 0; |
| unsigned int i, temp; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| lcd_bits = pconf->lcd_basic.lcd_bits; |
| channel_num = pconf->lcd_control.mlvds_config->channel_num; |
| pclk = pconf->lcd_timing.lcd_clk / 1000; |
| bit_rate = lcd_bits * 3 * pclk / channel_num; |
| |
| pconf->lcd_timing.bit_rate = bit_rate * 1000; |
| |
| if (lcd_debug_print_flag) { |
| LCDPR("channel_num=%u, bit_rate=%u.%03uMHz, pclk=%u.%03uMhz\n", |
| channel_num, (bit_rate / 1000), (bit_rate % 1000), |
| (pclk / 1000), (pclk % 1000)); |
| } |
| |
| /* pi_clk select */ |
| channel_sel0 = pconf->lcd_control.mlvds_config->channel_sel0; |
| channel_sel1 = pconf->lcd_control.mlvds_config->channel_sel1; |
| /* mlvds channel: //tx 12 channels |
| * 0: clk_a |
| * 1: d0_a |
| * 2: d1_a |
| * 3: d2_a |
| * 4: d3_a |
| * 5: d4_a |
| * 6: clk_b |
| * 7: d0_b |
| * 8: d1_b |
| * 9: d2_b |
| * 10: d3_b |
| * 11: d4_b |
| */ |
| for (i = 0; i < 8; i++) { |
| temp = (channel_sel0 >> (i*4)) & 0xf; |
| if ((temp == 0) || (temp == 6)) |
| pi_clk_sel |= (1 << i); |
| } |
| for (i = 0; i < 4; i++) { |
| temp = (channel_sel1 >> (i*4)) & 0xf; |
| if ((temp == 0) || (temp == 6)) |
| pi_clk_sel |= (1 << (i + 8)); |
| } |
| pconf->lcd_control.mlvds_config->pi_clk_sel = pi_clk_sel; |
| if (lcd_debug_print_flag) { |
| LCDPR( |
| "channel_sel0=0x%08x, channel_sel1=0x%08x, pi_clk_sel=0x%03x\n", |
| channel_sel0, channel_sel1, pi_clk_sel); |
| } |
| } |
| |
| static void lcd_p2p_config_set(struct lcd_config_s *pconf) |
| { |
| unsigned int bit_rate, pclk; |
| unsigned int lcd_bits, lane_num; |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s\n", __func__); |
| |
| lcd_bits = pconf->lcd_basic.lcd_bits; |
| lane_num = pconf->lcd_control.p2p_config->lane_num; |
| pclk = pconf->lcd_timing.lcd_clk / 1000; |
| switch (pconf->lcd_control.p2p_config->p2p_type) { |
| case P2P_CEDS: |
| if (pclk >= 600000) |
| bit_rate = pclk * 3 * lcd_bits / lane_num; |
| else |
| bit_rate = pclk * (3 * lcd_bits + 4) / lane_num; |
| break; |
| case P2P_CHPI: /* 8/10 coding */ |
| bit_rate = (pclk * 3 * lcd_bits * 10 / 8) / lane_num; |
| break; |
| default: |
| bit_rate = pclk * 3 * lcd_bits / lane_num; |
| break; |
| } |
| |
| pconf->lcd_timing.bit_rate = bit_rate * 1000; |
| |
| if (lcd_debug_print_flag) { |
| LCDPR("lane_num=%u, bit_rate=%u.%03uMHz, pclk=%u.%03uMhz\n", |
| lane_num, (bit_rate / 1000), (bit_rate % 1000), |
| (pclk / 1000), (pclk % 1000)); |
| } |
| } |
| |
| void lcd_tv_clk_config_change(struct lcd_config_s *pconf) |
| { |
| #ifdef CONFIG_AMLOGIC_VPU |
| request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL); |
| #endif |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_config_set(pconf); |
| break; |
| case LCD_MLVDS: |
| lcd_mlvds_config_set(pconf); |
| break; |
| case LCD_P2P: |
| lcd_p2p_config_set(pconf); |
| break; |
| default: |
| break; |
| } |
| lcd_clk_generate_parameter(pconf); |
| } |
| |
| void lcd_tv_clk_update(struct lcd_config_s *pconf) |
| { |
| lcd_tv_clk_config_change(pconf); |
| |
| if (pconf->lcd_basic.lcd_type == LCD_VBYONE) |
| lcd_vbyone_interrupt_enable(0); |
| lcd_clk_set(pconf); |
| if (pconf->lcd_basic.lcd_type == LCD_VBYONE) |
| lcd_vbyone_wait_stable(); |
| } |
| |
| void lcd_tv_config_update(struct lcd_config_s *pconf) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct vinfo_s *info; |
| |
| /* update lcd config sync_duration */ |
| info = lcd_drv->lcd_info; |
| pconf->lcd_timing.sync_duration_num = info->sync_duration_num; |
| pconf->lcd_timing.sync_duration_den = info->sync_duration_den; |
| |
| /* update clk & timing config */ |
| lcd_vmode_change(pconf); |
| info->video_clk = pconf->lcd_timing.lcd_clk; |
| info->htotal = pconf->lcd_basic.h_period; |
| info->vtotal = pconf->lcd_basic.v_period; |
| /* update interface timing */ |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_config_set(pconf); |
| break; |
| case LCD_MLVDS: |
| lcd_mlvds_config_set(pconf); |
| break; |
| case LCD_P2P: |
| lcd_p2p_config_set(pconf); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void lcd_tv_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("tv 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; |
| |
| #ifdef CONFIG_AMLOGIC_VPU |
| request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL); |
| switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_ON); |
| #endif |
| lcd_clk_gate_switch(1); |
| |
| /* init driver */ |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_VBYONE: |
| lcd_vbyone_interrupt_enable(0); |
| break; |
| default: |
| break; |
| } |
| |
| lcd_clk_set(pconf); |
| lcd_venc_set(pconf); |
| lcd_encl_tcon_set(pconf); |
| lcd_drv->lcd_mute_state = 1; |
| |
| lcd_vcbus_write(VENC_INTCTRL, 0x200); |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| } |
| |
| void lcd_tv_driver_disable_post(void) |
| { |
| lcd_vcbus_write(ENCL_VIDEO_EN, 0); /* disable encl */ |
| |
| lcd_clk_disable(); |
| lcd_clk_gate_switch(0); |
| #ifdef CONFIG_AMLOGIC_VPU |
| switch_vpu_mem_pd_vmod(VPU_VENCL, VPU_MEM_POWER_DOWN); |
| release_vpu_clk_vmod(VPU_VENCL); |
| #endif |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| } |
| |
| int lcd_tv_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; |
| |
| switch (pconf->lcd_basic.lcd_type) { |
| case LCD_LVDS: |
| lcd_lvds_control_set(pconf); |
| lcd_lvds_phy_set(pconf, 1); |
| break; |
| case LCD_VBYONE: |
| lcd_vbyone_pinmux_set(1); |
| lcd_vbyone_control_set(pconf); |
| lcd_vbyone_wait_hpd(pconf); |
| lcd_vbyone_phy_set(pconf, 1); |
| lcd_vx1_intr_request = 1; |
| if (lcd_drv->workqueue) { |
| queue_delayed_work(lcd_drv->workqueue, |
| &lcd_vx1_stable_delayed_work, |
| msecs_to_jiffies(LCD_VX1_WAIT_STABLE_DELAY)); |
| } else { |
| schedule_delayed_work(&lcd_vx1_stable_delayed_work, |
| msecs_to_jiffies(LCD_VX1_WAIT_STABLE_DELAY)); |
| } |
| break; |
| case LCD_MLVDS: |
| lcd_mlvds_control_set(pconf); |
| lcd_tcon_pinmux_set(1); |
| lcd_mlvds_phy_set(pconf, 1); |
| break; |
| case LCD_P2P: |
| lcd_tcon_pinmux_set(1); |
| lcd_p2p_phy_set(pconf, 1); |
| lcd_p2p_control_set(pconf); |
| break; |
| default: |
| break; |
| } |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| void lcd_tv_driver_disable(void) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| struct lcd_config_s *pconf; |
| int ret; |
| |
| vx1_training_wait_cnt = 0; |
| vx1_timeout_reset_flag = 0; |
| |
| 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_LVDS: |
| lcd_lvds_phy_set(pconf, 0); |
| lcd_lvds_disable(); |
| break; |
| case LCD_VBYONE: |
| lcd_vbyone_interrupt_enable(0); |
| lcd_vbyone_phy_set(pconf, 0); |
| lcd_vbyone_pinmux_set(0); |
| lcd_vbyone_disable(); |
| break; |
| case LCD_MLVDS: |
| lcd_mlvds_disable(); |
| lcd_mlvds_phy_set(pconf, 0); |
| lcd_tcon_pinmux_set(0); |
| break; |
| case LCD_P2P: |
| lcd_p2p_disable(); |
| lcd_p2p_phy_set(pconf, 0); |
| lcd_tcon_pinmux_set(0); |
| break; |
| default: |
| break; |
| } |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| } |
| |
| int lcd_tv_driver_change(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("tv driver change(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 -1; |
| |
| lcd_tv_config_update(pconf); |
| #ifdef CONFIG_AMLOGIC_VPU |
| request_vpu_clk_vmod(pconf->lcd_timing.lcd_clk, VPU_VENCL); |
| #endif |
| |
| if (lcd_drv->lcd_status & LCD_STATUS_ENCL_ON) { |
| if (pconf->lcd_basic.lcd_type == LCD_VBYONE) { |
| if (lcd_drv->lcd_status & LCD_STATUS_IF_ON) |
| lcd_vbyone_interrupt_enable(0); |
| } |
| |
| lcd_clk_change(pconf); |
| lcd_venc_change(pconf); |
| |
| if (pconf->lcd_basic.lcd_type == LCD_VBYONE) { |
| if (lcd_drv->lcd_status & LCD_STATUS_IF_ON) |
| lcd_vbyone_wait_stable(); |
| } |
| } else { |
| /* only change parameters when panel is off */ |
| switch (pconf->lcd_timing.clk_change) { |
| case LCD_CLK_PLL_CHANGE: |
| lcd_clk_generate_parameter(pconf); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| if (lcd_debug_print_flag) |
| LCDPR("%s finished\n", __func__); |
| return 0; |
| } |
| |
| #define VBYONE_IRQF IRQF_SHARED /* IRQF_DISABLED */ /* IRQF_SHARED */ |
| |
| int lcd_vbyone_interrupt_up(void) |
| { |
| unsigned int viu_vsync_irq = 0, venc_vx1_irq = 0; |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| lcd_vbyone_interrupt_init(lcd_drv); |
| vx1_lockn_wait_cnt = 0; |
| vx1_training_wait_cnt = 0; |
| vx1_timeout_reset_flag = 0; |
| vx1_training_stable_cnt = 0; |
| lcd_vx1_intr_request = 0; |
| lcd_encl_clk_err_cnt = 0; |
| |
| INIT_WORK(&lcd_vx1_reset_work, lcd_vx1_timeout_reset); |
| |
| INIT_DELAYED_WORK(&lcd_vx1_stable_delayed_work, |
| lcd_vx1_wait_stable_delayed); |
| |
| if (!lcd_drv->res_vsync_irq) { |
| LCDERR("res_vsync_irq is null\n"); |
| return -1; |
| } |
| viu_vsync_irq = lcd_drv->res_vsync_irq->start; |
| LCDPR("viu_vsync_irq: %d\n", viu_vsync_irq); |
| |
| if (request_irq(viu_vsync_irq, lcd_vbyone_vsync_isr, |
| IRQF_SHARED, "vbyone_vsync", (void *)"vbyone_vsync")) |
| LCDERR("can't request viu_vsync_irq\n"); |
| else { |
| if (lcd_debug_print_flag) |
| LCDPR("request viu_vsync_irq successful\n"); |
| } |
| |
| if (!lcd_drv->res_vx1_irq) { |
| LCDERR("res_vx1_irq is null\n"); |
| return -1; |
| } |
| venc_vx1_irq = lcd_drv->res_vx1_irq->start; |
| LCDPR("venc_vx1_irq: %d\n", venc_vx1_irq); |
| |
| if (request_irq(venc_vx1_irq, lcd_vbyone_interrupt_handler, 0, |
| "vbyone", (void *)"vbyone")) |
| LCDERR("can't request venc_vx1_irq\n"); |
| else { |
| if (lcd_debug_print_flag) |
| LCDPR("request venc_vx1_irq successful\n"); |
| } |
| |
| lcd_vx1_intr_request = 1; |
| lcd_vbyone_interrupt_enable(1); |
| |
| /* add timer to monitor hpll frequency */ |
| init_timer(&vx1_hpll_timer); |
| /* vx1_hpll_timer.data = NULL; */ |
| vx1_hpll_timer.function = lcd_vx1_hpll_timer_handler; |
| vx1_hpll_timer.expires = jiffies + VX1_HPLL_INTERVAL; |
| add_timer(&vx1_hpll_timer); |
| LCDPR("add vbyone hpll timer handler\n"); |
| |
| return 0; |
| } |
| |
| void lcd_vbyone_interrupt_down(void) |
| { |
| struct aml_lcd_drv_s *lcd_drv = aml_lcd_get_driver(); |
| |
| del_timer_sync(&vx1_hpll_timer); |
| |
| lcd_vbyone_interrupt_enable(0); |
| free_irq(lcd_drv->res_vx1_irq->start, (void *)"vbyone"); |
| free_irq(lcd_drv->res_vsync_irq->start, (void *)"vbyone_vsync"); |
| cancel_work_sync(&lcd_vx1_reset_work); |
| cancel_delayed_work(&lcd_vx1_stable_delayed_work); |
| |
| if (lcd_debug_print_flag) |
| LCDPR("free vbyone irq\n"); |
| } |