1/*
2Copyright (C) 1994-1999 The XFree86 Project, Inc.  All Rights Reserved.
3Copyright (C) 2000 Silicon Motion, Inc.  All Rights Reserved.
4Copyright (C) 2008 Mandriva Linux.  All Rights Reserved.
5Copyright (C) 2008 Francisco Jerez.  All Rights Reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining a copy of
8this software and associated documentation files (the "Software"), to deal in
9the Software without restriction, including without limitation the rights to
10use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11of the Software, and to permit persons to whom the Software is furnished to do
12so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
19NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the names of The XFree86 Project and
25Silicon Motion shall not be used in advertising or otherwise to promote the
26sale, use or other dealings in this Software without prior written
27authorization from The XFree86 Project or Silicon Motion.
28*/
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include "smi.h"
35#include "smi_crtc.h"
36#include "smi_501.h"
37
38/* Want to see register dumps for now */
39#undef VERBLEV
40#define VERBLEV		1
41
42/*
43 * Prototypes
44 */
45static void SMI501_CrtcHideCursor(xf86CrtcPtr crtc);
46
47/*
48 * Implementation
49 */
50static void
51SMI501_CrtcVideoInit_lcd(xf86CrtcPtr crtc)
52{
53    ScrnInfoPtr pScrn=crtc->scrn;
54    SMIPtr pSmi = SMIPTR(pScrn);
55    MSOCRegPtr mode = pSmi->mode;
56    int		pitch, width;
57
58    ENTER();
59
60    if (!pSmi->HwCursor)
61	SMI501_CrtcHideCursor(crtc);
62
63    mode->panel_display_ctl.value = READ_SCR(pSmi, PANEL_DISPLAY_CTL);
64    mode->panel_fb_width.value = READ_SCR(pSmi, PANEL_FB_WIDTH);
65
66    mode->panel_display_ctl.f.format =
67	pScrn->bitsPerPixel == 8 ? 0 :
68	pScrn->bitsPerPixel == 16 ? 1 : 2;
69
70    pitch = (((crtc->rotatedData? crtc->mode.HDisplay : pScrn->displayWidth) *
71	      pSmi->Bpp) + 15) & ~15;
72    width = ((crtc->mode.HDisplay * pSmi->Bpp) + 15) & ~ 15;
73
74    /* >> 4 because of the "unused bits" that should be set to 0 */
75    mode->panel_fb_width.f.offset = pitch >> 4;
76    mode->panel_fb_width.f.width = width >> 4;
77
78    mode->panel_display_ctl.f.gamma = pSmi->Bpp > 1;
79
80    WRITE_SCR(pSmi, PANEL_DISPLAY_CTL, mode->panel_display_ctl.value);
81    WRITE_SCR(pSmi, PANEL_FB_WIDTH, mode->panel_fb_width.value);
82
83    LEAVE();
84}
85
86static void
87SMI501_CrtcVideoInit_crt(xf86CrtcPtr crtc)
88{
89    ScrnInfoPtr pScrn=crtc->scrn;
90    SMIPtr pSmi = SMIPTR(pScrn);
91    MSOCRegPtr mode = pSmi->mode;
92    int		pitch, width;
93
94    ENTER();
95
96    if (!pSmi->HwCursor)
97	SMI501_CrtcHideCursor(crtc);
98
99    mode->crt_display_ctl.value = READ_SCR(pSmi, CRT_DISPLAY_CTL);
100    mode->crt_fb_width.value = READ_SCR(pSmi, CRT_FB_WIDTH);
101
102    mode->crt_display_ctl.f.format =
103	pScrn->bitsPerPixel == 8 ? 0 :
104	pScrn->bitsPerPixel == 16 ? 1 : 2;
105
106    pitch = (((crtc->rotatedData? crtc->mode.HDisplay : pScrn->displayWidth) *
107	      pSmi->Bpp) + 15) & ~15;
108    width = ((crtc->mode.HDisplay * pSmi->Bpp) + 15) & ~ 15;
109
110    /* >> 4 because of the "unused bits" that should be set to 0 */
111    mode->crt_fb_width.f.offset = pitch >> 4;
112    mode->crt_fb_width.f.width = width >> 4;
113
114    mode->crt_display_ctl.f.gamma = pSmi->Bpp > 1;
115
116    WRITE_SCR(pSmi, CRT_DISPLAY_CTL, mode->crt_display_ctl.value);
117    WRITE_SCR(pSmi, CRT_FB_WIDTH, mode->crt_fb_width.value);
118
119    LEAVE();
120}
121
122static void
123SMI501_CrtcAdjustFrame(xf86CrtcPtr crtc, int x, int y)
124{
125    ScrnInfoPtr pScrn=crtc->scrn;
126    SMIPtr pSmi = SMIPTR(pScrn);
127    xf86CrtcConfigPtr crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
128    MSOCRegPtr mode = pSmi->mode;
129    CARD32 Base;
130
131    ENTER();
132
133    if(crtc->rotatedData)
134	Base = (char*)crtc->rotatedData - (char*)pSmi->FBBase;
135    else
136	Base = pSmi->FBOffset + (x + y * pScrn->displayWidth) * pSmi->Bpp;
137
138    Base = (Base + 15) & ~15;
139
140    if (crtc == crtcConf->crtc[0]) {
141	mode->panel_fb_address.f.address = Base >> 4;
142	mode->panel_fb_address.f.pending = 1;
143	WRITE_SCR(pSmi, PANEL_FB_ADDRESS, mode->panel_fb_address.value);
144    }
145    else {
146	mode->crt_display_ctl.f.pixel = ((x * pSmi->Bpp) & 15) / pSmi->Bpp;
147	WRITE_SCR(pSmi, CRT_DISPLAY_CTL, mode->crt_display_ctl.value);
148	mode->crt_fb_address.f.address = Base >> 4;
149	mode->crt_fb_address.f.mselect = 0;
150	mode->crt_fb_address.f.pending = 1;
151	WRITE_SCR(pSmi, CRT_FB_ADDRESS, mode->crt_fb_address.value);
152    }
153
154    LEAVE();
155}
156
157static void
158SMI501_CrtcModeSet_lcd(xf86CrtcPtr crtc,
159		       DisplayModePtr xf86mode,
160		       DisplayModePtr adjusted_mode,
161		       int x, int y)
162{
163    ScrnInfoPtr pScrn=crtc->scrn;
164    SMIPtr pSmi = SMIPTR(pScrn);
165    MSOCRegPtr mode = pSmi->mode;
166    double	p2_diff, pll_diff;
167    int32_t	x2_select, x2_divider, x2_shift, x2_1xclck;
168
169    ENTER();
170
171    /* Initialize the display controller */
172
173    SMI501_CrtcVideoInit_lcd(crtc);
174
175    /* P2CLK have dividers 1, 3 and 5 */
176    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV,
177		   "Clock request %5.2f (max_divider %d)\n",
178		   (double)xf86mode->Clock, 5);
179    p2_diff = SMI501_FindClock(xf86mode->Clock, 5,
180			       (uint32_t)mode->device_id.f.revision >= 0xc0,
181			       &x2_1xclck, &x2_select, &x2_divider,
182			       &x2_shift);
183    mode->clock.f.p2_select = x2_select;
184    mode->clock.f.p2_divider = x2_divider;
185    mode->clock.f.p2_shift = x2_shift;
186    mode->clock.f.p2_1xclck = x2_1xclck;
187
188    /* Check if it is a SMI 502 */
189    /* FIXME May need to add a Boolean option here, (or use extra
190     * xorg.conf options?) to force it to not use 502 mode set. */
191    if ((uint32_t)mode->device_id.f.revision >= 0xc0) {
192	int32_t	m, n, xclck;
193
194	pll_diff = SMI501_FindPLLClock(xf86mode->Clock, &m, &n, &xclck);
195	if (pll_diff < p2_diff) {
196
197	    /* Zero the pre 502 bitfield */
198	    mode->clock.f.p2_select  = 0;
199	    mode->clock.f.p2_divider = 0;
200	    mode->clock.f.p2_shift   = 0;
201	    mode->clock.f.p2_1xclck  = 0;
202
203	    mode->clock.f.pll_select = 1;
204	    mode->pll_ctl.f.m = m;
205	    mode->pll_ctl.f.n = n;
206
207	    /* 0: Crystal input
208	     * 1: Test clock input */
209	    mode->pll_ctl.f.select = 0;
210
211	    /* 0: pll output divided by 1
212	     * 1: pll output divided by 2 */
213	    mode->pll_ctl.f.divider = xclck != 1;
214	    mode->pll_ctl.f.power = 1;
215	}
216	else
217	    mode->clock.f.pll_select = 0;
218    }
219    else
220	mode->clock.f.pll_select = 0;
221
222    mode->panel_display_ctl.f.enable = 1;
223    mode->panel_display_ctl.f.timing = 1;
224
225    mode->panel_wwidth.f.x = 0;
226    mode->panel_wwidth.f.width = xf86mode->HDisplay;
227
228    mode->panel_wheight.f.y = 0;
229    mode->panel_wheight.f.height = xf86mode->VDisplay;
230
231#ifdef USE_PANEL_CENTER
232    mode->panel_plane_tl.f.left = (pSmi->lcdWidth - xf86mode->HDisplay) >> 1;
233    mode->panel_plane_tl.f.top = (pSmi->lcdHeight - xf86mode->VDisplay) >> 1;
234
235    mode->panel_plane_br.f.right = mode->panel_plane_tl.f.left +
236	xf86mode->HDisplay - 1;
237    mode->panel_plane_br.f.bottom = mode->panel_plane_tl.f.top +
238	xf86mode->VDisplay - 1;
239#else
240    mode->panel_plane_tl.f.left = 0;
241    mode->panel_plane_tl.f.top = 0;
242
243    mode->panel_plane_br.f.right = xf86mode->HDisplay - 1;
244    mode->panel_plane_br.f.bottom = xf86mode->VDisplay - 1;
245#endif
246
247    /* 0 means pulse high */
248    mode->panel_display_ctl.f.hsync = !(xf86mode->Flags & V_PHSYNC);
249    mode->panel_display_ctl.f.vsync = !(xf86mode->Flags & V_PVSYNC);
250
251    mode->panel_htotal.f.total = xf86mode->HTotal - 1;
252    mode->panel_htotal.f.end = xf86mode->HDisplay - 1;
253
254    mode->panel_hsync.f.start = xf86mode->HSyncStart - 1;
255    mode->panel_hsync.f.width = xf86mode->HSyncEnd -
256	xf86mode->HSyncStart;
257
258    mode->panel_vtotal.f.total = xf86mode->VTotal - 1;
259    mode->panel_vtotal.f.end = xf86mode->VDisplay - 1;
260
261    mode->panel_vsync.f.start = xf86mode->VSyncStart;
262    mode->panel_vsync.f.height = xf86mode->VSyncEnd -
263	xf86mode->VSyncStart;
264
265
266    SMI501_WriteMode_lcd(pScrn,mode);
267    SMI501_CrtcAdjustFrame(crtc, x, y);
268
269    LEAVE();
270}
271
272static void
273SMI501_CrtcModeSet_crt(xf86CrtcPtr crtc,
274		       DisplayModePtr xf86mode,
275		       DisplayModePtr adjusted_mode,
276		       int x, int y)
277{
278    ScrnInfoPtr pScrn=crtc->scrn;
279    SMIPtr pSmi = SMIPTR(pScrn);
280    MSOCRegPtr mode = pSmi->mode;
281    int32_t	x2_select, x2_divider, x2_shift, x2_1xclck;
282
283    ENTER();
284
285    /* Initialize the display controller */
286
287    SMI501_CrtcVideoInit_crt(crtc);
288
289    /* V2CLK have dividers 1 and 3 */
290    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, VERBLEV,
291		   "Clock request %5.2f (max_divider %d)\n",
292		   (double)xf86mode->Clock, 3);
293    (void)SMI501_FindClock(xf86mode->Clock, 3,
294			   (uint32_t)mode->device_id.f.revision >= 0xc0,
295			   &x2_1xclck, &x2_select, &x2_divider, &x2_shift);
296    mode->clock.f.v2_select = x2_select;
297    mode->clock.f.v2_divider = x2_divider;
298    mode->clock.f.v2_shift = x2_shift;
299    mode->clock.f.v2_1xclck = x2_1xclck;
300
301    /* 0: select panel - 1: select crt */
302    mode->crt_display_ctl.f.select = 1;
303    mode->crt_display_ctl.f.enable = 1;
304    mode->crt_display_ctl.f.timing = 1;
305    /* 0: show pixels - 1: blank */
306    mode->crt_display_ctl.f.blank = 0;
307
308    mode->crt_fb_address.f.mextern = 0;	/* local memory */
309
310    /* 0 means pulse high */
311    mode->crt_display_ctl.f.hsync = !(xf86mode->Flags & V_PHSYNC);
312    mode->crt_display_ctl.f.vsync = !(xf86mode->Flags & V_PVSYNC);
313
314    mode->crt_htotal.f.total = xf86mode->HTotal - 1;
315    mode->crt_htotal.f.end = xf86mode->HDisplay - 1;
316
317    mode->crt_hsync.f.start = xf86mode->HSyncStart - 1;
318    mode->crt_hsync.f.width = xf86mode->HSyncEnd -
319	xf86mode->HSyncStart;
320
321    mode->crt_vtotal.f.total = xf86mode->VTotal - 1;
322    mode->crt_vtotal.f.end = xf86mode->VDisplay - 1;
323
324    mode->crt_vsync.f.start = xf86mode->VSyncStart;
325    mode->crt_vsync.f.height = xf86mode->VSyncEnd -
326	xf86mode->VSyncStart;
327
328    SMI501_WriteMode_crt(pScrn,mode);
329    SMI501_CrtcAdjustFrame(crtc, x, y);
330
331    LEAVE();
332}
333
334static void
335SMI501_CrtcLoadLUT(xf86CrtcPtr crtc)
336{
337    ScrnInfoPtr pScrn = crtc->scrn;
338    SMIPtr pSmi = SMIPTR(pScrn);
339    xf86CrtcConfigPtr crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
340    SMICrtcPrivatePtr crtcPriv = SMICRTC(crtc);
341    int i,port;
342
343    ENTER();
344
345    port = crtc == crtcConf->crtc[0] ? PANEL_PALETTE : CRT_PALETTE;
346    for (i = 0; i < 256; i++)
347	WRITE_SCR(pSmi, port + (i  <<  2),
348		  (crtcPriv->lut_r[i] >> 8 << 16) |
349		  (crtcPriv->lut_g[i] >> 8 << 8) |
350		  (crtcPriv->lut_b[i] >> 8) );
351
352    LEAVE();
353}
354
355static void
356SMI501_CrtcSetCursorColors(xf86CrtcPtr crtc, int bg, int fg)
357{
358    ScrnInfoPtr		pScrn = crtc->scrn;
359    SMIPtr		pSmi = SMIPTR(pScrn);
360    xf86CrtcConfigPtr	crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
361    int32_t		port, value;
362
363    ENTER();
364
365    /* for the SMI501 HWCursor, there are 4 possible colors, one of which
366     * is transparent:	M,S:  0,0 = Transparent
367     *						      0,1 = color 1
368     *						      1,0 = color 2
369     *						      1,1 = color 3
370     *	To simplify implementation, we use color2 == bg and
371     *					   color3 == fg
372     *	Color 1 is don't care, so we set it to color 2's value
373     */
374
375    /* Pack the true color components into 16 bit RGB -- 5:6:5 */
376    value = ((bg & 0xF80000) >> 8 |
377	     (bg & 0x00FC00) >> 5 |
378	     (bg & 0x0000F8) >> 3);
379
380    value |= ((bg & 0xF80000) <<  8 |
381	      (bg & 0x00FC00) << 11 |
382	      (bg & 0x0000F8) << 13);
383    port = crtc == crtcConf->crtc[0] ? 0x00f8 : 0x0238;
384    WRITE_DCR(pSmi, port, value);
385
386    value = ((fg & 0xF80000) >> 8 |
387	     (fg & 0x00FC00) >> 5 |
388	     (fg & 0x0000F8) >> 3);
389    port = crtc == crtcConf->crtc[0] ? 0x00fc : 0x023c;
390    WRITE_DCR(pSmi, port, value);
391
392    LEAVE();
393}
394
395static void
396SMI501_CrtcSetCursorPosition(xf86CrtcPtr crtc, int x, int y)
397{
398    ScrnInfoPtr		pScrn = crtc->scrn;
399    SMIPtr		pSmi = SMIPTR(pScrn);
400    xf86CrtcConfigPtr	crtcConf;
401#if SMI_CURSOR_ALPHA_PLANE
402    SMICrtcPrivatePtr	smi_crtc = SMICRTC(crtc);
403    MSOCRegPtr		mode;
404#endif
405    int32_t		port, offset;
406
407    ENTER();
408
409#if SMI_CURSOR_ALPHA_PLANE
410    if (smi_crtc->argb_cursor) {
411	mode = pSmi->mode;
412
413	/* uncomment next line if you want to see it rendering the cursor */
414	/* x = y = 0; */
415
416	mode->alpha_plane_tl.f.left = x;
417	mode->alpha_plane_tl.f.top = y;
418
419	mode->alpha_plane_br.f.right = x + SMI501_CURSOR_SIZE - 1;
420	mode->alpha_plane_br.f.bottom = y + SMI501_CURSOR_SIZE - 1;
421
422	WRITE_SCR(pSmi, ALPHA_PLANE_TL, mode->alpha_plane_tl.value);
423	WRITE_SCR(pSmi, ALPHA_PLANE_BR, mode->alpha_plane_br.value);
424    }
425    else
426#endif
427    {
428	crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
429
430	if (x >= 0)
431	    offset = x & SMI501_MASK_MAXBITS;
432	else
433	    offset = (-x & SMI501_MASK_MAXBITS) | SMI501_MASK_BOUNDARY;
434
435	if (y >= 0)
436	    offset |= (y & SMI501_MASK_MAXBITS) << 16;
437	else
438	    offset |= ((-y & SMI501_MASK_MAXBITS) | SMI501_MASK_BOUNDARY) << 16;
439
440	port = crtc == crtcConf->crtc[0] ? 0x00f4 : 0x0234;
441	WRITE_DCR(pSmi, port, offset);
442    }
443
444    LEAVE();
445}
446
447static void
448SMI501_CrtcShowCursor(xf86CrtcPtr crtc)
449{
450    ScrnInfoPtr		pScrn = crtc->scrn;
451    SMIPtr		pSmi = SMIPTR(pScrn);
452    xf86CrtcConfigPtr	crtcConf;
453#if SMI_CURSOR_ALPHA_PLANE
454    SMICrtcPrivatePtr	smi_crtc = SMICRTC(crtc);
455    MSOCRegPtr		mode;
456#endif
457    int32_t		port, value;
458
459    ENTER();
460
461#if SMI_CURSOR_ALPHA_PLANE
462    if (smi_crtc->argb_cursor) {
463	mode = pSmi->mode;
464
465	mode->alpha_display_ctl.f.enable = 1;
466	WRITE_SCR(pSmi, ALPHA_DISPLAY_CTL, mode->alpha_display_ctl.value);
467    }
468    else
469#endif
470    {
471	crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
472
473	port = crtc == crtcConf->crtc[0] ? 0x00f0 : 0x0230;
474	value = READ_DCR(pSmi, port);
475	value |= SMI501_MASK_HWCENABLE;
476	WRITE_DCR(pSmi, port, value);
477    }
478
479    LEAVE();
480}
481
482static void
483SMI501_CrtcHideCursor(xf86CrtcPtr crtc)
484{
485    ScrnInfoPtr		pScrn = crtc->scrn;
486    SMIPtr		pSmi = SMIPTR(pScrn);
487    xf86CrtcConfigPtr	crtcConf;
488#if SMI_CURSOR_ALPHA_PLANE
489    SMICrtcPrivatePtr	smi_crtc = SMICRTC(crtc);
490    MSOCRegPtr		mode;
491#endif
492    int32_t		port, value;
493
494    ENTER();
495
496#if SMI_CURSOR_ALPHA_PLANE
497    if (smi_crtc->argb_cursor) {
498	mode = pSmi->mode;
499
500	mode->alpha_display_ctl.f.enable = 0;
501	WRITE_SCR(pSmi, ALPHA_DISPLAY_CTL, mode->alpha_display_ctl.value);
502    }
503    else
504#endif
505    {
506	crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
507
508	port = crtc == crtcConf->crtc[0] ? 0x00f0 : 0x0230;
509	value = READ_DCR(pSmi, port);
510	value &= ~SMI501_MASK_HWCENABLE;
511	WRITE_DCR(pSmi, port, value);
512    }
513
514    LEAVE();
515}
516
517static void
518SMI501_CrtcLoadCursorImage(xf86CrtcPtr crtc, CARD8 *image)
519{
520    ScrnInfoPtr		pScrn = crtc->scrn;
521    SMIPtr		pSmi = SMIPTR(pScrn);
522#if SMI_CURSOR_ALPHA_PLANE
523    SMICrtcPrivatePtr	smi_crtc = SMICRTC(crtc);
524#endif
525    xf86CrtcConfigPtr	crtcConf = XF86_CRTC_CONFIG_PTR(pScrn);
526    int32_t		port, value;
527
528    ENTER();
529
530    port = crtc == crtcConf->crtc[0] ? 0x00f0 : 0x0230;
531    value = pSmi->FBCursorOffset + (port == 0x00f0 ? 0 : SMI501_CURSOR_SIZE);
532    WRITE_DCR(pSmi, port, value);
533    memcpy(pSmi->FBBase + value, image,
534	   /* FIXME 1024, but then, should not be using 64x64 cursors */
535	   (SMI501_MAX_CURSOR >> 2) * SMI501_MAX_CURSOR);
536#if SMI_CURSOR_ALPHA_PLANE
537    smi_crtc->argb_cursor = FALSE;
538#endif
539
540    LEAVE();
541}
542
543#if SMI_CURSOR_ALPHA_PLANE
544static void
545SMI501_CrtcLoadCursorArgb(xf86CrtcPtr crtc, CARD32 *image)
546{
547    ScrnInfoPtr		 pScrn = crtc->scrn;
548    SMIPtr		 pSmi = SMIPTR(pScrn);
549    SMICrtcPrivatePtr	 smi_crtc = SMICRTC(crtc);
550    MSOCRegPtr		 mode = pSmi->mode;
551    int16_t		*framebuffer;
552    int32_t		 x, y, bits;
553    int32_t		 format;
554
555    ENTER();
556
557#define ALPHA_RGB_565		1
558#define ALPHA_ARGB_4444		3
559
560    /* select alpha format */
561    mode->alpha_display_ctl.f.format = ALPHA_ARGB_4444;
562
563    /* 0: use per pixel alpha value  1: use alpha value specified in alpha */
564    if (mode->alpha_display_ctl.f.format == ALPHA_RGB_565) {
565	mode->alpha_display_ctl.f.select = 1;
566	/* 0 to 15, with 0 being transparent and 15 opaque */
567	mode->alpha_display_ctl.f.alpha = 7;
568    }
569    else {
570	/* use per pixel alpha */
571	mode->alpha_display_ctl.f.select = 0;
572    }
573
574    /* alpha layer buffer */
575    mode->alpha_fb_address.value = 0;
576    mode->alpha_fb_address.f.address = pSmi->FBCursorOffset >> 4;
577
578    /* more clearly: width = (SMI501_MAX_CURSOR << 1) >> 4
579     * as the structure is matching the register spec, where it says
580     * the first 4 bits are hardwired to zero */
581    mode->alpha_fb_width.f.offset = SMI501_MAX_CURSOR >> 3;
582    mode->alpha_fb_width.f.width = SMI501_MAX_CURSOR >> 3;
583
584    mode->alpha_chroma_key.f.value = 0;
585    mode->alpha_chroma_key.f.mask = 0;
586    /* enable chroma key */
587    mode->alpha_display_ctl.f.chromakey = 1;
588
589    framebuffer = (int16_t *)(pSmi->FBBase + pSmi->FBCursorOffset);
590    if (mode->alpha_display_ctl.f.format == ALPHA_RGB_565) {
591	/* convert image to rgb 5:6:5 */
592	for (y = 0; y < SMI501_MAX_CURSOR; y++) {
593	    for (x = 0; x < SMI501_MAX_CURSOR; x++) {
594		bits = image[y * SMI501_MAX_CURSOR + x];
595		framebuffer[y * SMI501_MAX_CURSOR + x] =
596		(((bits & 0xf80000) >> 8) |
597		 ((bits & 0x00fc00) >> 5) |
598		 ((bits & 0x0000f8) >> 3));
599	    }
600	}
601    }
602    else {
603	/* convert image to argb 4:4:4:4 */
604	for (y = 0; y < SMI501_MAX_CURSOR; y++) {
605	    for (x = 0; x < SMI501_MAX_CURSOR; x++) {
606		bits = image[y * SMI501_MAX_CURSOR + x];
607		framebuffer[y * SMI501_MAX_CURSOR + x] =
608		(((bits & 0xf0000000) >> 16) |
609		 ((bits & 0x00f00000) >> 12) |
610		 ((bits & 0x0000f000) >>  8) |
611		 ((bits & 0x000000f0) >>  4));
612	    }
613	}
614    }
615    SMI501_WriteMode_alpha(pScrn, mode);
616    smi_crtc->argb_cursor = TRUE;
617
618     LEAVE();
619 }
620#endif
621
622Bool
623SMI501_CrtcPreInit(ScrnInfoPtr pScrn)
624{
625    SMIPtr	pSmi = SMIPTR(pScrn);
626    xf86CrtcPtr crtc;
627    xf86CrtcFuncsPtr crtcFuncs;
628    SMICrtcPrivatePtr crtcPriv;
629
630    ENTER();
631
632    /* CRTC0 is LCD */
633    SMI_CrtcFuncsInit_base(&crtcFuncs, &crtcPriv);
634    crtcFuncs->mode_set		= SMI501_CrtcModeSet_lcd;
635    crtcPriv->adjust_frame	= SMI501_CrtcAdjustFrame;
636    crtcPriv->video_init	= SMI501_CrtcVideoInit_lcd;
637    crtcPriv->load_lut		= SMI501_CrtcLoadLUT;
638
639    if (pSmi->HwCursor) {
640	crtcFuncs->set_cursor_colors = SMI501_CrtcSetCursorColors;
641	crtcFuncs->set_cursor_position = SMI501_CrtcSetCursorPosition;
642	crtcFuncs->show_cursor = SMI501_CrtcShowCursor;
643	crtcFuncs->hide_cursor = SMI501_CrtcHideCursor;
644	crtcFuncs->load_cursor_image = SMI501_CrtcLoadCursorImage;
645#if SMI_CURSOR_ALPHA_PLANE
646	if (!pSmi->Dualhead)
647	    crtcFuncs->load_cursor_argb = SMI501_CrtcLoadCursorArgb;
648#endif
649    }
650
651    if (! (crtc = xf86CrtcCreate(pScrn, crtcFuncs)))
652	LEAVE(FALSE);
653    crtc->driver_private = crtcPriv;
654
655    /* CRTC1 is CRT */
656    if (pSmi->Dualhead) {
657	SMI_CrtcFuncsInit_base(&crtcFuncs, &crtcPriv);
658	crtcFuncs->mode_set	= SMI501_CrtcModeSet_crt;
659	crtcPriv->adjust_frame	= SMI501_CrtcAdjustFrame;
660	crtcPriv->video_init	= SMI501_CrtcVideoInit_crt;
661	crtcPriv->load_lut	= SMI501_CrtcLoadLUT;
662
663	if (pSmi->HwCursor) {
664	    crtcFuncs->set_cursor_colors = SMI501_CrtcSetCursorColors;
665	    crtcFuncs->set_cursor_position = SMI501_CrtcSetCursorPosition;
666	    crtcFuncs->show_cursor = SMI501_CrtcShowCursor;
667	    crtcFuncs->hide_cursor = SMI501_CrtcHideCursor;
668	    crtcFuncs->load_cursor_image = SMI501_CrtcLoadCursorImage;
669	}
670
671	if (! (crtc = xf86CrtcCreate(pScrn, crtcFuncs)))
672	    LEAVE(FALSE);
673	crtc->driver_private = crtcPriv;
674    }
675
676    LEAVE(TRUE);
677}
678
679