103b705cfSriastradh/*
203b705cfSriastradh * Copyright © 2013 Intel Corporation
303b705cfSriastradh *
403b705cfSriastradh * Permission is hereby granted, free of charge, to any person obtaining a
503b705cfSriastradh * copy of this software and associated documentation files (the "Software"),
603b705cfSriastradh * to deal in the Software without restriction, including without limitation
703b705cfSriastradh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
803b705cfSriastradh * and/or sell copies of the Software, and to permit persons to whom the
903b705cfSriastradh * Software is furnished to do so, subject to the following conditions:
1003b705cfSriastradh *
1103b705cfSriastradh * The above copyright notice and this permission notice (including the next
1203b705cfSriastradh * paragraph) shall be included in all copies or substantial portions of the
1303b705cfSriastradh * Software.
1403b705cfSriastradh *
1503b705cfSriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1603b705cfSriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1703b705cfSriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1803b705cfSriastradh * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1903b705cfSriastradh * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2003b705cfSriastradh * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2103b705cfSriastradh * SOFTWARE.
2203b705cfSriastradh *
2303b705cfSriastradh * Authors:
2403b705cfSriastradh *	Chris Wilson <chris@chris-wilson.co.uk>
2503b705cfSriastradh *
2603b705cfSriastradh */
2703b705cfSriastradh
2803b705cfSriastradh#ifdef HAVE_CONFIG_H
2903b705cfSriastradh#include "config.h"
3003b705cfSriastradh#endif
3103b705cfSriastradh
3203b705cfSriastradh#include "sna.h"
3303b705cfSriastradh
3442542f5fSchristosstatic bool add_fake_output(struct sna *sna, bool late);
3542542f5fSchristos
3603b705cfSriastradhstatic void
3703b705cfSriastradhsna_crtc_dpms(xf86CrtcPtr crtc, int mode)
3803b705cfSriastradh{
3903b705cfSriastradh}
4003b705cfSriastradh
4142542f5fSchristosstatic char *outputs_for_crtc(xf86CrtcPtr crtc, char *outputs, int max)
4203b705cfSriastradh{
4342542f5fSchristos	xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(crtc->scrn);
4442542f5fSchristos	int len, i;
4503b705cfSriastradh
4642542f5fSchristos	for (i = len = 0; i < config->num_output; i++) {
4742542f5fSchristos		xf86OutputPtr output = config->output[i];
4803b705cfSriastradh
4942542f5fSchristos		if (output->crtc != crtc)
5042542f5fSchristos			continue;
5142542f5fSchristos
5242542f5fSchristos		len += snprintf(outputs+len, max-len, "%s, ", output->name);
5342542f5fSchristos	}
5442542f5fSchristos	assert(len >= 2);
5542542f5fSchristos	outputs[len-2] = '\0';
5642542f5fSchristos
5742542f5fSchristos	return outputs;
5803b705cfSriastradh}
5903b705cfSriastradh
6042542f5fSchristosstatic const char *rotation_to_str(Rotation rotation)
6103b705cfSriastradh{
6242542f5fSchristos	switch (rotation & RR_Rotate_All) {
6342542f5fSchristos	case 0:
6442542f5fSchristos	case RR_Rotate_0: return "normal";
6542542f5fSchristos	case RR_Rotate_90: return "left";
6642542f5fSchristos	case RR_Rotate_180: return "inverted";
6742542f5fSchristos	case RR_Rotate_270: return "right";
6842542f5fSchristos	default: return "unknown";
6942542f5fSchristos	}
7003b705cfSriastradh}
7103b705cfSriastradh
7242542f5fSchristosstatic const char *reflection_to_str(Rotation rotation)
7303b705cfSriastradh{
7442542f5fSchristos	switch (rotation & RR_Reflect_All) {
7542542f5fSchristos	case 0: return "none";
7642542f5fSchristos	case RR_Reflect_X: return "X axis";
7742542f5fSchristos	case RR_Reflect_Y: return "Y axis";
7842542f5fSchristos	case RR_Reflect_X | RR_Reflect_Y: return "X and Y axes";
7942542f5fSchristos	default: return "invalid";
8042542f5fSchristos	}
8103b705cfSriastradh}
8203b705cfSriastradh
8342542f5fSchristosstatic Bool
8442542f5fSchristossna_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
8542542f5fSchristos			Rotation rotation, int x, int y)
8603b705cfSriastradh{
8742542f5fSchristos	char outputs[256];
8842542f5fSchristos
8942542f5fSchristos	xf86DrvMsg(crtc->scrn->scrnIndex, X_INFO,
9042542f5fSchristos		   "switch to mode %dx%d on %s, position (%d, %d), rotation %s, reflection %s\n",
9142542f5fSchristos		   mode->HDisplay, mode->VDisplay,
9242542f5fSchristos		   outputs_for_crtc(crtc, outputs, sizeof(outputs)),
9342542f5fSchristos		   x, y, rotation_to_str(rotation), reflection_to_str(rotation));
9442542f5fSchristos
9542542f5fSchristos	return TRUE;
9603b705cfSriastradh}
9703b705cfSriastradh
9803b705cfSriastradhstatic void
9903b705cfSriastradhsna_crtc_destroy(xf86CrtcPtr crtc)
10003b705cfSriastradh{
10103b705cfSriastradh}
10203b705cfSriastradh
10303b705cfSriastradhstatic const xf86CrtcFuncsRec sna_crtc_funcs = {
10403b705cfSriastradh	.dpms = sna_crtc_dpms,
10503b705cfSriastradh	.set_mode_major = sna_crtc_set_mode_major,
10603b705cfSriastradh	.destroy = sna_crtc_destroy,
10703b705cfSriastradh};
10803b705cfSriastradh
10903b705cfSriastradhstatic void
11003b705cfSriastradhsna_output_create_resources(xf86OutputPtr output)
11103b705cfSriastradh{
11203b705cfSriastradh}
11303b705cfSriastradh
11403b705cfSriastradhstatic Bool
11503b705cfSriastradhsna_output_set_property(xf86OutputPtr output, Atom property,
11603b705cfSriastradh			    RRPropertyValuePtr value)
11703b705cfSriastradh{
11803b705cfSriastradh	return TRUE;
11903b705cfSriastradh}
12003b705cfSriastradh
12103b705cfSriastradhstatic Bool
12203b705cfSriastradhsna_output_get_property(xf86OutputPtr output, Atom property)
12303b705cfSriastradh{
12403b705cfSriastradh	return FALSE;
12503b705cfSriastradh}
12603b705cfSriastradh
12703b705cfSriastradhstatic void
12803b705cfSriastradhsna_output_dpms(xf86OutputPtr output, int dpms)
12903b705cfSriastradh{
13003b705cfSriastradh}
13103b705cfSriastradh
13203b705cfSriastradhstatic xf86OutputStatus
13303b705cfSriastradhsna_output_detect(xf86OutputPtr output)
13403b705cfSriastradh{
13542542f5fSchristos	DBG(("%s(%s) has user modes? %d\n",
13642542f5fSchristos	     __FUNCTION__, output->name,
13742542f5fSchristos	     output->randr_output && output->randr_output->numUserModes));
13842542f5fSchristos
13942542f5fSchristos	if (output->randr_output && output->randr_output->numUserModes) {
14042542f5fSchristos		xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(output->scrn);
14142542f5fSchristos
14242542f5fSchristos		if (xf86_config->output[xf86_config->num_output-1] == output)
14342542f5fSchristos			add_fake_output(to_sna(output->scrn), true);
14442542f5fSchristos
14542542f5fSchristos		return XF86OutputStatusConnected;
14642542f5fSchristos	}
14742542f5fSchristos
14803b705cfSriastradh	return XF86OutputStatusDisconnected;
14903b705cfSriastradh}
15003b705cfSriastradh
15103b705cfSriastradhstatic Bool
15203b705cfSriastradhsna_output_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
15303b705cfSriastradh{
15442542f5fSchristos	if (mode->type & M_T_DEFAULT)
15542542f5fSchristos		return MODE_BAD;
15642542f5fSchristos
15703b705cfSriastradh	return MODE_OK;
15803b705cfSriastradh}
15903b705cfSriastradh
16003b705cfSriastradhstatic DisplayModePtr
16103b705cfSriastradhsna_output_get_modes(xf86OutputPtr output)
16203b705cfSriastradh{
16342542f5fSchristos	return NULL;
16403b705cfSriastradh}
16503b705cfSriastradh
16603b705cfSriastradhstatic void
16703b705cfSriastradhsna_output_destroy(xf86OutputPtr output)
16803b705cfSriastradh{
16903b705cfSriastradh}
17003b705cfSriastradh
17103b705cfSriastradhstatic const xf86OutputFuncsRec sna_output_funcs = {
17203b705cfSriastradh	.create_resources = sna_output_create_resources,
17303b705cfSriastradh#ifdef RANDR_12_INTERFACE
17403b705cfSriastradh	.set_property = sna_output_set_property,
17503b705cfSriastradh	.get_property = sna_output_get_property,
17603b705cfSriastradh#endif
17703b705cfSriastradh	.dpms = sna_output_dpms,
17803b705cfSriastradh	.detect = sna_output_detect,
17903b705cfSriastradh	.mode_valid = sna_output_mode_valid,
18003b705cfSriastradh
18103b705cfSriastradh	.get_modes = sna_output_get_modes,
18203b705cfSriastradh	.destroy = sna_output_destroy
18303b705cfSriastradh};
18403b705cfSriastradh
18503b705cfSriastradhstatic Bool
18603b705cfSriastradhsna_mode_resize(ScrnInfoPtr scrn, int width, int height)
18703b705cfSriastradh{
188fe8aea9eSmrg	ScreenPtr screen = xf86ScrnToScreen(scrn);
18942542f5fSchristos	PixmapPtr new_front;
19003b705cfSriastradh
19103b705cfSriastradh	DBG(("%s (%d, %d) -> (%d, %d)\n", __FUNCTION__,
19203b705cfSriastradh	     scrn->virtualX, scrn->virtualY,
19303b705cfSriastradh	     width, height));
19403b705cfSriastradh
19503b705cfSriastradh	if (scrn->virtualX == width && scrn->virtualY == height)
19603b705cfSriastradh		return TRUE;
19703b705cfSriastradh
19842542f5fSchristos	assert(to_sna_from_screen(screen)->front);
19942542f5fSchristos	assert(screen->GetScreenPixmap(screen) == to_sna_from_screen(screen)->front);
20003b705cfSriastradh
20103b705cfSriastradh	DBG(("%s: creating new framebuffer %dx%d\n",
20203b705cfSriastradh	     __FUNCTION__, width, height));
20303b705cfSriastradh
20403b705cfSriastradh	new_front = screen->CreatePixmap(screen,
20503b705cfSriastradh					 width, height, scrn->depth,
20642542f5fSchristos					 0);
20703b705cfSriastradh	if (!new_front)
20803b705cfSriastradh		return FALSE;
20903b705cfSriastradh
21003b705cfSriastradh	scrn->virtualX = width;
21103b705cfSriastradh	scrn->virtualY = height;
21203b705cfSriastradh	scrn->displayWidth = width;
21303b705cfSriastradh
21442542f5fSchristos	screen->SetScreenPixmap(new_front);
21542542f5fSchristos	assert(screen->GetScreenPixmap(screen) == new_front);
21642542f5fSchristos	assert(to_sna_from_screen(screen)->front == new_front);
21703b705cfSriastradh
21842542f5fSchristos	screen->DestroyPixmap(new_front);
21903b705cfSriastradh
22003b705cfSriastradh	return TRUE;
22103b705cfSriastradh}
22203b705cfSriastradh
22303b705cfSriastradhstatic const xf86CrtcConfigFuncsRec sna_mode_funcs = {
22403b705cfSriastradh	sna_mode_resize
22503b705cfSriastradh};
22603b705cfSriastradh
22742542f5fSchristosstatic bool add_fake_output(struct sna *sna, bool late)
22842542f5fSchristos{
22942542f5fSchristos	ScrnInfoPtr scrn = sna->scrn;
23042542f5fSchristos	xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
23142542f5fSchristos	xf86OutputPtr output;
23242542f5fSchristos	xf86CrtcPtr crtc;
23342542f5fSchristos	RROutputPtr clones[32];
23442542f5fSchristos	RRCrtcPtr crtcs[32];
23542542f5fSchristos	char buf[80];
23642542f5fSchristos	int i, len;
23742542f5fSchristos
23842542f5fSchristos	if (sna->mode.num_fake >= 32)
23942542f5fSchristos		return false;
24042542f5fSchristos
24142542f5fSchristos	DBG(("%s(late=%d, num_fake=%d)\n", __FUNCTION__, late, sna->mode.num_fake+1));
24242542f5fSchristos
24342542f5fSchristos	crtc = xf86CrtcCreate(scrn, &sna_crtc_funcs);
24442542f5fSchristos	if (crtc == NULL)
24542542f5fSchristos		return false;
24642542f5fSchristos
24742542f5fSchristos	len = sprintf(buf, "VIRTUAL%d", sna->mode.num_fake+1);
24842542f5fSchristos	output = xf86OutputCreate(scrn, &sna_output_funcs, buf);
24942542f5fSchristos	if (!output) {
25042542f5fSchristos		xf86CrtcDestroy(crtc);
25142542f5fSchristos		return false;
25242542f5fSchristos	}
25342542f5fSchristos
25442542f5fSchristos	output->mm_width = 0;
25542542f5fSchristos	output->mm_height = 0;
25642542f5fSchristos	output->interlaceAllowed = FALSE;
25742542f5fSchristos	output->subpixel_order = SubPixelNone;
258fe8aea9eSmrg	output->status = XF86OutputStatusDisconnected;
25942542f5fSchristos
26042542f5fSchristos	output->possible_crtcs = ~((1 << sna->mode.num_real_crtc) - 1);
26142542f5fSchristos	output->possible_clones = ~((1 << sna->mode.num_real_output) - 1);
26242542f5fSchristos
26342542f5fSchristos	if (late) {
26442542f5fSchristos		ScreenPtr screen = xf86ScrnToScreen(scrn);
26542542f5fSchristos
26642542f5fSchristos		crtc->randr_crtc = RRCrtcCreate(screen, crtc);
26742542f5fSchristos		output->randr_output = RROutputCreate(screen, buf, len, output);
26842542f5fSchristos		if (crtc->randr_crtc == NULL || output->randr_output == NULL) {
26942542f5fSchristos			xf86OutputDestroy(output);
27042542f5fSchristos			xf86CrtcDestroy(crtc);
27142542f5fSchristos			return false;
27242542f5fSchristos		}
27342542f5fSchristos
27442542f5fSchristos		RRPostPendingProperties(output->randr_output);
27542542f5fSchristos
27642542f5fSchristos		for (i = sna->mode.num_real_output; i < xf86_config->num_output; i++)
27742542f5fSchristos			clones[i - sna->mode.num_real_output] = xf86_config->output[i]->randr_output;
27842542f5fSchristos		assert(i - sna->mode.num_real_output == sna->mode.num_fake + 1);
27942542f5fSchristos
28042542f5fSchristos		for (i = sna->mode.num_real_crtc; i < xf86_config->num_crtc; i++)
28142542f5fSchristos			crtcs[i - sna->mode.num_real_crtc] = xf86_config->crtc[i]->randr_crtc;
28242542f5fSchristos		assert(i - sna->mode.num_real_crtc == sna->mode.num_fake + 1);
28342542f5fSchristos
28442542f5fSchristos		for (i = sna->mode.num_real_output; i < xf86_config->num_output; i++) {
28542542f5fSchristos			RROutputPtr rr_output = xf86_config->output[i]->randr_output;
28642542f5fSchristos
28742542f5fSchristos			if (!RROutputSetCrtcs(rr_output, crtcs, sna->mode.num_fake + 1) ||
28842542f5fSchristos			    !RROutputSetClones(rr_output, clones, sna->mode.num_fake + 1))
28942542f5fSchristos				goto err;
29042542f5fSchristos		}
29142542f5fSchristos
29242542f5fSchristos		RRCrtcSetRotations(crtc->randr_crtc,
29342542f5fSchristos				   RR_Rotate_All | RR_Reflect_All);
294fe8aea9eSmrg		if (!RRCrtcGammaSetSize(crtc->randr_crtc, 256))
295fe8aea9eSmrg			goto err;
29642542f5fSchristos	}
29742542f5fSchristos
29842542f5fSchristos	sna->mode.num_fake++;
29942542f5fSchristos	xf86DrvMsg(scrn->scrnIndex, X_INFO,
30042542f5fSchristos		   "Enabled output %s\n",
30142542f5fSchristos		   output->name);
30242542f5fSchristos	return true;
30342542f5fSchristos
30442542f5fSchristoserr:
30542542f5fSchristos	for (i = 0; i < xf86_config->num_output; i++) {
30642542f5fSchristos		output = xf86_config->output[i];
30742542f5fSchristos		if (output->driver_private)
30842542f5fSchristos			continue;
30942542f5fSchristos
31042542f5fSchristos		xf86OutputDestroy(output);
311fe8aea9eSmrg		i--;
31242542f5fSchristos	}
31342542f5fSchristos
31442542f5fSchristos	for (i = 0; i < xf86_config->num_crtc; i++) {
31542542f5fSchristos		crtc = xf86_config->crtc[i];
31642542f5fSchristos		if (crtc->driver_private)
31742542f5fSchristos			continue;
318fe8aea9eSmrg
31942542f5fSchristos		xf86CrtcDestroy(crtc);
320fe8aea9eSmrg		i--;
32142542f5fSchristos	}
32242542f5fSchristos	sna->mode.num_fake = -1;
32342542f5fSchristos	return false;
32442542f5fSchristos}
32542542f5fSchristos
32642542f5fSchristosbool sna_mode_fake_init(struct sna *sna, int num_fake)
32703b705cfSriastradh{
32842542f5fSchristos	bool ret;
32942542f5fSchristos
33042542f5fSchristos	if (num_fake == 0)
33142542f5fSchristos		return true;
33242542f5fSchristos
33342542f5fSchristos	if (sna->mode.num_real_crtc == 0) {
33442542f5fSchristos		xf86CrtcConfigInit(sna->scrn, &sna_mode_funcs);
33542542f5fSchristos		xf86CrtcSetSizeRange(sna->scrn, 1, 1, INT16_MAX, INT16_MAX);
33642542f5fSchristos	}
33742542f5fSchristos
33842542f5fSchristos	ret = true;
33942542f5fSchristos	while (ret && num_fake--)
34042542f5fSchristos		ret = add_fake_output(sna, false);
34142542f5fSchristos	return ret;
34203b705cfSriastradh}
343