drm/shared-core/radeon_ms_output.c

338 lines
8.7 KiB
C

/*
* Copyright 2007 Jérôme Glisse
* Copyright 2007 Alex Deucher
* Copyright 2007 Dave Airlie
* All Rights Reserved.
*
* 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 on 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 (including the
* next paragraph) 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
* NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
* THEIR SUPPLIERS 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.
*/
#include "radeon_ms.h"
static struct radeon_ms_output *radeon_ms_connector_get_output(
struct drm_radeon_private *dev_priv,
struct radeon_ms_connector *connector, int i)
{
if (connector->outputs[i] < 0) {
return NULL;
}
if (connector->outputs[i] >= RADEON_MAX_OUTPUTS) {
return NULL;
}
i = connector->outputs[i];
if (dev_priv->outputs[i] == NULL) {
return NULL;
}
if (dev_priv->outputs[i]->connector == NULL) {
return dev_priv->outputs[i];
}
if (dev_priv->outputs[i]->connector == connector) {
return dev_priv->outputs[i];
}
return NULL;
}
static void radeon_ms_output_dpms(struct drm_output *output, int mode)
{
struct drm_radeon_private *dev_priv = output->dev->dev_private;
struct radeon_ms_connector *connector = output->driver_private;
struct radeon_ms_output *routput = NULL;
int i;
if (connector == NULL) {
return;
}
for (i = 0; i < RADEON_MAX_OUTPUTS; i++) {
routput = radeon_ms_connector_get_output(dev_priv,
connector, i);
if (routput) {
routput->connector = connector;
routput->dpms(routput, mode);
}
}
radeon_ms_gpu_dpms(output->dev);
}
static int radeon_ms_output_mode_valid(struct drm_output *output,
struct drm_display_mode *mode)
{
struct radeon_ms_connector *connector = output->driver_private;
if (connector == NULL) {
return MODE_ERROR;
}
return MODE_OK;
}
static bool radeon_ms_output_mode_fixup(struct drm_output *output,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void radeon_ms_output_prepare(struct drm_output *output)
{
if (output->funcs->dpms) {
output->funcs->dpms(output, DPMSModeOff);
}
}
static void radeon_ms_output_commit(struct drm_output *output)
{
if (output->funcs->dpms) {
output->funcs->dpms(output, DPMSModeOn);
}
}
static void radeon_ms_output_mode_set(struct drm_output *output,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_radeon_private *dev_priv = output->dev->dev_private;
struct radeon_ms_connector *connector = output->driver_private;
struct radeon_ms_crtc *crtc;
struct radeon_ms_output *routput = NULL;
int i;
if (connector == NULL) {
return;
}
if (output->crtc == NULL) {
return;
}
crtc = output->crtc->driver_private;
connector->crtc = crtc->crtc;
/* catch unknown crtc */
switch (connector->crtc) {
case 1:
case 2:
break;
default:
/* error */
return;
}
for (i = 0; i < RADEON_MAX_OUTPUTS; i++) {
routput = radeon_ms_connector_get_output(dev_priv,
connector, i);
if (routput) {
routput->connector = connector;
routput->mode_set(routput, mode, adjusted_mode);
}
}
}
static enum drm_output_status radeon_ms_output_detect(struct drm_output *output)
{
struct radeon_ms_connector *connector = output->driver_private;
if (connector == NULL || connector->i2c == NULL) {
return output_status_unknown;
}
kfree(connector->edid);
connector->edid = drm_get_edid(output, &connector->i2c->adapter);
if (connector->edid == NULL) {
return output_status_unknown;
}
return output_status_connected;
}
static int radeon_ms_output_get_modes(struct drm_output *output)
{
struct radeon_ms_connector *connector = output->driver_private;
int ret = 0;
if (connector == NULL || connector->i2c == NULL) {
return 0;
}
if (connector->edid == NULL) {
return 0;
}
drm_mode_output_update_edid_property(output, connector->edid);
ret = drm_add_edid_modes(output, connector->edid);
kfree(connector->edid);
connector->edid = NULL;
return ret;
}
static void radeon_ms_output_cleanup(struct drm_output *output)
{
struct radeon_ms_connector *connector = output->driver_private;
if (connector == NULL) {
return;
}
if (connector->edid) {
kfree(connector->edid);
}
connector->edid = NULL;
connector->output = NULL;
output->driver_private = NULL;
}
static const struct drm_output_funcs radeon_ms_output_funcs = {
.dpms = radeon_ms_output_dpms,
.save = NULL,
.restore = NULL,
.mode_valid = radeon_ms_output_mode_valid,
.mode_fixup = radeon_ms_output_mode_fixup,
.prepare = radeon_ms_output_prepare,
.mode_set = radeon_ms_output_mode_set,
.commit = radeon_ms_output_commit,
.detect = radeon_ms_output_detect,
.get_modes = radeon_ms_output_get_modes,
.cleanup = radeon_ms_output_cleanup,
};
void radeon_ms_connectors_destroy(struct drm_device *dev)
{
struct drm_radeon_private *dev_priv = dev->dev_private;
struct radeon_ms_connector *connector = NULL;
int i = 0;
for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
if (dev_priv->connectors[i]) {
connector = dev_priv->connectors[i];
dev_priv->connectors[i] = NULL;
if (connector->output) {
drm_output_destroy(connector->output);
connector->output = NULL;
}
if (connector->i2c) {
radeon_ms_i2c_destroy(connector->i2c);
connector->i2c = NULL;
}
drm_free(connector,
sizeof(struct radeon_ms_connector),
DRM_MEM_DRIVER);
}
}
}
int radeon_ms_connectors_from_properties(struct drm_device *dev)
{
struct drm_radeon_private *dev_priv = dev->dev_private;
struct radeon_ms_connector *connector = NULL;
struct drm_output *output = NULL;
int i = 0;
radeon_ms_connectors_destroy(dev);
for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
if (dev_priv->properties->connectors[i]) {
connector =
drm_alloc(sizeof(struct radeon_ms_connector),
DRM_MEM_DRIVER);
if (connector == NULL) {
radeon_ms_connectors_destroy(dev);
return -ENOMEM;
}
memcpy(connector,
dev_priv->properties->connectors[i],
sizeof(struct radeon_ms_connector));
connector->i2c = radeon_ms_i2c_create(dev,
connector->i2c_reg, connector->type);
if (connector->i2c == NULL) {
radeon_ms_connectors_destroy(dev);
return -ENOMEM;
}
output = drm_output_create(dev,
&radeon_ms_output_funcs,
connector->type);
if (output == NULL) {
radeon_ms_connectors_destroy(dev);
return -EINVAL;
}
connector->output = output;
output->driver_private = connector;
output->possible_crtcs = 0x3;
dev_priv->connectors[i] = connector;
}
}
return 0;
}
void radeon_ms_outputs_destroy(struct drm_device *dev)
{
struct drm_radeon_private *dev_priv = dev->dev_private;
int i = 0;
for (i = 0; i < RADEON_MAX_OUTPUTS; i++) {
if (dev_priv->outputs[i]) {
drm_free(dev_priv->outputs[i],
sizeof(struct radeon_ms_output),
DRM_MEM_DRIVER);
dev_priv->outputs[i] = NULL;
}
}
}
int radeon_ms_outputs_from_properties(struct drm_device *dev)
{
struct drm_radeon_private *dev_priv = dev->dev_private;
int i = 0;
radeon_ms_outputs_destroy(dev);
for (i = 0; i < RADEON_MAX_OUTPUTS; i++) {
if (dev_priv->properties->outputs[i]) {
dev_priv->outputs[i] =
drm_alloc(sizeof(struct radeon_ms_output),
DRM_MEM_DRIVER);
if (dev_priv->outputs[i] == NULL) {
radeon_ms_outputs_destroy(dev);
return -ENOMEM;
}
memcpy(dev_priv->outputs[i],
dev_priv->properties->outputs[i],
sizeof(struct radeon_ms_output));
dev_priv->outputs[i]->dev = dev;
dev_priv->outputs[i]->initialize(dev_priv->outputs[i]);
}
}
return 0;
}
void radeon_ms_outputs_restore(struct drm_device *dev,
struct radeon_state *state)
{
struct drm_radeon_private *dev_priv = dev->dev_private;
int i;
for (i = 0; i < RADEON_MAX_OUTPUTS; i++) {
if (dev_priv->outputs[i]) {
dev_priv->outputs[i]->restore(dev_priv->outputs[i],
state);
}
}
}
void radeon_ms_outputs_save(struct drm_device *dev, struct radeon_state *state)
{
struct drm_radeon_private *dev_priv = dev->dev_private;
int i;
for (i = 0; i < RADEON_MAX_OUTPUTS; i++) {
if (dev_priv->outputs[i]) {
dev_priv->outputs[i]->save(dev_priv->outputs[i], state);
}
}
}