162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SiS 300/540/630[S]/730[S] 462306a36Sopenharmony_ci * SiS 315[E|PRO]/550/[M]65x/[M]66x[F|M|G]X/[M]74x[GX]/330/[M]76x[GX] 562306a36Sopenharmony_ci * XGI V3XT/V5/V8, Z7 662306a36Sopenharmony_ci * frame buffer driver for Linux kernels >= 2.4.14 and >=2.6.3 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Linux kernel specific extensions to init.c/init301.c 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2001-2005 Thomas Winischhofer, Vienna, Austria. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Author: Thomas Winischhofer <thomas@winischhofer.net> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "initdef.h" 1662306a36Sopenharmony_ci#include "vgatypes.h" 1762306a36Sopenharmony_ci#include "vstruct.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/fb.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciint sisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr, 2362306a36Sopenharmony_ci unsigned char modeno, unsigned char rateindex); 2462306a36Sopenharmony_ciint sisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno, 2562306a36Sopenharmony_ci unsigned char rateindex, struct fb_var_screeninfo *var); 2662306a36Sopenharmony_cibool sisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno, 2762306a36Sopenharmony_ci int *htotal, int *vtotal, unsigned char rateindex); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciextern bool SiSInitPtr(struct SiS_Private *SiS_Pr); 3062306a36Sopenharmony_ciextern bool SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *ModeNo, 3162306a36Sopenharmony_ci unsigned short *ModeIdIndex); 3262306a36Sopenharmony_ciextern void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata, 3362306a36Sopenharmony_ci int xres, int yres, struct fb_var_screeninfo *var, bool writeres); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciint 3662306a36Sopenharmony_cisisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr, unsigned char modeno, 3762306a36Sopenharmony_ci unsigned char rateindex) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci unsigned short ModeNo = modeno; 4062306a36Sopenharmony_ci unsigned short ModeIdIndex = 0, ClockIndex = 0; 4162306a36Sopenharmony_ci unsigned short RRTI = 0; 4262306a36Sopenharmony_ci int Clock; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if(!SiSInitPtr(SiS_Pr)) return 65000; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if(rateindex > 0) rateindex--; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#ifdef CONFIG_FB_SIS_315 4962306a36Sopenharmony_ci switch(ModeNo) { 5062306a36Sopenharmony_ci case 0x5a: ModeNo = 0x50; break; 5162306a36Sopenharmony_ci case 0x5b: ModeNo = 0x56; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci#endif 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if(!(SiS_SearchModeID(SiS_Pr, &ModeNo, &ModeIdIndex))) { 5662306a36Sopenharmony_ci printk(KERN_ERR "Could not find mode %x\n", ModeNo); 5762306a36Sopenharmony_ci return 65000; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci RRTI = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & HaveWideTiming) { 6362306a36Sopenharmony_ci if(SiS_Pr->SiS_UseWide == 1) { 6462306a36Sopenharmony_ci /* Wide screen: Ignore rateindex */ 6562306a36Sopenharmony_ci ClockIndex = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRTVCLK_WIDE; 6662306a36Sopenharmony_ci } else { 6762306a36Sopenharmony_ci RRTI += rateindex; 6862306a36Sopenharmony_ci ClockIndex = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRTVCLK_NORM; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } else { 7162306a36Sopenharmony_ci RRTI += rateindex; 7262306a36Sopenharmony_ci ClockIndex = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRTVCLK; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci Clock = SiS_Pr->SiS_VCLKData[ClockIndex].CLOCK * 1000; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return Clock; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint 8162306a36Sopenharmony_cisisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno, 8262306a36Sopenharmony_ci unsigned char rateindex, struct fb_var_screeninfo *var) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci unsigned short ModeNo = modeno; 8562306a36Sopenharmony_ci unsigned short ModeIdIndex = 0, index = 0, RRTI = 0; 8662306a36Sopenharmony_ci int j; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if(!SiSInitPtr(SiS_Pr)) return 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if(rateindex > 0) rateindex--; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#ifdef CONFIG_FB_SIS_315 9362306a36Sopenharmony_ci switch(ModeNo) { 9462306a36Sopenharmony_ci case 0x5a: ModeNo = 0x50; break; 9562306a36Sopenharmony_ci case 0x5b: ModeNo = 0x56; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci#endif 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if(!(SiS_SearchModeID(SiS_Pr, &ModeNo, &ModeIdIndex))) return 0; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci RRTI = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex; 10262306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & HaveWideTiming) { 10362306a36Sopenharmony_ci if(SiS_Pr->SiS_UseWide == 1) { 10462306a36Sopenharmony_ci /* Wide screen: Ignore rateindex */ 10562306a36Sopenharmony_ci index = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRT1CRTC_WIDE; 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci RRTI += rateindex; 10862306a36Sopenharmony_ci index = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRT1CRTC_NORM; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } else { 11162306a36Sopenharmony_ci RRTI += rateindex; 11262306a36Sopenharmony_ci index = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRT1CRTC; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci SiS_Generic_ConvertCRData(SiS_Pr, 11662306a36Sopenharmony_ci (unsigned char *)&SiS_Pr->SiS_CRT1Table[index].CR[0], 11762306a36Sopenharmony_ci SiS_Pr->SiS_RefIndex[RRTI].XRes, 11862306a36Sopenharmony_ci SiS_Pr->SiS_RefIndex[RRTI].YRes, 11962306a36Sopenharmony_ci var, false); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & 0x8000) 12262306a36Sopenharmony_ci var->sync &= ~FB_SYNC_VERT_HIGH_ACT; 12362306a36Sopenharmony_ci else 12462306a36Sopenharmony_ci var->sync |= FB_SYNC_VERT_HIGH_ACT; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & 0x4000) 12762306a36Sopenharmony_ci var->sync &= ~FB_SYNC_HOR_HIGH_ACT; 12862306a36Sopenharmony_ci else 12962306a36Sopenharmony_ci var->sync |= FB_SYNC_HOR_HIGH_ACT; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 13262306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & 0x0080) 13362306a36Sopenharmony_ci var->vmode = FB_VMODE_INTERLACED; 13462306a36Sopenharmony_ci else { 13562306a36Sopenharmony_ci j = 0; 13662306a36Sopenharmony_ci while(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID != 0xff) { 13762306a36Sopenharmony_ci if(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID == 13862306a36Sopenharmony_ci SiS_Pr->SiS_RefIndex[RRTI].ModeID) { 13962306a36Sopenharmony_ci if(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeFlag & DoubleScanMode) { 14062306a36Sopenharmony_ci var->vmode = FB_VMODE_DOUBLE; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci j++; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { 14962306a36Sopenharmony_ci#if 0 /* Do this? */ 15062306a36Sopenharmony_ci var->upper_margin <<= 1; 15162306a36Sopenharmony_ci var->lower_margin <<= 1; 15262306a36Sopenharmony_ci var->vsync_len <<= 1; 15362306a36Sopenharmony_ci#endif 15462306a36Sopenharmony_ci } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { 15562306a36Sopenharmony_ci var->upper_margin >>= 1; 15662306a36Sopenharmony_ci var->lower_margin >>= 1; 15762306a36Sopenharmony_ci var->vsync_len >>= 1; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return 1; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cibool 16462306a36Sopenharmony_cisisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno, int *htotal, 16562306a36Sopenharmony_ci int *vtotal, unsigned char rateindex) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci unsigned short ModeNo = modeno; 16862306a36Sopenharmony_ci unsigned short ModeIdIndex = 0, CRT1Index = 0; 16962306a36Sopenharmony_ci unsigned short RRTI = 0; 17062306a36Sopenharmony_ci unsigned char sr_data, cr_data, cr_data2; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if(!SiSInitPtr(SiS_Pr)) return false; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if(rateindex > 0) rateindex--; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#ifdef CONFIG_FB_SIS_315 17762306a36Sopenharmony_ci switch(ModeNo) { 17862306a36Sopenharmony_ci case 0x5a: ModeNo = 0x50; break; 17962306a36Sopenharmony_ci case 0x5b: ModeNo = 0x56; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci#endif 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if(!(SiS_SearchModeID(SiS_Pr, &ModeNo, &ModeIdIndex))) return false; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci RRTI = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].REFindex; 18662306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & HaveWideTiming) { 18762306a36Sopenharmony_ci if(SiS_Pr->SiS_UseWide == 1) { 18862306a36Sopenharmony_ci /* Wide screen: Ignore rateindex */ 18962306a36Sopenharmony_ci CRT1Index = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRT1CRTC_WIDE; 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci RRTI += rateindex; 19262306a36Sopenharmony_ci CRT1Index = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRT1CRTC_NORM; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci RRTI += rateindex; 19662306a36Sopenharmony_ci CRT1Index = SiS_Pr->SiS_RefIndex[RRTI].Ext_CRT1CRTC; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci sr_data = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14]; 20062306a36Sopenharmony_ci cr_data = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[0]; 20162306a36Sopenharmony_ci *htotal = (((cr_data & 0xff) | ((unsigned short) (sr_data & 0x03) << 8)) + 5) * 8; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci sr_data = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[13]; 20462306a36Sopenharmony_ci cr_data = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[6]; 20562306a36Sopenharmony_ci cr_data2 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[7]; 20662306a36Sopenharmony_ci *vtotal = ((cr_data & 0xFF) | 20762306a36Sopenharmony_ci ((unsigned short)(cr_data2 & 0x01) << 8) | 20862306a36Sopenharmony_ci ((unsigned short)(cr_data2 & 0x20) << 4) | 20962306a36Sopenharmony_ci ((unsigned short)(sr_data & 0x01) << 10)) + 2; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if(SiS_Pr->SiS_RefIndex[RRTI].Ext_InfoFlag & InterlaceMode) 21262306a36Sopenharmony_ci *vtotal *= 2; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return true; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci 219