xref: /haiku/src/add-ons/accelerants/radeon/flat_panel.c (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
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