xf86Rotate.c revision 05b261ec
1/*
2 * Copyright © 2006 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_XORG_CONFIG_H
24#include <xorg-config.h>
25#else
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29#endif
30
31#include <stddef.h>
32#include <string.h>
33#include <stdio.h>
34
35#include "xf86.h"
36#include "xf86DDC.h"
37#include "fb.h"
38#include "windowstr.h"
39#include "xf86Crtc.h"
40#include "xf86Modes.h"
41#include "xf86RandR12.h"
42#include "X11/extensions/render.h"
43#define DPMS_SERVER
44#include "X11/extensions/dpms.h"
45#include "X11/Xatom.h"
46
47/* borrowed from composite extension, move to Render and publish? */
48
49static VisualPtr
50compGetWindowVisual (WindowPtr pWin)
51{
52    ScreenPtr	    pScreen = pWin->drawable.pScreen;
53    VisualID	    vid = wVisual (pWin);
54    int		    i;
55
56    for (i = 0; i < pScreen->numVisuals; i++)
57	if (pScreen->visuals[i].vid == vid)
58	    return &pScreen->visuals[i];
59    return 0;
60}
61
62static PictFormatPtr
63compWindowFormat (WindowPtr pWin)
64{
65    ScreenPtr	pScreen = pWin->drawable.pScreen;
66
67    return PictureMatchVisual (pScreen, pWin->drawable.depth,
68			       compGetWindowVisual (pWin));
69}
70
71#define F(x)	IntToxFixed(x)
72
73static void
74PictureTransformIdentity (PictTransformPtr matrix)
75{
76    int	i;
77    memset (matrix, '\0', sizeof (PictTransform));
78    for (i = 0; i < 3; i++)
79	matrix->matrix[i][i] = F(1);
80}
81
82static Bool
83PictureTransformMultiply (PictTransformPtr dst, PictTransformPtr l, PictTransformPtr r)
84{
85    PictTransform   d;
86    int		    dx, dy;
87    int		    o;
88
89    for (dy = 0; dy < 3; dy++)
90	for (dx = 0; dx < 3; dx++)
91	{
92	    xFixed_48_16    v;
93	    xFixed_32_32    partial;
94	    v = 0;
95	    for (o = 0; o < 3; o++)
96	    {
97		partial = (xFixed_32_32) l->matrix[dy][o] * (xFixed_32_32) r->matrix[o][dx];
98		v += partial >> 16;
99	    }
100	    if (v > MAX_FIXED_48_16 || v < MIN_FIXED_48_16)
101		return FALSE;
102	    d.matrix[dy][dx] = (xFixed) v;
103	}
104    *dst = d;
105    return TRUE;
106}
107
108static void
109PictureTransformInitScale (PictTransformPtr t, xFixed sx, xFixed sy)
110{
111    memset (t, '\0', sizeof (PictTransform));
112    t->matrix[0][0] = sx;
113    t->matrix[1][1] = sy;
114    t->matrix[2][2] = F (1);
115}
116
117static xFixed
118fixed_inverse (xFixed x)
119{
120    return (xFixed) ((((xFixed_48_16) F(1)) * F(1)) / x);
121}
122
123static Bool
124PictureTransformScale (PictTransformPtr forward,
125		       PictTransformPtr reverse,
126		       xFixed sx, xFixed sy)
127{
128    PictTransform   t;
129
130    PictureTransformInitScale (&t, sx, sy);
131    if (!PictureTransformMultiply (forward, &t, forward))
132	return FALSE;
133    PictureTransformInitScale (&t, fixed_inverse (sx), fixed_inverse (sy));
134    if (!PictureTransformMultiply (reverse, reverse, &t))
135	return FALSE;
136    return TRUE;
137}
138
139static void
140PictureTransformInitRotate (PictTransformPtr t, xFixed c, xFixed s)
141{
142    memset (t, '\0', sizeof (PictTransform));
143    t->matrix[0][0] = c;
144    t->matrix[0][1] = -s;
145    t->matrix[1][0] = s;
146    t->matrix[1][1] = c;
147    t->matrix[2][2] = F (1);
148}
149
150static Bool
151PictureTransformRotate (PictTransformPtr forward,
152			PictTransformPtr reverse,
153			xFixed c, xFixed s)
154{
155    PictTransform   t;
156    PictureTransformInitRotate (&t, c, s);
157    if (!PictureTransformMultiply (forward, &t, forward))
158	return FALSE;
159
160    PictureTransformInitRotate (&t, c, -s);
161    if (!PictureTransformMultiply (reverse, reverse, &t))
162	return FALSE;
163    return TRUE;
164}
165
166static void
167PictureTransformInitTranslate (PictTransformPtr t, xFixed tx, xFixed ty)
168{
169    memset (t, '\0', sizeof (PictTransform));
170    t->matrix[0][0] = F (1);
171    t->matrix[0][2] = tx;
172    t->matrix[1][1] = F (1);
173    t->matrix[1][2] = ty;
174    t->matrix[2][2] = F (1);
175}
176
177static Bool
178PictureTransformTranslate (PictTransformPtr forward,
179			   PictTransformPtr reverse,
180			   xFixed tx, xFixed ty)
181{
182    PictTransform   t;
183    PictureTransformInitTranslate (&t, tx, ty);
184    if (!PictureTransformMultiply (forward, &t, forward))
185	return FALSE;
186
187    PictureTransformInitTranslate (&t, -tx, -ty);
188    if (!PictureTransformMultiply (reverse, reverse, &t))
189	return FALSE;
190    return TRUE;
191}
192
193static void
194PictureTransformBounds (BoxPtr b, PictTransformPtr matrix)
195{
196    PictVector	v[4];
197    int		i;
198    int		x1, y1, x2, y2;
199
200    v[0].vector[0] = F (b->x1);    v[0].vector[1] = F (b->y1);	v[0].vector[2] = F(1);
201    v[1].vector[0] = F (b->x2);    v[1].vector[1] = F (b->y1);	v[1].vector[2] = F(1);
202    v[2].vector[0] = F (b->x2);    v[2].vector[1] = F (b->y2);	v[2].vector[2] = F(1);
203    v[3].vector[0] = F (b->x1);    v[3].vector[1] = F (b->y2);	v[3].vector[2] = F(1);
204    for (i = 0; i < 4; i++)
205    {
206	PictureTransformPoint (matrix, &v[i]);
207	x1 = xFixedToInt (v[i].vector[0]);
208	y1 = xFixedToInt (v[i].vector[1]);
209	x2 = xFixedToInt (xFixedCeil (v[i].vector[0]));
210	y2 = xFixedToInt (xFixedCeil (v[i].vector[1]));
211	if (i == 0)
212	{
213	    b->x1 = x1; b->y1 = y1;
214	    b->x2 = x2; b->y2 = y2;
215	}
216	else
217	{
218	    if (x1 < b->x1) b->x1 = x1;
219	    if (y1 < b->y1) b->y1 = y1;
220	    if (x2 > b->x2) b->x2 = x2;
221	    if (y2 > b->y2) b->y2 = y2;
222	}
223    }
224}
225
226static Bool
227PictureTransformIsIdentity(PictTransform *t)
228{
229    return ((t->matrix[0][0] == t->matrix[1][1]) &&
230            (t->matrix[0][0] == t->matrix[2][2]) &&
231            (t->matrix[0][0] != 0) &&
232            (t->matrix[0][1] == 0) &&
233            (t->matrix[0][2] == 0) &&
234            (t->matrix[1][0] == 0) &&
235            (t->matrix[1][2] == 0) &&
236            (t->matrix[2][0] == 0) &&
237            (t->matrix[2][1] == 0));
238}
239
240#define toF(x)	((float) (x) / 65536.0f)
241
242static void
243PictureTransformErrorF (PictTransform *t)
244{
245    ErrorF ("{ { %f %f %f } { %f %f %f } { %f %f %f } }",
246	    toF(t->matrix[0][0]), toF(t->matrix[0][1]), toF(t->matrix[0][2]),
247	    toF(t->matrix[1][0]), toF(t->matrix[1][1]), toF(t->matrix[1][2]),
248	    toF(t->matrix[2][0]), toF(t->matrix[2][1]), toF(t->matrix[2][2]));
249}
250
251static Bool
252PictureTransformIsInverse (char *where, PictTransform *a, PictTransform *b)
253{
254    PictTransform   t;
255
256    PictureTransformMultiply (&t, a, b);
257    if (!PictureTransformIsIdentity (&t))
258    {
259	ErrorF ("%s: ", where);
260	PictureTransformErrorF (a);
261	ErrorF (" * ");
262	PictureTransformErrorF (b);
263	ErrorF (" = ");
264	PictureTransformErrorF (a);
265	ErrorF ("\n");
266	return FALSE;
267    }
268    return TRUE;
269}
270
271static void
272xf86RotateCrtcRedisplay (xf86CrtcPtr crtc, RegionPtr region)
273{
274    ScrnInfoPtr		scrn = crtc->scrn;
275    ScreenPtr		screen = scrn->pScreen;
276    WindowPtr		root = WindowTable[screen->myNum];
277    PixmapPtr		dst_pixmap = crtc->rotatedPixmap;
278    PictFormatPtr	format = compWindowFormat (WindowTable[screen->myNum]);
279    int			error;
280    PicturePtr		src, dst;
281    int			n = REGION_NUM_RECTS(region);
282    BoxPtr		b = REGION_RECTS(region);
283    XID			include_inferiors = IncludeInferiors;
284
285    src = CreatePicture (None,
286			 &root->drawable,
287			 format,
288			 CPSubwindowMode,
289			 &include_inferiors,
290			 serverClient,
291			 &error);
292    if (!src)
293	return;
294
295    dst = CreatePicture (None,
296			 &dst_pixmap->drawable,
297			 format,
298			 0L,
299			 NULL,
300			 serverClient,
301			 &error);
302    if (!dst)
303	return;
304
305    error = SetPictureTransform (src, &crtc->crtc_to_framebuffer);
306    if (error)
307	return;
308
309    while (n--)
310    {
311	BoxRec	dst_box;
312
313	dst_box = *b;
314	PictureTransformBounds (&dst_box, &crtc->framebuffer_to_crtc);
315	CompositePicture (PictOpSrc,
316			  src, NULL, dst,
317			  dst_box.x1, dst_box.y1, 0, 0, dst_box.x1, dst_box.y1,
318			  dst_box.x2 - dst_box.x1,
319			  dst_box.y2 - dst_box.y1);
320	b++;
321    }
322    FreePicture (src, None);
323    FreePicture (dst, None);
324}
325
326static void
327xf86CrtcDamageShadow (xf86CrtcPtr crtc)
328{
329    ScrnInfoPtr	pScrn = crtc->scrn;
330    BoxRec	damage_box;
331    RegionRec   damage_region;
332    ScreenPtr	pScreen = pScrn->pScreen;
333
334    damage_box.x1 = crtc->x;
335    damage_box.x2 = crtc->x + xf86ModeWidth (&crtc->mode, crtc->rotation);
336    damage_box.y1 = crtc->y;
337    damage_box.y2 = crtc->y + xf86ModeHeight (&crtc->mode, crtc->rotation);
338    REGION_INIT (pScreen, &damage_region, &damage_box, 1);
339    DamageDamageRegion (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
340			&damage_region);
341    REGION_UNINIT (pScreen, &damage_region);
342}
343
344static void
345xf86RotatePrepare (ScreenPtr pScreen)
346{
347    ScrnInfoPtr		pScrn = xf86Screens[pScreen->myNum];
348    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
349    int			c;
350
351    for (c = 0; c < xf86_config->num_crtc; c++)
352    {
353	xf86CrtcPtr crtc = xf86_config->crtc[c];
354
355	if (crtc->rotatedData && !crtc->rotatedPixmap)
356	{
357	    crtc->rotatedPixmap = crtc->funcs->shadow_create (crtc,
358							     crtc->rotatedData,
359							     crtc->mode.HDisplay,
360							     crtc->mode.VDisplay);
361	    if (!xf86_config->rotation_damage_registered)
362	    {
363		/* Hook damage to screen pixmap */
364		DamageRegister (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
365				xf86_config->rotation_damage);
366		xf86_config->rotation_damage_registered = TRUE;
367	    }
368
369	    xf86CrtcDamageShadow (crtc);
370	}
371    }
372}
373
374static Bool
375xf86RotateRedisplay(ScreenPtr pScreen)
376{
377    ScrnInfoPtr		pScrn = xf86Screens[pScreen->myNum];
378    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
379    DamagePtr		damage = xf86_config->rotation_damage;
380    RegionPtr		region;
381
382    if (!damage)
383	return FALSE;
384    xf86RotatePrepare (pScreen);
385    region = DamageRegion(damage);
386    if (REGION_NOTEMPTY(pScreen, region))
387    {
388	int			c;
389	SourceValidateProcPtr	SourceValidate;
390
391	/*
392	 * SourceValidate is used by the software cursor code
393	 * to pull the cursor off of the screen when reading
394	 * bits from the frame buffer. Bypassing this function
395	 * leaves the software cursor in place
396	 */
397	SourceValidate = pScreen->SourceValidate;
398	pScreen->SourceValidate = NULL;
399
400	for (c = 0; c < xf86_config->num_crtc; c++)
401	{
402	    xf86CrtcPtr	    crtc = xf86_config->crtc[c];
403
404	    if (crtc->rotation != RR_Rotate_0 && crtc->enabled)
405	    {
406		RegionRec   crtc_damage;
407
408		/* compute portion of damage that overlaps crtc */
409		REGION_INIT(pScreen, &crtc_damage, &crtc->bounds, 1);
410		REGION_INTERSECT (pScreen, &crtc_damage, &crtc_damage, region);
411
412		/* update damaged region */
413		if (REGION_NOTEMPTY(pScreen, &crtc_damage))
414    		    xf86RotateCrtcRedisplay (crtc, &crtc_damage);
415
416		REGION_UNINIT (pScreen, &crtc_damage);
417	    }
418	}
419	pScreen->SourceValidate = SourceValidate;
420	DamageEmpty(damage);
421    }
422    return TRUE;
423}
424
425static void
426xf86RotateBlockHandler(int screenNum, pointer blockData,
427		       pointer pTimeout, pointer pReadmask)
428{
429    ScreenPtr		pScreen = screenInfo.screens[screenNum];
430    ScrnInfoPtr		pScrn = xf86Screens[screenNum];
431    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
432
433    pScreen->BlockHandler = xf86_config->BlockHandler;
434    (*pScreen->BlockHandler) (screenNum, blockData, pTimeout, pReadmask);
435    if (xf86RotateRedisplay(pScreen))
436    {
437	/* Re-wrap if rotation is still happening */
438	xf86_config->BlockHandler = pScreen->BlockHandler;
439	pScreen->BlockHandler = xf86RotateBlockHandler;
440    }
441}
442
443static void
444xf86RotateDestroy (xf86CrtcPtr crtc)
445{
446    ScrnInfoPtr		pScrn = crtc->scrn;
447    ScreenPtr		pScreen = pScrn->pScreen;
448    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
449    int			c;
450
451    /* Free memory from rotation */
452    if (crtc->rotatedPixmap || crtc->rotatedData)
453    {
454	crtc->funcs->shadow_destroy (crtc, crtc->rotatedPixmap, crtc->rotatedData);
455	crtc->rotatedPixmap = NULL;
456	crtc->rotatedData = NULL;
457    }
458
459    for (c = 0; c < xf86_config->num_crtc; c++)
460	if (xf86_config->crtc[c]->rotatedPixmap ||
461	    xf86_config->crtc[c]->rotatedData)
462	    return;
463
464    /*
465     * Clean up damage structures when no crtcs are rotated
466     */
467    if (xf86_config->rotation_damage)
468    {
469	/* Free damage structure */
470	if (xf86_config->rotation_damage_registered)
471	{
472	    DamageUnregister (&(*pScreen->GetScreenPixmap)(pScreen)->drawable,
473			      xf86_config->rotation_damage);
474	    xf86_config->rotation_damage_registered = FALSE;
475	}
476	DamageDestroy (xf86_config->rotation_damage);
477	xf86_config->rotation_damage = NULL;
478    }
479}
480
481_X_EXPORT void
482xf86RotateCloseScreen (ScreenPtr screen)
483{
484    ScrnInfoPtr		scrn = xf86Screens[screen->myNum];
485    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
486    int			c;
487
488    for (c = 0; c < xf86_config->num_crtc; c++)
489	xf86RotateDestroy (xf86_config->crtc[c]);
490}
491
492_X_EXPORT Bool
493xf86CrtcRotate (xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation)
494{
495    ScrnInfoPtr		pScrn = crtc->scrn;
496    xf86CrtcConfigPtr   xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
497    ScreenPtr		pScreen = pScrn->pScreen;
498    PictTransform	crtc_to_fb, fb_to_crtc;
499
500    PictureTransformIdentity (&crtc_to_fb);
501    PictureTransformIdentity (&fb_to_crtc);
502    PictureTransformIsInverse ("identity", &crtc_to_fb, &fb_to_crtc);
503    if (rotation != RR_Rotate_0)
504    {
505	xFixed	rot_cos, rot_sin, rot_dx, rot_dy;
506	xFixed	scale_x, scale_y, scale_dx, scale_dy;
507	int	mode_w = crtc->mode.HDisplay;
508	int	mode_h = crtc->mode.VDisplay;
509
510	/* rotation */
511	switch (rotation & 0xf) {
512	default:
513	case RR_Rotate_0:
514	    rot_cos = F ( 1);	    rot_sin = F ( 0);
515	    rot_dx  = F ( 0);	    rot_dy  = F ( 0);
516	    break;
517	case RR_Rotate_90:
518	    rot_cos = F ( 0);	    rot_sin = F ( 1);
519	    rot_dx =  F ( mode_h);  rot_dy  = F (0);
520	    break;
521	case RR_Rotate_180:
522	    rot_cos = F (-1);	    rot_sin = F ( 0);
523	    rot_dx  = F (mode_w);   rot_dy  = F ( mode_h);
524	    break;
525	case RR_Rotate_270:
526	    rot_cos = F ( 0);	    rot_sin = F (-1);
527	    rot_dx  = F ( 0);	    rot_dy  = F ( mode_w);
528	    break;
529	}
530
531	PictureTransformRotate (&crtc_to_fb, &fb_to_crtc, rot_cos, rot_sin);
532	PictureTransformIsInverse ("rotate", &crtc_to_fb, &fb_to_crtc);
533
534	PictureTransformTranslate (&crtc_to_fb, &fb_to_crtc, rot_dx, rot_dy);
535	PictureTransformIsInverse ("rotate translate", &crtc_to_fb, &fb_to_crtc);
536
537	/* reflection */
538	scale_x = F (1);
539	scale_dx = 0;
540	scale_y = F (1);
541	scale_dy = 0;
542	if (rotation & RR_Reflect_X)
543	{
544	    scale_x = F(-1);
545	    if (rotation & (RR_Rotate_0|RR_Rotate_180))
546		scale_dx = F(mode_w);
547	    else
548		scale_dx = F(mode_h);
549	}
550	if (rotation & RR_Reflect_Y)
551	{
552	    scale_y = F(-1);
553	    if (rotation & (RR_Rotate_0|RR_Rotate_180))
554		scale_dy = F(mode_h);
555	    else
556		scale_dy = F(mode_w);
557	}
558
559	PictureTransformScale (&crtc_to_fb, &fb_to_crtc, scale_x, scale_y);
560	PictureTransformIsInverse ("scale", &crtc_to_fb, &fb_to_crtc);
561
562	PictureTransformTranslate (&crtc_to_fb, &fb_to_crtc, scale_dx, scale_dy);
563	PictureTransformIsInverse ("scale translate", &crtc_to_fb, &fb_to_crtc);
564
565    }
566
567    /*
568     * If the untranslated transformation is the identity,
569     * disable the shadow buffer
570     */
571    if (PictureTransformIsIdentity (&crtc_to_fb))
572    {
573	crtc->transform_in_use = FALSE;
574	PictureTransformInitTranslate (&crtc->crtc_to_framebuffer,
575				       F (-crtc->x), F (-crtc->y));
576	PictureTransformInitTranslate (&crtc->framebuffer_to_crtc,
577				       F ( crtc->x), F ( crtc->y));
578	xf86RotateDestroy (crtc);
579    }
580    else
581    {
582	PictureTransformTranslate (&crtc_to_fb, &fb_to_crtc, F(crtc->x), F(crtc->y));
583	PictureTransformIsInverse ("offset", &crtc_to_fb, &fb_to_crtc);
584
585	/*
586	 * these are the size of the shadow pixmap, which
587	 * matches the mode, not the pre-rotated copy in the
588	 * frame buffer
589	 */
590	int	    width = mode->HDisplay;
591	int	    height = mode->VDisplay;
592	void	    *shadowData = crtc->rotatedData;
593	PixmapPtr   shadow = crtc->rotatedPixmap;
594	int	    old_width = shadow ? shadow->drawable.width : 0;
595	int	    old_height = shadow ? shadow->drawable.height : 0;
596
597	/* Allocate memory for rotation */
598	if (old_width != width || old_height != height)
599	{
600	    if (shadow || shadowData)
601	    {
602		crtc->funcs->shadow_destroy (crtc, shadow, shadowData);
603		crtc->rotatedPixmap = NULL;
604		crtc->rotatedData = NULL;
605	    }
606	    shadowData = crtc->funcs->shadow_allocate (crtc, width, height);
607	    if (!shadowData)
608		goto bail1;
609	    crtc->rotatedData = shadowData;
610	    /* shadow will be damaged in xf86RotatePrepare */
611	}
612	else
613	{
614	    /* mark shadowed area as damaged so it will be repainted */
615	    xf86CrtcDamageShadow (crtc);
616	}
617
618	if (!xf86_config->rotation_damage)
619	{
620	    /* Create damage structure */
621	    xf86_config->rotation_damage = DamageCreate (NULL, NULL,
622						DamageReportNone,
623						TRUE, pScreen, pScreen);
624	    if (!xf86_config->rotation_damage)
625		goto bail2;
626
627	    /* Wrap block handler */
628	    xf86_config->BlockHandler = pScreen->BlockHandler;
629	    pScreen->BlockHandler = xf86RotateBlockHandler;
630	}
631	if (0)
632	{
633    bail2:
634	    if (shadow || shadowData)
635	    {
636		crtc->funcs->shadow_destroy (crtc, shadow, shadowData);
637		crtc->rotatedPixmap = NULL;
638		crtc->rotatedData = NULL;
639	    }
640    bail1:
641	    if (old_width && old_height)
642		crtc->rotatedPixmap = crtc->funcs->shadow_create (crtc,
643								  NULL,
644								  old_width,
645								  old_height);
646	    return FALSE;
647	}
648	crtc->transform_in_use = TRUE;
649	crtc->crtc_to_framebuffer = crtc_to_fb;
650	crtc->framebuffer_to_crtc = fb_to_crtc;
651	crtc->bounds.x1 = 0;
652	crtc->bounds.x2 = crtc->mode.HDisplay;
653	crtc->bounds.y1 = 0;
654	crtc->bounds.y2 = crtc->mode.VDisplay;
655	PictureTransformBounds (&crtc->bounds, &crtc_to_fb);
656    }
657
658    /* All done */
659    return TRUE;
660}
661