diff options
Diffstat (limited to 'drivers/video/via/dvi.c')
-rw-r--r-- | drivers/video/via/dvi.c | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c new file mode 100644 index 00000000000..d6965447ca6 --- /dev/null +++ b/drivers/video/via/dvi.c @@ -0,0 +1,682 @@ +/* + * Copyright 1998-2008 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2008 S3 Graphics, 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, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE.See the GNU General Public License + * for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include "global.h" + +static void tmds_register_write(int index, u8 data); +static int tmds_register_read(int index); +static int tmds_register_read_bytes(int index, u8 *buff, int buff_len); +static int check_reduce_blanking_mode(int mode_index, + int refresh_rate); +static int dvi_get_panel_size_from_DDCv1(void); +static int dvi_get_panel_size_from_DDCv2(void); +static unsigned char dvi_get_panel_info(void); +static int viafb_dvi_query_EDID(void); + +static int check_tmds_chip(int device_id_subaddr, int device_id) +{ + if (tmds_register_read(device_id_subaddr) == device_id) + return OK; + else + return FAIL; +} + +void viafb_init_dvi_size(void) +{ + DEBUG_MSG(KERN_INFO "viafb_init_dvi_size()\n"); + DEBUG_MSG(KERN_INFO + "viaparinfo->tmds_setting_info->get_dvi_size_method %d\n", + viaparinfo->tmds_setting_info->get_dvi_size_method); + + switch (viaparinfo->tmds_setting_info->get_dvi_size_method) { + case GET_DVI_SIZE_BY_SYSTEM_BIOS: + break; + case GET_DVI_SZIE_BY_HW_STRAPPING: + break; + case GET_DVI_SIZE_BY_VGA_BIOS: + default: + dvi_get_panel_info(); + break; + } + return; +} + +int viafb_tmds_trasmitter_identify(void) +{ + unsigned char sr2a = 0, sr1e = 0, sr3e = 0; + + /* Turn on ouputting pad */ + switch (viaparinfo->chip_info->gfx_chip_name) { + case UNICHROME_K8M890: + /*=* DFP Low Pad on *=*/ + sr2a = viafb_read_reg(VIASR, SR2A); + viafb_write_reg_mask(SR2A, VIASR, 0x03, BIT0 + BIT1); + break; + + case UNICHROME_P4M900: + case UNICHROME_P4M890: + /* DFP Low Pad on */ + sr2a = viafb_read_reg(VIASR, SR2A); + viafb_write_reg_mask(SR2A, VIASR, 0x03, BIT0 + BIT1); + /* DVP0 Pad on */ + sr1e = viafb_read_reg(VIASR, SR1E); + viafb_write_reg_mask(SR1E, VIASR, 0xC0, BIT6 + BIT7); + break; + + default: + /* DVP0/DVP1 Pad on */ + sr1e = viafb_read_reg(VIASR, SR1E); + viafb_write_reg_mask(SR1E, VIASR, 0xF0, BIT4 + + BIT5 + BIT6 + BIT7); + /* SR3E[1]Multi-function selection: + 0 = Emulate I2C and DDC bus by GPIO2/3/4. */ + sr3e = viafb_read_reg(VIASR, SR3E); + viafb_write_reg_mask(SR3E, VIASR, 0x0, BIT5); + break; + } + + /* Check for VT1632: */ + viaparinfo->chip_info->tmds_chip_info.tmds_chip_name = VT1632_TMDS; + viaparinfo->chip_info-> + tmds_chip_info.tmds_chip_slave_addr = VT1632_TMDS_I2C_ADDR; + viaparinfo->chip_info->tmds_chip_info.i2c_port = I2CPORTINDEX; + if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID) != FAIL) { + /* + * Currently only support 12bits,dual edge,add 24bits mode later + */ + tmds_register_write(0x08, 0x3b); + + DEBUG_MSG(KERN_INFO "\n VT1632 TMDS ! \n"); + DEBUG_MSG(KERN_INFO "\n %2d", + viaparinfo->chip_info->tmds_chip_info.tmds_chip_name); + DEBUG_MSG(KERN_INFO "\n %2d", + viaparinfo->chip_info->tmds_chip_info.i2c_port); + return OK; + } else { + viaparinfo->chip_info->tmds_chip_info.i2c_port = GPIOPORTINDEX; + if (check_tmds_chip(VT1632_DEVICE_ID_REG, VT1632_DEVICE_ID) + != FAIL) { + tmds_register_write(0x08, 0x3b); + DEBUG_MSG(KERN_INFO "\n VT1632 TMDS ! \n"); + DEBUG_MSG(KERN_INFO "\n %2d", + viaparinfo->chip_info-> + tmds_chip_info.tmds_chip_name); + DEBUG_MSG(KERN_INFO "\n %2d", + viaparinfo->chip_info-> + tmds_chip_info.i2c_port); + return OK; + } + } + + viaparinfo->chip_info->tmds_chip_info.tmds_chip_name = INTEGRATED_TMDS; + + if ((viaparinfo->chip_info->gfx_chip_name == UNICHROME_CX700) && + ((viafb_display_hardware_layout == HW_LAYOUT_DVI_ONLY) || + (viafb_display_hardware_layout == HW_LAYOUT_LCD_DVI))) { + DEBUG_MSG(KERN_INFO "\n Integrated TMDS ! \n"); + return OK; + } + + switch (viaparinfo->chip_info->gfx_chip_name) { + case UNICHROME_K8M890: + viafb_write_reg(SR2A, VIASR, sr2a); + break; + + case UNICHROME_P4M900: + case UNICHROME_P4M890: + viafb_write_reg(SR2A, VIASR, sr2a); + viafb_write_reg(SR1E, VIASR, sr1e); + break; + + default: + viafb_write_reg(SR1E, VIASR, sr1e); + viafb_write_reg(SR3E, VIASR, sr3e); + break; + } + + viaparinfo->chip_info-> + tmds_chip_info.tmds_chip_name = NON_TMDS_TRANSMITTER; + viaparinfo->chip_info->tmds_chip_info. + tmds_chip_slave_addr = VT1632_TMDS_I2C_ADDR; + return FAIL; +} + +static void tmds_register_write(int index, u8 data) +{ + viaparinfo->i2c_stuff.i2c_port = + viaparinfo->chip_info->tmds_chip_info.i2c_port; + + viafb_i2c_writebyte(viaparinfo->chip_info->tmds_chip_info. + tmds_chip_slave_addr, index, + data); +} + +static int tmds_register_read(int index) +{ + u8 data; + + viaparinfo->i2c_stuff.i2c_port = + viaparinfo->chip_info->tmds_chip_info.i2c_port; + viafb_i2c_readbyte((u8) viaparinfo->chip_info-> + tmds_chip_info.tmds_chip_slave_addr, + (u8) index, &data); + return data; +} + +static int tmds_register_read_bytes(int index, u8 *buff, int buff_len) +{ + viaparinfo->i2c_stuff.i2c_port = + viaparinfo->chip_info->tmds_chip_info.i2c_port; + viafb_i2c_readbytes((u8) viaparinfo->chip_info->tmds_chip_info. + tmds_chip_slave_addr, (u8) index, buff, buff_len); + return 0; +} + +static int check_reduce_blanking_mode(int mode_index, + int refresh_rate) +{ + if (refresh_rate != 60) + return false; + + switch (mode_index) { + /* Following modes have reduce blanking mode. */ + case VIA_RES_1360X768: + case VIA_RES_1400X1050: + case VIA_RES_1440X900: + case VIA_RES_1600X900: + case VIA_RES_1680X1050: + case VIA_RES_1920X1080: + case VIA_RES_1920X1200: + break; + + default: + DEBUG_MSG(KERN_INFO + "This dvi mode %d have no reduce blanking mode!\n", + mode_index); + return false; + } + + return true; +} + +/* DVI Set Mode */ +void viafb_dvi_set_mode(int video_index, int mode_bpp, int set_iga) +{ + struct VideoModeTable *videoMode = NULL; + struct crt_mode_table *pDviTiming; + unsigned long desirePixelClock, maxPixelClock; + int status = 0; + videoMode = viafb_get_modetbl_pointer(video_index); + pDviTiming = videoMode->crtc; + desirePixelClock = pDviTiming->clk / 1000000; + maxPixelClock = (unsigned long)viaparinfo-> + tmds_setting_info->max_pixel_clock; + + DEBUG_MSG(KERN_INFO "\nDVI_set_mode!!\n"); + + if ((maxPixelClock != 0) && (desirePixelClock > maxPixelClock)) { + /*Check if reduce-blanking mode is exist */ + status = + check_reduce_blanking_mode(video_index, + pDviTiming->refresh_rate); + if (status) { + video_index += 100; /*Use reduce-blanking mode */ + videoMode = viafb_get_modetbl_pointer(video_index); + pDviTiming = videoMode->crtc; + DEBUG_MSG(KERN_INFO + "DVI use reduce blanking mode %d!!\n", + video_index); + } + } + viafb_fill_crtc_timing(pDviTiming, video_index, mode_bpp / 8, set_iga); + viafb_set_output_path(DEVICE_DVI, set_iga, + viaparinfo->chip_info->tmds_chip_info.output_interface); +} + +/* Sense DVI Connector */ +int viafb_dvi_sense(void) +{ + u8 RegSR1E = 0, RegSR3E = 0, RegCR6B = 0, RegCR91 = 0, + RegCR93 = 0, RegCR9B = 0, data; + int ret = false; + + DEBUG_MSG(KERN_INFO "viafb_dvi_sense!!\n"); + + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266) { + /* DI1 Pad on */ + RegSR1E = viafb_read_reg(VIASR, SR1E); + viafb_write_reg(SR1E, VIASR, RegSR1E | 0x30); + + /* CR6B[0]VCK Input Selection: 1 = External clock. */ + RegCR6B = viafb_read_reg(VIACR, CR6B); + viafb_write_reg(CR6B, VIACR, RegCR6B | 0x08); + + /* CR91[4] VDD On [3] Data On [2] VEE On [1] Back Light Off + [0] Software Control Power Sequence */ + RegCR91 = viafb_read_reg(VIACR, CR91); + viafb_write_reg(CR91, VIACR, 0x1D); + + /* CR93[7] DI1 Data Source Selection: 1 = DSP2. + CR93[5] DI1 Clock Source: 1 = internal. + CR93[4] DI1 Clock Polarity. + CR93[3:1] DI1 Clock Adjust. CR93[0] DI1 enable */ + RegCR93 = viafb_read_reg(VIACR, CR93); + viafb_write_reg(CR93, VIACR, 0x01); + } else { + /* DVP0/DVP1 Pad on */ + RegSR1E = viafb_read_reg(VIASR, SR1E); + viafb_write_reg(SR1E, VIASR, RegSR1E | 0xF0); + + /* SR3E[1]Multi-function selection: + 0 = Emulate I2C and DDC bus by GPIO2/3/4. */ + RegSR3E = viafb_read_reg(VIASR, SR3E); + viafb_write_reg(SR3E, VIASR, RegSR3E & (~0x20)); + + /* CR91[4] VDD On [3] Data On [2] VEE On [1] Back Light Off + [0] Software Control Power Sequence */ + RegCR91 = viafb_read_reg(VIACR, CR91); + viafb_write_reg(CR91, VIACR, 0x1D); + + /*CR9B[4] DVP1 Data Source Selection: 1 = From secondary + display.CR9B[2:0] DVP1 Clock Adjust */ + RegCR9B = viafb_read_reg(VIACR, CR9B); + viafb_write_reg(CR9B, VIACR, 0x01); + } + + data = (u8) tmds_register_read(0x09); + if (data & 0x04) + ret = true; + + if (ret == false) { + if (viafb_dvi_query_EDID()) + ret = true; + } + + /* Restore status */ + viafb_write_reg(SR1E, VIASR, RegSR1E); + viafb_write_reg(CR91, VIACR, RegCR91); + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266) { + viafb_write_reg(CR6B, VIACR, RegCR6B); + viafb_write_reg(CR93, VIACR, RegCR93); + } else { + viafb_write_reg(SR3E, VIASR, RegSR3E); + viafb_write_reg(CR9B, VIACR, RegCR9B); + } + + return ret; +} + +/* Query Flat Panel's EDID Table Version Through DVI Connector */ +static int viafb_dvi_query_EDID(void) +{ + u8 data0, data1; + int restore; + + DEBUG_MSG(KERN_INFO "viafb_dvi_query_EDID!!\n"); + + restore = viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr; + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = 0xA0; + + data0 = (u8) tmds_register_read(0x00); + data1 = (u8) tmds_register_read(0x01); + if ((data0 == 0) && (data1 == 0xFF)) { + viaparinfo->chip_info-> + tmds_chip_info.tmds_chip_slave_addr = restore; + return EDID_VERSION_1; /* Found EDID1 Table */ + } + + data0 = (u8) tmds_register_read(0x00); + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = restore; + if (data0 == 0x20) + return EDID_VERSION_2; /* Found EDID2 Table */ + else + return false; +} + +/* + * + * int dvi_get_panel_size_from_DDCv1(void) + * + * - Get Panel Size Using EDID1 Table + * + * Return Type: int + * + */ +static int dvi_get_panel_size_from_DDCv1(void) +{ + int i, max_h = 0, max_v = 0, tmp, restore; + unsigned char rData; + unsigned char EDID_DATA[18]; + + DEBUG_MSG(KERN_INFO "\n dvi_get_panel_size_from_DDCv1 \n"); + + restore = viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr; + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = 0xA0; + + rData = tmds_register_read(0x23); + if (rData & 0x3C) + max_h = 640; + if (rData & 0xC0) + max_h = 720; + if (rData & 0x03) + max_h = 800; + + rData = tmds_register_read(0x24); + if (rData & 0xC0) + max_h = 800; + if (rData & 0x1E) + max_h = 1024; + if (rData & 0x01) + max_h = 1280; + + for (i = 0x25; i < 0x6D; i++) { + switch (i) { + case 0x26: + case 0x28: + case 0x2A: + case 0x2C: + case 0x2E: + case 0x30: + case 0x32: + case 0x34: + rData = tmds_register_read(i); + if (rData == 1) + break; + /* data = (data + 31) * 8 */ + tmp = (rData + 31) << 3; + if (tmp > max_h) + max_h = tmp; + break; + + case 0x36: + case 0x48: + case 0x5A: + case 0x6C: + tmds_register_read_bytes(i, EDID_DATA, 10); + if (!(EDID_DATA[0] || EDID_DATA[1])) { + /* The first two byte must be zero. */ + if (EDID_DATA[3] == 0xFD) { + /* To get max pixel clock. */ + viaparinfo->tmds_setting_info-> + max_pixel_clock = EDID_DATA[9] * 10; + } + } + break; + + default: + break; + } + } + + switch (max_h) { + case 640: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_640X480; + break; + case 800: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_800X600; + break; + case 1024: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1024X768; + break; + case 1280: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1280X1024; + break; + case 1400: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1400X1050; + break; + case 1440: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1440X1050; + break; + case 1600: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1600X1200; + break; + case 1920: + if (max_v == 1200) { + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1920X1200; + } else { + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1920X1080; + } + + break; + default: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1024X768; + DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d !\ + set default panel size.\n", max_h); + break; + } + + DEBUG_MSG(KERN_INFO "DVI max pixelclock = %d\n", + viaparinfo->tmds_setting_info->max_pixel_clock); + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = restore; + return viaparinfo->tmds_setting_info->dvi_panel_size; +} + +/* + * + * int dvi_get_panel_size_from_DDCv2(void) + * + * - Get Panel Size Using EDID2 Table + * + * Return Type: int + * + */ +static int dvi_get_panel_size_from_DDCv2(void) +{ + int HSize = 0, restore; + unsigned char R_Buffer[2]; + + DEBUG_MSG(KERN_INFO "\n dvi_get_panel_size_from_DDCv2 \n"); + + restore = viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr; + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = 0xA2; + + /* Horizontal: 0x76, 0x77 */ + tmds_register_read_bytes(0x76, R_Buffer, 2); + HSize = R_Buffer[0]; + HSize += R_Buffer[1] << 8; + + switch (HSize) { + case 640: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_640X480; + break; + case 800: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_800X600; + break; + case 1024: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1024X768; + break; + case 1280: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1280X1024; + break; + case 1400: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1400X1050; + break; + case 1440: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1440X1050; + break; + case 1600: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1600X1200; + break; + default: + viaparinfo->tmds_setting_info->dvi_panel_size = + VIA_RES_1024X768; + DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d!\ + set default panel size.\n", HSize); + break; + } + + viaparinfo->chip_info->tmds_chip_info.tmds_chip_slave_addr = restore; + return viaparinfo->tmds_setting_info->dvi_panel_size; +} + +/* + * + * unsigned char dvi_get_panel_info(void) + * + * - Get Panel Size + * + * Return Type: unsigned char + */ +static unsigned char dvi_get_panel_info(void) +{ + unsigned char dvipanelsize; + DEBUG_MSG(KERN_INFO "dvi_get_panel_info! \n"); + + viafb_dvi_sense(); + switch (viafb_dvi_query_EDID()) { + case 1: + dvi_get_panel_size_from_DDCv1(); + break; + case 2: + dvi_get_panel_size_from_DDCv2(); + break; + default: + break; + } + + DEBUG_MSG(KERN_INFO "dvi panel size is %2d \n", + viaparinfo->tmds_setting_info->dvi_panel_size); + dvipanelsize = (unsigned char)(viaparinfo-> + tmds_setting_info->dvi_panel_size); + return dvipanelsize; +} + +/* If Disable DVI, turn off pad */ +void viafb_dvi_disable(void) +{ + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DVP0) + viafb_write_reg(SR1E, VIASR, + viafb_read_reg(VIASR, SR1E) & (~0xC0)); + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DVP1) + viafb_write_reg(SR1E, VIASR, + viafb_read_reg(VIASR, SR1E) & (~0x30)); + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DFP_HIGH) + viafb_write_reg(SR2A, VIASR, + viafb_read_reg(VIASR, SR2A) & (~0x0C)); + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DFP_LOW) + viafb_write_reg(SR2A, VIASR, + viafb_read_reg(VIASR, SR2A) & (~0x03)); + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_TMDS) + /* Turn off TMDS power. */ + viafb_write_reg(CRD2, VIACR, + viafb_read_reg(VIACR, CRD2) | 0x08); +} + +/* If Enable DVI, turn off pad */ +void viafb_dvi_enable(void) +{ + u8 data; + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DVP0) { + viafb_write_reg(SR1E, VIASR, + viafb_read_reg(VIASR, SR1E) | 0xC0); + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266) + tmds_register_write(0x88, 0x3b); + else + /*clear CR91[5] to direct on display period + in the secondary diplay path */ + viafb_write_reg(CR91, VIACR, + viafb_read_reg(VIACR, CR91) & 0xDF); + } + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DVP1) { + viafb_write_reg(SR1E, VIASR, + viafb_read_reg(VIASR, SR1E) | 0x30); + + /*fix dvi cann't be enabled with MB VT5718C4 - Al Zhang */ + if (viaparinfo->chip_info->gfx_chip_name == UNICHROME_CLE266) { + tmds_register_write(0x88, 0x3b); + } else { + /*clear CR91[5] to direct on display period + in the secondary diplay path */ + viafb_write_reg(CR91, VIACR, + viafb_read_reg(VIACR, CR91) & 0xDF); + } + + /*fix DVI cannot enable on EPIA-M board */ + if (viafb_platform_epia_dvi == 1) { + viafb_write_reg_mask(CR91, VIACR, 0x1f, 0x1f); + viafb_write_reg_mask(CR88, VIACR, 0x00, BIT6 + BIT0); + if (viafb_bus_width == 24) { + if (viafb_device_lcd_dualedge == 1) + data = 0x3F; + else + data = 0x37; + viafb_i2c_writebyte(viaparinfo->chip_info-> + tmds_chip_info. + tmds_chip_slave_addr, + 0x08, data); + } + } + } + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DFP_HIGH) { + viafb_write_reg(SR2A, VIASR, + viafb_read_reg(VIASR, SR2A) | 0x0C); + viafb_write_reg(CR91, VIACR, + viafb_read_reg(VIACR, CR91) & 0xDF); + } + + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_DFP_LOW) { + viafb_write_reg(SR2A, VIASR, + viafb_read_reg(VIASR, SR2A) | 0x03); + viafb_write_reg(CR91, VIACR, + viafb_read_reg(VIACR, CR91) & 0xDF); + } + if (viaparinfo->chip_info-> + tmds_chip_info.output_interface == INTERFACE_TMDS) { + /* Turn on Display period in the panel path. */ + viafb_write_reg_mask(CR91, VIACR, 0, BIT7); + + /* Turn on TMDS power. */ + viafb_write_reg_mask(CRD2, VIACR, 0, BIT3); + } +} + |