/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Dave Airlie
* Alon Levy
*/
#include "linux/crc32.h"
#include "qxl_drv.h"
#include "qxl_object.h"
#include "drm_crtc_helper.h"
static void qxl_crtc_set_to_mode(struct qxl_device *qdev,
struct drm_connector *connector,
struct qxl_head *head)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode, *t;
int width = head->width;
int height = head->height;
if (width < 320 || height < 240) {
qxl_io_log(qdev, "%s: bad head: %dx%d", width, height);
width = 1024;
height = 768;
}
if (width * height * 4 > 16*1024*1024) {
width = 1024;
height = 768;
}
/* TODO: go over regular modes and removed preferred? */
list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
drm_mode_remove(connector, mode);
mode = drm_cvt_mode(dev, width, height, 60, false, false, false);
mode->type |= DRM_MODE_TYPE_PREFERRED;
mode->status = MODE_OK;
drm_mode_probed_add(connector, mode);
qxl_io_log(qdev, "%s: %d x %d\n", __func__, width, height);
}
void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev)
{
struct drm_connector *connector;
int i;
struct drm_device *dev = qdev->ddev;
i = 0;
qxl_io_log(qdev, "%s: %d, %d\n", __func__,
dev->mode_config.num_connector,
qdev->monitors_config->count);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (i > qdev->monitors_config->count) {
/* crtc will be reported as disabled */
continue;
}
qxl_crtc_set_to_mode(qdev, connector,
&qdev->monitors_config->heads[i]);
++i;
}
}
void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count)
{
if (qdev->client_monitors_config &&
count > qdev->client_monitors_config->count) {
kfree(qdev->client_monitors_config);
qdev->client_monitors_config = NULL;
}
if (!qdev->client_monitors_config) {
qdev->client_monitors_config = kzalloc(
sizeof(struct qxl_monitors_config) +
sizeof(struct qxl_head) * count, GFP_KERNEL);
if (!qdev->client_monitors_config) {
qxl_io_log(qdev,
"%s: allocation failure for %u heads\n",
__func__, count);
return;
}
}
qdev->client_monitors_config->count = count;
}
static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev)
{
int i;
int num_monitors;
uint32_t crc;
BUG_ON(!qdev->monitors_config);
num_monitors = qdev->rom->client_monitors_config.count;
crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config,
sizeof(qdev->rom->client_monitors_config));
if (crc != qdev->rom->client_monitors_config_crc) {
qxl_io_log(qdev, "crc mismatch: have %X (%d) != %X\n", crc,
sizeof(qdev->rom->client_monitors_config),
qdev->rom->client_monitors_config_crc);
return 1;
}
if (num_monitors > qdev->monitors_config->max_allowed) {
DRM_INFO("client monitors list will be truncated: %d < %d\n",
qdev->monitors_config->max_allowed, num_monitors);
num_monitors = qdev->monitors_config->max_allowed;
} else {
num_monitors = qdev->rom->client_monitors_config.count;
}
qxl_alloc_client_monitors_config(qdev, num_monitors);
/* we copy max from the client but it isn't used */
qdev->client_monitors_config->max_allowed =
qdev->monitors_config->max_allowed;
for (i = 0 ; i < qdev->client_monitors_config->count ; ++i) {
struct qxl_urect *c_rect =
&qdev