1 /* 2 Copyright (c) 2002, Thomas Kurschel 3 4 5 Part of Radeon accelerant 6 7 Flat panel support 8 */ 9 10 #include "radeon_accelerant.h" 11 #include <malloc.h> 12 #include "mmio.h" 13 #include "fp_regs.h" 14 #include "ddc_regs.h" 15 #include "utils.h" 16 #include "ddc.h" 17 18 // calculcate flat panel crtc registers 19 void Radeon_CalcFPRegisters( accelerator_info *ai, virtual_port *port, fp_info *fp_port, display_mode *mode, port_regs *values ) 20 { 21 vuint8 *regs = ai->regs; 22 uint xres = mode->timing.h_display; 23 uint yres = mode->timing.v_display; 24 uint64 Hratio, Vratio; 25 26 // we read old values first, as we only want to change 27 // some bits of them 28 // (in general, we could setup all of them, but noone 29 // else does it, so we don't mess around with them as well) 30 values->fp_gen_cntl = INREG( regs, RADEON_FP_GEN_CNTL ); 31 values->fp_horz_stretch = INREG( regs, RADEON_FP_HORZ_STRETCH ); 32 values->fp_vert_stretch = INREG( regs, RADEON_FP_VERT_STRETCH ); 33 values->lvds_gen_cntl = INREG( regs, RADEON_LVDS_GEN_CNTL ); 34 35 SHOW_FLOW( 2, "before: fp_gen_cntl=%lx, horz=%lx, vert=%lx, lvds_gen_cntl=%lx", 36 values->fp_gen_cntl, values->fp_horz_stretch, values->fp_vert_stretch, 37 values->lvds_gen_cntl ); 38 39 if( xres > fp_port->panel_xres ) 40 xres = fp_port->panel_xres; 41 if( yres > fp_port->panel_yres ) 42 yres = fp_port->panel_yres; 43 44 // ouch: we must not use floating point in kernel, 45 // we obey and use fixed point instead 46 Hratio = FIX_SCALE * (uint32)xres / fp_port->panel_xres; 47 Vratio = FIX_SCALE * (uint32)yres / fp_port->panel_yres; 48 49 fp_port->h_ratio = Hratio; 50 fp_port->v_ratio = Vratio; 51 52 if( Hratio == FIX_SCALE ) { 53 values->fp_horz_stretch &= 54 ~(RADEON_HORZ_STRETCH_BLEND | 55 RADEON_HORZ_STRETCH_ENABLE); 56 } else { 57 uint32 stretch; 58 59 stretch = (uint32)((Hratio * RADEON_HORZ_STRETCH_RATIO_MAX + 60 FIX_SCALE / 2) >> FIX_SHIFT) & RADEON_HORZ_STRETCH_RATIO_MASK; 61 62 values->fp_horz_stretch = stretch 63 | (values->fp_horz_stretch & (RADEON_HORZ_PANEL_SIZE | 64 RADEON_HORZ_FP_LOOP_STRETCH | 65 RADEON_HORZ_AUTO_RATIO_INC)); 66 values->fp_horz_stretch |= 67 RADEON_HORZ_STRETCH_BLEND | 68 RADEON_HORZ_STRETCH_ENABLE; 69 } 70 values->fp_horz_stretch &= ~RADEON_HORZ_AUTO_RATIO; 71 72 if( Vratio == FIX_SCALE ) { 73 values->fp_vert_stretch &= 74 ~(RADEON_VERT_STRETCH_ENABLE | 75 RADEON_VERT_STRETCH_BLEND); 76 } else { 77 uint32 stretch; 78 79 stretch = (uint32)((Vratio * RADEON_VERT_STRETCH_RATIO_MAX + 80 FIX_SCALE / 2) >> FIX_SHIFT) & RADEON_VERT_STRETCH_RATIO_MASK; 81 82 values->fp_vert_stretch = stretch 83 | (values->fp_vert_stretch & (RADEON_VERT_PANEL_SIZE | 84 RADEON_VERT_STRETCH_RESERVED)); 85 values->fp_vert_stretch |= 86 RADEON_VERT_STRETCH_ENABLE | 87 RADEON_VERT_STRETCH_BLEND; 88 } 89 values->fp_vert_stretch &= ~RADEON_VERT_AUTO_RATIO_EN; 90 91 values->fp_gen_cntl = values->fp_gen_cntl & (uint32) 92 ~(RADEON_FP_SEL_CRTC2 | 93 RADEON_FP_RMX_HVSYNC_CONTROL_EN | 94 RADEON_FP_DFP_SYNC_SEL | 95 RADEON_FP_CRT_SYNC_SEL | 96 RADEON_FP_CRTC_LOCK_8DOT | 97 RADEON_FP_USE_SHADOW_EN | 98 RADEON_FP_CRTC_USE_SHADOW_VEND | 99 RADEON_FP_CRT_SYNC_ALT); 100 values->fp_gen_cntl |= 101 RADEON_FP_CRTC_DONT_SHADOW_VPAR | 102 RADEON_FP_CRTC_DONT_SHADOW_HEND; 103 104 values->fp_gen_cntl |= port->is_crtc2 ? RADEON_FP_SEL_CRTC2 : 0; 105 /* values->fp_gen_cntl |= RADEON_FP_SEL_CRTC2; 106 values->fp_gen_cntl &= ~RADEON_FP_USE_SHADOW_EN;*/ 107 108 SHOW_FLOW( 3, "FP2: %d", INREG( ai->regs, RADEON_FP2_GEN_CNTL )); 109 110 if( fp_port->disp_type == dt_lvds ) { 111 values->lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_BLON); 112 values->fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); 113 } else if( fp_port->disp_type == dt_dvi_1 ) { 114 values->fp_gen_cntl |= (RADEON_FP_FPON | RADEON_FP_TMDS_EN); 115 // enabling 8 bit data may be dangerous; BIOS should have taken care of that 116 values->fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; 117 } 118 119 /*values->fp_gen_cntl = RADEON_FP_SEL_CRTC2 120 | RADEON_FP_CRTC_LOCK_8DOT;*/ 121 122 SHOW_FLOW( 2, "after: fp_gen_cntl=%lx, horz=%lx, vert=%lx, lvds_gen_cntl=%lx", 123 values->fp_gen_cntl, values->fp_horz_stretch, values->fp_vert_stretch, 124 values->lvds_gen_cntl ); 125 } 126 127 // write flat panel registers 128 void Radeon_ProgramFPRegisters( accelerator_info *ai, fp_info *fp_port, port_regs *values ) 129 { 130 uint32 tmp; 131 vuint8 *regs = ai->regs; 132 133 SHOW_FLOW0( 2, "" ); 134 135 OUTREG( regs, RADEON_FP_HORZ_STRETCH, values->fp_horz_stretch ); 136 OUTREG( regs, RADEON_FP_VERT_STRETCH, values->fp_vert_stretch ); 137 OUTREG( regs, RADEON_FP_GEN_CNTL, values->fp_gen_cntl ); 138 139 if( fp_port->disp_type == dt_lvds ) { 140 tmp = INREG( regs, RADEON_LVDS_GEN_CNTL ); 141 142 SHOW_FLOW( 3, "old: %x, new: %x", tmp, values->lvds_gen_cntl ); 143 144 if((tmp & (RADEON_LVDS_ON | RADEON_LVDS_BLON)) == 145 (values->lvds_gen_cntl & (RADEON_LVDS_ON | RADEON_LVDS_BLON)) ) 146 { 147 SHOW_FLOW0( 3, "Write through" ); 148 OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl ); 149 } else { 150 if( values->lvds_gen_cntl & (RADEON_LVDS_ON | RADEON_LVDS_BLON) ) { 151 SHOW_FLOW0( 3, "Switching off" ); 152 //snooze( fp_port->panel_pwr_delay * 1000); 153 OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl ); 154 } else { 155 SHOW_FLOW0( 3, "Switching on" ); 156 OUTREG( regs, RADEON_LVDS_GEN_CNTL, 157 values->lvds_gen_cntl | RADEON_LVDS_BLON ); 158 //snooze( fp_port->panel_pwr_delay * 1000 ); 159 OUTREG( regs, RADEON_LVDS_GEN_CNTL, values->lvds_gen_cntl ); 160 } 161 } 162 } 163 } 164 165 typedef struct { 166 accelerator_info *ai; 167 uint32 port; 168 } ddc_port_info; 169 170 static status_t get_signals( void *cookie, int *clk, int *data ) 171 { 172 ddc_port_info *info = (ddc_port_info *)cookie; 173 vuint8 *regs = info->ai->regs; 174 uint32 value; 175 176 value = INREG( regs, info->port ); 177 178 *clk = (value >> RADEON_GPIO_Y_SHIFT_1) & 1; 179 *data = (value >> RADEON_GPIO_Y_SHIFT_0) & 1; 180 181 return B_OK; 182 } 183 184 static status_t set_signals( void *cookie, int clk, int data ) 185 { 186 ddc_port_info *info = (ddc_port_info *)cookie; 187 vuint8 *regs = info->ai->regs; 188 uint32 value; 189 190 value = INREG( regs, info->port ); 191 value &= ~(RADEON_GPIO_A_1 | RADEON_GPIO_A_0); 192 value &= ~(RADEON_GPIO_EN_0 | RADEON_GPIO_EN_1); 193 value |= ((1-clk) << RADEON_GPIO_EN_SHIFT_1) | ((1-data) << RADEON_GPIO_EN_SHIFT_0); 194 195 OUTREG( regs, info->port, value ); 196 197 return B_OK; 198 } 199 200 // read edid data of flat panel and setup its timing accordingly 201 status_t Radeon_ReadFPEDID( accelerator_info *ai, shared_info *si ) 202 { 203 i2c_bus bus; 204 ddc_port_info info; 205 edid1_info edid; 206 fp_info *fp = &si->fp_port; 207 status_t res; 208 void *vdif; 209 size_t vdif_len; 210 uint32 max_hsize, max_vsize; 211 int i; 212 213 info.ai = ai; 214 info.port = RADEON_GPIO_DVI_DDC; 215 // info.port = RADEON_GPIO_VGA_DDC; 216 217 bus.cookie = &info; 218 bus.set_signals = &set_signals; 219 bus.get_signals = &get_signals; 220 221 // get edid 222 res = ddc2_read_edid1( &bus, &edid, &vdif, &vdif_len ); 223 if( res != B_OK ) 224 return res; 225 226 if( vdif != NULL ) 227 free( vdif ); 228 229 SHOW_FLOW0( 2, "EDID data read from DVI port via DDC2:" ); 230 edid_dump( &edid ); 231 232 // find detailed timing with maximum resolution 233 max_hsize = max_vsize = 0; 234 235 for( i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; ++i ) { 236 if( edid.detailed_monitor[i].monitor_desc_type == edid1_is_detailed_timing ) { 237 edid1_detailed_timing *timing = &edid.detailed_monitor[i].data.detailed_timing; 238 239 if( timing->h_size > max_hsize && timing->v_size > max_vsize ) { 240 SHOW_FLOW( 2, "Found DDC data for mode %dx%d", 241 (int)timing->h_active, (int)timing->v_active ); 242 243 max_hsize = timing->h_active; 244 max_vsize = timing->v_active; 245 246 // copy it to timing specification 247 fp->panel_xres = timing->h_active; 248 fp->h_blank = timing->h_blank; 249 fp->h_over_plus = timing->h_sync_off; 250 fp->h_sync_width = timing->h_sync_width; 251 252 fp->panel_yres = timing->v_active; 253 fp->v_blank = timing->v_blank; 254 fp->v_over_plus = timing->v_sync_off; 255 fp->v_sync_width = timing->v_sync_width; 256 257 // BeOS uses kHz, but the timing is in 10 kHz 258 fp->dot_clock = timing->pixel_clock * 10; 259 } 260 } 261 } 262 263 if( max_hsize == 0 ) 264 return B_ERROR; 265 266 SHOW_INFO( 2, "h_disp=%d, h_blank=%d, h_over_plus=%d, h_sync_width=%d", 267 fp->panel_xres, fp->h_blank, fp->h_over_plus, fp->h_sync_width ); 268 SHOW_INFO( 2, "v_disp=%d, v_blank=%d, v_over_plus=%d, v_sync_width=%d", 269 fp->panel_yres, fp->v_blank, fp->v_over_plus, fp->v_sync_width ); 270 SHOW_INFO( 2, "pixel_clock=%d kHz", fp->dot_clock ); 271 272 return B_OK; 273 } 274