#include "drv_types.h" #include #include #include #include "hdmi_processing.h" #include "hdmi_switch.h" #include "hdmi_hpd.h" #include "gpioi2c.h" #include "hdmi_time.h" #define bSwitchDBG ((*(UINT32*)DBGCONFIG2ADDR)&DBGCFG_CECSWITCH) #ifdef __KERNEL__ #ifndef CONFIG_SUPPORT_DEBUG_MESSAGE #define switchdbg(fmt,args...) #else #define switchdbg(fmt,args...) printk(bSwitchDBG?("[HS] " fmt):"", ## args) #endif #endif static struct timer_list hpd_timer; static struct work_struct hpd_wq; static UINT8 it6633_readi2c(UINT8 addr) { UINT8 val = 0; hdmi_i2c_read(addr, &val, 1); return val; } static void it6633_writei2c(UINT8 addr, UINT8 val) { hdmi_i2c_write(addr, &val, 1); } static void hpd_timer_schedule(struct timer_list *, UINT32); static BOOL bToggle = false; static BOOL bSignalCheck = false; static UINT32 hpd_status = 0; static UINT8 sw_port = 0xff; static void apply_switch_hpd(UINT32 hpd) { UINT8 byte; byte = it6633_readi2c(0x29); byte &= 0xc1; byte |= ((hpd & 0x1) << 3); byte |= ((hpd & 0x2) << 1); byte |= ((hpd & 0x4) >> 1); switchdbg("%s byte 0x%02x\n", __FUNCTION__, byte); it6633_writei2c(0x29, byte); } static void toggle_hpd(UINT8 port) { UINT8 byte; switchdbg("%s port %s\n", __FUNCTION__, port == 0 ? "A" : port == 1 ? "B" : port == 2 ? "C" : "X" ); byte = it6633_readi2c(0x29); byte &= (~(1 << (3 - (unsigned char)port))); it6633_writei2c(0x29, byte); it6633_writei2c(0x04, 0x00); byte = it6633_readi2c(0x29); byte &= 0xcf; byte |= 0x20; it6633_writei2c(0x29, byte); switchdbg("delay 500 ms\n"); HDMI_DelayMs(500); byte &= 0xcf; it6633_writei2c(0x29, byte); byte = 0xB0 | ((unsigned char)port << 2) | (unsigned char)port; it6633_writei2c(0x06, byte); } static void check_hpd(void *dummy) { UINT8 diff_hpd_status = 0; UINT8 local_hpd_status = 0; local_hpd_status = it6633_readi2c(0x24) & 0x7; diff_hpd_status = local_hpd_status ^ hpd_status; hpd_status = local_hpd_status; if (bToggle || diff_hpd_status) { apply_switch_hpd(hpd_status); switchdbg("sw_port 0x%02x diff_hpd_status 0x%02x\n", sw_port, diff_hpd_status); if (sw_port != 0xff && (diff_hpd_status & hpd_status & (1 << sw_port))) { switchdbg("delay 20 ms\n"); HDMI_DelayMs(20); bToggle = true; } else { hdmi_hpd_update(); } } if (bToggle && sw_port != 0xff) { toggle_hpd(sw_port); bToggle = false; hdmi_hpd_update(); } if (bSignalCheck) { bSignalCheck = false; hdmi_signal_check_start(); } } static void HPD_TIMER(void* dummy) { /* Schedule working queue to check hpd now */ schedule_work(&hpd_wq); /* Check hpd periodically */ hpd_timer_schedule(&hpd_timer, 50); } static void hpd_timer_schedule(struct timer_list *timer, UINT32 ten_ms) { del_timer(timer); timer->expires = jiffies + (HZ / 100) * ten_ms; // 10ms per unit timer->data = (UINT32)NULL; timer->function = (void (*)(ULONG))HPD_TIMER; add_timer(timer); } UINT32 hdmi_switch_hpd_status(void) { /* Return switch hpd status */ // bit[0] - A port // bit[1] - B port // bit[2] - C port return hpd_status; } void hdmi_switch_disable(void) { switchdbg("%s\n", __FUNCTION__); it6633_writei2c(0x04, 0x08); } void hdmi_switch_enable(INT8 port) { switchdbg("%s port %s\n", __FUNCTION__, port == 0 ? "A" : port == 1 ? "B" : port == 2 ? "C" : "unknown"); if (port < 0) { sw_port = 0xff; return; } port &= 0x3; sw_port = (unsigned char)port; bToggle = true; bSignalCheck = true; hpd_timer_schedule(&hpd_timer, 0); } void hdmi_switch_init(void) { switchdbg("%s\n", __FUNCTION__); /* Setup i2c mode, device id, and address length */ hdmi_i2c_setup(USE_HW_I2C_SLAVE, 0x94, 1); it6633_writei2c(0x04, 0x08); /* Init DDC 5V status */ hpd_status = 0; bToggle = false; /* Schedule timer to check hpd periodically */ INIT_WORK(&hpd_wq, (work_func_t)check_hpd); init_timer(&hpd_timer); /* Schedule the 2 seconds timer, it will be rescheduled when current hdmi switch source is selected */ hpd_timer_schedule(&hpd_timer, 200); /* Notice hdmi driver the current hpd status */ } void hdmi_switch_power(BOOL bPwr) { switchdbg("%s(%s)\n", __FUNCTION__, bPwr ? "on" : "off"); }