1/*
2	Copyright (c) 2002-2004, Thomas Kurschel
3
4
5	Part of Radeon accelerant
6
7	Hardware cursor support.
8*/
9
10
11#include "radeon_accelerant.h"
12#include "GlobalData.h"
13#include "generic.h"
14#include "mmio.h"
15#include "crtc_regs.h"
16
17static void moveOneCursor( accelerator_info *ai, int crtc_idx, int x, int y );
18
19// set standard foreground/background colours
20void Radeon_SetCursorColors( accelerator_info *ai, int crtc_idx )
21{
22	SHOW_FLOW0( 3, "" );
23
24	if( crtc_idx == 0 ) {
25		OUTREG( ai->regs, RADEON_CUR_CLR0, 0xffffff );
26		OUTREG( ai->regs, RADEON_CUR_CLR1, 0 );
27	} else {
28		OUTREG( ai->regs, RADEON_CUR2_CLR0, 0xffffff );
29		OUTREG( ai->regs, RADEON_CUR2_CLR1, 0 );
30	}
31}
32
33// public function to set shape of cursor
34status_t SET_CURSOR_SHAPE( uint16 width, uint16 height, uint16 hot_x, uint16 hot_y,
35						   uint8 *andMask, uint8 *xorMask)
36{
37	virtual_card *vc = ai->vc;
38	uint8 *fb_cursor = vc->cursor.data;
39	int row, col_byte;
40
41	/* NOTE: Currently, for BeOS, cursor width and height must be equal to 16. */
42/*	if( width != 16 || height != 16 )
43		return B_ERROR;*/
44
45	if( hot_x >= width || hot_y >= height )
46		return B_ERROR;
47
48	// TBD: should we sync here? I'd say so, but if I fail, we deadlock
49
50	vc->cursor.hot_x = hot_x;
51	vc->cursor.hot_y = hot_y;
52
53	for( row = 0; row < 64; ++row ) {
54		for( col_byte = 0; col_byte < 64 / 8; ++col_byte ) {
55			if( row < height && col_byte < (width + 7) / 8 ) {
56				fb_cursor[row * 64/8 * 2 + col_byte] = *andMask++;
57				fb_cursor[row * 64/8 * 2 + col_byte + 64/8] = *xorMask++;
58			} else {
59				fb_cursor[row * 64/8 * 2 + col_byte] = 0xff;
60				fb_cursor[row * 64/8 * 2 + col_byte + 64/8] = 0;
61			}
62		}
63	}
64
65	return B_OK;
66}
67
68
69// public function to move cursor
70void MOVE_CURSOR(uint16 x, uint16 y)
71{
72	virtual_card *vc = ai->vc;
73	bool move_screen = false;
74	uint16 hds, vds;
75
76	// alignment mask for horizontal position
77	uint16 h_adjust = 7;
78
79	ACQUIRE_BEN( ai->si->engine.lock );
80
81	hds = vc->mode.h_display_start;
82	vds = vc->mode.v_display_start;
83
84	// clamp cursor (negative positions are impossible due to uint16)
85	if (x >= vc->mode.virtual_width)
86		x = vc->mode.virtual_width - 1;
87	if (y >= vc->mode.virtual_height)
88		y = vc->mode.virtual_height - 1;
89
90	// if scrolling enabled, i.e. we have a larger virtual screen,
91	// pan display accordingly
92	if( vc->scroll ) {
93		if( x >= (vc->mode.timing.h_display + hds) ) {
94			hds = ((x - vc->mode.timing.h_display) + 1 + h_adjust) & ~h_adjust;
95			move_screen = true;
96		} else if( x < hds ) {
97			hds = x & ~h_adjust;
98			move_screen = true;
99		}
100		if( y >= (vc->mode.timing.v_display + vds) ) {
101			vds = y - vc->mode.timing.v_display + 1;
102			move_screen = true;
103		} else if( y < vds ) {
104			vds = y;
105			move_screen = true;
106		}
107
108		if( move_screen )
109			Radeon_MoveDisplay( ai, hds, vds );
110	}
111
112	// adjust according to virtual screen position
113	x -= hds;
114	y -= vds;
115
116	// go
117	if( vc->used_crtc[0] )
118		moveOneCursor( ai, 0, x, y );
119	if( vc->used_crtc[1] )
120		moveOneCursor( ai, 1, x, y );
121
122	RELEASE_BEN( ai->si->engine.lock );
123}
124
125
126// public function to show cursor
127void SHOW_CURSOR( bool is_visible )
128{
129	virtual_card *vc = ai->vc;
130
131	SHOW_FLOW0( 4, "" );
132
133	ACQUIRE_BEN( ai->si->engine.lock );
134
135	// this is the public statement
136	vc->cursor.is_visible = is_visible;
137
138	// the following functions take also care to not
139	// show the cursor if it's on the other port
140	if( vc->used_crtc[0] )
141		Radeon_ShowCursor( ai, 0 );
142	if( vc->used_crtc[1] )
143		Radeon_ShowCursor( ai, 1 );
144
145	RELEASE_BEN( ai->si->engine.lock );
146}
147
148
149// move cursor on one port
150//	main_port - common data is stored here
151void moveOneCursor( accelerator_info *ai, int crtc_idx, int x, int y )
152{
153	virtual_card *vc = ai->vc;
154	crtc_info *crtc = &ai->si->crtc[crtc_idx];
155	int xorigin, yorigin;
156	bool prev_state;
157
158	// adjust according to relative screen position
159	x -= crtc->rel_x;
160	y -= crtc->rel_y;
161
162	// and to hot spot
163	x -= vc->cursor.hot_x;
164	y -= vc->cursor.hot_y;
165
166	// check whether the cursor is (partially) visible on this screen
167   	prev_state = crtc->cursor_on_screen;
168	crtc->cursor_on_screen = true;
169
170	// in theory, cursor can be up to 64 pixels off screen,
171	// but there were display errors
172	if( y > crtc->mode.timing.v_display ||
173		x > crtc->mode.timing.h_display ||
174		x <= -16 || y <= -16 )
175	{
176		crtc->cursor_on_screen = false;
177	}
178
179	if( prev_state != crtc->cursor_on_screen )
180		Radeon_ShowCursor( ai, crtc_idx );
181
182	if( !crtc->cursor_on_screen )
183		return;
184
185	// if upper-left corner of cursor is outside of
186	// screen, we have to use special registers to clip it
187	xorigin = 0;
188	yorigin = 0;
189
190	if( x < 0 )
191		xorigin = -x;
192
193	if( y < 0 )
194		yorigin = -y;
195
196	if( crtc_idx == 0 )	{
197		OUTREG( ai->regs, RADEON_CUR_HORZ_VERT_OFF, RADEON_CUR_LOCK
198			| (xorigin << 16)
199			| yorigin );
200		OUTREG( ai->regs, RADEON_CUR_HORZ_VERT_POSN, RADEON_CUR_LOCK
201			| ((xorigin ? 0 : x) << 16)
202			| (yorigin ? 0 : y) );
203		OUTREG( ai->regs, RADEON_CUR_OFFSET,
204			vc->cursor.fb_offset + xorigin + yorigin * 16 );
205	} else {
206		OUTREG( ai->regs, RADEON_CUR2_HORZ_VERT_OFF, RADEON_CUR2_LOCK
207			| (xorigin << 16)
208			| yorigin );
209		OUTREG( ai->regs, RADEON_CUR2_HORZ_VERT_POSN, RADEON_CUR2_LOCK
210			| ((xorigin ? 0 : x) << 16)
211			| (yorigin ? 0 : y) );
212		OUTREG( ai->regs, RADEON_CUR2_OFFSET,
213			vc->cursor.fb_offset + xorigin + yorigin * 16 );
214	}
215}
216
217
218// show cursor on one port, depending on official whishes and whether
219// cursor is located on this subscreen
220void Radeon_ShowCursor( accelerator_info *ai, int crtc_idx )
221{
222	virtual_card *vc = ai->vc;
223	crtc_info *crtc = &ai->si->crtc[crtc_idx];
224	uint32 tmp;
225
226	if( crtc_idx == 0 ) {
227		tmp = INREG( ai->regs, RADEON_CRTC_GEN_CNTL );
228
229		if( vc->cursor.is_visible && crtc->cursor_on_screen ) {
230			tmp |= RADEON_CRTC_CUR_EN;
231		} else {
232			tmp &= ~RADEON_CRTC_CUR_EN;
233		}
234
235		OUTREG( ai->regs, RADEON_CRTC_GEN_CNTL, tmp );
236
237	} else {
238		tmp = INREG( ai->regs, RADEON_CRTC2_GEN_CNTL );
239
240		if( vc->cursor.is_visible && crtc->cursor_on_screen )
241			tmp |= RADEON_CRTC2_CUR_EN;
242		else
243			tmp &= ~RADEON_CRTC2_CUR_EN;
244
245		OUTREG( ai->regs, RADEON_CRTC2_GEN_CNTL, tmp );
246	}
247}
248