/drivers/video/drm/drm_crtc.c |
---|
0,0 → 1,2477 |
/* |
* Copyright (c) 2006-2008 Intel Corporation |
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie> |
* Copyright (c) 2008 Red Hat Inc. |
* |
* DRM core CRTC related functions |
* |
* Permission to use, copy, modify, distribute, and sell this software and its |
* documentation for any purpose is hereby granted without fee, provided that |
* the above copyright notice appear in all copies and that both that copyright |
* notice and this permission notice appear in supporting documentation, and |
* that the name of the copyright holders not be used in advertising or |
* publicity pertaining to distribution of the software without specific, |
* written prior permission. The copyright holders make no representations |
* about the suitability of this software for any purpose. It is provided "as |
* is" without express or implied warranty. |
* |
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
* OF THIS SOFTWARE. |
* |
* Authors: |
* Keith Packard |
* Eric Anholt <eric@anholt.net> |
* Dave Airlie <airlied@linux.ie> |
* Jesse Barnes <jesse.barnes@intel.com> |
*/ |
#include <list.h> |
#include "drm.h" |
#include "drmP.h" |
#include "drm_crtc.h" |
struct drm_prop_enum_list { |
int type; |
char *name; |
}; |
/* Avoid boilerplate. I'm tired of typing. */ |
#define DRM_ENUM_NAME_FN(fnname, list) \ |
char *fnname(int val) \ |
{ \ |
int i; \ |
for (i = 0; i < ARRAY_SIZE(list); i++) { \ |
if (list[i].type == val) \ |
return list[i].name; \ |
} \ |
return "(unknown)"; \ |
} |
/* |
* Global properties |
*/ |
static struct drm_prop_enum_list drm_dpms_enum_list[] = |
{ { DRM_MODE_DPMS_ON, "On" }, |
{ DRM_MODE_DPMS_STANDBY, "Standby" }, |
{ DRM_MODE_DPMS_SUSPEND, "Suspend" }, |
{ DRM_MODE_DPMS_OFF, "Off" } |
}; |
DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) |
/* |
* Optional properties |
*/ |
static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = |
{ |
{ DRM_MODE_SCALE_NON_GPU, "Non-GPU" }, |
{ DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" }, |
{ DRM_MODE_SCALE_NO_SCALE, "No scale" }, |
{ DRM_MODE_SCALE_ASPECT, "Aspect" }, |
}; |
static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = |
{ |
{ DRM_MODE_DITHERING_OFF, "Off" }, |
{ DRM_MODE_DITHERING_ON, "On" }, |
}; |
/* |
* Non-global properties, but "required" for certain connectors. |
*/ |
static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = |
{ |
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ |
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ |
{ DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ |
}; |
DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) |
static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = |
{ |
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ |
{ DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ |
{ DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ |
}; |
DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, |
drm_dvi_i_subconnector_enum_list) |
static struct drm_prop_enum_list drm_tv_select_enum_list[] = |
{ |
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ |
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ |
{ DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ |
{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ |
}; |
DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) |
static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = |
{ |
{ DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ |
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ |
{ DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ |
{ DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ |
}; |
DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, |
drm_tv_subconnector_enum_list) |
struct drm_conn_prop_enum_list { |
int type; |
char *name; |
int count; |
}; |
/* |
* Connector and encoder types. |
*/ |
static struct drm_conn_prop_enum_list drm_connector_enum_list[] = |
{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, |
{ DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, |
{ DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, |
{ DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, |
{ DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, |
{ DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, |
{ DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, |
{ DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, |
{ DRM_MODE_CONNECTOR_Component, "Component", 0 }, |
{ DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN", 0 }, |
{ DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort", 0 }, |
{ DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 }, |
{ DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 }, |
}; |
static struct drm_prop_enum_list drm_encoder_enum_list[] = |
{ { DRM_MODE_ENCODER_NONE, "None" }, |
{ DRM_MODE_ENCODER_DAC, "DAC" }, |
{ DRM_MODE_ENCODER_TMDS, "TMDS" }, |
{ DRM_MODE_ENCODER_LVDS, "LVDS" }, |
{ DRM_MODE_ENCODER_TVDAC, "TV" }, |
}; |
char *drm_get_encoder_name(struct drm_encoder *encoder) |
{ |
static char buf[32]; |
snprintf(buf, 32, "%s-%d", |
drm_encoder_enum_list[encoder->encoder_type].name, |
encoder->base.id); |
return buf; |
} |
char *drm_get_connector_name(struct drm_connector *connector) |
{ |
static char buf[32]; |
snprintf(buf, 32, "%s-%d", |
drm_connector_enum_list[connector->connector_type].name, |
connector->connector_type_id); |
return buf; |
} |
EXPORT_SYMBOL(drm_get_connector_name); |
char *drm_get_connector_status_name(enum drm_connector_status status) |
{ |
if (status == connector_status_connected) |
return "connected"; |
else if (status == connector_status_disconnected) |
return "disconnected"; |
else |
return "unknown"; |
} |
/** |
* drm_mode_object_get - allocate a new identifier |
* @dev: DRM device |
* @ptr: object pointer, used to generate unique ID |
* @type: object type |
* |
* LOCKING: |
* |
* Create a unique identifier based on @ptr in @dev's identifier space. Used |
* for tracking modes, CRTCs and connectors. |
* |
* RETURNS: |
* New unique (relative to other objects in @dev) integer identifier for the |
* object. |
*/ |
static int drm_mode_object_get(struct drm_device *dev, |
struct drm_mode_object *obj, uint32_t obj_type) |
{ |
int new_id = 0; |
int ret; |
again: |
if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { |
DRM_ERROR("Ran out memory getting a mode number\n"); |
return -EINVAL; |
} |
// mutex_lock(&dev->mode_config.idr_mutex); |
ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); |
// mutex_unlock(&dev->mode_config.idr_mutex); |
if (ret == -EAGAIN) |
goto again; |
obj->id = new_id; |
obj->type = obj_type; |
return 0; |
} |
/** |
* drm_mode_object_put - free an identifer |
* @dev: DRM device |
* @id: ID to free |
* |
* LOCKING: |
* Caller must hold DRM mode_config lock. |
* |
* Free @id from @dev's unique identifier pool. |
*/ |
static void drm_mode_object_put(struct drm_device *dev, |
struct drm_mode_object *object) |
{ |
// mutex_lock(&dev->mode_config.idr_mutex); |
idr_remove(&dev->mode_config.crtc_idr, object->id); |
// mutex_unlock(&dev->mode_config.idr_mutex); |
} |
void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) |
{ |
struct drm_mode_object *obj = NULL; |
// mutex_lock(&dev->mode_config.idr_mutex); |
obj = idr_find(&dev->mode_config.crtc_idr, id); |
if (!obj || (obj->type != type) || (obj->id != id)) |
obj = NULL; |
// mutex_unlock(&dev->mode_config.idr_mutex); |
return obj; |
} |
EXPORT_SYMBOL(drm_mode_object_find); |
#if 0 |
/** |
* drm_crtc_from_fb - find the CRTC structure associated with an fb |
* @dev: DRM device |
* @fb: framebuffer in question |
* |
* LOCKING: |
* Caller must hold mode_config lock. |
* |
* Find CRTC in the mode_config structure that matches @fb. |
* |
* RETURNS: |
* Pointer to the CRTC or NULL if it wasn't found. |
*/ |
struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev, |
struct drm_framebuffer *fb) |
{ |
struct drm_crtc *crtc; |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->fb == fb) |
return crtc; |
} |
return NULL; |
} |
/** |
* drm_framebuffer_init - initialize a framebuffer |
* @dev: DRM device |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Allocates an ID for the framebuffer's parent mode object, sets its mode |
* functions & device file and adds it to the master fd list. |
* |
* RETURNS: |
* Zero on success, error code on falure. |
*/ |
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, |
const struct drm_framebuffer_funcs *funcs) |
{ |
int ret; |
ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); |
if (ret) { |
return ret; |
} |
fb->dev = dev; |
fb->funcs = funcs; |
dev->mode_config.num_fb++; |
list_add(&fb->head, &dev->mode_config.fb_list); |
return 0; |
} |
EXPORT_SYMBOL(drm_framebuffer_init); |
/** |
* drm_framebuffer_cleanup - remove a framebuffer object |
* @fb: framebuffer to remove |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes |
* it, setting it to NULL. |
*/ |
void drm_framebuffer_cleanup(struct drm_framebuffer *fb) |
{ |
struct drm_device *dev = fb->dev; |
struct drm_crtc *crtc; |
/* remove from any CRTC */ |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (crtc->fb == fb) |
crtc->fb = NULL; |
} |
drm_mode_object_put(dev, &fb->base); |
list_del(&fb->head); |
dev->mode_config.num_fb--; |
} |
EXPORT_SYMBOL(drm_framebuffer_cleanup); |
#endif |
/** |
* drm_crtc_init - Initialise a new CRTC object |
* @dev: DRM device |
* @crtc: CRTC object to init |
* @funcs: callbacks for the new CRTC |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Inits a new object created as base part of an driver crtc object. |
*/ |
void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, |
const struct drm_crtc_funcs *funcs) |
{ |
crtc->dev = dev; |
crtc->funcs = funcs; |
// mutex_lock(&dev->mode_config.mutex); |
drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); |
list_add_tail(&crtc->head, &dev->mode_config.crtc_list); |
dev->mode_config.num_crtc++; |
// mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_crtc_init); |
/** |
* drm_crtc_cleanup - Cleans up the core crtc usage. |
* @crtc: CRTC to cleanup |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Cleanup @crtc. Removes from drm modesetting space |
* does NOT free object, caller does that. |
*/ |
void drm_crtc_cleanup(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
if (crtc->gamma_store) { |
kfree(crtc->gamma_store); |
crtc->gamma_store = NULL; |
} |
drm_mode_object_put(dev, &crtc->base); |
list_del(&crtc->head); |
dev->mode_config.num_crtc--; |
} |
EXPORT_SYMBOL(drm_crtc_cleanup); |
/** |
* drm_mode_probed_add - add a mode to a connector's probed mode list |
* @connector: connector the new mode |
* @mode: mode data |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Add @mode to @connector's mode list for later use. |
*/ |
void drm_mode_probed_add(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
list_add(&mode->head, &connector->probed_modes); |
} |
EXPORT_SYMBOL(drm_mode_probed_add); |
/** |
* drm_mode_remove - remove and free a mode |
* @connector: connector list to modify |
* @mode: mode to remove |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Remove @mode from @connector's mode list, then free it. |
*/ |
void drm_mode_remove(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
list_del(&mode->head); |
kfree(mode); |
} |
EXPORT_SYMBOL(drm_mode_remove); |
/** |
* drm_connector_init - Init a preallocated connector |
* @dev: DRM device |
* @connector: the connector to init |
* @funcs: callbacks for this connector |
* @name: user visible name of the connector |
* |
* LOCKING: |
* Caller must hold @dev's mode_config lock. |
* |
* Initialises a preallocated connector. Connectors should be |
* subclassed as part of driver connector objects. |
*/ |
void drm_connector_init(struct drm_device *dev, |
struct drm_connector *connector, |
const struct drm_connector_funcs *funcs, |
int connector_type) |
{ |
// mutex_lock(&dev->mode_config.mutex); |
connector->dev = dev; |
connector->funcs = funcs; |
drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); |
connector->connector_type = connector_type; |
connector->connector_type_id = |
++drm_connector_enum_list[connector_type].count; /* TODO */ |
INIT_LIST_HEAD(&connector->user_modes); |
INIT_LIST_HEAD(&connector->probed_modes); |
INIT_LIST_HEAD(&connector->modes); |
connector->edid_blob_ptr = NULL; |
list_add_tail(&connector->head, &dev->mode_config.connector_list); |
dev->mode_config.num_connector++; |
drm_connector_attach_property(connector, |
dev->mode_config.edid_property, 0); |
drm_connector_attach_property(connector, |
dev->mode_config.dpms_property, 0); |
// mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_connector_init); |
/** |
* drm_connector_cleanup - cleans up an initialised connector |
* @connector: connector to cleanup |
* |
* LOCKING: |
* Caller must hold @dev's mode_config lock. |
* |
* Cleans up the connector but doesn't free the object. |
*/ |
void drm_connector_cleanup(struct drm_connector *connector) |
{ |
struct drm_device *dev = connector->dev; |
struct drm_display_mode *mode, *t; |
list_for_each_entry_safe(mode, t, &connector->probed_modes, head) |
drm_mode_remove(connector, mode); |
list_for_each_entry_safe(mode, t, &connector->modes, head) |
drm_mode_remove(connector, mode); |
list_for_each_entry_safe(mode, t, &connector->user_modes, head) |
drm_mode_remove(connector, mode); |
// mutex_lock(&dev->mode_config.mutex); |
drm_mode_object_put(dev, &connector->base); |
list_del(&connector->head); |
// mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_connector_cleanup); |
void drm_encoder_init(struct drm_device *dev, |
struct drm_encoder *encoder, |
const struct drm_encoder_funcs *funcs, |
int encoder_type) |
{ |
// mutex_lock(&dev->mode_config.mutex); |
encoder->dev = dev; |
drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); |
encoder->encoder_type = encoder_type; |
encoder->funcs = funcs; |
list_add_tail(&encoder->head, &dev->mode_config.encoder_list); |
dev->mode_config.num_encoder++; |
// mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_encoder_init); |
void drm_encoder_cleanup(struct drm_encoder *encoder) |
{ |
struct drm_device *dev = encoder->dev; |
// mutex_lock(&dev->mode_config.mutex); |
drm_mode_object_put(dev, &encoder->base); |
list_del(&encoder->head); |
// mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_encoder_cleanup); |
/** |
* drm_mode_create - create a new display mode |
* @dev: DRM device |
* |
* LOCKING: |
* Caller must hold DRM mode_config lock. |
* |
* Create a new drm_display_mode, give it an ID, and return it. |
* |
* RETURNS: |
* Pointer to new mode on success, NULL on error. |
*/ |
struct drm_display_mode *drm_mode_create(struct drm_device *dev) |
{ |
struct drm_display_mode *nmode; |
nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); |
if (!nmode) |
return NULL; |
drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE); |
return nmode; |
} |
EXPORT_SYMBOL(drm_mode_create); |
/** |
* drm_mode_destroy - remove a mode |
* @dev: DRM device |
* @mode: mode to remove |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Free @mode's unique identifier, then free it. |
*/ |
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) |
{ |
drm_mode_object_put(dev, &mode->base); |
kfree(mode); |
} |
EXPORT_SYMBOL(drm_mode_destroy); |
static int drm_mode_create_standard_connector_properties(struct drm_device *dev) |
{ |
struct drm_property *edid; |
struct drm_property *dpms; |
int i; |
/* |
* Standard properties (apply to all connectors) |
*/ |
edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | |
DRM_MODE_PROP_IMMUTABLE, |
"EDID", 0); |
dev->mode_config.edid_property = edid; |
dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM, |
"DPMS", ARRAY_SIZE(drm_dpms_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++) |
drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type, |
drm_dpms_enum_list[i].name); |
dev->mode_config.dpms_property = dpms; |
return 0; |
} |
/** |
* drm_mode_create_dvi_i_properties - create DVI-I specific connector properties |
* @dev: DRM device |
* |
* Called by a driver the first time a DVI-I connector is made. |
*/ |
int drm_mode_create_dvi_i_properties(struct drm_device *dev) |
{ |
struct drm_property *dvi_i_selector; |
struct drm_property *dvi_i_subconnector; |
int i; |
if (dev->mode_config.dvi_i_select_subconnector_property) |
return 0; |
dvi_i_selector = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, |
"select subconnector", |
ARRAY_SIZE(drm_dvi_i_select_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++) |
drm_property_add_enum(dvi_i_selector, i, |
drm_dvi_i_select_enum_list[i].type, |
drm_dvi_i_select_enum_list[i].name); |
dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; |
dvi_i_subconnector = |
drm_property_create(dev, DRM_MODE_PROP_ENUM | |
DRM_MODE_PROP_IMMUTABLE, |
"subconnector", |
ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++) |
drm_property_add_enum(dvi_i_subconnector, i, |
drm_dvi_i_subconnector_enum_list[i].type, |
drm_dvi_i_subconnector_enum_list[i].name); |
dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); |
/** |
* drm_create_tv_properties - create TV specific connector properties |
* @dev: DRM device |
* @num_modes: number of different TV formats (modes) supported |
* @modes: array of pointers to strings containing name of each format |
* |
* Called by a driver's TV initialization routine, this function creates |
* the TV specific connector properties for a given device. Caller is |
* responsible for allocating a list of format names and passing them to |
* this routine. |
*/ |
int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, |
char *modes[]) |
{ |
struct drm_property *tv_selector; |
struct drm_property *tv_subconnector; |
int i; |
if (dev->mode_config.tv_select_subconnector_property) |
return 0; |
/* |
* Basic connector properties |
*/ |
tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM, |
"select subconnector", |
ARRAY_SIZE(drm_tv_select_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++) |
drm_property_add_enum(tv_selector, i, |
drm_tv_select_enum_list[i].type, |
drm_tv_select_enum_list[i].name); |
dev->mode_config.tv_select_subconnector_property = tv_selector; |
tv_subconnector = |
drm_property_create(dev, DRM_MODE_PROP_ENUM | |
DRM_MODE_PROP_IMMUTABLE, "subconnector", |
ARRAY_SIZE(drm_tv_subconnector_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++) |
drm_property_add_enum(tv_subconnector, i, |
drm_tv_subconnector_enum_list[i].type, |
drm_tv_subconnector_enum_list[i].name); |
dev->mode_config.tv_subconnector_property = tv_subconnector; |
/* |
* Other, TV specific properties: margins & TV modes. |
*/ |
dev->mode_config.tv_left_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"left margin", 2); |
dev->mode_config.tv_left_margin_property->values[0] = 0; |
dev->mode_config.tv_left_margin_property->values[1] = 100; |
dev->mode_config.tv_right_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"right margin", 2); |
dev->mode_config.tv_right_margin_property->values[0] = 0; |
dev->mode_config.tv_right_margin_property->values[1] = 100; |
dev->mode_config.tv_top_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"top margin", 2); |
dev->mode_config.tv_top_margin_property->values[0] = 0; |
dev->mode_config.tv_top_margin_property->values[1] = 100; |
dev->mode_config.tv_bottom_margin_property = |
drm_property_create(dev, DRM_MODE_PROP_RANGE, |
"bottom margin", 2); |
dev->mode_config.tv_bottom_margin_property->values[0] = 0; |
dev->mode_config.tv_bottom_margin_property->values[1] = 100; |
dev->mode_config.tv_mode_property = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, |
"mode", num_modes); |
for (i = 0; i < num_modes; i++) |
drm_property_add_enum(dev->mode_config.tv_mode_property, i, |
i, modes[i]); |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_create_tv_properties); |
/** |
* drm_mode_create_scaling_mode_property - create scaling mode property |
* @dev: DRM device |
* |
* Called by a driver the first time it's needed, must be attached to desired |
* connectors. |
*/ |
int drm_mode_create_scaling_mode_property(struct drm_device *dev) |
{ |
struct drm_property *scaling_mode; |
int i; |
if (dev->mode_config.scaling_mode_property) |
return 0; |
scaling_mode = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode", |
ARRAY_SIZE(drm_scaling_mode_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) |
drm_property_add_enum(scaling_mode, i, |
drm_scaling_mode_enum_list[i].type, |
drm_scaling_mode_enum_list[i].name); |
dev->mode_config.scaling_mode_property = scaling_mode; |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); |
/** |
* drm_mode_create_dithering_property - create dithering property |
* @dev: DRM device |
* |
* Called by a driver the first time it's needed, must be attached to desired |
* connectors. |
*/ |
int drm_mode_create_dithering_property(struct drm_device *dev) |
{ |
struct drm_property *dithering_mode; |
int i; |
if (dev->mode_config.dithering_mode_property) |
return 0; |
dithering_mode = |
drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering", |
ARRAY_SIZE(drm_dithering_mode_enum_list)); |
for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++) |
drm_property_add_enum(dithering_mode, i, |
drm_dithering_mode_enum_list[i].type, |
drm_dithering_mode_enum_list[i].name); |
dev->mode_config.dithering_mode_property = dithering_mode; |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_create_dithering_property); |
/** |
* drm_mode_config_init - initialize DRM mode_configuration structure |
* @dev: DRM device |
* |
* LOCKING: |
* None, should happen single threaded at init time. |
* |
* Initialize @dev's mode_config structure, used for tracking the graphics |
* configuration of @dev. |
*/ |
void drm_mode_config_init(struct drm_device *dev) |
{ |
// mutex_init(&dev->mode_config.mutex); |
// mutex_init(&dev->mode_config.idr_mutex); |
INIT_LIST_HEAD(&dev->mode_config.fb_list); |
INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list); |
INIT_LIST_HEAD(&dev->mode_config.crtc_list); |
INIT_LIST_HEAD(&dev->mode_config.connector_list); |
INIT_LIST_HEAD(&dev->mode_config.encoder_list); |
INIT_LIST_HEAD(&dev->mode_config.property_list); |
INIT_LIST_HEAD(&dev->mode_config.property_blob_list); |
idr_init(&dev->mode_config.crtc_idr); |
// mutex_lock(&dev->mode_config.mutex); |
drm_mode_create_standard_connector_properties(dev); |
// mutex_unlock(&dev->mode_config.mutex); |
/* Just to be sure */ |
dev->mode_config.num_fb = 0; |
dev->mode_config.num_connector = 0; |
dev->mode_config.num_crtc = 0; |
dev->mode_config.num_encoder = 0; |
} |
EXPORT_SYMBOL(drm_mode_config_init); |
int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) |
{ |
uint32_t total_objects = 0; |
total_objects += dev->mode_config.num_crtc; |
total_objects += dev->mode_config.num_connector; |
total_objects += dev->mode_config.num_encoder; |
if (total_objects == 0) |
return -EINVAL; |
group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); |
if (!group->id_list) |
return -ENOMEM; |
group->num_crtcs = 0; |
group->num_connectors = 0; |
group->num_encoders = 0; |
return 0; |
} |
int drm_mode_group_init_legacy_group(struct drm_device *dev, |
struct drm_mode_group *group) |
{ |
struct drm_crtc *crtc; |
struct drm_encoder *encoder; |
struct drm_connector *connector; |
int ret; |
if ((ret = drm_mode_group_init(dev, group))) |
return ret; |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
group->id_list[group->num_crtcs++] = crtc->base.id; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) |
group->id_list[group->num_crtcs + group->num_encoders++] = |
encoder->base.id; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
group->id_list[group->num_crtcs + group->num_encoders + |
group->num_connectors++] = connector->base.id; |
return 0; |
} |
/** |
* drm_mode_config_cleanup - free up DRM mode_config info |
* @dev: DRM device |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Free up all the connectors and CRTCs associated with this DRM device, then |
* free up the framebuffers and associated buffer objects. |
* |
* FIXME: cleanup any dangling user buffer objects too |
*/ |
void drm_mode_config_cleanup(struct drm_device *dev) |
{ |
struct drm_connector *connector, *ot; |
struct drm_crtc *crtc, *ct; |
struct drm_encoder *encoder, *enct; |
struct drm_framebuffer *fb, *fbt; |
struct drm_property *property, *pt; |
list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, |
head) { |
encoder->funcs->destroy(encoder); |
} |
list_for_each_entry_safe(connector, ot, |
&dev->mode_config.connector_list, head) { |
connector->funcs->destroy(connector); |
} |
list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, |
head) { |
drm_property_destroy(dev, property); |
} |
list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
fb->funcs->destroy(fb); |
} |
list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { |
crtc->funcs->destroy(crtc); |
} |
} |
EXPORT_SYMBOL(drm_mode_config_cleanup); |
/** |
* drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo |
* @out: drm_mode_modeinfo struct to return to the user |
* @in: drm_display_mode to use |
* |
* LOCKING: |
* None. |
* |
* Convert a drm_display_mode into a drm_mode_modeinfo structure to return to |
* the user. |
*/ |
void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, |
struct drm_display_mode *in) |
{ |
out->clock = in->clock; |
out->hdisplay = in->hdisplay; |
out->hsync_start = in->hsync_start; |
out->hsync_end = in->hsync_end; |
out->htotal = in->htotal; |
out->hskew = in->hskew; |
out->vdisplay = in->vdisplay; |
out->vsync_start = in->vsync_start; |
out->vsync_end = in->vsync_end; |
out->vtotal = in->vtotal; |
out->vscan = in->vscan; |
out->vrefresh = in->vrefresh; |
out->flags = in->flags; |
out->type = in->type; |
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); |
out->name[DRM_DISPLAY_MODE_LEN-1] = 0; |
} |
/** |
* drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode |
* @out: drm_display_mode to return to the user |
* @in: drm_mode_modeinfo to use |
* |
* LOCKING: |
* None. |
* |
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to |
* the caller. |
*/ |
void drm_crtc_convert_umode(struct drm_display_mode *out, |
struct drm_mode_modeinfo *in) |
{ |
out->clock = in->clock; |
out->hdisplay = in->hdisplay; |
out->hsync_start = in->hsync_start; |
out->hsync_end = in->hsync_end; |
out->htotal = in->htotal; |
out->hskew = in->hskew; |
out->vdisplay = in->vdisplay; |
out->vsync_start = in->vsync_start; |
out->vsync_end = in->vsync_end; |
out->vtotal = in->vtotal; |
out->vscan = in->vscan; |
out->vrefresh = in->vrefresh; |
out->flags = in->flags; |
out->type = in->type; |
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); |
out->name[DRM_DISPLAY_MODE_LEN-1] = 0; |
} |
#if 0 |
/** |
* drm_mode_getresources - get graphics configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Construct a set of configuration description structures and return |
* them to the user, including CRTC, connector and framebuffer configuration. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getresources(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_card_res *card_res = data; |
struct list_head *lh; |
struct drm_framebuffer *fb; |
struct drm_connector *connector; |
struct drm_crtc *crtc; |
struct drm_encoder *encoder; |
int ret = 0; |
int connector_count = 0; |
int crtc_count = 0; |
int fb_count = 0; |
int encoder_count = 0; |
int copied = 0, i; |
uint32_t __user *fb_id; |
uint32_t __user *crtc_id; |
uint32_t __user *connector_id; |
uint32_t __user *encoder_id; |
struct drm_mode_group *mode_group; |
mutex_lock(&dev->mode_config.mutex); |
/* |
* For the non-control nodes we need to limit the list of resources |
* by IDs in the group list for this node |
*/ |
list_for_each(lh, &file_priv->fbs) |
fb_count++; |
mode_group = &file_priv->master->minor->mode_group; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
list_for_each(lh, &dev->mode_config.crtc_list) |
crtc_count++; |
list_for_each(lh, &dev->mode_config.connector_list) |
connector_count++; |
list_for_each(lh, &dev->mode_config.encoder_list) |
encoder_count++; |
} else { |
crtc_count = mode_group->num_crtcs; |
connector_count = mode_group->num_connectors; |
encoder_count = mode_group->num_encoders; |
} |
card_res->max_height = dev->mode_config.max_height; |
card_res->min_height = dev->mode_config.min_height; |
card_res->max_width = dev->mode_config.max_width; |
card_res->min_width = dev->mode_config.min_width; |
/* handle this in 4 parts */ |
/* FBs */ |
if (card_res->count_fbs >= fb_count) { |
copied = 0; |
fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; |
list_for_each_entry(fb, &file_priv->fbs, head) { |
if (put_user(fb->base.id, fb_id + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
card_res->count_fbs = fb_count; |
/* CRTCs */ |
if (card_res->count_crtcs >= crtc_count) { |
copied = 0; |
crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, |
head) { |
DRM_DEBUG("CRTC ID is %d\n", crtc->base.id); |
if (put_user(crtc->base.id, crtc_id + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} else { |
for (i = 0; i < mode_group->num_crtcs; i++) { |
if (put_user(mode_group->id_list[i], |
crtc_id + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
} |
card_res->count_crtcs = crtc_count; |
/* Encoders */ |
if (card_res->count_encoders >= encoder_count) { |
copied = 0; |
encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
list_for_each_entry(encoder, |
&dev->mode_config.encoder_list, |
head) { |
DRM_DEBUG("ENCODER ID is %d\n", |
encoder->base.id); |
if (put_user(encoder->base.id, encoder_id + |
copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} else { |
for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { |
if (put_user(mode_group->id_list[i], |
encoder_id + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
} |
card_res->count_encoders = encoder_count; |
/* Connectors */ |
if (card_res->count_connectors >= connector_count) { |
copied = 0; |
connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; |
if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
list_for_each_entry(connector, |
&dev->mode_config.connector_list, |
head) { |
DRM_DEBUG("CONNECTOR ID is %d\n", |
connector->base.id); |
if (put_user(connector->base.id, |
connector_id + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} else { |
int start = mode_group->num_crtcs + |
mode_group->num_encoders; |
for (i = start; i < start + mode_group->num_connectors; i++) { |
if (put_user(mode_group->id_list[i], |
connector_id + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
} |
card_res->count_connectors = connector_count; |
DRM_DEBUG("Counted %d %d %d\n", card_res->count_crtcs, |
card_res->count_connectors, card_res->count_encoders); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_mode_getcrtc - get CRTC configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* |
* Construct a CRTC configuration structure to return to the user. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getcrtc(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_crtc *crtc_resp = data; |
struct drm_crtc *crtc; |
struct drm_mode_object *obj; |
int ret = 0; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, crtc_resp->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
crtc_resp->x = crtc->x; |
crtc_resp->y = crtc->y; |
crtc_resp->gamma_size = crtc->gamma_size; |
if (crtc->fb) |
crtc_resp->fb_id = crtc->fb->base.id; |
else |
crtc_resp->fb_id = 0; |
if (crtc->enabled) { |
drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); |
crtc_resp->mode_valid = 1; |
} else { |
crtc_resp->mode_valid = 0; |
} |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_mode_getconnector - get connector configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* |
* Construct a connector configuration structure to return to the user. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getconnector(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_connector *out_resp = data; |
struct drm_mode_object *obj; |
struct drm_connector *connector; |
struct drm_display_mode *mode; |
int mode_count = 0; |
int props_count = 0; |
int encoders_count = 0; |
int ret = 0; |
int copied = 0; |
int i; |
struct drm_mode_modeinfo u_mode; |
struct drm_mode_modeinfo __user *mode_ptr; |
uint32_t __user *prop_ptr; |
uint64_t __user *prop_values; |
uint32_t __user *encoder_ptr; |
memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); |
DRM_DEBUG("connector id %d:\n", out_resp->connector_id); |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, out_resp->connector_id, |
DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
connector = obj_to_connector(obj); |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] != 0) { |
props_count++; |
} |
} |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] != 0) { |
encoders_count++; |
} |
} |
if (out_resp->count_modes == 0) { |
connector->funcs->fill_modes(connector, |
dev->mode_config.max_width, |
dev->mode_config.max_height); |
} |
/* delayed so we get modes regardless of pre-fill_modes state */ |
list_for_each_entry(mode, &connector->modes, head) |
mode_count++; |
out_resp->connector_id = connector->base.id; |
out_resp->connector_type = connector->connector_type; |
out_resp->connector_type_id = connector->connector_type_id; |
out_resp->mm_width = connector->display_info.width_mm; |
out_resp->mm_height = connector->display_info.height_mm; |
out_resp->subpixel = connector->display_info.subpixel_order; |
out_resp->connection = connector->status; |
if (connector->encoder) |
out_resp->encoder_id = connector->encoder->base.id; |
else |
out_resp->encoder_id = 0; |
/* |
* This ioctl is called twice, once to determine how much space is |
* needed, and the 2nd time to fill it. |
*/ |
if ((out_resp->count_modes >= mode_count) && mode_count) { |
copied = 0; |
mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr; |
list_for_each_entry(mode, &connector->modes, head) { |
drm_crtc_convert_to_umode(&u_mode, mode); |
if (copy_to_user(mode_ptr + copied, |
&u_mode, sizeof(u_mode))) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
out_resp->count_modes = mode_count; |
if ((out_resp->count_props >= props_count) && props_count) { |
copied = 0; |
prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr); |
prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr); |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] != 0) { |
if (put_user(connector->property_ids[i], |
prop_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
if (put_user(connector->property_values[i], |
prop_values + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
} |
out_resp->count_props = props_count; |
if ((out_resp->count_encoders >= encoders_count) && encoders_count) { |
copied = 0; |
encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr); |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] != 0) { |
if (put_user(connector->encoder_ids[i], |
encoder_ptr + copied)) { |
ret = -EFAULT; |
goto out; |
} |
copied++; |
} |
} |
} |
out_resp->count_encoders = encoders_count; |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
int drm_mode_getencoder(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_get_encoder *enc_resp = data; |
struct drm_mode_object *obj; |
struct drm_encoder *encoder; |
int ret = 0; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, enc_resp->encoder_id, |
DRM_MODE_OBJECT_ENCODER); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
encoder = obj_to_encoder(obj); |
if (encoder->crtc) |
enc_resp->crtc_id = encoder->crtc->base.id; |
else |
enc_resp->crtc_id = 0; |
enc_resp->encoder_type = encoder->encoder_type; |
enc_resp->encoder_id = encoder->base.id; |
enc_resp->possible_crtcs = encoder->possible_crtcs; |
enc_resp->possible_clones = encoder->possible_clones; |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_mode_setcrtc - set CRTC configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* |
* Build a new CRTC configuration based on user request. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_setcrtc(struct drm_device *dev, void *data, |
struct drm_file *file_priv) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_mode_crtc *crtc_req = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc, *crtcfb; |
struct drm_connector **connector_set = NULL, *connector; |
struct drm_framebuffer *fb = NULL; |
struct drm_display_mode *mode = NULL; |
struct drm_mode_set set; |
uint32_t __user *set_connectors_ptr; |
int ret = 0; |
int i; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, crtc_req->crtc_id, |
DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req->crtc_id); |
ret = -EINVAL; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
if (crtc_req->mode_valid) { |
/* If we have a mode we need a framebuffer. */ |
/* If we pass -1, set the mode with the currently bound fb */ |
if (crtc_req->fb_id == -1) { |
list_for_each_entry(crtcfb, |
&dev->mode_config.crtc_list, head) { |
if (crtcfb == crtc) { |
DRM_DEBUG("Using current fb for setmode\n"); |
fb = crtc->fb; |
} |
} |
} else { |
obj = drm_mode_object_find(dev, crtc_req->fb_id, |
DRM_MODE_OBJECT_FB); |
if (!obj) { |
DRM_DEBUG("Unknown FB ID%d\n", crtc_req->fb_id); |
ret = -EINVAL; |
goto out; |
} |
fb = obj_to_fb(obj); |
} |
mode = drm_mode_create(dev); |
drm_crtc_convert_umode(mode, &crtc_req->mode); |
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
} |
if (crtc_req->count_connectors == 0 && mode) { |
DRM_DEBUG("Count connectors is 0 but mode set\n"); |
ret = -EINVAL; |
goto out; |
} |
if (crtc_req->count_connectors > 0 && !mode && !fb) { |
DRM_DEBUG("Count connectors is %d but no mode or fb set\n", |
crtc_req->count_connectors); |
ret = -EINVAL; |
goto out; |
} |
if (crtc_req->count_connectors > 0) { |
u32 out_id; |
/* Avoid unbounded kernel memory allocation */ |
if (crtc_req->count_connectors > config->num_connector) { |
ret = -EINVAL; |
goto out; |
} |
connector_set = kmalloc(crtc_req->count_connectors * |
sizeof(struct drm_connector *), |
GFP_KERNEL); |
if (!connector_set) { |
ret = -ENOMEM; |
goto out; |
} |
for (i = 0; i < crtc_req->count_connectors; i++) { |
set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr; |
if (get_user(out_id, &set_connectors_ptr[i])) { |
ret = -EFAULT; |
goto out; |
} |
obj = drm_mode_object_find(dev, out_id, |
DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
DRM_DEBUG("Connector id %d unknown\n", out_id); |
ret = -EINVAL; |
goto out; |
} |
connector = obj_to_connector(obj); |
connector_set[i] = connector; |
} |
} |
set.crtc = crtc; |
set.x = crtc_req->x; |
set.y = crtc_req->y; |
set.mode = mode; |
set.connectors = connector_set; |
set.num_connectors = crtc_req->count_connectors; |
set.fb =fb; |
ret = crtc->funcs->set_config(&set); |
out: |
kfree(connector_set); |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
int drm_mode_cursor_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_cursor *req = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
int ret = 0; |
DRM_DEBUG("\n"); |
if (!req->flags) { |
DRM_ERROR("no operation set\n"); |
return -EINVAL; |
} |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
DRM_DEBUG("Unknown CRTC ID %d\n", req->crtc_id); |
ret = -EINVAL; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
if (req->flags & DRM_MODE_CURSOR_BO) { |
if (!crtc->funcs->cursor_set) { |
DRM_ERROR("crtc does not support cursor\n"); |
ret = -ENXIO; |
goto out; |
} |
/* Turns off the cursor if handle is 0 */ |
ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, |
req->width, req->height); |
} |
if (req->flags & DRM_MODE_CURSOR_MOVE) { |
if (crtc->funcs->cursor_move) { |
ret = crtc->funcs->cursor_move(crtc, req->x, req->y); |
} else { |
DRM_ERROR("crtc does not support cursor\n"); |
ret = -EFAULT; |
goto out; |
} |
} |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_mode_addfb - add an FB to the graphics configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Add a new FB to the specified CRTC, given a user request. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_addfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_fb_cmd *r = data; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_framebuffer *fb; |
int ret = 0; |
if ((config->min_width > r->width) || (r->width > config->max_width)) { |
DRM_ERROR("mode new framebuffer width not within limits\n"); |
return -EINVAL; |
} |
if ((config->min_height > r->height) || (r->height > config->max_height)) { |
DRM_ERROR("mode new framebuffer height not within limits\n"); |
return -EINVAL; |
} |
mutex_lock(&dev->mode_config.mutex); |
/* TODO check buffer is sufficently large */ |
/* TODO setup destructor callback */ |
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); |
if (!fb) { |
DRM_ERROR("could not create framebuffer\n"); |
ret = -EINVAL; |
goto out; |
} |
r->fb_id = fb->base.id; |
list_add(&fb->filp_head, &file_priv->fbs); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_mode_rmfb - remove an FB from the configuration |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Remove the FB specified by the user. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_rmfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_framebuffer *fb = NULL; |
struct drm_framebuffer *fbl = NULL; |
uint32_t *id = data; |
int ret = 0; |
int found = 0; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); |
/* TODO check that we realy get a framebuffer back. */ |
if (!obj) { |
DRM_ERROR("mode invalid framebuffer id\n"); |
ret = -EINVAL; |
goto out; |
} |
fb = obj_to_fb(obj); |
list_for_each_entry(fbl, &file_priv->fbs, filp_head) |
if (fb == fbl) |
found = 1; |
if (!found) { |
DRM_ERROR("tried to remove a fb that we didn't own\n"); |
ret = -EINVAL; |
goto out; |
} |
/* TODO release all crtc connected to the framebuffer */ |
/* TODO unhock the destructor from the buffer object */ |
list_del(&fb->filp_head); |
fb->funcs->destroy(fb); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_mode_getfb - get FB info |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* LOCKING: |
* Caller? (FIXME) |
* |
* Lookup the FB given its ID and return info about it. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_getfb(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_fb_cmd *r = data; |
struct drm_mode_object *obj; |
struct drm_framebuffer *fb; |
int ret = 0; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); |
if (!obj) { |
DRM_ERROR("invalid framebuffer id\n"); |
ret = -EINVAL; |
goto out; |
} |
fb = obj_to_fb(obj); |
r->height = fb->height; |
r->width = fb->width; |
r->depth = fb->depth; |
r->bpp = fb->bits_per_pixel; |
r->pitch = fb->pitch; |
fb->funcs->create_handle(fb, file_priv, &r->handle); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_fb_release - remove and free the FBs on this file |
* @filp: file * from the ioctl |
* |
* LOCKING: |
* Takes mode config lock. |
* |
* Destroy all the FBs associated with @filp. |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
void drm_fb_release(struct drm_file *priv) |
{ |
struct drm_device *dev = priv->minor->dev; |
struct drm_framebuffer *fb, *tfb; |
mutex_lock(&dev->mode_config.mutex); |
list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { |
list_del(&fb->filp_head); |
fb->funcs->destroy(fb); |
} |
mutex_unlock(&dev->mode_config.mutex); |
} |
#endif |
/** |
* drm_mode_attachmode - add a mode to the user mode list |
* @dev: DRM device |
* @connector: connector to add the mode to |
* @mode: mode to add |
* |
* Add @mode to @connector's user mode list. |
*/ |
static int drm_mode_attachmode(struct drm_device *dev, |
struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
int ret = 0; |
list_add_tail(&mode->head, &connector->user_modes); |
return ret; |
} |
int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, |
struct drm_display_mode *mode) |
{ |
struct drm_connector *connector; |
int ret = 0; |
struct drm_display_mode *dup_mode; |
int need_dup = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder) |
break; |
if (connector->encoder->crtc == crtc) { |
if (need_dup) |
dup_mode = drm_mode_duplicate(dev, mode); |
else |
dup_mode = mode; |
ret = drm_mode_attachmode(dev, connector, dup_mode); |
if (ret) |
return ret; |
need_dup = 1; |
} |
} |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_attachmode_crtc); |
static int drm_mode_detachmode(struct drm_device *dev, |
struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
int found = 0; |
int ret = 0; |
struct drm_display_mode *match_mode, *t; |
list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { |
if (drm_mode_equal(match_mode, mode)) { |
list_del(&match_mode->head); |
drm_mode_destroy(dev, match_mode); |
found = 1; |
break; |
} |
} |
if (!found) |
ret = -EINVAL; |
return ret; |
} |
int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) |
{ |
struct drm_connector *connector; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
drm_mode_detachmode(dev, connector, mode); |
} |
return 0; |
} |
EXPORT_SYMBOL(drm_mode_detachmode_crtc); |
#if 0 |
/** |
* drm_fb_attachmode - Attach a user mode to an connector |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* This attaches a user specified mode to an connector. |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_attachmode_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_mode_cmd *mode_cmd = data; |
struct drm_connector *connector; |
struct drm_display_mode *mode; |
struct drm_mode_object *obj; |
struct drm_mode_modeinfo *umode = &mode_cmd->mode; |
int ret = 0; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
connector = obj_to_connector(obj); |
mode = drm_mode_create(dev); |
if (!mode) { |
ret = -ENOMEM; |
goto out; |
} |
drm_crtc_convert_umode(mode, umode); |
ret = drm_mode_attachmode(dev, connector, mode); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
/** |
* drm_fb_detachmode - Detach a user specified mode from an connector |
* @inode: inode from the ioctl |
* @filp: file * from the ioctl |
* @cmd: cmd from ioctl |
* @arg: arg from ioctl |
* |
* Called by the user via ioctl. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_mode_detachmode_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_mode_mode_cmd *mode_cmd = data; |
struct drm_connector *connector; |
struct drm_display_mode mode; |
struct drm_mode_modeinfo *umode = &mode_cmd->mode; |
int ret = 0; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
connector = obj_to_connector(obj); |
drm_crtc_convert_umode(&mode, umode); |
ret = drm_mode_detachmode(dev, connector, &mode); |
out: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
#endif |
struct drm_property *drm_property_create(struct drm_device *dev, int flags, |
const char *name, int num_values) |
{ |
struct drm_property *property = NULL; |
property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); |
if (!property) |
return NULL; |
if (num_values) { |
property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); |
if (!property->values) |
goto fail; |
} |
drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); |
property->flags = flags; |
property->num_values = num_values; |
INIT_LIST_HEAD(&property->enum_blob_list); |
if (name) |
strncpy(property->name, name, DRM_PROP_NAME_LEN); |
list_add_tail(&property->head, &dev->mode_config.property_list); |
return property; |
fail: |
kfree(property); |
return NULL; |
} |
EXPORT_SYMBOL(drm_property_create); |
int drm_property_add_enum(struct drm_property *property, int index, |
uint64_t value, const char *name) |
{ |
struct drm_property_enum *prop_enum; |
if (!(property->flags & DRM_MODE_PROP_ENUM)) |
return -EINVAL; |
if (!list_empty(&property->enum_blob_list)) { |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) { |
if (prop_enum->value == value) { |
strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); |
prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; |
return 0; |
} |
} |
} |
prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); |
if (!prop_enum) |
return -ENOMEM; |
strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); |
prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; |
prop_enum->value = value; |
property->values[index] = value; |
list_add_tail(&prop_enum->head, &property->enum_blob_list); |
return 0; |
} |
EXPORT_SYMBOL(drm_property_add_enum); |
void drm_property_destroy(struct drm_device *dev, struct drm_property *property) |
{ |
struct drm_property_enum *prop_enum, *pt; |
list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { |
list_del(&prop_enum->head); |
kfree(prop_enum); |
} |
if (property->num_values) |
kfree(property->values); |
drm_mode_object_put(dev, &property->base); |
list_del(&property->head); |
kfree(property); |
} |
EXPORT_SYMBOL(drm_property_destroy); |
int drm_connector_attach_property(struct drm_connector *connector, |
struct drm_property *property, uint64_t init_val) |
{ |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == 0) { |
connector->property_ids[i] = property->base.id; |
connector->property_values[i] = init_val; |
break; |
} |
} |
if (i == DRM_CONNECTOR_MAX_PROPERTY) |
return -EINVAL; |
return 0; |
} |
EXPORT_SYMBOL(drm_connector_attach_property); |
int drm_connector_property_set_value(struct drm_connector *connector, |
struct drm_property *property, uint64_t value) |
{ |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == property->base.id) { |
connector->property_values[i] = value; |
break; |
} |
} |
if (i == DRM_CONNECTOR_MAX_PROPERTY) |
return -EINVAL; |
return 0; |
} |
EXPORT_SYMBOL(drm_connector_property_set_value); |
int drm_connector_property_get_value(struct drm_connector *connector, |
struct drm_property *property, uint64_t *val) |
{ |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == property->base.id) { |
*val = connector->property_values[i]; |
break; |
} |
} |
if (i == DRM_CONNECTOR_MAX_PROPERTY) |
return -EINVAL; |
return 0; |
} |
EXPORT_SYMBOL(drm_connector_property_get_value); |
#if 0 |
int drm_mode_getproperty_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_mode_get_property *out_resp = data; |
struct drm_property *property; |
int enum_count = 0; |
int blob_count = 0; |
int value_count = 0; |
int ret = 0, i; |
int copied; |
struct drm_property_enum *prop_enum; |
struct drm_mode_property_enum __user *enum_ptr; |
struct drm_property_blob *prop_blob; |
uint32_t *blob_id_ptr; |
uint64_t __user *values_ptr; |
uint32_t __user *blob_length_ptr; |
// mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); |
if (!obj) { |
ret = -EINVAL; |
goto done; |
} |
property = obj_to_property(obj); |
if (property->flags & DRM_MODE_PROP_ENUM) { |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) |
enum_count++; |
} else if (property->flags & DRM_MODE_PROP_BLOB) { |
list_for_each_entry(prop_blob, &property->enum_blob_list, head) |
blob_count++; |
} |
value_count = property->num_values; |
strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); |
out_resp->name[DRM_PROP_NAME_LEN-1] = 0; |
out_resp->flags = property->flags; |
if ((out_resp->count_values >= value_count) && value_count) { |
values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr; |
for (i = 0; i < value_count; i++) { |
if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { |
ret = -EFAULT; |
goto done; |
} |
} |
} |
out_resp->count_values = value_count; |
if (property->flags & DRM_MODE_PROP_ENUM) { |
if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { |
copied = 0; |
enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr; |
list_for_each_entry(prop_enum, &property->enum_blob_list, head) { |
if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { |
ret = -EFAULT; |
goto done; |
} |
if (copy_to_user(&enum_ptr[copied].name, |
&prop_enum->name, DRM_PROP_NAME_LEN)) { |
ret = -EFAULT; |
goto done; |
} |
copied++; |
} |
} |
out_resp->count_enum_blobs = enum_count; |
} |
if (property->flags & DRM_MODE_PROP_BLOB) { |
if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { |
copied = 0; |
blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr; |
blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr; |
list_for_each_entry(prop_blob, &property->enum_blob_list, head) { |
if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { |
ret = -EFAULT; |
goto done; |
} |
if (put_user(prop_blob->length, blob_length_ptr + copied)) { |
ret = -EFAULT; |
goto done; |
} |
copied++; |
} |
} |
out_resp->count_enum_blobs = blob_count; |
} |
done: |
// mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
#endif |
static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, |
void *data) |
{ |
struct drm_property_blob *blob; |
if (!length || !data) |
return NULL; |
blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); |
if (!blob) |
return NULL; |
blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob)); |
blob->length = length; |
memcpy(blob->data, data, length); |
drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); |
list_add_tail(&blob->head, &dev->mode_config.property_blob_list); |
return blob; |
} |
static void drm_property_destroy_blob(struct drm_device *dev, |
struct drm_property_blob *blob) |
{ |
drm_mode_object_put(dev, &blob->base); |
list_del(&blob->head); |
kfree(blob); |
} |
#if 0 |
int drm_mode_getblob_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_object *obj; |
struct drm_mode_get_blob *out_resp = data; |
struct drm_property_blob *blob; |
int ret = 0; |
void *blob_ptr; |
mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); |
if (!obj) { |
ret = -EINVAL; |
goto done; |
} |
blob = obj_to_blob(obj); |
if (out_resp->length == blob->length) { |
blob_ptr = (void *)(unsigned long)out_resp->data; |
if (copy_to_user(blob_ptr, blob->data, blob->length)){ |
ret = -EFAULT; |
goto done; |
} |
} |
out_resp->length = blob->length; |
done: |
mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
#endif |
int drm_mode_connector_update_edid_property(struct drm_connector *connector, |
struct edid *edid) |
{ |
struct drm_device *dev = connector->dev; |
int ret = 0; |
if (connector->edid_blob_ptr) |
drm_property_destroy_blob(dev, connector->edid_blob_ptr); |
/* Delete edid, when there is none. */ |
if (!edid) { |
connector->edid_blob_ptr = NULL; |
ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, 0); |
return ret; |
} |
connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid); |
ret = drm_connector_property_set_value(connector, |
dev->mode_config.edid_property, |
connector->edid_blob_ptr->base.id); |
return ret; |
} |
EXPORT_SYMBOL(drm_mode_connector_update_edid_property); |
#if 0 |
int drm_mode_connector_property_set_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_connector_set_property *out_resp = data; |
struct drm_mode_object *obj; |
struct drm_property *property; |
struct drm_connector *connector; |
int ret = -EINVAL; |
int i; |
// mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); |
if (!obj) { |
goto out; |
} |
connector = obj_to_connector(obj); |
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) { |
if (connector->property_ids[i] == out_resp->prop_id) |
break; |
} |
if (i == DRM_CONNECTOR_MAX_PROPERTY) { |
goto out; |
} |
obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); |
if (!obj) { |
goto out; |
} |
property = obj_to_property(obj); |
if (property->flags & DRM_MODE_PROP_IMMUTABLE) |
goto out; |
if (property->flags & DRM_MODE_PROP_RANGE) { |
if (out_resp->value < property->values[0]) |
goto out; |
if (out_resp->value > property->values[1]) |
goto out; |
} else { |
int found = 0; |
for (i = 0; i < property->num_values; i++) { |
if (property->values[i] == out_resp->value) { |
found = 1; |
break; |
} |
} |
if (!found) { |
goto out; |
} |
} |
/* Do DPMS ourselves */ |
if (property == connector->dev->mode_config.dpms_property) { |
if (connector->funcs->dpms) |
(*connector->funcs->dpms)(connector, (int) out_resp->value); |
ret = 0; |
} else if (connector->funcs->set_property) |
ret = connector->funcs->set_property(connector, property, out_resp->value); |
/* store the property value if succesful */ |
if (!ret) |
drm_connector_property_set_value(connector, property, out_resp->value); |
out: |
// mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
#endif |
int drm_mode_connector_attach_encoder(struct drm_connector *connector, |
struct drm_encoder *encoder) |
{ |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] == 0) { |
connector->encoder_ids[i] = encoder->base.id; |
return 0; |
} |
} |
return -ENOMEM; |
} |
EXPORT_SYMBOL(drm_mode_connector_attach_encoder); |
void drm_mode_connector_detach_encoder(struct drm_connector *connector, |
struct drm_encoder *encoder) |
{ |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] == encoder->base.id) { |
connector->encoder_ids[i] = 0; |
if (connector->encoder == encoder) |
connector->encoder = NULL; |
break; |
} |
} |
} |
EXPORT_SYMBOL(drm_mode_connector_detach_encoder); |
bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, |
int gamma_size) |
{ |
crtc->gamma_size = gamma_size; |
crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); |
if (!crtc->gamma_store) { |
crtc->gamma_size = 0; |
return false; |
} |
return true; |
} |
EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); |
#if 0 |
int drm_mode_gamma_set_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_crtc_lut *crtc_lut = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
void *r_base, *g_base, *b_base; |
int size; |
int ret = 0; |
// mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
/* memcpy into gamma store */ |
if (crtc_lut->gamma_size != crtc->gamma_size) { |
ret = -EINVAL; |
goto out; |
} |
size = crtc_lut->gamma_size * (sizeof(uint16_t)); |
r_base = crtc->gamma_store; |
if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { |
ret = -EFAULT; |
goto out; |
} |
g_base = r_base + size; |
if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { |
ret = -EFAULT; |
goto out; |
} |
b_base = g_base + size; |
if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { |
ret = -EFAULT; |
goto out; |
} |
crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); |
out: |
// mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
int drm_mode_gamma_get_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv) |
{ |
struct drm_mode_crtc_lut *crtc_lut = data; |
struct drm_mode_object *obj; |
struct drm_crtc *crtc; |
void *r_base, *g_base, *b_base; |
int size; |
int ret = 0; |
// mutex_lock(&dev->mode_config.mutex); |
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); |
if (!obj) { |
ret = -EINVAL; |
goto out; |
} |
crtc = obj_to_crtc(obj); |
/* memcpy into gamma store */ |
if (crtc_lut->gamma_size != crtc->gamma_size) { |
ret = -EINVAL; |
goto out; |
} |
size = crtc_lut->gamma_size * (sizeof(uint16_t)); |
r_base = crtc->gamma_store; |
if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { |
ret = -EFAULT; |
goto out; |
} |
g_base = r_base + size; |
if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { |
ret = -EFAULT; |
goto out; |
} |
b_base = g_base + size; |
if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { |
ret = -EFAULT; |
goto out; |
} |
out: |
// mutex_unlock(&dev->mode_config.mutex); |
return ret; |
} |
#endif |
/drivers/video/drm/drm_crtc_helper.c |
---|
0,0 → 1,1095 |
/* |
* Copyright (c) 2006-2008 Intel Corporation |
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie> |
* |
* DRM core CRTC related functions |
* |
* Permission to use, copy, modify, distribute, and sell this software and its |
* documentation for any purpose is hereby granted without fee, provided that |
* the above copyright notice appear in all copies and that both that copyright |
* notice and this permission notice appear in supporting documentation, and |
* that the name of the copyright holders not be used in advertising or |
* publicity pertaining to distribution of the software without specific, |
* written prior permission. The copyright holders make no representations |
* about the suitability of this software for any purpose. It is provided "as |
* is" without express or implied warranty. |
* |
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
* OF THIS SOFTWARE. |
* |
* Authors: |
* Keith Packard |
* Eric Anholt <eric@anholt.net> |
* Dave Airlie <airlied@linux.ie> |
* Jesse Barnes <jesse.barnes@intel.com> |
*/ |
#include "drmP.h" |
#include "drm_crtc.h" |
#include "drm_crtc_helper.h" |
/* |
* Detailed mode info for 800x600@60Hz |
*/ |
static struct drm_display_mode std_modes[] = { |
{ DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 40000, 800, 840, |
968, 1056, 0, 600, 601, 605, 628, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, |
}; |
static void drm_mode_validate_flag(struct drm_connector *connector, |
int flags) |
{ |
struct drm_display_mode *mode, *t; |
if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE)) |
return; |
list_for_each_entry_safe(mode, t, &connector->modes, head) { |
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && |
!(flags & DRM_MODE_FLAG_INTERLACE)) |
mode->status = MODE_NO_INTERLACE; |
if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && |
!(flags & DRM_MODE_FLAG_DBLSCAN)) |
mode->status = MODE_NO_DBLESCAN; |
} |
return; |
} |
/** |
* drm_helper_probe_connector_modes - get complete set of display modes |
* @dev: DRM device |
* @maxX: max width for modes |
* @maxY: max height for modes |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Based on @dev's mode_config layout, scan all the connectors and try to detect |
* modes on them. Modes will first be added to the connector's probed_modes |
* list, then culled (based on validity and the @maxX, @maxY parameters) and |
* put into the normal modes list. |
* |
* Intended to be used either at bootup time or when major configuration |
* changes have occurred. |
* |
* FIXME: take into account monitor limits |
* |
* RETURNS: |
* Number of modes found on @connector. |
*/ |
int drm_helper_probe_single_connector_modes(struct drm_connector *connector, |
uint32_t maxX, uint32_t maxY) |
{ |
struct drm_device *dev = connector->dev; |
struct drm_display_mode *mode, *t; |
struct drm_connector_helper_funcs *connector_funcs = |
connector->helper_private; |
int count = 0; |
int mode_flags = 0; |
DRM_DEBUG("%s\n", drm_get_connector_name(connector)); |
/* set all modes to the unverified state */ |
list_for_each_entry_safe(mode, t, &connector->modes, head) |
mode->status = MODE_UNVERIFIED; |
connector->status = connector->funcs->detect(connector); |
if (connector->status == connector_status_disconnected) { |
DRM_DEBUG("%s is disconnected\n", |
drm_get_connector_name(connector)); |
/* TODO set EDID to NULL */ |
return 0; |
} |
count = (*connector_funcs->get_modes)(connector); |
if (!count) |
return 0; |
drm_mode_connector_list_update(connector); |
if (maxX && maxY) |
drm_mode_validate_size(dev, &connector->modes, maxX, |
maxY, 0); |
if (connector->interlace_allowed) |
mode_flags |= DRM_MODE_FLAG_INTERLACE; |
if (connector->doublescan_allowed) |
mode_flags |= DRM_MODE_FLAG_DBLSCAN; |
drm_mode_validate_flag(connector, mode_flags); |
list_for_each_entry_safe(mode, t, &connector->modes, head) { |
if (mode->status == MODE_OK) |
mode->status = connector_funcs->mode_valid(connector, |
mode); |
} |
drm_mode_prune_invalid(dev, &connector->modes, true); |
if (list_empty(&connector->modes)) |
return 0; |
drm_mode_sort(&connector->modes); |
DRM_DEBUG("Probed modes for %s\n", drm_get_connector_name(connector)); |
list_for_each_entry_safe(mode, t, &connector->modes, head) { |
mode->vrefresh = drm_mode_vrefresh(mode); |
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
drm_mode_debug_printmodeline(mode); |
} |
return count; |
} |
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); |
int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX, |
uint32_t maxY) |
{ |
struct drm_connector *connector; |
int count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
count += drm_helper_probe_single_connector_modes(connector, |
maxX, maxY); |
} |
return count; |
} |
EXPORT_SYMBOL(drm_helper_probe_connector_modes); |
static void drm_helper_add_std_modes(struct drm_device *dev, |
struct drm_connector *connector) |
{ |
struct drm_display_mode *mode, *t; |
int i; |
for (i = 0; i < ARRAY_SIZE(std_modes); i++) { |
struct drm_display_mode *stdmode; |
/* |
* When no valid EDID modes are available we end up |
* here and bailed in the past, now we add some standard |
* modes and move on. |
*/ |
stdmode = drm_mode_duplicate(dev, &std_modes[i]); |
drm_mode_probed_add(connector, stdmode); |
drm_mode_list_concat(&connector->probed_modes, |
&connector->modes); |
DRM_DEBUG("Adding mode %s to %s\n", stdmode->name, |
drm_get_connector_name(connector)); |
} |
drm_mode_sort(&connector->modes); |
DRM_DEBUG("Added std modes on %s\n", drm_get_connector_name(connector)); |
list_for_each_entry_safe(mode, t, &connector->modes, head) { |
mode->vrefresh = drm_mode_vrefresh(mode); |
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); |
drm_mode_debug_printmodeline(mode); |
} |
} |
/** |
* drm_helper_encoder_in_use - check if a given encoder is in use |
* @encoder: encoder to check |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Walk @encoders's DRM device's mode_config and see if it's in use. |
* |
* RETURNS: |
* True if @encoder is part of the mode_config, false otherwise. |
*/ |
bool drm_helper_encoder_in_use(struct drm_encoder *encoder) |
{ |
struct drm_connector *connector; |
struct drm_device *dev = encoder->dev; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
if (connector->encoder == encoder) |
return true; |
return false; |
} |
EXPORT_SYMBOL(drm_helper_encoder_in_use); |
/** |
* drm_helper_crtc_in_use - check if a given CRTC is in a mode_config |
* @crtc: CRTC to check |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Walk @crtc's DRM device's mode_config and see if it's in use. |
* |
* RETURNS: |
* True if @crtc is part of the mode_config, false otherwise. |
*/ |
bool drm_helper_crtc_in_use(struct drm_crtc *crtc) |
{ |
struct drm_encoder *encoder; |
struct drm_device *dev = crtc->dev; |
/* FIXME: Locking around list access? */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) |
if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) |
return true; |
return false; |
} |
EXPORT_SYMBOL(drm_helper_crtc_in_use); |
/** |
* drm_disable_unused_functions - disable unused objects |
* @dev: DRM device |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* If an connector or CRTC isn't part of @dev's mode_config, it can be disabled |
* by calling its dpms function, which should power it off. |
*/ |
void drm_helper_disable_unused_functions(struct drm_device *dev) |
{ |
struct drm_encoder *encoder; |
struct drm_encoder_helper_funcs *encoder_funcs; |
struct drm_crtc *crtc; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
encoder_funcs = encoder->helper_private; |
if (!drm_helper_encoder_in_use(encoder)) |
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
} |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
crtc->enabled = drm_helper_crtc_in_use(crtc); |
if (!crtc->enabled) { |
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); |
crtc->fb = NULL; |
} |
} |
} |
EXPORT_SYMBOL(drm_helper_disable_unused_functions); |
static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height) |
{ |
struct drm_display_mode *mode; |
list_for_each_entry(mode, &connector->modes, head) { |
if (drm_mode_width(mode) > width || |
drm_mode_height(mode) > height) |
continue; |
if (mode->type & DRM_MODE_TYPE_PREFERRED) |
return mode; |
} |
return NULL; |
} |
static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
{ |
bool enable; |
if (strict) { |
enable = connector->status == connector_status_connected; |
} else { |
enable = connector->status != connector_status_disconnected; |
} |
return enable; |
} |
static void drm_enable_connectors(struct drm_device *dev, bool *enabled) |
{ |
bool any_enabled = false; |
struct drm_connector *connector; |
int i = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
enabled[i] = drm_connector_enabled(connector, true); |
DRM_DEBUG("connector %d enabled? %s\n", connector->base.id, |
enabled[i] ? "yes" : "no"); |
any_enabled |= enabled[i]; |
i++; |
} |
if (any_enabled) |
return; |
i = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
enabled[i] = drm_connector_enabled(connector, false); |
i++; |
} |
} |
static bool drm_target_preferred(struct drm_device *dev, |
struct drm_display_mode **modes, |
bool *enabled, int width, int height) |
{ |
struct drm_connector *connector; |
int i = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (enabled[i] == false) { |
i++; |
continue; |
} |
DRM_DEBUG("looking for preferred mode on connector %d\n", |
connector->base.id); |
modes[i] = drm_has_preferred_mode(connector, width, height); |
/* No preferred modes, pick one off the list */ |
if (!modes[i] && !list_empty(&connector->modes)) { |
list_for_each_entry(modes[i], &connector->modes, head) |
break; |
} |
DRM_DEBUG("found mode %s\n", modes[i] ? modes[i]->name : |
"none"); |
i++; |
} |
return true; |
} |
static int drm_pick_crtcs(struct drm_device *dev, |
struct drm_crtc **best_crtcs, |
struct drm_display_mode **modes, |
int n, int width, int height) |
{ |
int c, o; |
struct drm_connector *connector; |
struct drm_connector_helper_funcs *connector_funcs; |
struct drm_encoder *encoder; |
struct drm_crtc *best_crtc; |
int my_score, best_score, score; |
struct drm_crtc **crtcs, *crtc; |
if (n == dev->mode_config.num_connector) |
return 0; |
c = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (c == n) |
break; |
c++; |
} |
best_crtcs[n] = NULL; |
best_crtc = NULL; |
best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height); |
if (modes[n] == NULL) |
return best_score; |
crtcs = kmalloc(dev->mode_config.num_connector * |
sizeof(struct drm_crtc *), GFP_KERNEL); |
if (!crtcs) |
return best_score; |
my_score = 1; |
if (connector->status == connector_status_connected) |
my_score++; |
if (drm_has_preferred_mode(connector, width, height)) |
my_score++; |
connector_funcs = connector->helper_private; |
encoder = connector_funcs->best_encoder(connector); |
if (!encoder) |
goto out; |
connector->encoder = encoder; |
/* select a crtc for this connector and then attempt to configure |
remaining connectors */ |
c = 0; |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if ((connector->encoder->possible_crtcs & (1 << c)) == 0) { |
c++; |
continue; |
} |
for (o = 0; o < n; o++) |
if (best_crtcs[o] == crtc) |
break; |
if (o < n) { |
/* ignore cloning for now */ |
c++; |
continue; |
} |
crtcs[n] = crtc; |
memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *)); |
score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1, |
width, height); |
if (score > best_score) { |
best_crtc = crtc; |
best_score = score; |
memcpy(best_crtcs, crtcs, |
dev->mode_config.num_connector * |
sizeof(struct drm_crtc *)); |
} |
c++; |
} |
out: |
kfree(crtcs); |
return best_score; |
} |
static void drm_setup_crtcs(struct drm_device *dev) |
{ |
struct drm_crtc **crtcs; |
struct drm_display_mode **modes; |
struct drm_encoder *encoder; |
struct drm_connector *connector; |
bool *enabled; |
int width, height; |
int i, ret; |
DRM_DEBUG("\n"); |
width = dev->mode_config.max_width; |
height = dev->mode_config.max_height; |
/* clean out all the encoder/crtc combos */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
encoder->crtc = NULL; |
} |
crtcs = kcalloc(dev->mode_config.num_connector, |
sizeof(struct drm_crtc *), GFP_KERNEL); |
modes = kcalloc(dev->mode_config.num_connector, |
sizeof(struct drm_display_mode *), GFP_KERNEL); |
enabled = kcalloc(dev->mode_config.num_connector, |
sizeof(bool), GFP_KERNEL); |
drm_enable_connectors(dev, enabled); |
ret = drm_target_preferred(dev, modes, enabled, width, height); |
if (!ret) |
DRM_ERROR("Unable to find initial modes\n"); |
DRM_DEBUG("picking CRTCs for %dx%d config\n", width, height); |
drm_pick_crtcs(dev, crtcs, modes, 0, width, height); |
i = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
struct drm_display_mode *mode = modes[i]; |
struct drm_crtc *crtc = crtcs[i]; |
if (connector->encoder == NULL) { |
i++; |
continue; |
} |
if (mode && crtc) { |
DRM_DEBUG("desired mode %s set on crtc %d\n", |
mode->name, crtc->base.id); |
crtc->desired_mode = mode; |
connector->encoder->crtc = crtc; |
} else |
connector->encoder->crtc = NULL; |
i++; |
} |
kfree(crtcs); |
kfree(modes); |
kfree(enabled); |
} |
/** |
* drm_encoder_crtc_ok - can a given crtc drive a given encoder? |
* @encoder: encoder to test |
* @crtc: crtc to test |
* |
* Return false if @encoder can't be driven by @crtc, true otherwise. |
*/ |
static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, |
struct drm_crtc *crtc) |
{ |
struct drm_device *dev; |
struct drm_crtc *tmp; |
int crtc_mask = 1; |
WARN(!crtc, "checking null crtc?"); |
dev = crtc->dev; |
list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { |
if (tmp == crtc) |
break; |
crtc_mask <<= 1; |
} |
if (encoder->possible_crtcs & crtc_mask) |
return true; |
return false; |
} |
/* |
* Check the CRTC we're going to map each output to vs. its current |
* CRTC. If they don't match, we have to disable the output and the CRTC |
* since the driver will have to re-route things. |
*/ |
static void |
drm_crtc_prepare_encoders(struct drm_device *dev) |
{ |
struct drm_encoder_helper_funcs *encoder_funcs; |
struct drm_encoder *encoder; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
encoder_funcs = encoder->helper_private; |
/* Disable unused encoders */ |
if (encoder->crtc == NULL) |
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
/* Disable encoders whose CRTC is about to change */ |
if (encoder_funcs->get_crtc && |
encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) |
(*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); |
} |
} |
/** |
* drm_crtc_set_mode - set a mode |
* @crtc: CRTC to program |
* @mode: mode to use |
* @x: width of mode |
* @y: height of mode |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Try to set @mode on @crtc. Give @crtc and its associated connectors a chance |
* to fixup or reject the mode prior to trying to set it. |
* |
* RETURNS: |
* True if the mode was set successfully, or false otherwise. |
*/ |
bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
int x, int y, |
struct drm_framebuffer *old_fb) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_display_mode *adjusted_mode, saved_mode; |
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
struct drm_encoder_helper_funcs *encoder_funcs; |
int saved_x, saved_y; |
struct drm_encoder *encoder; |
bool ret = true; |
adjusted_mode = drm_mode_duplicate(dev, mode); |
crtc->enabled = drm_helper_crtc_in_use(crtc); |
if (!crtc->enabled) |
return true; |
saved_mode = crtc->mode; |
saved_x = crtc->x; |
saved_y = crtc->y; |
/* Update crtc values up front so the driver can rely on them for mode |
* setting. |
*/ |
crtc->mode = *mode; |
crtc->x = x; |
crtc->y = y; |
/* Pass our mode to the connectors and the CRTC to give them a chance to |
* adjust it according to limitations or connector properties, and also |
* a chance to reject the mode entirely. |
*/ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc != crtc) |
continue; |
encoder_funcs = encoder->helper_private; |
if (!(ret = encoder_funcs->mode_fixup(encoder, mode, |
adjusted_mode))) { |
goto done; |
} |
} |
if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { |
goto done; |
} |
/* Prepare the encoders and CRTCs before setting the mode. */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc != crtc) |
continue; |
encoder_funcs = encoder->helper_private; |
/* Disable the encoders as the first thing we do. */ |
encoder_funcs->prepare(encoder); |
} |
drm_crtc_prepare_encoders(dev); |
crtc_funcs->prepare(crtc); |
/* Set up the DPLL and any encoders state that needs to adjust or depend |
* on the DPLL. |
*/ |
ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
if (!ret) |
goto done; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc != crtc) |
continue; |
DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder), |
mode->name, mode->base.id); |
encoder_funcs = encoder->helper_private; |
encoder_funcs->mode_set(encoder, mode, adjusted_mode); |
} |
/* Now enable the clocks, plane, pipe, and connectors that we set up. */ |
crtc_funcs->commit(crtc); |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc != crtc) |
continue; |
encoder_funcs = encoder->helper_private; |
encoder_funcs->commit(encoder); |
} |
/* XXX free adjustedmode */ |
drm_mode_destroy(dev, adjusted_mode); |
/* FIXME: add subpixel order */ |
done: |
if (!ret) { |
crtc->mode = saved_mode; |
crtc->x = saved_x; |
crtc->y = saved_y; |
} |
return ret; |
} |
EXPORT_SYMBOL(drm_crtc_helper_set_mode); |
/** |
* drm_crtc_helper_set_config - set a new config from userspace |
* @crtc: CRTC to setup |
* @crtc_info: user provided configuration |
* @new_mode: new mode to set |
* @connector_set: set of connectors for the new config |
* @fb: new framebuffer |
* |
* LOCKING: |
* Caller must hold mode config lock. |
* |
* Setup a new configuration, provided by the user in @crtc_info, and enable |
* it. |
* |
* RETURNS: |
* Zero. (FIXME) |
*/ |
int drm_crtc_helper_set_config(struct drm_mode_set *set) |
{ |
struct drm_device *dev; |
struct drm_crtc **save_crtcs, *new_crtc; |
struct drm_encoder **save_encoders, *new_encoder; |
struct drm_framebuffer *old_fb = NULL; |
bool save_enabled; |
bool mode_changed = false; |
bool fb_changed = false; |
struct drm_connector *connector; |
int count = 0, ro, fail = 0; |
struct drm_crtc_helper_funcs *crtc_funcs; |
int ret = 0; |
DRM_DEBUG("\n"); |
if (!set) |
return -EINVAL; |
if (!set->crtc) |
return -EINVAL; |
if (!set->crtc->helper_private) |
return -EINVAL; |
crtc_funcs = set->crtc->helper_private; |
DRM_DEBUG("crtc: %p %d fb: %p connectors: %p num_connectors: %d (x, y) (%i, %i)\n", |
set->crtc, set->crtc->base.id, set->fb, set->connectors, |
(int)set->num_connectors, set->x, set->y); |
dev = set->crtc->dev; |
/* save previous config */ |
save_enabled = set->crtc->enabled; |
/* |
* We do mode_config.num_connectors here since we'll look at the |
* CRTC and encoder associated with each connector later. |
*/ |
save_crtcs = kzalloc(dev->mode_config.num_connector * |
sizeof(struct drm_crtc *), GFP_KERNEL); |
if (!save_crtcs) |
return -ENOMEM; |
save_encoders = kzalloc(dev->mode_config.num_connector * |
sizeof(struct drm_encoders *), GFP_KERNEL); |
if (!save_encoders) { |
kfree(save_crtcs); |
return -ENOMEM; |
} |
/* We should be able to check here if the fb has the same properties |
* and then just flip_or_move it */ |
if (set->crtc->fb != set->fb) { |
/* If we have no fb then treat it as a full mode set */ |
if (set->crtc->fb == NULL) { |
DRM_DEBUG("crtc has no fb, full mode set\n"); |
mode_changed = true; |
} else if ((set->fb->bits_per_pixel != |
set->crtc->fb->bits_per_pixel) || |
set->fb->depth != set->crtc->fb->depth) |
fb_changed = true; |
else |
fb_changed = true; |
} |
if (set->x != set->crtc->x || set->y != set->crtc->y) |
fb_changed = true; |
if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
DRM_DEBUG("modes are different, full mode set\n"); |
drm_mode_debug_printmodeline(&set->crtc->mode); |
drm_mode_debug_printmodeline(set->mode); |
mode_changed = true; |
} |
/* a) traverse passed in connector list and get encoders for them */ |
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
struct drm_connector_helper_funcs *connector_funcs = |
connector->helper_private; |
save_encoders[count++] = connector->encoder; |
new_encoder = connector->encoder; |
for (ro = 0; ro < set->num_connectors; ro++) { |
if (set->connectors[ro] == connector) { |
new_encoder = connector_funcs->best_encoder(connector); |
/* if we can't get an encoder for a connector |
we are setting now - then fail */ |
if (new_encoder == NULL) |
/* don't break so fail path works correct */ |
fail = 1; |
break; |
} |
} |
if (new_encoder != connector->encoder) { |
DRM_DEBUG("encoder changed, full mode switch\n"); |
mode_changed = true; |
connector->encoder = new_encoder; |
} |
} |
if (fail) { |
ret = -EINVAL; |
goto fail_no_encoder; |
} |
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder) |
continue; |
save_crtcs[count++] = connector->encoder->crtc; |
if (connector->encoder->crtc == set->crtc) |
new_crtc = NULL; |
else |
new_crtc = connector->encoder->crtc; |
for (ro = 0; ro < set->num_connectors; ro++) { |
if (set->connectors[ro] == connector) |
new_crtc = set->crtc; |
} |
/* Make sure the new CRTC will work with the encoder */ |
if (new_crtc && |
!drm_encoder_crtc_ok(connector->encoder, new_crtc)) { |
ret = -EINVAL; |
goto fail_set_mode; |
} |
if (new_crtc != connector->encoder->crtc) { |
DRM_DEBUG("crtc changed, full mode switch\n"); |
mode_changed = true; |
connector->encoder->crtc = new_crtc; |
} |
DRM_DEBUG("setting connector %d crtc to %p\n", |
connector->base.id, new_crtc); |
} |
/* mode_set_base is not a required function */ |
if (fb_changed && !crtc_funcs->mode_set_base) |
mode_changed = true; |
if (mode_changed) { |
old_fb = set->crtc->fb; |
set->crtc->fb = set->fb; |
set->crtc->enabled = (set->mode != NULL); |
if (set->mode != NULL) { |
DRM_DEBUG("attempting to set mode from userspace\n"); |
drm_mode_debug_printmodeline(set->mode); |
if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
set->x, set->y, |
old_fb)) { |
DRM_ERROR("failed to set mode on crtc %p\n", |
set->crtc); |
ret = -EINVAL; |
goto fail_set_mode; |
} |
/* TODO are these needed? */ |
set->crtc->desired_x = set->x; |
set->crtc->desired_y = set->y; |
set->crtc->desired_mode = set->mode; |
} |
drm_helper_disable_unused_functions(dev); |
} else if (fb_changed) { |
old_fb = set->crtc->fb; |
if (set->crtc->fb != set->fb) |
set->crtc->fb = set->fb; |
ret = crtc_funcs->mode_set_base(set->crtc, |
set->x, set->y, old_fb); |
if (ret != 0) |
goto fail_set_mode; |
} |
kfree(save_encoders); |
kfree(save_crtcs); |
return 0; |
fail_set_mode: |
set->crtc->enabled = save_enabled; |
set->crtc->fb = old_fb; |
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
if (!connector->encoder) |
continue; |
connector->encoder->crtc = save_crtcs[count++]; |
} |
fail_no_encoder: |
kfree(save_crtcs); |
count = 0; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
connector->encoder = save_encoders[count++]; |
} |
kfree(save_encoders); |
return ret; |
} |
EXPORT_SYMBOL(drm_crtc_helper_set_config); |
bool drm_helper_plugged_event(struct drm_device *dev) |
{ |
DRM_DEBUG("\n"); |
drm_helper_probe_connector_modes(dev, dev->mode_config.max_width, |
dev->mode_config.max_height); |
drm_setup_crtcs(dev); |
/* alert the driver fb layer */ |
dev->mode_config.funcs->fb_changed(dev); |
/* FIXME: send hotplug event */ |
return true; |
} |
/** |
* drm_initial_config - setup a sane initial connector configuration |
* @dev: DRM device |
* |
* LOCKING: |
* Called at init time, must take mode config lock. |
* |
* Scan the CRTCs and connectors and try to put together an initial setup. |
* At the moment, this is a cloned configuration across all heads with |
* a new framebuffer object as the backing store. |
* |
* RETURNS: |
* Zero if everything went ok, nonzero otherwise. |
*/ |
bool drm_helper_initial_config(struct drm_device *dev) |
{ |
struct drm_connector *connector; |
int count = 0; |
count = drm_helper_probe_connector_modes(dev, |
dev->mode_config.max_width, |
dev->mode_config.max_height); |
/* |
* None of the available connectors had any modes, so add some |
* and try to light them up anyway |
*/ |
if (!count) { |
DRM_ERROR("connectors have no modes, using standard modes\n"); |
list_for_each_entry(connector, |
&dev->mode_config.connector_list, |
head) |
drm_helper_add_std_modes(dev, connector); |
} |
drm_setup_crtcs(dev); |
/* alert the driver fb layer */ |
dev->mode_config.funcs->fb_changed(dev); |
return 0; |
} |
EXPORT_SYMBOL(drm_helper_initial_config); |
static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) |
{ |
int dpms = DRM_MODE_DPMS_OFF; |
struct drm_connector *connector; |
struct drm_device *dev = encoder->dev; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
if (connector->encoder == encoder) |
if (connector->dpms < dpms) |
dpms = connector->dpms; |
return dpms; |
} |
static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) |
{ |
int dpms = DRM_MODE_DPMS_OFF; |
struct drm_connector *connector; |
struct drm_device *dev = crtc->dev; |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) |
if (connector->encoder && connector->encoder->crtc == crtc) |
if (connector->dpms < dpms) |
dpms = connector->dpms; |
return dpms; |
} |
/** |
* drm_helper_connector_dpms |
* @connector affected connector |
* @mode DPMS mode |
* |
* Calls the low-level connector DPMS function, then |
* calls appropriate encoder and crtc DPMS functions as well |
*/ |
void drm_helper_connector_dpms(struct drm_connector *connector, int mode) |
{ |
struct drm_encoder *encoder = connector->encoder; |
struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; |
int old_dpms; |
if (mode == connector->dpms) |
return; |
old_dpms = connector->dpms; |
connector->dpms = mode; |
/* from off to on, do crtc then encoder */ |
if (mode < old_dpms) { |
if (crtc) { |
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
if (crtc_funcs->dpms) |
(*crtc_funcs->dpms) (crtc, |
drm_helper_choose_crtc_dpms(crtc)); |
} |
if (encoder) { |
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
if (encoder_funcs->dpms) |
(*encoder_funcs->dpms) (encoder, |
drm_helper_choose_encoder_dpms(encoder)); |
} |
} |
/* from on to off, do encoder then crtc */ |
if (mode > old_dpms) { |
if (encoder) { |
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; |
if (encoder_funcs->dpms) |
(*encoder_funcs->dpms) (encoder, |
drm_helper_choose_encoder_dpms(encoder)); |
} |
if (crtc) { |
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
if (crtc_funcs->dpms) |
(*crtc_funcs->dpms) (crtc, |
drm_helper_choose_crtc_dpms(crtc)); |
} |
} |
return; |
} |
EXPORT_SYMBOL(drm_helper_connector_dpms); |
/** |
* drm_hotplug_stage_two |
* @dev DRM device |
* @connector hotpluged connector |
* |
* LOCKING. |
* Caller must hold mode config lock, function might grab struct lock. |
* |
* Stage two of a hotplug. |
* |
* RETURNS: |
* Zero on success, errno on failure. |
*/ |
int drm_helper_hotplug_stage_two(struct drm_device *dev) |
{ |
drm_helper_plugged_event(dev); |
return 0; |
} |
EXPORT_SYMBOL(drm_helper_hotplug_stage_two); |
int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
struct drm_mode_fb_cmd *mode_cmd) |
{ |
fb->width = mode_cmd->width; |
fb->height = mode_cmd->height; |
fb->pitch = mode_cmd->pitch; |
fb->bits_per_pixel = mode_cmd->bpp; |
fb->depth = mode_cmd->depth; |
return 0; |
} |
EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); |
int drm_helper_resume_force_mode(struct drm_device *dev) |
{ |
struct drm_crtc *crtc; |
int ret; |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
if (!crtc->enabled) |
continue; |
ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, |
crtc->x, crtc->y, crtc->fb); |
if (ret == false) |
DRM_ERROR("failed to set mode on crtc %p\n", crtc); |
} |
return 0; |
} |
EXPORT_SYMBOL(drm_helper_resume_force_mode); |
/drivers/video/drm/drm_edid.c |
---|
0,0 → 1,797 |
/* |
* Copyright (c) 2006 Luc Verhaegen (quirks list) |
* Copyright (c) 2007-2008 Intel Corporation |
* Jesse Barnes <jesse.barnes@intel.com> |
* |
* DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from |
* FB layer. |
* Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> |
* |
* 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, sub license, |
* 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 |
* THE AUTHORS OR COPYRIGHT HOLDERS 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 <linux/kernel.h> |
//#include <linux/i2c.h> |
//#include <linux/i2c-algo-bit.h> |
#include "drmP.h" |
#include "drm_edid.h" |
/* |
* TODO: |
* - support EDID 1.4 (incl. CE blocks) |
*/ |
/* |
* EDID blocks out in the wild have a variety of bugs, try to collect |
* them here (note that userspace may work around broken monitors first, |
* but fixes should make their way here so that the kernel "just works" |
* on as many displays as possible). |
*/ |
/* First detailed mode wrong, use largest 60Hz mode */ |
#define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) |
/* Reported 135MHz pixel clock is too high, needs adjustment */ |
#define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) |
/* Prefer the largest mode at 75 Hz */ |
#define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) |
/* Detail timing is in cm not mm */ |
#define EDID_QUIRK_DETAILED_IN_CM (1 << 3) |
/* Detailed timing descriptors have bogus size values, so just take the |
* maximum size and use that. |
*/ |
#define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) |
/* Monitor forgot to set the first detailed is preferred bit. */ |
#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) |
/* use +hsync +vsync for detailed mode */ |
#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) |
static struct edid_quirk { |
char *vendor; |
int product_id; |
u32 quirks; |
} edid_quirk_list[] = { |
/* Acer AL1706 */ |
{ "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, |
/* Acer F51 */ |
{ "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, |
/* Unknown Acer */ |
{ "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, |
/* Belinea 10 15 55 */ |
{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, |
{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, |
/* Envision Peripherals, Inc. EN-7100e */ |
{ "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, |
/* Funai Electronics PM36B */ |
{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | |
EDID_QUIRK_DETAILED_IN_CM }, |
/* LG Philips LCD LP154W01-A5 */ |
{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, |
{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, |
/* Philips 107p5 CRT */ |
{ "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, |
/* Proview AY765C */ |
{ "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, |
/* Samsung SyncMaster 205BW. Note: irony */ |
{ "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, |
/* Samsung SyncMaster 22[5-6]BW */ |
{ "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, |
{ "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, |
}; |
/* Valid EDID header has these bytes */ |
static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; |
/** |
* edid_is_valid - sanity check EDID data |
* @edid: EDID data |
* |
* Sanity check the EDID block by looking at the header, the version number |
* and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's |
* valid. |
*/ |
static bool edid_is_valid(struct edid *edid) |
{ |
int i; |
u8 csum = 0; |
u8 *raw_edid = (u8 *)edid; |
if (memcmp(edid->header, edid_header, sizeof(edid_header))) |
goto bad; |
if (edid->version != 1) { |
DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); |
goto bad; |
} |
if (edid->revision > 4) |
DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); |
for (i = 0; i < EDID_LENGTH; i++) |
csum += raw_edid[i]; |
if (csum) { |
DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); |
goto bad; |
} |
return 1; |
bad: |
if (raw_edid) { |
DRM_ERROR("Raw EDID:\n"); |
// print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); |
// printk("\n"); |
} |
return 0; |
} |
/** |
* edid_vendor - match a string against EDID's obfuscated vendor field |
* @edid: EDID to match |
* @vendor: vendor string |
* |
* Returns true if @vendor is in @edid, false otherwise |
*/ |
static bool edid_vendor(struct edid *edid, char *vendor) |
{ |
char edid_vendor[3]; |
edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; |
edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | |
((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; |
edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; |
return !strncmp(edid_vendor, vendor, 3); |
} |
/** |
* edid_get_quirks - return quirk flags for a given EDID |
* @edid: EDID to process |
* |
* This tells subsequent routines what fixes they need to apply. |
*/ |
static u32 edid_get_quirks(struct edid *edid) |
{ |
struct edid_quirk *quirk; |
int i; |
for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { |
quirk = &edid_quirk_list[i]; |
if (edid_vendor(edid, quirk->vendor) && |
(EDID_PRODUCT_ID(edid) == quirk->product_id)) |
return quirk->quirks; |
} |
return 0; |
} |
#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) |
#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) |
/** |
* edid_fixup_preferred - set preferred modes based on quirk list |
* @connector: has mode list to fix up |
* @quirks: quirks list |
* |
* Walk the mode list for @connector, clearing the preferred status |
* on existing modes and setting it anew for the right mode ala @quirks. |
*/ |
static void edid_fixup_preferred(struct drm_connector *connector, |
u32 quirks) |
{ |
struct drm_display_mode *t, *cur_mode, *preferred_mode; |
int target_refresh = 0; |
if (list_empty(&connector->probed_modes)) |
return; |
if (quirks & EDID_QUIRK_PREFER_LARGE_60) |
target_refresh = 60; |
if (quirks & EDID_QUIRK_PREFER_LARGE_75) |
target_refresh = 75; |
preferred_mode = list_first_entry(&connector->probed_modes, |
struct drm_display_mode, head); |
list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { |
cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; |
if (cur_mode == preferred_mode) |
continue; |
/* Largest mode is preferred */ |
if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) |
preferred_mode = cur_mode; |
/* At a given size, try to get closest to target refresh */ |
if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && |
MODE_REFRESH_DIFF(cur_mode, target_refresh) < |
MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { |
preferred_mode = cur_mode; |
} |
} |
preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; |
} |
/** |
* drm_mode_std - convert standard mode info (width, height, refresh) into mode |
* @t: standard timing params |
* |
* Take the standard timing params (in this case width, aspect, and refresh) |
* and convert them into a real mode using CVT. |
* |
* Punts for now, but should eventually use the FB layer's CVT based mode |
* generation code. |
*/ |
struct drm_display_mode *drm_mode_std(struct drm_device *dev, |
struct std_timing *t) |
{ |
struct drm_display_mode *mode; |
int hsize = t->hsize * 8 + 248, vsize; |
unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) |
>> EDID_TIMING_ASPECT_SHIFT; |
mode = drm_mode_create(dev); |
if (!mode) |
return NULL; |
if (aspect_ratio == 0) |
vsize = (hsize * 10) / 16; |
else if (aspect_ratio == 1) |
vsize = (hsize * 3) / 4; |
else if (aspect_ratio == 2) |
vsize = (hsize * 4) / 5; |
else |
vsize = (hsize * 9) / 16; |
drm_mode_set_name(mode); |
return mode; |
} |
/** |
* drm_mode_detailed - create a new mode from an EDID detailed timing section |
* @dev: DRM device (needed to create new mode) |
* @edid: EDID block |
* @timing: EDID detailed timing info |
* @quirks: quirks to apply |
* |
* An EDID detailed timing block contains enough info for us to create and |
* return a new struct drm_display_mode. |
*/ |
static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, |
struct edid *edid, |
struct detailed_timing *timing, |
u32 quirks) |
{ |
struct drm_display_mode *mode; |
struct detailed_pixel_timing *pt = &timing->data.pixel_data; |
unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; |
unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; |
unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; |
unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; |
unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; |
unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; |
unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4; |
unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); |
/* ignore tiny modes */ |
if (hactive < 64 || vactive < 64) |
return NULL; |
if (pt->misc & DRM_EDID_PT_STEREO) { |
printk(KERN_WARNING "stereo mode not supported\n"); |
return NULL; |
} |
if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { |
printk(KERN_WARNING "integrated sync not supported\n"); |
return NULL; |
} |
mode = drm_mode_create(dev); |
if (!mode) |
return NULL; |
mode->type = DRM_MODE_TYPE_DRIVER; |
if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) |
timing->pixel_clock = cpu_to_le16(1088); |
mode->clock = le16_to_cpu(timing->pixel_clock) * 10; |
mode->hdisplay = hactive; |
mode->hsync_start = mode->hdisplay + hsync_offset; |
mode->hsync_end = mode->hsync_start + hsync_pulse_width; |
mode->htotal = mode->hdisplay + hblank; |
mode->vdisplay = vactive; |
mode->vsync_start = mode->vdisplay + vsync_offset; |
mode->vsync_end = mode->vsync_start + vsync_pulse_width; |
mode->vtotal = mode->vdisplay + vblank; |
drm_mode_set_name(mode); |
if (pt->misc & DRM_EDID_PT_INTERLACED) |
mode->flags |= DRM_MODE_FLAG_INTERLACE; |
if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { |
pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; |
} |
mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? |
DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; |
mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? |
DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; |
mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; |
mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; |
if (quirks & EDID_QUIRK_DETAILED_IN_CM) { |
mode->width_mm *= 10; |
mode->height_mm *= 10; |
} |
if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { |
mode->width_mm = edid->width_cm * 10; |
mode->height_mm = edid->height_cm * 10; |
} |
return mode; |
} |
/* |
* Detailed mode info for the EDID "established modes" data to use. |
*/ |
static struct drm_display_mode edid_est_modes[] = { |
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, |
968, 1056, 0, 600, 601, 605, 628, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ |
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, |
896, 1024, 0, 600, 601, 603, 625, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ |
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, |
720, 840, 0, 480, 481, 484, 500, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ |
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, |
704, 832, 0, 480, 489, 491, 520, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ |
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, |
768, 864, 0, 480, 483, 486, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ |
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, |
752, 800, 0, 480, 490, 492, 525, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ |
{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, |
846, 900, 0, 400, 421, 423, 449, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ |
{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, |
846, 900, 0, 400, 412, 414, 449, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ |
{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, |
1440, 1688, 0, 1024, 1025, 1028, 1066, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ |
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, |
1136, 1312, 0, 768, 769, 772, 800, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ |
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, |
1184, 1328, 0, 768, 771, 777, 806, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ |
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, |
1184, 1344, 0, 768, 771, 777, 806, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ |
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, |
1208, 1264, 0, 768, 768, 776, 817, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ |
{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, |
928, 1152, 0, 624, 625, 628, 667, 0, |
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ |
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, |
896, 1056, 0, 600, 601, 604, 625, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ |
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, |
976, 1040, 0, 600, 637, 643, 666, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ |
{ DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, |
1344, 1600, 0, 864, 865, 868, 900, 0, |
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ |
}; |
#define EDID_EST_TIMINGS 16 |
#define EDID_STD_TIMINGS 8 |
#define EDID_DETAILED_TIMINGS 4 |
/** |
* add_established_modes - get est. modes from EDID and add them |
* @edid: EDID block to scan |
* |
* Each EDID block contains a bitmap of the supported "established modes" list |
* (defined above). Tease them out and add them to the global modes list. |
*/ |
static int add_established_modes(struct drm_connector *connector, struct edid *edid) |
{ |
struct drm_device *dev = connector->dev; |
unsigned long est_bits = edid->established_timings.t1 | |
(edid->established_timings.t2 << 8) | |
((edid->established_timings.mfg_rsvd & 0x80) << 9); |
int i, modes = 0; |
for (i = 0; i <= EDID_EST_TIMINGS; i++) |
if (est_bits & (1<<i)) { |
struct drm_display_mode *newmode; |
newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
modes++; |
} |
} |
return modes; |
} |
/** |
* add_standard_modes - get std. modes from EDID and add them |
* @edid: EDID block to scan |
* |
* Standard modes can be calculated using the CVT standard. Grab them from |
* @edid, calculate them, and add them to the list. |
*/ |
static int add_standard_modes(struct drm_connector *connector, struct edid *edid) |
{ |
struct drm_device *dev = connector->dev; |
int i, modes = 0; |
for (i = 0; i < EDID_STD_TIMINGS; i++) { |
struct std_timing *t = &edid->standard_timings[i]; |
struct drm_display_mode *newmode; |
/* If std timings bytes are 1, 1 it's empty */ |
if (t->hsize == 1 && t->vfreq_aspect == 1) |
continue; |
newmode = drm_mode_std(dev, &edid->standard_timings[i]); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
modes++; |
} |
} |
return modes; |
} |
/** |
* add_detailed_modes - get detailed mode info from EDID data |
* @connector: attached connector |
* @edid: EDID block to scan |
* @quirks: quirks to apply |
* |
* Some of the detailed timing sections may contain mode information. Grab |
* it and add it to the list. |
*/ |
static int add_detailed_info(struct drm_connector *connector, |
struct edid *edid, u32 quirks) |
{ |
struct drm_device *dev = connector->dev; |
int i, j, modes = 0; |
for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { |
struct detailed_timing *timing = &edid->detailed_timings[i]; |
struct detailed_non_pixel *data = &timing->data.other_data; |
struct drm_display_mode *newmode; |
/* EDID up to and including 1.2 may put monitor info here */ |
if (edid->version == 1 && edid->revision < 3) |
continue; |
/* Detailed mode timing */ |
if (timing->pixel_clock) { |
newmode = drm_mode_detailed(dev, edid, timing, quirks); |
if (!newmode) |
continue; |
/* First detailed mode is preferred */ |
if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING)) |
newmode->type |= DRM_MODE_TYPE_PREFERRED; |
drm_mode_probed_add(connector, newmode); |
modes++; |
continue; |
} |
/* Other timing or info */ |
switch (data->type) { |
case EDID_DETAIL_MONITOR_SERIAL: |
break; |
case EDID_DETAIL_MONITOR_STRING: |
break; |
case EDID_DETAIL_MONITOR_RANGE: |
/* Get monitor range data */ |
break; |
case EDID_DETAIL_MONITOR_NAME: |
break; |
case EDID_DETAIL_MONITOR_CPDATA: |
break; |
case EDID_DETAIL_STD_MODES: |
/* Five modes per detailed section */ |
for (j = 0; j < 5; i++) { |
struct std_timing *std; |
struct drm_display_mode *newmode; |
std = &data->data.timings[j]; |
newmode = drm_mode_std(dev, std); |
if (newmode) { |
drm_mode_probed_add(connector, newmode); |
modes++; |
} |
} |
break; |
default: |
break; |
} |
} |
return modes; |
} |
#define DDC_ADDR 0x50 |
/** |
* Get EDID information via I2C. |
* |
* \param adapter : i2c device adaptor |
* \param buf : EDID data buffer to be filled |
* \param len : EDID data buffer length |
* \return 0 on success or -1 on failure. |
* |
* Try to fetch EDID information by calling i2c driver function. |
*/ |
int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, |
unsigned char *buf, int len) |
{ |
unsigned char start = 0x0; |
struct i2c_msg msgs[] = { |
{ |
.addr = DDC_ADDR, |
.flags = 0, |
.len = 1, |
.buf = &start, |
}, { |
.addr = DDC_ADDR, |
.flags = I2C_M_RD, |
.len = len, |
.buf = buf, |
} |
}; |
if (i2c_transfer(adapter, msgs, 2) == 2) |
return 0; |
// dev_info(&adapter->dev, "unable to read EDID block.\n"); |
return -1; |
} |
EXPORT_SYMBOL(drm_do_probe_ddc_edid); |
static int drm_ddc_read_edid(struct drm_connector *connector, |
struct i2c_adapter *adapter, |
char *buf, int len) |
{ |
int ret; |
ret = drm_do_probe_ddc_edid(adapter, buf, len); |
if (ret != 0) { |
// dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", |
// drm_get_connector_name(connector)); |
goto end; |
} |
if (!edid_is_valid((struct edid *)buf)) { |
// dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", |
// drm_get_connector_name(connector)); |
ret = -1; |
} |
end: |
return ret; |
} |
#define MAX_EDID_EXT_NUM 4 |
/** |
* drm_get_edid - get EDID data, if available |
* @connector: connector we're probing |
* @adapter: i2c adapter to use for DDC |
* |
* Poke the given connector's i2c channel to grab EDID data if possible. |
* |
* Return edid data or NULL if we couldn't find any. |
*/ |
struct edid *drm_get_edid(struct drm_connector *connector, |
struct i2c_adapter *adapter) |
{ |
int ret; |
struct edid *edid; |
edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1), |
GFP_KERNEL); |
if (edid == NULL) { |
// dev_warn(&connector->dev->pdev->dev, |
// "Failed to allocate EDID\n"); |
goto end; |
} |
/* Read first EDID block */ |
ret = drm_ddc_read_edid(connector, adapter, |
(unsigned char *)edid, EDID_LENGTH); |
if (ret != 0) |
goto clean_up; |
/* There are EDID extensions to be read */ |
if (edid->extensions != 0) { |
int edid_ext_num = edid->extensions; |
if (edid_ext_num > MAX_EDID_EXT_NUM) { |
// dev_warn(&connector->dev->pdev->dev, |
// "The number of extension(%d) is " |
// "over max (%d), actually read number (%d)\n", |
// edid_ext_num, MAX_EDID_EXT_NUM, |
// MAX_EDID_EXT_NUM); |
/* Reset EDID extension number to be read */ |
edid_ext_num = MAX_EDID_EXT_NUM; |
} |
/* Read EDID including extensions too */ |
ret = drm_ddc_read_edid(connector, adapter, (char *)edid, |
EDID_LENGTH * (edid_ext_num + 1)); |
if (ret != 0) |
goto clean_up; |
} |
connector->display_info.raw_edid = (char *)edid; |
goto end; |
clean_up: |
kfree(edid); |
edid = NULL; |
end: |
return edid; |
} |
EXPORT_SYMBOL(drm_get_edid); |
#define HDMI_IDENTIFIER 0x000C03 |
#define VENDOR_BLOCK 0x03 |
/** |
* drm_detect_hdmi_monitor - detect whether monitor is hdmi. |
* @edid: monitor EDID information |
* |
* Parse the CEA extension according to CEA-861-B. |
* Return true if HDMI, false if not or unknown. |
*/ |
bool drm_detect_hdmi_monitor(struct edid *edid) |
{ |
char *edid_ext = NULL; |
int i, hdmi_id, edid_ext_num; |
int start_offset, end_offset; |
bool is_hdmi = false; |
/* No EDID or EDID extensions */ |
if (edid == NULL || edid->extensions == 0) |
goto end; |
/* Chose real EDID extension number */ |
edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? |
MAX_EDID_EXT_NUM : edid->extensions; |
/* Find CEA extension */ |
for (i = 0; i < edid_ext_num; i++) { |
edid_ext = (char *)edid + EDID_LENGTH * (i + 1); |
/* This block is CEA extension */ |
if (edid_ext[0] == 0x02) |
break; |
} |
if (i == edid_ext_num) |
goto end; |
/* Data block offset in CEA extension block */ |
start_offset = 4; |
end_offset = edid_ext[2]; |
/* |
* Because HDMI identifier is in Vendor Specific Block, |
* search it from all data blocks of CEA extension. |
*/ |
for (i = start_offset; i < end_offset; |
/* Increased by data block len */ |
i += ((edid_ext[i] & 0x1f) + 1)) { |
/* Find vendor specific block */ |
if ((edid_ext[i] >> 5) == VENDOR_BLOCK) { |
hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) | |
edid_ext[i + 3] << 16; |
/* Find HDMI identifier */ |
if (hdmi_id == HDMI_IDENTIFIER) |
is_hdmi = true; |
break; |
} |
} |
end: |
return is_hdmi; |
} |
EXPORT_SYMBOL(drm_detect_hdmi_monitor); |
/** |
* drm_add_edid_modes - add modes from EDID data, if available |
* @connector: connector we're probing |
* @edid: edid data |
* |
* Add the specified modes to the connector's mode list. |
* |
* Return number of modes added or 0 if we couldn't find any. |
*/ |
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) |
{ |
int num_modes = 0; |
u32 quirks; |
if (edid == NULL) { |
return 0; |
} |
if (!edid_is_valid(edid)) { |
// dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", |
// drm_get_connector_name(connector)); |
return 0; |
} |
quirks = edid_get_quirks(edid); |
num_modes += add_established_modes(connector, edid); |
num_modes += add_standard_modes(connector, edid); |
num_modes += add_detailed_info(connector, edid, quirks); |
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) |
edid_fixup_preferred(connector, quirks); |
connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0; |
connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0; |
connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0; |
connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0; |
connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0; |
connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5; |
connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0; |
connector->display_info.width_mm = edid->width_cm * 10; |
connector->display_info.height_mm = edid->height_cm * 10; |
connector->display_info.gamma = edid->gamma; |
connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0; |
connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0; |
connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3; |
connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0; |
connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0; |
connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0; |
connector->display_info.gamma = edid->gamma; |
return num_modes; |
} |
EXPORT_SYMBOL(drm_add_edid_modes); |
/drivers/video/drm/drm_mm.c |
---|
0,0 → 1,369 |
/************************************************************************** |
* |
* Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. |
* 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 the rights to use, copy, modify, merge, publish, |
* distribute, sub license, 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 |
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS 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. |
* |
* |
**************************************************************************/ |
/* |
* Generic simple memory manager implementation. Intended to be used as a base |
* class implementation for more advanced memory managers. |
* |
* Note that the algorithm used is quite simple and there might be substantial |
* performance gains if a smarter free list is implemented. Currently it is just an |
* unordered stack of free regions. This could easily be improved if an RB-tree |
* is used instead. At least if we expect heavy fragmentation. |
* |
* Aligned allocations can also see improvement. |
* |
* Authors: |
* Thomas Hellström <thomas-at-tungstengraphics-dot-com> |
*/ |
//#include "drmP.h" |
#include "drm_mm.h" |
//#include <linux/slab.h> |
#define MM_UNUSED_TARGET 4 |
unsigned long drm_mm_tail_space(struct drm_mm *mm) |
{ |
struct list_head *tail_node; |
struct drm_mm_node *entry; |
tail_node = mm->ml_entry.prev; |
entry = list_entry(tail_node, struct drm_mm_node, ml_entry); |
if (!entry->free) |
return 0; |
return entry->size; |
} |
int drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size) |
{ |
struct list_head *tail_node; |
struct drm_mm_node *entry; |
tail_node = mm->ml_entry.prev; |
entry = list_entry(tail_node, struct drm_mm_node, ml_entry); |
if (!entry->free) |
return -ENOMEM; |
if (entry->size <= size) |
return -ENOMEM; |
entry->size -= size; |
return 0; |
} |
static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic) |
{ |
struct drm_mm_node *child; |
child = malloc(sizeof(*child)); |
if (unlikely(child == NULL)) { |
spin_lock(&mm->unused_lock); |
if (list_empty(&mm->unused_nodes)) |
child = NULL; |
else { |
child = |
list_entry(mm->unused_nodes.next, |
struct drm_mm_node, fl_entry); |
list_del(&child->fl_entry); |
--mm->num_unused; |
} |
spin_unlock(&mm->unused_lock); |
} |
return child; |
} |
int drm_mm_pre_get(struct drm_mm *mm) |
{ |
struct drm_mm_node *node; |
spin_lock(&mm->unused_lock); |
while (mm->num_unused < MM_UNUSED_TARGET) { |
spin_unlock(&mm->unused_lock); |
node = kmalloc(sizeof(*node), GFP_KERNEL); |
spin_lock(&mm->unused_lock); |
if (unlikely(node == NULL)) { |
int ret = (mm->num_unused < 2) ? -ENOMEM : 0; |
spin_unlock(&mm->unused_lock); |
return ret; |
} |
++mm->num_unused; |
list_add_tail(&node->fl_entry, &mm->unused_nodes); |
} |
spin_unlock(&mm->unused_lock); |
return 0; |
} |
//EXPORT_SYMBOL(drm_mm_pre_get); |
static int drm_mm_create_tail_node(struct drm_mm *mm, |
unsigned long start, |
unsigned long size, int atomic) |
{ |
struct drm_mm_node *child; |
child = drm_mm_kmalloc(mm, atomic); |
if (unlikely(child == NULL)) |
return -ENOMEM; |
child->free = 1; |
child->size = size; |
child->start = start; |
child->mm = mm; |
list_add_tail(&child->ml_entry, &mm->ml_entry); |
list_add_tail(&child->fl_entry, &mm->fl_entry); |
return 0; |
} |
int drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size, int atomic) |
{ |
struct list_head *tail_node; |
struct drm_mm_node *entry; |
tail_node = mm->ml_entry.prev; |
entry = list_entry(tail_node, struct drm_mm_node, ml_entry); |
if (!entry->free) { |
return drm_mm_create_tail_node(mm, entry->start + entry->size, |
size, atomic); |
} |
entry->size += size; |
return 0; |
} |
static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent, |
unsigned long size, |
int atomic) |
{ |
struct drm_mm_node *child; |
child = drm_mm_kmalloc(parent->mm, atomic); |
if (unlikely(child == NULL)) |
return NULL; |
INIT_LIST_HEAD(&child->fl_entry); |
child->free = 0; |
child->size = size; |
child->start = parent->start; |
child->mm = parent->mm; |
list_add_tail(&child->ml_entry, &parent->ml_entry); |
INIT_LIST_HEAD(&child->fl_entry); |
parent->size -= size; |
parent->start += size; |
return child; |
} |
struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, |
unsigned long size, |
unsigned alignment, |
int atomic) |
{ |
struct drm_mm_node *align_splitoff = NULL; |
unsigned tmp = 0; |
if (alignment) |
tmp = node->start % alignment; |
if (tmp) { |
align_splitoff = |
drm_mm_split_at_start(node, alignment - tmp, atomic); |
if (unlikely(align_splitoff == NULL)) |
return NULL; |
} |
if (node->size == size) { |
list_del_init(&node->fl_entry); |
node->free = 0; |
} else { |
node = drm_mm_split_at_start(node, size, atomic); |
} |
if (align_splitoff) |
drm_mm_put_block(align_splitoff); |
return node; |
} |
//EXPORT_SYMBOL(drm_mm_get_block_generic); |
/* |
* Put a block. Merge with the previous and / or next block if they are free. |
* Otherwise add to the free stack. |
*/ |
void drm_mm_put_block(struct drm_mm_node *cur) |
{ |
struct drm_mm *mm = cur->mm; |
struct list_head *cur_head = &cur->ml_entry; |
struct list_head *root_head = &mm->ml_entry; |
struct drm_mm_node *prev_node = NULL; |
struct drm_mm_node *next_node; |
int merged = 0; |
if (cur_head->prev != root_head) { |
prev_node = |
list_entry(cur_head->prev, struct drm_mm_node, ml_entry); |
if (prev_node->free) { |
prev_node->size += cur->size; |
merged = 1; |
} |
} |
if (cur_head->next != root_head) { |
next_node = |
list_entry(cur_head->next, struct drm_mm_node, ml_entry); |
if (next_node->free) { |
if (merged) { |
prev_node->size += next_node->size; |
list_del(&next_node->ml_entry); |
list_del(&next_node->fl_entry); |
if (mm->num_unused < MM_UNUSED_TARGET) { |
list_add(&next_node->fl_entry, |
&mm->unused_nodes); |
++mm->num_unused; |
} else |
kfree(next_node); |
} else { |
next_node->size += cur->size; |
next_node->start = cur->start; |
merged = 1; |
} |
} |
} |
if (!merged) { |
cur->free = 1; |
list_add(&cur->fl_entry, &mm->fl_entry); |
} else { |
list_del(&cur->ml_entry); |
if (mm->num_unused < MM_UNUSED_TARGET) { |
list_add(&cur->fl_entry, &mm->unused_nodes); |
++mm->num_unused; |
} else |
kfree(cur); |
} |
} |
//EXPORT_SYMBOL(drm_mm_put_block); |
struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, |
unsigned long size, |
unsigned alignment, int best_match) |
{ |
struct list_head *list; |
const struct list_head *free_stack = &mm->fl_entry; |
struct drm_mm_node *entry; |
struct drm_mm_node *best; |
unsigned long best_size; |
unsigned wasted; |
best = NULL; |
best_size = ~0UL; |
list_for_each(list, free_stack) { |
entry = list_entry(list, struct drm_mm_node, fl_entry); |
wasted = 0; |
if (entry->size < size) |
continue; |
if (alignment) { |
register unsigned tmp = entry->start % alignment; |
if (tmp) |
wasted += alignment - tmp; |
} |
if (entry->size >= size + wasted) { |
if (!best_match) |
return entry; |
if (size < best_size) { |
best = entry; |
best_size = entry->size; |
} |
} |
} |
return best; |
} |
//EXPORT_SYMBOL(drm_mm_search_free); |
int drm_mm_clean(struct drm_mm * mm) |
{ |
struct list_head *head = &mm->ml_entry; |
return (head->next->next == head); |
} |
//EXPORT_SYMBOL(drm_mm_clean); |
int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) |
{ |
INIT_LIST_HEAD(&mm->ml_entry); |
INIT_LIST_HEAD(&mm->fl_entry); |
INIT_LIST_HEAD(&mm->unused_nodes); |
mm->num_unused = 0; |
spin_lock_init(&mm->unused_lock); |
return drm_mm_create_tail_node(mm, start, size, 0); |
} |
//EXPORT_SYMBOL(drm_mm_init); |
void drm_mm_takedown(struct drm_mm * mm) |
{ |
struct list_head *bnode = mm->fl_entry.next; |
struct drm_mm_node *entry; |
struct drm_mm_node *next; |
entry = list_entry(bnode, struct drm_mm_node, fl_entry); |
if (entry->ml_entry.next != &mm->ml_entry || |
entry->fl_entry.next != &mm->fl_entry) { |
DRM_ERROR("Memory manager not clean. Delaying takedown\n"); |
return; |
} |
list_del(&entry->fl_entry); |
list_del(&entry->ml_entry); |
kfree(entry); |
spin_lock(&mm->unused_lock); |
list_for_each_entry_safe(entry, next, &mm->unused_nodes, fl_entry) { |
list_del(&entry->fl_entry); |
kfree(entry); |
--mm->num_unused; |
} |
spin_unlock(&mm->unused_lock); |
BUG_ON(mm->num_unused != 0); |
} |
//EXPORT_SYMBOL(drm_mm_takedown); |
/drivers/video/drm/drm_modes.c |
---|
0,0 → 1,580 |
/* |
* The list_sort function is (presumably) licensed under the GPL (see the |
* top level "COPYING" file for details). |
* |
* The remainder of this file is: |
* |
* Copyright © 1997-2003 by The XFree86 Project, Inc. |
* Copyright © 2007 Dave Airlie |
* Copyright © 2007-2008 Intel Corporation |
* Jesse Barnes <jesse.barnes@intel.com> |
* |
* 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. |
* |
* Except as contained in this notice, the name of the copyright holder(s) |
* and author(s) shall not be used in advertising or otherwise to promote |
* the sale, use or other dealings in this Software without prior written |
* authorization from the copyright holder(s) and author(s). |
*/ |
#include <list.h> |
#include "drmP.h" |
#include "drm.h" |
#include "drm_crtc.h" |
#define DRM_MODESET_DEBUG "drm_mode" |
/** |
* drm_mode_debug_printmodeline - debug print a mode |
* @dev: DRM device |
* @mode: mode to print |
* |
* LOCKING: |
* None. |
* |
* Describe @mode using DRM_DEBUG. |
*/ |
void drm_mode_debug_printmodeline(struct drm_display_mode *mode) |
{ |
DRM_DEBUG_MODE(DRM_MODESET_DEBUG, |
"Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", |
mode->base.id, mode->name, mode->vrefresh, mode->clock, |
mode->hdisplay, mode->hsync_start, |
mode->hsync_end, mode->htotal, |
mode->vdisplay, mode->vsync_start, |
mode->vsync_end, mode->vtotal, mode->type, mode->flags); |
} |
EXPORT_SYMBOL(drm_mode_debug_printmodeline); |
/** |
* drm_mode_set_name - set the name on a mode |
* @mode: name will be set in this mode |
* |
* LOCKING: |
* None. |
* |
* Set the name of @mode to a standard format. |
*/ |
void drm_mode_set_name(struct drm_display_mode *mode) |
{ |
snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, |
mode->vdisplay); |
} |
EXPORT_SYMBOL(drm_mode_set_name); |
/** |
* drm_mode_list_concat - move modes from one list to another |
* @head: source list |
* @new: dst list |
* |
* LOCKING: |
* Caller must ensure both lists are locked. |
* |
* Move all the modes from @head to @new. |
*/ |
void drm_mode_list_concat(struct list_head *head, struct list_head *new) |
{ |
struct list_head *entry, *tmp; |
list_for_each_safe(entry, tmp, head) { |
list_move_tail(entry, new); |
} |
} |
EXPORT_SYMBOL(drm_mode_list_concat); |
/** |
* drm_mode_width - get the width of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @mode's width (hdisplay) value. |
* |
* FIXME: is this needed? |
* |
* RETURNS: |
* @mode->hdisplay |
*/ |
int drm_mode_width(struct drm_display_mode *mode) |
{ |
return mode->hdisplay; |
} |
EXPORT_SYMBOL(drm_mode_width); |
/** |
* drm_mode_height - get the height of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @mode's height (vdisplay) value. |
* |
* FIXME: is this needed? |
* |
* RETURNS: |
* @mode->vdisplay |
*/ |
int drm_mode_height(struct drm_display_mode *mode) |
{ |
return mode->vdisplay; |
} |
EXPORT_SYMBOL(drm_mode_height); |
/** |
* drm_mode_vrefresh - get the vrefresh of a mode |
* @mode: mode |
* |
* LOCKING: |
* None. |
* |
* Return @mode's vrefresh rate or calculate it if necessary. |
* |
* FIXME: why is this needed? shouldn't vrefresh be set already? |
* |
* RETURNS: |
* Vertical refresh rate of @mode x 1000. For precision reasons. |
*/ |
int drm_mode_vrefresh(struct drm_display_mode *mode) |
{ |
int refresh = 0; |
unsigned int calc_val; |
if (mode->vrefresh > 0) |
refresh = mode->vrefresh; |
else if (mode->htotal > 0 && mode->vtotal > 0) { |
/* work out vrefresh the value will be x1000 */ |
calc_val = (mode->clock * 1000); |
calc_val /= mode->htotal; |
calc_val *= 1000; |
calc_val /= mode->vtotal; |
refresh = calc_val; |
if (mode->flags & DRM_MODE_FLAG_INTERLACE) |
refresh *= 2; |
if (mode->flags & DRM_MODE_FLAG_DBLSCAN) |
refresh /= 2; |
if (mode->vscan > 1) |
refresh /= mode->vscan; |
} |
return refresh; |
} |
EXPORT_SYMBOL(drm_mode_vrefresh); |
/** |
* drm_mode_set_crtcinfo - set CRTC modesetting parameters |
* @p: mode |
* @adjust_flags: unused? (FIXME) |
* |
* LOCKING: |
* None. |
* |
* Setup the CRTC modesetting parameters for @p, adjusting if necessary. |
*/ |
void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) |
{ |
if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) |
return; |
p->crtc_hdisplay = p->hdisplay; |
p->crtc_hsync_start = p->hsync_start; |
p->crtc_hsync_end = p->hsync_end; |
p->crtc_htotal = p->htotal; |
p->crtc_hskew = p->hskew; |
p->crtc_vdisplay = p->vdisplay; |
p->crtc_vsync_start = p->vsync_start; |
p->crtc_vsync_end = p->vsync_end; |
p->crtc_vtotal = p->vtotal; |
if (p->flags & DRM_MODE_FLAG_INTERLACE) { |
if (adjust_flags & CRTC_INTERLACE_HALVE_V) { |
p->crtc_vdisplay /= 2; |
p->crtc_vsync_start /= 2; |
p->crtc_vsync_end /= 2; |
p->crtc_vtotal /= 2; |
} |
p->crtc_vtotal |= 1; |
} |
if (p->flags & DRM_MODE_FLAG_DBLSCAN) { |
p->crtc_vdisplay *= 2; |
p->crtc_vsync_start *= 2; |
p->crtc_vsync_end *= 2; |
p->crtc_vtotal *= 2; |
} |
if (p->vscan > 1) { |
p->crtc_vdisplay *= p->vscan; |
p->crtc_vsync_start *= p->vscan; |
p->crtc_vsync_end *= p->vscan; |
p->crtc_vtotal *= p->vscan; |
} |
p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); |
p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); |
p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); |
p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); |
p->crtc_hadjusted = false; |
p->crtc_vadjusted = false; |
} |
EXPORT_SYMBOL(drm_mode_set_crtcinfo); |
/** |
* drm_mode_duplicate - allocate and duplicate an existing mode |
* @m: mode to duplicate |
* |
* LOCKING: |
* None. |
* |
* Just allocate a new mode, copy the existing mode into it, and return |
* a pointer to it. Used to create new instances of established modes. |
*/ |
struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, |
struct drm_display_mode *mode) |
{ |
struct drm_display_mode *nmode; |
int new_id; |
nmode = drm_mode_create(dev); |
if (!nmode) |
return NULL; |
new_id = nmode->base.id; |
*nmode = *mode; |
nmode->base.id = new_id; |
INIT_LIST_HEAD(&nmode->head); |
return nmode; |
} |
EXPORT_SYMBOL(drm_mode_duplicate); |
/** |
* drm_mode_equal - test modes for equality |
* @mode1: first mode |
* @mode2: second mode |
* |
* LOCKING: |
* None. |
* |
* Check to see if @mode1 and @mode2 are equivalent. |
* |
* RETURNS: |
* True if the modes are equal, false otherwise. |
*/ |
bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) |
{ |
/* do clock check convert to PICOS so fb modes get matched |
* the same */ |
if (mode1->clock && mode2->clock) { |
if (KHZ2PICOS(mode1->clock) != KHZ2PICOS(mode2->clock)) |
return false; |
} else if (mode1->clock != mode2->clock) |
return false; |
if (mode1->hdisplay == mode2->hdisplay && |
mode1->hsync_start == mode2->hsync_start && |
mode1->hsync_end == mode2->hsync_end && |
mode1->htotal == mode2->htotal && |
mode1->hskew == mode2->hskew && |
mode1->vdisplay == mode2->vdisplay && |
mode1->vsync_start == mode2->vsync_start && |
mode1->vsync_end == mode2->vsync_end && |
mode1->vtotal == mode2->vtotal && |
mode1->vscan == mode2->vscan && |
mode1->flags == mode2->flags) |
return true; |
return false; |
} |
EXPORT_SYMBOL(drm_mode_equal); |
/** |
* drm_mode_validate_size - make sure modes adhere to size constraints |
* @dev: DRM device |
* @mode_list: list of modes to check |
* @maxX: maximum width |
* @maxY: maximum height |
* @maxPitch: max pitch |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* The DRM device (@dev) has size and pitch limits. Here we validate the |
* modes we probed for @dev against those limits and set their status as |
* necessary. |
*/ |
void drm_mode_validate_size(struct drm_device *dev, |
struct list_head *mode_list, |
int maxX, int maxY, int maxPitch) |
{ |
struct drm_display_mode *mode; |
list_for_each_entry(mode, mode_list, head) { |
if (maxPitch > 0 && mode->hdisplay > maxPitch) |
mode->status = MODE_BAD_WIDTH; |
if (maxX > 0 && mode->hdisplay > maxX) |
mode->status = MODE_VIRTUAL_X; |
if (maxY > 0 && mode->vdisplay > maxY) |
mode->status = MODE_VIRTUAL_Y; |
} |
} |
EXPORT_SYMBOL(drm_mode_validate_size); |
/** |
* drm_mode_validate_clocks - validate modes against clock limits |
* @dev: DRM device |
* @mode_list: list of modes to check |
* @min: minimum clock rate array |
* @max: maximum clock rate array |
* @n_ranges: number of clock ranges (size of arrays) |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* Some code may need to check a mode list against the clock limits of the |
* device in question. This function walks the mode list, testing to make |
* sure each mode falls within a given range (defined by @min and @max |
* arrays) and sets @mode->status as needed. |
*/ |
void drm_mode_validate_clocks(struct drm_device *dev, |
struct list_head *mode_list, |
int *min, int *max, int n_ranges) |
{ |
struct drm_display_mode *mode; |
int i; |
list_for_each_entry(mode, mode_list, head) { |
bool good = false; |
for (i = 0; i < n_ranges; i++) { |
if (mode->clock >= min[i] && mode->clock <= max[i]) { |
good = true; |
break; |
} |
} |
if (!good) |
mode->status = MODE_CLOCK_RANGE; |
} |
} |
EXPORT_SYMBOL(drm_mode_validate_clocks); |
/** |
* drm_mode_prune_invalid - remove invalid modes from mode list |
* @dev: DRM device |
* @mode_list: list of modes to check |
* @verbose: be verbose about it |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* Once mode list generation is complete, a caller can use this routine to |
* remove invalid modes from a mode list. If any of the modes have a |
* status other than %MODE_OK, they are removed from @mode_list and freed. |
*/ |
void drm_mode_prune_invalid(struct drm_device *dev, |
struct list_head *mode_list, bool verbose) |
{ |
struct drm_display_mode *mode, *t; |
list_for_each_entry_safe(mode, t, mode_list, head) { |
if (mode->status != MODE_OK) { |
list_del(&mode->head); |
if (verbose) { |
drm_mode_debug_printmodeline(mode); |
DRM_DEBUG_MODE(DRM_MODESET_DEBUG, |
"Not using %s mode %d\n", |
mode->name, mode->status); |
} |
drm_mode_destroy(dev, mode); |
} |
} |
} |
EXPORT_SYMBOL(drm_mode_prune_invalid); |
/** |
* drm_mode_compare - compare modes for favorability |
* @lh_a: list_head for first mode |
* @lh_b: list_head for second mode |
* |
* LOCKING: |
* None. |
* |
* Compare two modes, given by @lh_a and @lh_b, returning a value indicating |
* which is better. |
* |
* RETURNS: |
* Negative if @lh_a is better than @lh_b, zero if they're equivalent, or |
* positive if @lh_b is better than @lh_a. |
*/ |
static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b) |
{ |
struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); |
struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); |
int diff; |
diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - |
((a->type & DRM_MODE_TYPE_PREFERRED) != 0); |
if (diff) |
return diff; |
diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; |
if (diff) |
return diff; |
diff = b->clock - a->clock; |
return diff; |
} |
/* FIXME: what we don't have a list sort function? */ |
/* list sort from Mark J Roberts (mjr@znex.org) */ |
void list_sort(struct list_head *head, |
int (*cmp)(struct list_head *a, struct list_head *b)) |
{ |
struct list_head *p, *q, *e, *list, *tail, *oldhead; |
int insize, nmerges, psize, qsize, i; |
list = head->next; |
list_del(head); |
insize = 1; |
for (;;) { |
p = oldhead = list; |
list = tail = NULL; |
nmerges = 0; |
while (p) { |
nmerges++; |
q = p; |
psize = 0; |
for (i = 0; i < insize; i++) { |
psize++; |
q = q->next == oldhead ? NULL : q->next; |
if (!q) |
break; |
} |
qsize = insize; |
while (psize > 0 || (qsize > 0 && q)) { |
if (!psize) { |
e = q; |
q = q->next; |
qsize--; |
if (q == oldhead) |
q = NULL; |
} else if (!qsize || !q) { |
e = p; |
p = p->next; |
psize--; |
if (p == oldhead) |
p = NULL; |
} else if (cmp(p, q) <= 0) { |
e = p; |
p = p->next; |
psize--; |
if (p == oldhead) |
p = NULL; |
} else { |
e = q; |
q = q->next; |
qsize--; |
if (q == oldhead) |
q = NULL; |
} |
if (tail) |
tail->next = e; |
else |
list = e; |
e->prev = tail; |
tail = e; |
} |
p = q; |
} |
tail->next = list; |
list->prev = tail; |
if (nmerges <= 1) |
break; |
insize *= 2; |
} |
head->next = list; |
head->prev = list->prev; |
list->prev->next = head; |
list->prev = head; |
} |
/** |
* drm_mode_sort - sort mode list |
* @mode_list: list to sort |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* Sort @mode_list by favorability, putting good modes first. |
*/ |
void drm_mode_sort(struct list_head *mode_list) |
{ |
list_sort(mode_list, drm_mode_compare); |
} |
EXPORT_SYMBOL(drm_mode_sort); |
/** |
* drm_mode_connector_list_update - update the mode list for the connector |
* @connector: the connector to update |
* |
* LOCKING: |
* Caller must hold a lock protecting @mode_list. |
* |
* This moves the modes from the @connector probed_modes list |
* to the actual mode list. It compares the probed mode against the current |
* list and only adds different modes. All modes unverified after this point |
* will be removed by the prune invalid modes. |
*/ |
void drm_mode_connector_list_update(struct drm_connector *connector) |
{ |
struct drm_display_mode *mode; |
struct drm_display_mode *pmode, *pt; |
int found_it; |
list_for_each_entry_safe(pmode, pt, &connector->probed_modes, |
head) { |
found_it = 0; |
/* go through current modes checking for the new probed mode */ |
list_for_each_entry(mode, &connector->modes, head) { |
if (drm_mode_equal(pmode, mode)) { |
found_it = 1; |
/* if equal delete the probed mode */ |
mode->status = pmode->status; |
list_del(&pmode->head); |
drm_mode_destroy(connector->dev, pmode); |
break; |
} |
} |
if (!found_it) { |
list_move_tail(&pmode->head, &connector->modes); |
} |
} |
} |
EXPORT_SYMBOL(drm_mode_connector_list_update); |
/drivers/video/drm/include/drm.h |
---|
0,0 → 1,744 |
/** |
* \file drm.h |
* Header for the Direct Rendering Manager |
* |
* \author Rickard E. (Rik) Faith <faith@valinux.com> |
* |
* \par Acknowledgments: |
* Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic \c cmpxchg. |
*/ |
/* |
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. |
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. |
* 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 |
* 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 NONINFRINGEMENT. IN NO EVENT SHALL |
* VA LINUX SYSTEMS AND/OR ITS 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. |
*/ |
#ifndef _DRM_H_ |
#define _DRM_H_ |
#include <types.h> |
#include <errno-base.h> |
//#include <asm/ioctl.h> /* For _IO* macros */ |
#define DRM_MAJOR 226 |
#define DRM_MAX_MINOR 15 |
#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ |
#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ |
#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ |
#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ |
#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ |
#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ |
#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) |
#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) |
#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) |
typedef unsigned int drm_handle_t; |
typedef unsigned int drm_context_t; |
typedef unsigned int drm_drawable_t; |
typedef unsigned int drm_magic_t; |
/** |
* Cliprect. |
* |
* \warning: If you change this structure, make sure you change |
* XF86DRIClipRectRec in the server as well |
* |
* \note KW: Actually it's illegal to change either for |
* backwards-compatibility reasons. |
*/ |
struct drm_clip_rect { |
unsigned short x1; |
unsigned short y1; |
unsigned short x2; |
unsigned short y2; |
}; |
/** |
* Drawable information. |
*/ |
struct drm_drawable_info { |
unsigned int num_rects; |
struct drm_clip_rect *rects; |
}; |
/** |
* Texture region, |
*/ |
struct drm_tex_region { |
unsigned char next; |
unsigned char prev; |
unsigned char in_use; |
unsigned char padding; |
unsigned int age; |
}; |
/** |
* Hardware lock. |
* |
* The lock structure is a simple cache-line aligned integer. To avoid |
* processor bus contention on a multiprocessor system, there should not be any |
* other data stored in the same cache line. |
*/ |
struct drm_hw_lock { |
__volatile__ unsigned int lock; /**< lock variable */ |
char padding[60]; /**< Pad to cache line */ |
}; |
/** |
* DRM_IOCTL_VERSION ioctl argument type. |
* |
* \sa drmGetVersion(). |
*/ |
struct drm_version { |
int version_major; /**< Major version */ |
int version_minor; /**< Minor version */ |
int version_patchlevel; /**< Patch level */ |
size_t name_len; /**< Length of name buffer */ |
char __user *name; /**< Name of driver */ |
size_t date_len; /**< Length of date buffer */ |
char __user *date; /**< User-space buffer to hold date */ |
size_t desc_len; /**< Length of desc buffer */ |
char __user *desc; /**< User-space buffer to hold desc */ |
}; |
/** |
* DRM_IOCTL_GET_UNIQUE ioctl argument type. |
* |
* \sa drmGetBusid() and drmSetBusId(). |
*/ |
struct drm_unique { |
size_t unique_len; /**< Length of unique */ |
char __user *unique; /**< Unique name for driver instantiation */ |
}; |
struct drm_list { |
int count; /**< Length of user-space structures */ |
struct drm_version __user *version; |
}; |
struct drm_block { |
int unused; |
}; |
/** |
* DRM_IOCTL_CONTROL ioctl argument type. |
* |
* \sa drmCtlInstHandler() and drmCtlUninstHandler(). |
*/ |
struct drm_control { |
enum { |
DRM_ADD_COMMAND, |
DRM_RM_COMMAND, |
DRM_INST_HANDLER, |
DRM_UNINST_HANDLER |
} func; |
int irq; |
}; |
/** |
* Type of memory to map. |
*/ |
enum drm_map_type { |
_DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ |
_DRM_REGISTERS = 1, /**< no caching, no core dump */ |
_DRM_SHM = 2, /**< shared, cached */ |
_DRM_AGP = 3, /**< AGP/GART */ |
_DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ |
_DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */ |
_DRM_GEM = 6, /**< GEM object */ |
}; |
/** |
* Memory mapping flags. |
*/ |
enum drm_map_flags { |
_DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ |
_DRM_READ_ONLY = 0x02, |
_DRM_LOCKED = 0x04, /**< shared, cached, locked */ |
_DRM_KERNEL = 0x08, /**< kernel requires access */ |
_DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ |
_DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ |
_DRM_REMOVABLE = 0x40, /**< Removable mapping */ |
_DRM_DRIVER = 0x80 /**< Managed by driver */ |
}; |
struct drm_ctx_priv_map { |
unsigned int ctx_id; /**< Context requesting private mapping */ |
void *handle; /**< Handle of map */ |
}; |
/** |
* DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls |
* argument type. |
* |
* \sa drmAddMap(). |
*/ |
struct drm_map { |
unsigned long offset; /**< Requested physical address (0 for SAREA)*/ |
unsigned long size; /**< Requested physical size (bytes) */ |
enum drm_map_type type; /**< Type of memory to map */ |
enum drm_map_flags flags; /**< Flags */ |
void *handle; /**< User-space: "Handle" to pass to mmap() */ |
/**< Kernel-space: kernel-virtual address */ |
int mtrr; /**< MTRR slot used */ |
/* Private data */ |
}; |
/** |
* DRM_IOCTL_GET_CLIENT ioctl argument type. |
*/ |
struct drm_client { |
int idx; /**< Which client desired? */ |
int auth; /**< Is client authenticated? */ |
unsigned long pid; /**< Process ID */ |
unsigned long uid; /**< User ID */ |
unsigned long magic; /**< Magic */ |
unsigned long iocs; /**< Ioctl count */ |
}; |
enum drm_stat_type { |
_DRM_STAT_LOCK, |
_DRM_STAT_OPENS, |
_DRM_STAT_CLOSES, |
_DRM_STAT_IOCTLS, |
_DRM_STAT_LOCKS, |
_DRM_STAT_UNLOCKS, |
_DRM_STAT_VALUE, /**< Generic value */ |
_DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ |
_DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ |
_DRM_STAT_IRQ, /**< IRQ */ |
_DRM_STAT_PRIMARY, /**< Primary DMA bytes */ |
_DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ |
_DRM_STAT_DMA, /**< DMA */ |
_DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ |
_DRM_STAT_MISSED /**< Missed DMA opportunity */ |
/* Add to the *END* of the list */ |
}; |
/** |
* DRM_IOCTL_GET_STATS ioctl argument type. |
*/ |
struct drm_stats { |
unsigned long count; |
struct { |
unsigned long value; |
enum drm_stat_type type; |
} data[15]; |
}; |
/** |
* Hardware locking flags. |
*/ |
enum drm_lock_flags { |
_DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ |
_DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ |
_DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ |
_DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ |
/* These *HALT* flags aren't supported yet |
-- they will be used to support the |
full-screen DGA-like mode. */ |
_DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ |
_DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ |
}; |
/** |
* DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. |
* |
* \sa drmGetLock() and drmUnlock(). |
*/ |
struct drm_lock { |
int context; |
enum drm_lock_flags flags; |
}; |
/** |
* DMA flags |
* |
* \warning |
* These values \e must match xf86drm.h. |
* |
* \sa drm_dma. |
*/ |
enum drm_dma_flags { |
/* Flags for DMA buffer dispatch */ |
_DRM_DMA_BLOCK = 0x01, /**< |
* Block until buffer dispatched. |
* |
* \note The buffer may not yet have |
* been processed by the hardware -- |
* getting a hardware lock with the |
* hardware quiescent will ensure |
* that the buffer has been |
* processed. |
*/ |
_DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ |
_DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ |
/* Flags for DMA buffer request */ |
_DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ |
_DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ |
_DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ |
}; |
/** |
* DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. |
* |
* \sa drmAddBufs(). |
*/ |
struct drm_buf_desc { |
int count; /**< Number of buffers of this size */ |
int size; /**< Size in bytes */ |
int low_mark; /**< Low water mark */ |
int high_mark; /**< High water mark */ |
enum { |
_DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ |
_DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ |
_DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ |
_DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ |
_DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ |
} flags; |
unsigned long agp_start; /**< |
* Start address of where the AGP buffers are |
* in the AGP aperture |
*/ |
}; |
/** |
* DRM_IOCTL_INFO_BUFS ioctl argument type. |
*/ |
struct drm_buf_info { |
int count; /**< Entries in list */ |
struct drm_buf_desc __user *list; |
}; |
/** |
* DRM_IOCTL_FREE_BUFS ioctl argument type. |
*/ |
struct drm_buf_free { |
int count; |
int __user *list; |
}; |
/** |
* Buffer information |
* |
* \sa drm_buf_map. |
*/ |
struct drm_buf_pub { |
int idx; /**< Index into the master buffer list */ |
int total; /**< Buffer size */ |
int used; /**< Amount of buffer in use (for DMA) */ |
void __user *address; /**< Address of buffer */ |
}; |
/** |
* DRM_IOCTL_MAP_BUFS ioctl argument type. |
*/ |
struct drm_buf_map { |
int count; /**< Length of the buffer list */ |
void __user *virtual; /**< Mmap'd area in user-virtual */ |
struct drm_buf_pub __user *list; /**< Buffer information */ |
}; |
/** |
* DRM_IOCTL_DMA ioctl argument type. |
* |
* Indices here refer to the offset into the buffer list in drm_buf_get. |
* |
* \sa drmDMA(). |
*/ |
struct drm_dma { |
int context; /**< Context handle */ |
int send_count; /**< Number of buffers to send */ |
int __user *send_indices; /**< List of handles to buffers */ |
int __user *send_sizes; /**< Lengths of data to send */ |
enum drm_dma_flags flags; /**< Flags */ |
int request_count; /**< Number of buffers requested */ |
int request_size; /**< Desired size for buffers */ |
int __user *request_indices; /**< Buffer information */ |
int __user *request_sizes; |
int granted_count; /**< Number of buffers granted */ |
}; |
enum drm_ctx_flags { |
_DRM_CONTEXT_PRESERVED = 0x01, |
_DRM_CONTEXT_2DONLY = 0x02 |
}; |
/** |
* DRM_IOCTL_ADD_CTX ioctl argument type. |
* |
* \sa drmCreateContext() and drmDestroyContext(). |
*/ |
struct drm_ctx { |
drm_context_t handle; |
enum drm_ctx_flags flags; |
}; |
/** |
* DRM_IOCTL_RES_CTX ioctl argument type. |
*/ |
struct drm_ctx_res { |
int count; |
struct drm_ctx __user *contexts; |
}; |
/** |
* DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. |
*/ |
struct drm_draw { |
drm_drawable_t handle; |
}; |
/** |
* DRM_IOCTL_UPDATE_DRAW ioctl argument type. |
*/ |
typedef enum { |
DRM_DRAWABLE_CLIPRECTS, |
} drm_drawable_info_type_t; |
struct drm_update_draw { |
drm_drawable_t handle; |
unsigned int type; |
unsigned int num; |
unsigned long long data; |
}; |
/** |
* DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. |
*/ |
struct drm_auth { |
drm_magic_t magic; |
}; |
/** |
* DRM_IOCTL_IRQ_BUSID ioctl argument type. |
* |
* \sa drmGetInterruptFromBusID(). |
*/ |
struct drm_irq_busid { |
int irq; /**< IRQ number */ |
int busnum; /**< bus number */ |
int devnum; /**< device number */ |
int funcnum; /**< function number */ |
}; |
enum drm_vblank_seq_type { |
_DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ |
_DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ |
_DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ |
_DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ |
_DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ |
_DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking, unsupported */ |
}; |
#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) |
#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \ |
_DRM_VBLANK_NEXTONMISS) |
struct drm_wait_vblank_request { |
enum drm_vblank_seq_type type; |
unsigned int sequence; |
unsigned long signal; |
}; |
struct drm_wait_vblank_reply { |
enum drm_vblank_seq_type type; |
unsigned int sequence; |
long tval_sec; |
long tval_usec; |
}; |
/** |
* DRM_IOCTL_WAIT_VBLANK ioctl argument type. |
* |
* \sa drmWaitVBlank(). |
*/ |
union drm_wait_vblank { |
struct drm_wait_vblank_request request; |
struct drm_wait_vblank_reply reply; |
}; |
#define _DRM_PRE_MODESET 1 |
#define _DRM_POST_MODESET 2 |
/** |
* DRM_IOCTL_MODESET_CTL ioctl argument type |
* |
* \sa drmModesetCtl(). |
*/ |
struct drm_modeset_ctl { |
__u32 crtc; |
__u32 cmd; |
}; |
/** |
* DRM_IOCTL_AGP_ENABLE ioctl argument type. |
* |
* \sa drmAgpEnable(). |
*/ |
struct drm_agp_mode { |
unsigned long mode; /**< AGP mode */ |
}; |
/** |
* DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. |
* |
* \sa drmAgpAlloc() and drmAgpFree(). |
*/ |
struct drm_agp_buffer { |
unsigned long size; /**< In bytes -- will round to page boundary */ |
unsigned long handle; /**< Used for binding / unbinding */ |
unsigned long type; /**< Type of memory to allocate */ |
unsigned long physical; /**< Physical used by i810 */ |
}; |
/** |
* DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. |
* |
* \sa drmAgpBind() and drmAgpUnbind(). |
*/ |
struct drm_agp_binding { |
unsigned long handle; /**< From drm_agp_buffer */ |
unsigned long offset; /**< In bytes -- will round to page boundary */ |
}; |
/** |
* DRM_IOCTL_AGP_INFO ioctl argument type. |
* |
* \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), |
* drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), |
* drmAgpVendorId() and drmAgpDeviceId(). |
*/ |
struct drm_agp_info { |
int agp_version_major; |
int agp_version_minor; |
unsigned long mode; |
unsigned long aperture_base; /* physical address */ |
unsigned long aperture_size; /* bytes */ |
unsigned long memory_allowed; /* bytes */ |
unsigned long memory_used; |
/* PCI information */ |
unsigned short id_vendor; |
unsigned short id_device; |
}; |
/** |
* DRM_IOCTL_SG_ALLOC ioctl argument type. |
*/ |
struct drm_scatter_gather { |
unsigned long size; /**< In bytes -- will round to page boundary */ |
unsigned long handle; /**< Used for mapping / unmapping */ |
}; |
/** |
* DRM_IOCTL_SET_VERSION ioctl argument type. |
*/ |
struct drm_set_version { |
int drm_di_major; |
int drm_di_minor; |
int drm_dd_major; |
int drm_dd_minor; |
}; |
/** DRM_IOCTL_GEM_CLOSE ioctl argument type */ |
struct drm_gem_close { |
/** Handle of the object to be closed. */ |
__u32 handle; |
__u32 pad; |
}; |
/** DRM_IOCTL_GEM_FLINK ioctl argument type */ |
struct drm_gem_flink { |
/** Handle for the object being named */ |
__u32 handle; |
/** Returned global name */ |
__u32 name; |
}; |
/** DRM_IOCTL_GEM_OPEN ioctl argument type */ |
struct drm_gem_open { |
/** Name of object being opened */ |
__u32 name; |
/** Returned handle for the object */ |
__u32 handle; |
/** Returned size of the object */ |
__u64 size; |
}; |
#include "drm_mode.h" |
/* |
#define DRM_IOCTL_BASE 'd' |
#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) |
#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) |
#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) |
#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) |
#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version) |
#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, struct drm_unique) |
#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, struct drm_auth) |
#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, struct drm_irq_busid) |
#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, struct drm_map) |
#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) |
#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) |
#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) |
#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) |
#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) |
#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) |
#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) |
#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) |
#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) |
#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, struct drm_block) |
#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, struct drm_block) |
#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, struct drm_control) |
#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, struct drm_map) |
#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, struct drm_buf_desc) |
#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, struct drm_buf_desc) |
#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, struct drm_buf_info) |
#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, struct drm_buf_map) |
#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, struct drm_buf_free) |
#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, struct drm_map) |
#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) |
#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) |
#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) |
#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) |
#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) |
#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) |
#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) |
#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, struct drm_ctx) |
#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, struct drm_ctx) |
#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, struct drm_ctx) |
#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, struct drm_ctx_res) |
#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, struct drm_draw) |
#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, struct drm_draw) |
#define DRM_IOCTL_DMA DRM_IOWR(0x29, struct drm_dma) |
#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, struct drm_lock) |
#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) |
#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) |
#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) |
#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) |
#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, struct drm_agp_mode) |
#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, struct drm_agp_info) |
#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, struct drm_agp_buffer) |
#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, struct drm_agp_buffer) |
#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) |
#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) |
#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) |
#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) |
#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) |
#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) |
#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) |
#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc) |
#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) |
#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) |
#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) |
#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) |
#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) |
#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) |
#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) |
#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) |
#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) |
#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) |
#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) |
#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) |
#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) |
#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) |
*/ |
/** |
* Device specific ioctls should only be in their respective headers |
* The device specific ioctl range is from 0x40 to 0x99. |
* Generic IOCTLS restart at 0xA0. |
* |
* \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and |
* drmCommandReadWrite(). |
*/ |
#define DRM_COMMAND_BASE 0x40 |
#define DRM_COMMAND_END 0xA0 |
/* typedef area */ |
#ifndef __KERNEL__ |
typedef struct drm_clip_rect drm_clip_rect_t; |
typedef struct drm_drawable_info drm_drawable_info_t; |
typedef struct drm_tex_region drm_tex_region_t; |
typedef struct drm_hw_lock drm_hw_lock_t; |
typedef struct drm_version drm_version_t; |
typedef struct drm_unique drm_unique_t; |
typedef struct drm_list drm_list_t; |
typedef struct drm_block drm_block_t; |
typedef struct drm_control drm_control_t; |
typedef enum drm_map_type drm_map_type_t; |
typedef enum drm_map_flags drm_map_flags_t; |
typedef struct drm_ctx_priv_map drm_ctx_priv_map_t; |
typedef struct drm_map drm_map_t; |
typedef struct drm_client drm_client_t; |
typedef enum drm_stat_type drm_stat_type_t; |
typedef struct drm_stats drm_stats_t; |
typedef enum drm_lock_flags drm_lock_flags_t; |
typedef struct drm_lock drm_lock_t; |
typedef enum drm_dma_flags drm_dma_flags_t; |
typedef struct drm_buf_desc drm_buf_desc_t; |
typedef struct drm_buf_info drm_buf_info_t; |
typedef struct drm_buf_free drm_buf_free_t; |
typedef struct drm_buf_pub drm_buf_pub_t; |
typedef struct drm_buf_map drm_buf_map_t; |
typedef struct drm_dma drm_dma_t; |
typedef union drm_wait_vblank drm_wait_vblank_t; |
typedef struct drm_agp_mode drm_agp_mode_t; |
typedef enum drm_ctx_flags drm_ctx_flags_t; |
typedef struct drm_ctx drm_ctx_t; |
typedef struct drm_ctx_res drm_ctx_res_t; |
typedef struct drm_draw drm_draw_t; |
typedef struct drm_update_draw drm_update_draw_t; |
typedef struct drm_auth drm_auth_t; |
typedef struct drm_irq_busid drm_irq_busid_t; |
typedef enum drm_vblank_seq_type drm_vblank_seq_type_t; |
typedef struct drm_agp_buffer drm_agp_buffer_t; |
typedef struct drm_agp_binding drm_agp_binding_t; |
typedef struct drm_agp_info drm_agp_info_t; |
typedef struct drm_scatter_gather drm_scatter_gather_t; |
typedef struct drm_set_version drm_set_version_t; |
#endif |
#endif |
/drivers/video/drm/include/drmP.h |
---|
0,0 → 1,1497 |
/** |
* \file drmP.h |
* Private header for Direct Rendering Manager |
* |
* \author Rickard E. (Rik) Faith <faith@valinux.com> |
* \author Gareth Hughes <gareth@valinux.com> |
*/ |
/* |
* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. |
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. |
* 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 |
* 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 NONINFRINGEMENT. IN NO EVENT SHALL |
* VA LINUX SYSTEMS AND/OR ITS 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. |
*/ |
#ifndef _DRM_P_H_ |
#define _DRM_P_H_ |
#include <pci.h> |
#include <drm.h> |
#include <drm_edid.h> |
#include <drm_crtc.h> |
//#include <linux/idr.h> |
struct drm_file; |
struct drm_device; |
//#include "drm_os_linux.h" |
//#include "drm_hashtab.h" |
//#include "drm_mm.h" |
#define DRM_UT_CORE 0x01 |
#define DRM_UT_DRIVER 0x02 |
#define DRM_UT_KMS 0x04 |
#define DRM_UT_MODE 0x08 |
#define KHZ2PICOS(a) (1000000000UL/(a)) |
extern void drm_ut_debug_printk(unsigned int request_level, |
const char *prefix, |
const char *function_name, |
const char *format, ...); |
#define DRM_DEBUG_MODE(prefix, fmt, args...) \ |
do { \ |
dbgprintf("drm debug: %s" fmt, \ |
__func__, ##args); \ |
} while (0) |
#if 0 |
/***********************************************************************/ |
/** \name DRM template customization defaults */ |
/*@{*/ |
/* driver capabilities and requirements mask */ |
#define DRIVER_USE_AGP 0x1 |
#define DRIVER_REQUIRE_AGP 0x2 |
#define DRIVER_USE_MTRR 0x4 |
#define DRIVER_PCI_DMA 0x8 |
#define DRIVER_SG 0x10 |
#define DRIVER_HAVE_DMA 0x20 |
#define DRIVER_HAVE_IRQ 0x40 |
#define DRIVER_IRQ_SHARED 0x80 |
#define DRIVER_IRQ_VBL 0x100 |
#define DRIVER_DMA_QUEUE 0x200 |
#define DRIVER_FB_DMA 0x400 |
#define DRIVER_IRQ_VBL2 0x800 |
#define DRIVER_GEM 0x1000 |
#define DRIVER_MODESET 0x2000 |
/***********************************************************************/ |
/** \name Begin the DRM... */ |
/*@{*/ |
#define DRM_DEBUG_CODE 2 /**< Include debugging code if > 1, then |
also include looping detection. */ |
#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ |
#define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */ |
#define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */ |
#define DRM_LOOPING_LIMIT 5000000 |
#define DRM_TIME_SLICE (HZ/20) /**< Time slice for GLXContexts */ |
#define DRM_LOCK_SLICE 1 /**< Time slice for lock, in jiffies */ |
#define DRM_FLAG_DEBUG 0x01 |
#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) |
#define DRM_MAP_HASH_OFFSET 0x10000000 |
/*@}*/ |
/***********************************************************************/ |
/** \name Macros to make printk easier */ |
/*@{*/ |
/** |
* Error output. |
* |
* \param fmt printf() like format string. |
* \param arg arguments |
*/ |
#define DRM_ERROR(fmt, arg...) \ |
printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* " fmt , __func__ , ##arg) |
/** |
* Memory error output. |
* |
* \param area memory area where the error occurred. |
* \param fmt printf() like format string. |
* \param arg arguments |
*/ |
#define DRM_MEM_ERROR(area, fmt, arg...) \ |
printk(KERN_ERR "[" DRM_NAME ":%s:%s] *ERROR* " fmt , __func__, \ |
drm_mem_stats[area].name , ##arg) |
#define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) |
/** |
* Debug output. |
* |
* \param fmt printf() like format string. |
* \param arg arguments |
*/ |
#if DRM_DEBUG_CODE |
#define DRM_DEBUG(fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_CORE, DRM_NAME, \ |
__func__, fmt, ##args); \ |
} while (0) |
#define DRM_DEBUG_DRIVER(prefix, fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_DRIVER, prefix, \ |
__func__, fmt, ##args); \ |
} while (0) |
#define DRM_DEBUG_KMS(prefix, fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_KMS, prefix, \ |
__func__, fmt, ##args); \ |
} while (0) |
#define DRM_DEBUG_MODE(prefix, fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_MODE, prefix, \ |
__func__, fmt, ##args); \ |
} while (0) |
#define DRM_LOG(fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_CORE, NULL, \ |
NULL, fmt, ##args); \ |
} while (0) |
#define DRM_LOG_KMS(fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_KMS, NULL, \ |
NULL, fmt, ##args); \ |
} while (0) |
#define DRM_LOG_MODE(fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_MODE, NULL, \ |
NULL, fmt, ##args); \ |
} while (0) |
#define DRM_LOG_DRIVER(fmt, args...) \ |
do { \ |
drm_ut_debug_printk(DRM_UT_DRIVER, NULL, \ |
NULL, fmt, ##args); \ |
} while (0) |
#else |
#define DRM_DEBUG_DRIVER(prefix, fmt, args...) do { } while (0) |
#define DRM_DEBUG_KMS(prefix, fmt, args...) do { } while (0) |
#define DRM_DEBUG_MODE(prefix, fmt, args...) do { } while (0) |
#define DRM_DEBUG(fmt, arg...) do { } while (0) |
#define DRM_LOG(fmt, arg...) do { } while (0) |
#define DRM_LOG_KMS(fmt, args...) do { } while (0) |
#define DRM_LOG_MODE(fmt, arg...) do { } while (0) |
#define DRM_LOG_DRIVER(fmt, arg...) do { } while (0) |
#endif |
#define DRM_PROC_LIMIT (PAGE_SIZE-80) |
#define DRM_PROC_PRINT(fmt, arg...) \ |
len += sprintf(&buf[len], fmt , ##arg); \ |
if (len > DRM_PROC_LIMIT) { *eof = 1; return len - offset; } |
#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \ |
len += sprintf(&buf[len], fmt , ##arg); \ |
if (len > DRM_PROC_LIMIT) { ret; *eof = 1; return len - offset; } |
/*@}*/ |
/***********************************************************************/ |
/** \name Internal types and structures */ |
/*@{*/ |
#define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x) |
#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) |
#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) |
#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist) |
#define DRM_IF_VERSION(maj, min) (maj << 16 | min) |
/** |
* Get the private SAREA mapping. |
* |
* \param _dev DRM device. |
* \param _ctx context number. |
* \param _map output mapping. |
*/ |
#define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ |
(_map) = (_dev)->context_sareas[_ctx]; \ |
} while(0) |
/** |
* Test that the hardware lock is held by the caller, returning otherwise. |
* |
* \param dev DRM device. |
* \param filp file pointer of the caller. |
*/ |
#define LOCK_TEST_WITH_RETURN( dev, _file_priv ) \ |
do { \ |
if (!_DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock) || \ |
_file_priv->master->lock.file_priv != _file_priv) { \ |
DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\ |
__func__, _DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock),\ |
_file_priv->master->lock.file_priv, _file_priv); \ |
return -EINVAL; \ |
} \ |
} while (0) |
/** |
* Copy and IOCTL return string to user space |
*/ |
#define DRM_COPY( name, value ) \ |
len = strlen( value ); \ |
if ( len > name##_len ) len = name##_len; \ |
name##_len = strlen( value ); \ |
if ( len && name ) { \ |
if ( copy_to_user( name, value, len ) ) \ |
return -EFAULT; \ |
} |
/** |
* Ioctl function type. |
* |
* \param inode device inode. |
* \param file_priv DRM file private pointer. |
* \param cmd command. |
* \param arg argument. |
*/ |
typedef int drm_ioctl_t(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd, |
unsigned long arg); |
#define DRM_AUTH 0x1 |
#define DRM_MASTER 0x2 |
#define DRM_ROOT_ONLY 0x4 |
#define DRM_CONTROL_ALLOW 0x8 |
struct drm_ioctl_desc { |
unsigned int cmd; |
int flags; |
drm_ioctl_t *func; |
}; |
/** |
* Creates a driver or general drm_ioctl_desc array entry for the given |
* ioctl, for use by drm_ioctl(). |
*/ |
#define DRM_IOCTL_DEF(ioctl, _func, _flags) \ |
[DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags} |
struct drm_magic_entry { |
struct list_head head; |
struct drm_hash_item hash_item; |
struct drm_file *priv; |
}; |
struct drm_vma_entry { |
struct list_head head; |
struct vm_area_struct *vma; |
pid_t pid; |
}; |
/** |
* DMA buffer. |
*/ |
struct drm_buf { |
int idx; /**< Index into master buflist */ |
int total; /**< Buffer size */ |
int order; /**< log-base-2(total) */ |
int used; /**< Amount of buffer in use (for DMA) */ |
unsigned long offset; /**< Byte offset (used internally) */ |
void *address; /**< Address of buffer */ |
unsigned long bus_address; /**< Bus address of buffer */ |
struct drm_buf *next; /**< Kernel-only: used for free list */ |
__volatile__ int waiting; /**< On kernel DMA queue */ |
__volatile__ int pending; /**< On hardware DMA queue */ |
wait_queue_head_t dma_wait; /**< Processes waiting */ |
struct drm_file *file_priv; /**< Private of holding file descr */ |
int context; /**< Kernel queue for this buffer */ |
int while_locked; /**< Dispatch this buffer while locked */ |
enum { |
DRM_LIST_NONE = 0, |
DRM_LIST_FREE = 1, |
DRM_LIST_WAIT = 2, |
DRM_LIST_PEND = 3, |
DRM_LIST_PRIO = 4, |
DRM_LIST_RECLAIM = 5 |
} list; /**< Which list we're on */ |
int dev_priv_size; /**< Size of buffer private storage */ |
void *dev_private; /**< Per-buffer private storage */ |
}; |
/** bufs is one longer than it has to be */ |
struct drm_waitlist { |
int count; /**< Number of possible buffers */ |
struct drm_buf **bufs; /**< List of pointers to buffers */ |
struct drm_buf **rp; /**< Read pointer */ |
struct drm_buf **wp; /**< Write pointer */ |
struct drm_buf **end; /**< End pointer */ |
spinlock_t read_lock; |
spinlock_t write_lock; |
}; |
struct drm_freelist { |
int initialized; /**< Freelist in use */ |
atomic_t count; /**< Number of free buffers */ |
struct drm_buf *next; /**< End pointer */ |
wait_queue_head_t waiting; /**< Processes waiting on free bufs */ |
int low_mark; /**< Low water mark */ |
int high_mark; /**< High water mark */ |
atomic_t wfh; /**< If waiting for high mark */ |
spinlock_t lock; |
}; |
typedef struct drm_dma_handle { |
dma_addr_t busaddr; |
void *vaddr; |
size_t size; |
} drm_dma_handle_t; |
/** |
* Buffer entry. There is one of this for each buffer size order. |
*/ |
struct drm_buf_entry { |
int buf_size; /**< size */ |
int buf_count; /**< number of buffers */ |
struct drm_buf *buflist; /**< buffer list */ |
int seg_count; |
int page_order; |
struct drm_dma_handle **seglist; |
struct drm_freelist freelist; |
}; |
/** File private data */ |
struct drm_file { |
int authenticated; |
pid_t pid; |
uid_t uid; |
drm_magic_t magic; |
unsigned long ioctl_count; |
struct list_head lhead; |
struct drm_minor *minor; |
unsigned long lock_count; |
/** Mapping of mm object handles to object pointers. */ |
struct idr object_idr; |
/** Lock for synchronization of access to object_idr. */ |
spinlock_t table_lock; |
struct file *filp; |
void *driver_priv; |
int is_master; /* this file private is a master for a minor */ |
struct drm_master *master; /* master this node is currently associated with |
N.B. not always minor->master */ |
struct list_head fbs; |
}; |
/** Wait queue */ |
struct drm_queue { |
atomic_t use_count; /**< Outstanding uses (+1) */ |
atomic_t finalization; /**< Finalization in progress */ |
atomic_t block_count; /**< Count of processes waiting */ |
atomic_t block_read; /**< Queue blocked for reads */ |
wait_queue_head_t read_queue; /**< Processes waiting on block_read */ |
atomic_t block_write; /**< Queue blocked for writes */ |
wait_queue_head_t write_queue; /**< Processes waiting on block_write */ |
atomic_t total_queued; /**< Total queued statistic */ |
atomic_t total_flushed; /**< Total flushes statistic */ |
atomic_t total_locks; /**< Total locks statistics */ |
enum drm_ctx_flags flags; /**< Context preserving and 2D-only */ |
struct drm_waitlist waitlist; /**< Pending buffers */ |
wait_queue_head_t flush_queue; /**< Processes waiting until flush */ |
}; |
/** |
* Lock data. |
*/ |
struct drm_lock_data { |
struct drm_hw_lock *hw_lock; /**< Hardware lock */ |
/** Private of lock holder's file (NULL=kernel) */ |
struct drm_file *file_priv; |
wait_queue_head_t lock_queue; /**< Queue of blocked processes */ |
unsigned long lock_time; /**< Time of last lock in jiffies */ |
spinlock_t spinlock; |
uint32_t kernel_waiters; |
uint32_t user_waiters; |
int idle_has_lock; |
}; |
/** |
* DMA data. |
*/ |
struct drm_device_dma { |
struct drm_buf_entry bufs[DRM_MAX_ORDER + 1]; /**< buffers, grouped by their size order */ |
int buf_count; /**< total number of buffers */ |
struct drm_buf **buflist; /**< Vector of pointers into drm_device_dma::bufs */ |
int seg_count; |
int page_count; /**< number of pages */ |
unsigned long *pagelist; /**< page list */ |
unsigned long byte_count; |
enum { |
_DRM_DMA_USE_AGP = 0x01, |
_DRM_DMA_USE_SG = 0x02, |
_DRM_DMA_USE_FB = 0x04, |
_DRM_DMA_USE_PCI_RO = 0x08 |
} flags; |
}; |
/** |
* AGP memory entry. Stored as a doubly linked list. |
*/ |
struct drm_agp_mem { |
unsigned long handle; /**< handle */ |
DRM_AGP_MEM *memory; |
unsigned long bound; /**< address */ |
int pages; |
struct list_head head; |
}; |
/** |
* AGP data. |
* |
* \sa drm_agp_init() and drm_device::agp. |
*/ |
struct drm_agp_head { |
DRM_AGP_KERN agp_info; /**< AGP device information */ |
struct list_head memory; |
unsigned long mode; /**< AGP mode */ |
struct agp_bridge_data *bridge; |
int enabled; /**< whether the AGP bus as been enabled */ |
int acquired; /**< whether the AGP device has been acquired */ |
unsigned long base; |
int agp_mtrr; |
int cant_use_aperture; |
unsigned long page_mask; |
}; |
/** |
* Scatter-gather memory. |
*/ |
struct drm_sg_mem { |
unsigned long handle; |
void *virtual; |
int pages; |
struct page **pagelist; |
dma_addr_t *busaddr; |
}; |
struct drm_sigdata { |
int context; |
struct drm_hw_lock *lock; |
}; |
/** |
* Kernel side of a mapping |
*/ |
struct drm_local_map { |
resource_size_t offset; /**< Requested physical address (0 for SAREA)*/ |
unsigned long size; /**< Requested physical size (bytes) */ |
enum drm_map_type type; /**< Type of memory to map */ |
enum drm_map_flags flags; /**< Flags */ |
void *handle; /**< User-space: "Handle" to pass to mmap() */ |
/**< Kernel-space: kernel-virtual address */ |
int mtrr; /**< MTRR slot used */ |
}; |
typedef struct drm_local_map drm_local_map_t; |
/** |
* Mappings list |
*/ |
struct drm_map_list { |
struct list_head head; /**< list head */ |
struct drm_hash_item hash; |
struct drm_local_map *map; /**< mapping */ |
uint64_t user_token; |
struct drm_master *master; |
struct drm_mm_node *file_offset_node; /**< fake offset */ |
}; |
/** |
* Context handle list |
*/ |
struct drm_ctx_list { |
struct list_head head; /**< list head */ |
drm_context_t handle; /**< context handle */ |
struct drm_file *tag; /**< associated fd private data */ |
}; |
/* location of GART table */ |
#define DRM_ATI_GART_MAIN 1 |
#define DRM_ATI_GART_FB 2 |
#define DRM_ATI_GART_PCI 1 |
#define DRM_ATI_GART_PCIE 2 |
#define DRM_ATI_GART_IGP 3 |
struct drm_ati_pcigart_info { |
int gart_table_location; |
int gart_reg_if; |
void *addr; |
dma_addr_t bus_addr; |
dma_addr_t table_mask; |
struct drm_dma_handle *table_handle; |
struct drm_local_map mapping; |
int table_size; |
}; |
/** |
* GEM specific mm private for tracking GEM objects |
*/ |
struct drm_gem_mm { |
struct drm_mm offset_manager; /**< Offset mgmt for buffer objects */ |
struct drm_open_hash offset_hash; /**< User token hash table for maps */ |
}; |
/** |
* This structure defines the drm_mm memory object, which will be used by the |
* DRM for its buffer objects. |
*/ |
struct drm_gem_object { |
/** Reference count of this object */ |
struct kref refcount; |
/** Handle count of this object. Each handle also holds a reference */ |
struct kref handlecount; |
/** Related drm device */ |
struct drm_device *dev; |
/** File representing the shmem storage */ |
struct file *filp; |
/* Mapping info for this object */ |
struct drm_map_list map_list; |
/** |
* Size of the object, in bytes. Immutable over the object's |
* lifetime. |
*/ |
size_t size; |
/** |
* Global name for this object, starts at 1. 0 means unnamed. |
* Access is covered by the object_name_lock in the related drm_device |
*/ |
int name; |
/** |
* Memory domains. These monitor which caches contain read/write data |
* related to the object. When transitioning from one set of domains |
* to another, the driver is called to ensure that caches are suitably |
* flushed and invalidated |
*/ |
uint32_t read_domains; |
uint32_t write_domain; |
/** |
* While validating an exec operation, the |
* new read/write domain values are computed here. |
* They will be transferred to the above values |
* at the point that any cache flushing occurs |
*/ |
uint32_t pending_read_domains; |
uint32_t pending_write_domain; |
void *driver_private; |
}; |
#include "drm_crtc.h" |
/* per-master structure */ |
struct drm_master { |
struct kref refcount; /* refcount for this master */ |
struct list_head head; /**< each minor contains a list of masters */ |
struct drm_minor *minor; /**< link back to minor we are a master for */ |
char *unique; /**< Unique identifier: e.g., busid */ |
int unique_len; /**< Length of unique field */ |
int unique_size; /**< amount allocated */ |
int blocked; /**< Blocked due to VC switch? */ |
/** \name Authentication */ |
/*@{ */ |
struct drm_open_hash magiclist; |
struct list_head magicfree; |
/*@} */ |
struct drm_lock_data lock; /**< Information on hardware lock */ |
void *driver_priv; /**< Private structure for driver to use */ |
}; |
/** |
* DRM driver structure. This structure represent the common code for |
* a family of cards. There will one drm_device for each card present |
* in this family |
*/ |
struct drm_driver { |
int (*load) (struct drm_device *, unsigned long flags); |
int (*firstopen) (struct drm_device *); |
int (*open) (struct drm_device *, struct drm_file *); |
void (*preclose) (struct drm_device *, struct drm_file *file_priv); |
void (*postclose) (struct drm_device *, struct drm_file *); |
void (*lastclose) (struct drm_device *); |
int (*unload) (struct drm_device *); |
int (*suspend) (struct drm_device *, pm_message_t state); |
int (*resume) (struct drm_device *); |
int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv); |
void (*dma_ready) (struct drm_device *); |
int (*dma_quiescent) (struct drm_device *); |
int (*context_ctor) (struct drm_device *dev, int context); |
int (*context_dtor) (struct drm_device *dev, int context); |
int (*kernel_context_switch) (struct drm_device *dev, int old, |
int new); |
void (*kernel_context_switch_unlock) (struct drm_device *dev); |
/** |
* get_vblank_counter - get raw hardware vblank counter |
* @dev: DRM device |
* @crtc: counter to fetch |
* |
* Driver callback for fetching a raw hardware vblank counter |
* for @crtc. If a device doesn't have a hardware counter, the |
* driver can simply return the value of drm_vblank_count and |
* make the enable_vblank() and disable_vblank() hooks into no-ops, |
* leaving interrupts enabled at all times. |
* |
* Wraparound handling and loss of events due to modesetting is dealt |
* with in the DRM core code. |
* |
* RETURNS |
* Raw vblank counter value. |
*/ |
u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); |
/** |
* enable_vblank - enable vblank interrupt events |
* @dev: DRM device |
* @crtc: which irq to enable |
* |
* Enable vblank interrupts for @crtc. If the device doesn't have |
* a hardware vblank counter, this routine should be a no-op, since |
* interrupts will have to stay on to keep the count accurate. |
* |
* RETURNS |
* Zero on success, appropriate errno if the given @crtc's vblank |
* interrupt cannot be enabled. |
*/ |
int (*enable_vblank) (struct drm_device *dev, int crtc); |
/** |
* disable_vblank - disable vblank interrupt events |
* @dev: DRM device |
* @crtc: which irq to enable |
* |
* Disable vblank interrupts for @crtc. If the device doesn't have |
* a hardware vblank counter, this routine should be a no-op, since |
* interrupts will have to stay on to keep the count accurate. |
*/ |
void (*disable_vblank) (struct drm_device *dev, int crtc); |
/** |
* Called by \c drm_device_is_agp. Typically used to determine if a |
* card is really attached to AGP or not. |
* |
* \param dev DRM device handle |
* |
* \returns |
* One of three values is returned depending on whether or not the |
* card is absolutely \b not AGP (return of 0), absolutely \b is AGP |
* (return of 1), or may or may not be AGP (return of 2). |
*/ |
int (*device_is_agp) (struct drm_device *dev); |
/* these have to be filled in */ |
irqreturn_t(*irq_handler) (DRM_IRQ_ARGS); |
void (*irq_preinstall) (struct drm_device *dev); |
int (*irq_postinstall) (struct drm_device *dev); |
void (*irq_uninstall) (struct drm_device *dev); |
void (*reclaim_buffers) (struct drm_device *dev, |
struct drm_file * file_priv); |
void (*reclaim_buffers_locked) (struct drm_device *dev, |
struct drm_file *file_priv); |
void (*reclaim_buffers_idlelocked) (struct drm_device *dev, |
struct drm_file *file_priv); |
resource_size_t (*get_map_ofs) (struct drm_local_map * map); |
resource_size_t (*get_reg_ofs) (struct drm_device *dev); |
void (*set_version) (struct drm_device *dev, |
struct drm_set_version *sv); |
/* Master routines */ |
int (*master_create)(struct drm_device *dev, struct drm_master *master); |
void (*master_destroy)(struct drm_device *dev, struct drm_master *master); |
int (*proc_init)(struct drm_minor *minor); |
void (*proc_cleanup)(struct drm_minor *minor); |
int (*debugfs_init)(struct drm_minor *minor); |
void (*debugfs_cleanup)(struct drm_minor *minor); |
/** |
* Driver-specific constructor for drm_gem_objects, to set up |
* obj->driver_private. |
* |
* Returns 0 on success. |
*/ |
int (*gem_init_object) (struct drm_gem_object *obj); |
void (*gem_free_object) (struct drm_gem_object *obj); |
/* Driver private ops for this object */ |
struct vm_operations_struct *gem_vm_ops; |
int major; |
int minor; |
int patchlevel; |
char *name; |
char *desc; |
char *date; |
u32 driver_features; |
int dev_priv_size; |
struct drm_ioctl_desc *ioctls; |
int num_ioctls; |
struct file_operations fops; |
struct pci_driver pci_driver; |
/* List of devices hanging off this driver */ |
struct list_head device_list; |
}; |
#define DRM_MINOR_UNASSIGNED 0 |
#define DRM_MINOR_LEGACY 1 |
#define DRM_MINOR_CONTROL 2 |
#define DRM_MINOR_RENDER 3 |
/** |
* debugfs node list. This structure represents a debugfs file to |
* be created by the drm core |
*/ |
struct drm_debugfs_list { |
const char *name; /** file name */ |
int (*show)(struct seq_file*, void*); /** show callback */ |
u32 driver_features; /**< Required driver features for this entry */ |
}; |
/** |
* debugfs node structure. This structure represents a debugfs file. |
*/ |
struct drm_debugfs_node { |
struct list_head list; |
struct drm_minor *minor; |
struct drm_debugfs_list *debugfs_ent; |
struct dentry *dent; |
}; |
/** |
* Info file list entry. This structure represents a debugfs or proc file to |
* be created by the drm core |
*/ |
struct drm_info_list { |
const char *name; /** file name */ |
int (*show)(struct seq_file*, void*); /** show callback */ |
u32 driver_features; /**< Required driver features for this entry */ |
void *data; |
}; |
/** |
* debugfs node structure. This structure represents a debugfs file. |
*/ |
struct drm_info_node { |
struct list_head list; |
struct drm_minor *minor; |
struct drm_info_list *info_ent; |
struct dentry *dent; |
}; |
/** |
* DRM minor structure. This structure represents a drm minor number. |
*/ |
struct drm_minor { |
int index; /**< Minor device number */ |
int type; /**< Control or render */ |
dev_t device; /**< Device number for mknod */ |
struct device kdev; /**< Linux device */ |
struct drm_device *dev; |
struct proc_dir_entry *proc_root; /**< proc directory entry */ |
struct drm_info_node proc_nodes; |
struct dentry *debugfs_root; |
struct drm_info_node debugfs_nodes; |
struct drm_master *master; /* currently active master for this node */ |
struct list_head master_list; |
struct drm_mode_group mode_group; |
}; |
#endif |
/** |
* DRM device structure. This structure represent a complete card that |
* may contain multiple heads. |
*/ |
struct drm_device { |
struct list_head driver_item; /**< list of devices per driver */ |
char *devname; /**< For /proc/interrupts */ |
int if_version; /**< Highest interface version set */ |
/** \name Locks */ |
/*@{ */ |
// spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ |
// struct mutex struct_mutex; /**< For others */ |
/*@} */ |
/** \name Usage Counters */ |
/*@{ */ |
int open_count; /**< Outstanding files open */ |
// atomic_t ioctl_count; /**< Outstanding IOCTLs pending */ |
// atomic_t vma_count; /**< Outstanding vma areas open */ |
int buf_use; /**< Buffers in use -- cannot alloc */ |
// atomic_t buf_alloc; /**< Buffer allocation in progress */ |
/*@} */ |
/** \name Performance counters */ |
/*@{ */ |
unsigned long counters; |
// enum drm_stat_type types[15]; |
// atomic_t counts[15]; |
/*@} */ |
struct list_head filelist; |
/** \name Memory management */ |
/*@{ */ |
struct list_head maplist; /**< Linked list of regions */ |
int map_count; /**< Number of mappable regions */ |
// struct drm_open_hash map_hash; /**< User token hash table for maps */ |
/** \name Context handle management */ |
/*@{ */ |
struct list_head ctxlist; /**< Linked list of context handles */ |
int ctx_count; /**< Number of context handles */ |
// struct mutex ctxlist_mutex; /**< For ctxlist */ |
// struct idr ctx_idr; |
struct list_head vmalist; /**< List of vmas (for debugging) */ |
/*@} */ |
/** \name DMA queues (contexts) */ |
/*@{ */ |
int queue_count; /**< Number of active DMA queues */ |
int queue_reserved; /**< Number of reserved DMA queues */ |
int queue_slots; /**< Actual length of queuelist */ |
// struct drm_queue **queuelist; /**< Vector of pointers to DMA queues */ |
// struct drm_device_dma *dma; /**< Optional pointer for DMA support */ |
/*@} */ |
/** \name Context support */ |
/*@{ */ |
int irq_enabled; /**< True if irq handler is enabled */ |
__volatile__ long context_flag; /**< Context swapping flag */ |
__volatile__ long interrupt_flag; /**< Interruption handler flag */ |
__volatile__ long dma_flag; /**< DMA dispatch flag */ |
// struct timer_list timer; /**< Timer for delaying ctx switch */ |
// wait_queue_head_t context_wait; /**< Processes waiting on ctx switch */ |
int last_checked; /**< Last context checked for DMA */ |
int last_context; /**< Last current context */ |
unsigned long last_switch; /**< jiffies at last context switch */ |
/*@} */ |
// struct work_struct work; |
/** \name VBLANK IRQ support */ |
/*@{ */ |
/* |
* At load time, disabling the vblank interrupt won't be allowed since |
* old clients may not call the modeset ioctl and therefore misbehave. |
* Once the modeset ioctl *has* been called though, we can safely |
* disable them when unused. |
*/ |
int vblank_disable_allowed; |
// wait_queue_head_t *vbl_queue; /**< VBLANK wait queue */ |
// atomic_t *_vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */ |
// spinlock_t vbl_lock; |
// atomic_t *vblank_refcount; /* number of users of vblank interruptsper crtc */ |
u32 *last_vblank; /* protected by dev->vbl_lock, used */ |
/* for wraparound handling */ |
int *vblank_enabled; /* so we don't call enable more than |
once per disable */ |
int *vblank_inmodeset; /* Display driver is setting mode */ |
u32 *last_vblank_wait; /* Last vblank seqno waited per CRTC */ |
// struct timer_list vblank_disable_timer; |
u32 max_vblank_count; /**< size of vblank counter register */ |
/*@} */ |
// cycles_t ctx_start; |
// cycles_t lck_start; |
// struct fasync_struct *buf_async;/**< Processes waiting for SIGIO */ |
// wait_queue_head_t buf_readers; /**< Processes waiting to read */ |
// wait_queue_head_t buf_writers; /**< Processes waiting to ctx switch */ |
// struct drm_agp_head *agp; /**< AGP data */ |
struct pci_dev *pdev; /**< PCI device structure */ |
int pci_vendor; /**< PCI vendor id */ |
int pci_device; /**< PCI device id */ |
// struct drm_sg_mem *sg; /**< Scatter gather memory */ |
int num_crtcs; /**< Number of CRTCs on this device */ |
void *dev_private; /**< device private data */ |
void *mm_private; |
// struct address_space *dev_mapping; |
// struct drm_sigdata sigdata; /**< For block_all_signals */ |
// sigset_t sigmask; |
// struct drm_driver *driver; |
// struct drm_local_map *agp_buffer_map; |
// unsigned int agp_buffer_token; |
// struct drm_minor *control; /**< Control node for card */ |
// struct drm_minor *primary; /**< render type primary screen head */ |
/** \name Drawable information */ |
/*@{ */ |
// spinlock_t drw_lock; |
// struct idr drw_idr; |
/*@} */ |
struct drm_mode_config mode_config; /**< Current mode config */ |
/** \name GEM information */ |
/*@{ */ |
// spinlock_t object_name_lock; |
// struct idr object_name_idr; |
// atomic_t object_count; |
// atomic_t object_memory; |
// atomic_t pin_count; |
// atomic_t pin_memory; |
// atomic_t gtt_count; |
// atomic_t gtt_memory; |
// uint32_t gtt_total; |
uint32_t invalidate_domains; /* domains pending invalidation */ |
uint32_t flush_domains; /* domains pending flush */ |
/*@} */ |
}; |
#if 0 |
static inline int drm_dev_to_irq(struct drm_device *dev) |
{ |
return dev->pdev->irq; |
} |
static __inline__ int drm_core_check_feature(struct drm_device *dev, |
int feature) |
{ |
return ((dev->driver->driver_features & feature) ? 1 : 0); |
} |
#ifdef __alpha__ |
#define drm_get_pci_domain(dev) dev->hose->index |
#else |
#define drm_get_pci_domain(dev) 0 |
#endif |
#if __OS_HAS_AGP |
static inline int drm_core_has_AGP(struct drm_device *dev) |
{ |
return drm_core_check_feature(dev, DRIVER_USE_AGP); |
} |
#else |
#define drm_core_has_AGP(dev) (0) |
#endif |
#if __OS_HAS_MTRR |
static inline int drm_core_has_MTRR(struct drm_device *dev) |
{ |
return drm_core_check_feature(dev, DRIVER_USE_MTRR); |
} |
#define DRM_MTRR_WC MTRR_TYPE_WRCOMB |
static inline int drm_mtrr_add(unsigned long offset, unsigned long size, |
unsigned int flags) |
{ |
return mtrr_add(offset, size, flags, 1); |
} |
static inline int drm_mtrr_del(int handle, unsigned long offset, |
unsigned long size, unsigned int flags) |
{ |
return mtrr_del(handle, offset, size); |
} |
#else |
#define drm_core_has_MTRR(dev) (0) |
#define DRM_MTRR_WC 0 |
static inline int drm_mtrr_add(unsigned long offset, unsigned long size, |
unsigned int flags) |
{ |
return 0; |
} |
static inline int drm_mtrr_del(int handle, unsigned long offset, |
unsigned long size, unsigned int flags) |
{ |
return 0; |
} |
#endif |
/******************************************************************/ |
/** \name Internal function definitions */ |
/*@{*/ |
/* Driver support (drm_drv.h) */ |
extern int drm_init(struct drm_driver *driver); |
extern void drm_exit(struct drm_driver *driver); |
extern int drm_ioctl(struct inode *inode, struct file *filp, |
unsigned int cmd, unsigned long arg); |
extern long drm_compat_ioctl(struct file *filp, |
unsigned int cmd, unsigned long arg); |
extern int drm_lastclose(struct drm_device *dev); |
/* Device support (drm_fops.h) */ |
extern int drm_open(struct inode *inode, struct file *filp); |
extern int drm_stub_open(struct inode *inode, struct file *filp); |
extern int drm_fasync(int fd, struct file *filp, int on); |
extern int drm_release(struct inode *inode, struct file *filp); |
/* Mapping support (drm_vm.h) */ |
extern int drm_mmap(struct file *filp, struct vm_area_struct *vma); |
extern int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma); |
extern void drm_vm_open_locked(struct vm_area_struct *vma); |
extern resource_size_t drm_core_get_map_ofs(struct drm_local_map * map); |
extern resource_size_t drm_core_get_reg_ofs(struct drm_device *dev); |
extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); |
/* Memory management support (drm_memory.h) */ |
#include "drm_memory.h" |
extern void drm_mem_init(void); |
extern int drm_mem_info(char *buf, char **start, off_t offset, |
int request, int *eof, void *data); |
extern void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area); |
extern DRM_AGP_MEM *drm_alloc_agp(struct drm_device *dev, int pages, u32 type); |
extern int drm_free_agp(DRM_AGP_MEM * handle, int pages); |
extern int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start); |
extern DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev, |
struct page **pages, |
unsigned long num_pages, |
uint32_t gtt_offset, |
uint32_t type); |
extern int drm_unbind_agp(DRM_AGP_MEM * handle); |
/* Misc. IOCTL support (drm_ioctl.h) */ |
extern int drm_irq_by_busid(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_getunique(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_setunique(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_getmap(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_getclient(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_getstats(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_setversion(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_noop(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
/* Context IOCTL support (drm_context.h) */ |
extern int drm_resctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_addctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_modctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_getctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_switchctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_newctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_rmctx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_ctxbitmap_init(struct drm_device *dev); |
extern void drm_ctxbitmap_cleanup(struct drm_device *dev); |
extern void drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle); |
extern int drm_setsareactx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_getsareactx(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
/* Drawable IOCTL support (drm_drawable.h) */ |
extern int drm_adddraw(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_rmdraw(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_update_drawable_info(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern struct drm_drawable_info *drm_get_drawable_info(struct drm_device *dev, |
drm_drawable_t id); |
extern void drm_drawable_free_all(struct drm_device *dev); |
/* Authentication IOCTL support (drm_auth.h) */ |
extern int drm_getmagic(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_authmagic(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
/* Cache management (drm_cache.c) */ |
void drm_clflush_pages(struct page *pages[], unsigned long num_pages); |
/* Locking IOCTL support (drm_lock.h) */ |
extern int drm_lock(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_unlock(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); |
extern int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context); |
extern void drm_idlelock_take(struct drm_lock_data *lock_data); |
extern void drm_idlelock_release(struct drm_lock_data *lock_data); |
/* |
* These are exported to drivers so that they can implement fencing using |
* DMA quiscent + idle. DMA quiescent usually requires the hardware lock. |
*/ |
extern int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv); |
/* Buffer management support (drm_bufs.h) */ |
extern int drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc * request); |
extern int drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc * request); |
extern int drm_addmap(struct drm_device *dev, resource_size_t offset, |
unsigned int size, enum drm_map_type type, |
enum drm_map_flags flags, struct drm_local_map **map_ptr); |
extern int drm_addmap_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_rmmap(struct drm_device *dev, struct drm_local_map *map); |
extern int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map); |
extern int drm_rmmap_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_addbufs(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_infobufs(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_markbufs(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_freebufs(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_mapbufs(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_order(unsigned long size); |
extern resource_size_t drm_get_resource_start(struct drm_device *dev, |
unsigned int resource); |
extern resource_size_t drm_get_resource_len(struct drm_device *dev, |
unsigned int resource); |
/* DMA support (drm_dma.h) */ |
extern int drm_dma_setup(struct drm_device *dev); |
extern void drm_dma_takedown(struct drm_device *dev); |
extern void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf); |
extern void drm_core_reclaim_buffers(struct drm_device *dev, |
struct drm_file *filp); |
/* IRQ support (drm_irq.h) */ |
extern int drm_control(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern irqreturn_t drm_irq_handler(DRM_IRQ_ARGS); |
extern int drm_irq_install(struct drm_device *dev); |
extern int drm_irq_uninstall(struct drm_device *dev); |
extern void drm_driver_irq_preinstall(struct drm_device *dev); |
extern void drm_driver_irq_postinstall(struct drm_device *dev); |
extern void drm_driver_irq_uninstall(struct drm_device *dev); |
extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); |
extern int drm_wait_vblank(struct drm_device *dev, void *data, |
struct drm_file *filp); |
extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); |
extern u32 drm_vblank_count(struct drm_device *dev, int crtc); |
extern void drm_handle_vblank(struct drm_device *dev, int crtc); |
extern int drm_vblank_get(struct drm_device *dev, int crtc); |
extern void drm_vblank_put(struct drm_device *dev, int crtc); |
extern void drm_vblank_cleanup(struct drm_device *dev); |
/* Modesetting support */ |
extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc); |
extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc); |
extern int drm_modeset_ctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
/* AGP/GART support (drm_agpsupport.h) */ |
extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); |
extern int drm_agp_acquire(struct drm_device *dev); |
extern int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_release(struct drm_device *dev); |
extern int drm_agp_release_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode); |
extern int drm_agp_enable_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info); |
extern int drm_agp_info_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request); |
extern int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request); |
extern int drm_agp_free_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request); |
extern int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request); |
extern int drm_agp_bind_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern DRM_AGP_MEM *drm_agp_allocate_memory(struct agp_bridge_data *bridge, size_t pages, u32 type); |
extern int drm_agp_free_memory(DRM_AGP_MEM * handle); |
extern int drm_agp_bind_memory(DRM_AGP_MEM * handle, off_t start); |
extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle); |
extern void drm_agp_chipset_flush(struct drm_device *dev); |
/* Stub support (drm_stub.h) */ |
extern int drm_setmaster_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
struct drm_master *drm_master_create(struct drm_minor *minor); |
extern struct drm_master *drm_master_get(struct drm_master *master); |
extern void drm_master_put(struct drm_master **master); |
extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, |
struct drm_driver *driver); |
extern void drm_put_dev(struct drm_device *dev); |
extern int drm_put_minor(struct drm_minor **minor); |
extern unsigned int drm_debug; |
extern struct class *drm_class; |
extern struct proc_dir_entry *drm_proc_root; |
extern struct dentry *drm_debugfs_root; |
extern struct idr drm_minors_idr; |
extern struct drm_local_map *drm_getsarea(struct drm_device *dev); |
/* Proc support (drm_proc.h) */ |
extern int drm_proc_init(struct drm_minor *minor, int minor_id, |
struct proc_dir_entry *root); |
extern int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root); |
/* Debugfs support */ |
#if defined(CONFIG_DEBUG_FS) |
extern int drm_debugfs_init(struct drm_minor *minor, int minor_id, |
struct dentry *root); |
extern int drm_debugfs_create_files(struct drm_info_list *files, int count, |
struct dentry *root, struct drm_minor *minor); |
extern int drm_debugfs_remove_files(struct drm_info_list *files, int count, |
struct drm_minor *minor); |
extern int drm_debugfs_cleanup(struct drm_minor *minor); |
#endif |
/* Info file support */ |
extern int drm_name_info(struct seq_file *m, void *data); |
extern int drm_vm_info(struct seq_file *m, void *data); |
extern int drm_queues_info(struct seq_file *m, void *data); |
extern int drm_bufs_info(struct seq_file *m, void *data); |
extern int drm_vblank_info(struct seq_file *m, void *data); |
extern int drm_clients_info(struct seq_file *m, void* data); |
extern int drm_gem_name_info(struct seq_file *m, void *data); |
extern int drm_gem_object_info(struct seq_file *m, void* data); |
#if DRM_DEBUG_CODE |
extern int drm_vma_info(struct seq_file *m, void *data); |
#endif |
/* Scatter Gather Support (drm_scatter.h) */ |
extern void drm_sg_cleanup(struct drm_sg_mem * entry); |
extern int drm_sg_alloc_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
extern int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request); |
extern int drm_sg_free(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
/* ATI PCIGART support (ati_pcigart.h) */ |
extern int drm_ati_pcigart_init(struct drm_device *dev, |
struct drm_ati_pcigart_info * gart_info); |
extern int drm_ati_pcigart_cleanup(struct drm_device *dev, |
struct drm_ati_pcigart_info * gart_info); |
extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size, |
size_t align, dma_addr_t maxaddr); |
extern void __drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); |
extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); |
/* sysfs support (drm_sysfs.c) */ |
struct drm_sysfs_class; |
extern struct class *drm_sysfs_create(struct module *owner, char *name); |
extern void drm_sysfs_destroy(void); |
extern int drm_sysfs_device_add(struct drm_minor *minor); |
extern void drm_sysfs_hotplug_event(struct drm_device *dev); |
extern void drm_sysfs_device_remove(struct drm_minor *minor); |
extern char *drm_get_connector_status_name(enum drm_connector_status status); |
extern int drm_sysfs_connector_add(struct drm_connector *connector); |
extern void drm_sysfs_connector_remove(struct drm_connector *connector); |
/* Graphics Execution Manager library functions (drm_gem.c) */ |
int drm_gem_init(struct drm_device *dev); |
void drm_gem_destroy(struct drm_device *dev); |
void drm_gem_object_free(struct kref *kref); |
struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, |
size_t size); |
void drm_gem_object_handle_free(struct kref *kref); |
void drm_gem_vm_open(struct vm_area_struct *vma); |
void drm_gem_vm_close(struct vm_area_struct *vma); |
int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); |
static inline void |
drm_gem_object_reference(struct drm_gem_object *obj) |
{ |
kref_get(&obj->refcount); |
} |
static inline void |
drm_gem_object_unreference(struct drm_gem_object *obj) |
{ |
if (obj == NULL) |
return; |
kref_put(&obj->refcount, drm_gem_object_free); |
} |
int drm_gem_handle_create(struct drm_file *file_priv, |
struct drm_gem_object *obj, |
int *handlep); |
static inline void |
drm_gem_object_handle_reference(struct drm_gem_object *obj) |
{ |
drm_gem_object_reference(obj); |
kref_get(&obj->handlecount); |
} |
static inline void |
drm_gem_object_handle_unreference(struct drm_gem_object *obj) |
{ |
if (obj == NULL) |
return; |
/* |
* Must bump handle count first as this may be the last |
* ref, in which case the object would disappear before we |
* checked for a name |
*/ |
kref_put(&obj->handlecount, drm_gem_object_handle_free); |
drm_gem_object_unreference(obj); |
} |
struct drm_gem_object *drm_gem_object_lookup(struct drm_device *dev, |
struct drm_file *filp, |
int handle); |
int drm_gem_close_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
int drm_gem_flink_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
int drm_gem_open_ioctl(struct drm_device *dev, void *data, |
struct drm_file *file_priv); |
void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); |
void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); |
extern void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev); |
extern void drm_core_ioremap_wc(struct drm_local_map *map, struct drm_device *dev); |
extern void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev); |
static __inline__ struct drm_local_map *drm_core_findmap(struct drm_device *dev, |
unsigned int token) |
{ |
struct drm_map_list *_entry; |
list_for_each_entry(_entry, &dev->maplist, head) |
if (_entry->user_token == token) |
return _entry->map; |
return NULL; |
} |
static __inline__ int drm_device_is_agp(struct drm_device *dev) |
{ |
if (dev->driver->device_is_agp != NULL) { |
int err = (*dev->driver->device_is_agp) (dev); |
if (err != 2) { |
return err; |
} |
} |
return pci_find_capability(dev->pdev, PCI_CAP_ID_AGP); |
} |
static __inline__ int drm_device_is_pcie(struct drm_device *dev) |
{ |
return pci_find_capability(dev->pdev, PCI_CAP_ID_EXP); |
} |
static __inline__ void drm_core_dropmap(struct drm_local_map *map) |
{ |
} |
static __inline__ void *drm_calloc_large(size_t nmemb, size_t size) |
{ |
if (size * nmemb <= PAGE_SIZE) |
return kcalloc(nmemb, size, GFP_KERNEL); |
if (size != 0 && nmemb > ULONG_MAX / size) |
return NULL; |
return __vmalloc(size * nmemb, |
GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL); |
} |
static __inline void drm_free_large(void *ptr) |
{ |
if (!is_vmalloc_addr(ptr)) |
return kfree(ptr); |
vfree(ptr); |
} |
/*@}*/ |
#endif |
#endif |
/drivers/video/drm/include/drm_crtc.h |
---|
113,7 → 113,7 |
struct drm_display_mode { |
/* Header */ |
// struct list_head head; |
struct list_head head; |
struct drm_mode_object base; |
char name[DRM_DISPLAY_MODE_LEN]; |
246,7 → 246,7 |
struct drm_framebuffer { |
struct drm_device *dev; |
// struct list_head head; |
struct list_head head; |
struct drm_mode_object base; |
const struct drm_framebuffer_funcs *funcs; |
unsigned int pitch; |
257,13 → 257,13 |
int bits_per_pixel; |
int flags; |
void *fbdev; |
u32_t pseudo_palette[17]; |
// struct list_head filp_head; |
u32 pseudo_palette[17]; |
struct list_head filp_head; |
}; |
struct drm_property_blob { |
struct drm_mode_object base; |
// struct list_head head; |
struct list_head head; |
unsigned int length; |
void *data; |
}; |
270,12 → 270,12 |
struct drm_property_enum { |
uint64_t value; |
// struct list_head head; |
struct list_head head; |
char name[DRM_PROP_NAME_LEN]; |
}; |
struct drm_property { |
// struct list_head head; |
struct list_head head; |
struct drm_mode_object base; |
uint32_t flags; |
char name[DRM_PROP_NAME_LEN]; |
282,7 → 282,7 |
uint32_t num_values; |
uint64_t *values; |
// struct list_head enum_blob_list; |
struct list_head enum_blob_list; |
}; |
struct drm_crtc; |
348,7 → 348,7 |
*/ |
struct drm_crtc { |
struct drm_device *dev; |
// struct list_head head; |
struct list_head head; |
struct drm_mode_object base; |
415,7 → 415,7 |
*/ |
struct drm_encoder { |
struct drm_device *dev; |
// struct list_head head; |
struct list_head head; |
struct drm_mode_object base; |
int encoder_type; |
447,7 → 447,7 |
struct drm_device *dev; |
// struct device kdev; |
struct device_attribute *attr; |
// struct list_head head; |
struct list_head head; |
struct drm_mode_object base; |
455,18 → 455,18 |
int connector_type_id; |
bool interlace_allowed; |
bool doublescan_allowed; |
// struct list_head modes; /* list of modes on this connector */ |
struct list_head modes; /* list of modes on this connector */ |
int initial_x, initial_y; |
enum drm_connector_status status; |
/* these are modes added by probing with DDC or the BIOS */ |
// struct list_head probed_modes; |
struct list_head probed_modes; |
struct drm_display_info display_info; |
const struct drm_connector_funcs *funcs; |
// struct list_head user_modes; |
struct list_head user_modes; |
struct drm_property_blob *edid_blob_ptr; |
u32_t property_ids[DRM_CONNECTOR_MAX_PROPERTY]; |
uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; |
490,7 → 490,7 |
* This is used to set modes. |
*/ |
struct drm_mode_set { |
// struct list_head head; |
struct list_head head; |
struct drm_framebuffer *fb; |
struct drm_crtc *crtc; |
533,22 → 533,22 |
struct drm_mode_config { |
// struct mutex mutex; /* protects configuration (mode lists etc.) */ |
// struct mutex idr_mutex; /* for IDR management */ |
// struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ |
struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ |
/* this is limited to one for now */ |
int num_fb; |
// struct list_head fb_list; |
struct list_head fb_list; |
int num_connector; |
// struct list_head connector_list; |
struct list_head connector_list; |
int num_encoder; |
// struct list_head encoder_list; |
struct list_head encoder_list; |
int num_crtc; |
// struct list_head crtc_list; |
struct list_head crtc_list; |
// struct list_head property_list; |
struct list_head property_list; |
/* in-kernel framebuffers - hung of filp_head in drm_framebuffer */ |
// struct list_head fb_kernel_list; |
struct list_head fb_kernel_list; |
int min_width, min_height; |
int max_width, max_height; |
556,7 → 556,7 |
resource_size_t fb_base; |
/* pointers to standard properties */ |
// struct list_head property_blob_list; |
struct list_head property_blob_list; |
struct drm_property *edid_property; |
struct drm_property *dpms_property; |
586,7 → 586,6 |
#define obj_to_property(x) container_of(x, struct drm_property, base) |
#define obj_to_blob(x) container_of(x, struct drm_property_blob, base) |
extern void drm_crtc_init(struct drm_device *dev, |
struct drm_crtc *crtc, |
const struct drm_crtc_funcs *funcs); |
612,8 → 611,11 |
extern char *drm_get_dvi_i_select_name(int val); |
extern char *drm_get_tv_subconnector_name(int val); |
extern char *drm_get_tv_select_name(int val); |
extern void drm_fb_release(struct drm_file *file_priv); |
extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); |
//extern void drm_fb_release(struct drm_file *file_priv); |
//extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); |
//extern struct edid *drm_get_edid(struct drm_connector *connector, |
// struct i2c_adapter *adapter); |
//extern int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, |
736,4 → 738,6 |
extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, |
void *data, struct drm_file *file_priv); |
//extern bool drm_detect_hdmi_monitor(struct edid *edid); |
#endif /* __DRM_CRTC_H__ */ |
/drivers/video/drm/include/drm_crtc_helper.h |
---|
0,0 → 1,126 |
/* |
* Copyright © 2006 Keith Packard |
* Copyright © 2007-2008 Dave Airlie |
* Copyright © 2007-2008 Intel Corporation |
* Jesse Barnes <jesse.barnes@intel.com> |
* |
* 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. |
*/ |
/* |
* The DRM mode setting helper functions are common code for drivers to use if |
* they wish. Drivers are not forced to use this code in their |
* implementations but it would be useful if they code they do use at least |
* provides a consistent interface and operation to userspace |
*/ |
#ifndef __DRM_CRTC_HELPER_H__ |
#define __DRM_CRTC_HELPER_H__ |
//#include <linux/spinlock.h> |
//#include <linux/types.h> |
//#include <linux/idr.h> |
//#include <linux/fb.h> |
struct drm_crtc_helper_funcs { |
/* |
* Control power levels on the CRTC. If the mode passed in is |
* unsupported, the provider must use the next lowest power level. |
*/ |
void (*dpms)(struct drm_crtc *crtc, int mode); |
void (*prepare)(struct drm_crtc *crtc); |
void (*commit)(struct drm_crtc *crtc); |
/* Provider can fixup or change mode timings before modeset occurs */ |
bool (*mode_fixup)(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode); |
/* Actually set the mode */ |
int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, int x, int y, |
struct drm_framebuffer *old_fb); |
/* Move the crtc on the current fb to the given position *optional* */ |
int (*mode_set_base)(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb); |
}; |
struct drm_encoder_helper_funcs { |
void (*dpms)(struct drm_encoder *encoder, int mode); |
void (*save)(struct drm_encoder *encoder); |
void (*restore)(struct drm_encoder *encoder); |
bool (*mode_fixup)(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode); |
void (*prepare)(struct drm_encoder *encoder); |
void (*commit)(struct drm_encoder *encoder); |
void (*mode_set)(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode); |
struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder); |
/* detect for DAC style encoders */ |
enum drm_connector_status (*detect)(struct drm_encoder *encoder, |
struct drm_connector *connector); |
}; |
struct drm_connector_helper_funcs { |
int (*get_modes)(struct drm_connector *connector); |
int (*mode_valid)(struct drm_connector *connector, |
struct drm_display_mode *mode); |
struct drm_encoder *(*best_encoder)(struct drm_connector *connector); |
}; |
extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); |
extern void drm_helper_disable_unused_functions(struct drm_device *dev); |
extern int drm_helper_hotplug_stage_two(struct drm_device *dev); |
extern bool drm_helper_initial_config(struct drm_device *dev); |
extern int drm_crtc_helper_set_config(struct drm_mode_set *set); |
extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
int x, int y, |
struct drm_framebuffer *old_fb); |
extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc); |
extern void drm_helper_connector_dpms(struct drm_connector *connector, int mode); |
extern int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, |
struct drm_mode_fb_cmd *mode_cmd); |
static inline void drm_crtc_helper_add(struct drm_crtc *crtc, |
const struct drm_crtc_helper_funcs *funcs) |
{ |
crtc->helper_private = (void *)funcs; |
} |
static inline void drm_encoder_helper_add(struct drm_encoder *encoder, |
const struct drm_encoder_helper_funcs *funcs) |
{ |
encoder->helper_private = (void *)funcs; |
} |
static inline void drm_connector_helper_add(struct drm_connector *connector, |
const struct drm_connector_helper_funcs *funcs) |
{ |
connector->helper_private = (void *)funcs; |
} |
extern int drm_helper_resume_force_mode(struct drm_device *dev); |
#endif |
/drivers/video/drm/include/drm_edid.h |
---|
0,0 → 1,289 |
/* |
* Copyright © 2007-2008 Intel Corporation |
* Jesse Barnes <jesse.barnes@intel.com> |
* |
* 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. |
*/ |
#ifndef __DRM_EDID_H__ |
#define __DRM_EDID_H__ |
#include <types.h> |
#define EDID_LENGTH 128 |
#define DDC_ADDR 0x50 |
struct est_timings { |
u8 t1; |
u8 t2; |
u8 mfg_rsvd; |
} __attribute__((packed)); |
/* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ |
#define EDID_TIMING_ASPECT_SHIFT 6 |
#define EDID_TIMING_ASPECT_MASK (0x3 << EDID_TIMING_ASPECT_SHIFT) |
/* need to add 60 */ |
#define EDID_TIMING_VFREQ_SHIFT 0 |
#define EDID_TIMING_VFREQ_MASK (0x3f << EDID_TIMING_VFREQ_SHIFT) |
struct std_timing { |
u8 hsize; /* need to multiply by 8 then add 248 */ |
u8 vfreq_aspect; |
} __attribute__((packed)); |
#define DRM_EDID_PT_HSYNC_POSITIVE (1 << 1) |
#define DRM_EDID_PT_VSYNC_POSITIVE (1 << 2) |
#define DRM_EDID_PT_SEPARATE_SYNC (3 << 3) |
#define DRM_EDID_PT_STEREO (1 << 5) |
#define DRM_EDID_PT_INTERLACED (1 << 7) |
/* If detailed data is pixel timing */ |
struct detailed_pixel_timing { |
u8 hactive_lo; |
u8 hblank_lo; |
u8 hactive_hblank_hi; |
u8 vactive_lo; |
u8 vblank_lo; |
u8 vactive_vblank_hi; |
u8 hsync_offset_lo; |
u8 hsync_pulse_width_lo; |
u8 vsync_offset_pulse_width_lo; |
u8 hsync_vsync_offset_pulse_width_hi; |
u8 width_mm_lo; |
u8 height_mm_lo; |
u8 width_height_mm_hi; |
u8 hborder; |
u8 vborder; |
u8 misc; |
} __attribute__((packed)); |
/* If it's not pixel timing, it'll be one of the below */ |
struct detailed_data_string { |
u8 str[13]; |
} __attribute__((packed)); |
struct detailed_data_monitor_range { |
u8 min_vfreq; |
u8 max_vfreq; |
u8 min_hfreq_khz; |
u8 max_hfreq_khz; |
u8 pixel_clock_mhz; /* need to multiply by 10 */ |
u16 sec_gtf_toggle; /* A000=use above, 20=use below */ |
u8 hfreq_start_khz; /* need to multiply by 2 */ |
u8 c; /* need to divide by 2 */ |
u16 m; |
u8 k; |
u8 j; /* need to divide by 2 */ |
} __attribute__((packed)); |
struct detailed_data_wpindex { |
u8 white_yx_lo; /* Lower 2 bits each */ |
u8 white_x_hi; |
u8 white_y_hi; |
u8 gamma; /* need to divide by 100 then add 1 */ |
} __attribute__((packed)); |
struct detailed_data_color_point { |
u8 windex1; |
u8 wpindex1[3]; |
u8 windex2; |
u8 wpindex2[3]; |
} __attribute__((packed)); |
struct detailed_non_pixel { |
u8 pad1; |
u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name |
fb=color point data, fa=standard timing data, |
f9=undefined, f8=mfg. reserved */ |
u8 pad2; |
union { |
struct detailed_data_string str; |
struct detailed_data_monitor_range range; |
struct detailed_data_wpindex color; |
struct std_timing timings[5]; |
} data; |
} __attribute__((packed)); |
#define EDID_DETAIL_STD_MODES 0xfa |
#define EDID_DETAIL_MONITOR_CPDATA 0xfb |
#define EDID_DETAIL_MONITOR_NAME 0xfc |
#define EDID_DETAIL_MONITOR_RANGE 0xfd |
#define EDID_DETAIL_MONITOR_STRING 0xfe |
#define EDID_DETAIL_MONITOR_SERIAL 0xff |
struct detailed_timing { |
u16 pixel_clock; /* need to multiply by 10 KHz */ |
union { |
struct detailed_pixel_timing pixel_data; |
struct detailed_non_pixel other_data; |
} data; |
} __attribute__((packed)); |
#define DRM_EDID_INPUT_SERRATION_VSYNC (1 << 0) |
#define DRM_EDID_INPUT_SYNC_ON_GREEN (1 << 1) |
#define DRM_EDID_INPUT_COMPOSITE_SYNC (1 << 2) |
#define DRM_EDID_INPUT_SEPARATE_SYNCS (1 << 3) |
#define DRM_EDID_INPUT_BLANK_TO_BLACK (1 << 4) |
#define DRM_EDID_INPUT_VIDEO_LEVEL (3 << 5) |
#define DRM_EDID_INPUT_DIGITAL (1 << 7) /* bits below must be zero if set */ |
#define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 0) |
#define DRM_EDID_FEATURE_PREFERRED_TIMING (1 << 1) |
#define DRM_EDID_FEATURE_STANDARD_COLOR (1 << 2) |
#define DRM_EDID_FEATURE_DISPLAY_TYPE (3 << 3) /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ |
#define DRM_EDID_FEATURE_PM_ACTIVE_OFF (1 << 5) |
#define DRM_EDID_FEATURE_PM_SUSPEND (1 << 6) |
#define DRM_EDID_FEATURE_PM_STANDBY (1 << 7) |
struct edid { |
u8 header[8]; |
/* Vendor & product info */ |
u8 mfg_id[2]; |
u8 prod_code[2]; |
u32 serial; /* FIXME: byte order */ |
u8 mfg_week; |
u8 mfg_year; |
/* EDID version */ |
u8 version; |
u8 revision; |
/* Display info: */ |
u8 input; |
u8 width_cm; |
u8 height_cm; |
u8 gamma; |
u8 features; |
/* Color characteristics */ |
u8 red_green_lo; |
u8 black_white_lo; |
u8 red_x; |
u8 red_y; |
u8 green_x; |
u8 green_y; |
u8 blue_x; |
u8 blue_y; |
u8 white_x; |
u8 white_y; |
/* Est. timings and mfg rsvd timings*/ |
struct est_timings established_timings; |
/* Standard timings 1-8*/ |
struct std_timing standard_timings[8]; |
/* Detailing timings 1-4 */ |
struct detailed_timing detailed_timings[4]; |
/* Number of 128 byte ext. blocks */ |
u8 extensions; |
/* Checksum */ |
u8 checksum; |
} __attribute__((packed)); |
#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) |
#define KOBJ_NAME_LEN 20 |
#define I2C_NAME_SIZE 20 |
/* --- Defines for bit-adapters --------------------------------------- */ |
/* |
* This struct contains the hw-dependent functions of bit-style adapters to |
* manipulate the line states, and to init any hw-specific features. This is |
* only used if you have more than one hw-type of adapter running. |
*/ |
struct i2c_algo_bit_data { |
void *data; /* private data for lowlevel routines */ |
void (*setsda) (void *data, int state); |
void (*setscl) (void *data, int state); |
int (*getsda) (void *data); |
int (*getscl) (void *data); |
/* local settings */ |
int udelay; /* half clock cycle time in us, |
minimum 2 us for fast-mode I2C, |
minimum 5 us for standard-mode I2C and SMBus, |
maximum 50 us for SMBus */ |
int timeout; /* in jiffies */ |
}; |
struct i2c_client; |
/* |
* i2c_adapter is the structure used to identify a physical i2c bus along |
* with the access algorithms necessary to access it. |
*/ |
struct i2c_adapter { |
// struct module *owner; |
unsigned int id; |
unsigned int class; |
// const struct i2c_algorithm *algo; /* the algorithm to access the bus */ |
void *algo_data; |
/* --- administration stuff. */ |
int (*client_register)(struct i2c_client *); |
int (*client_unregister)(struct i2c_client *); |
/* data fields that are valid for all devices */ |
u8 level; /* nesting level for lockdep */ |
// struct mutex bus_lock; |
// struct mutex clist_lock; |
int timeout; |
int retries; |
// struct device dev; /* the adapter device */ |
int nr; |
struct list_head clients; /* DEPRECATED */ |
char name[48]; |
// struct completion dev_released; |
}; |
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) |
struct i2c_client { |
unsigned short flags; /* div., see below */ |
unsigned short addr; /* chip address - NOTE: 7bit */ |
/* addresses are stored in the */ |
/* _LOWER_ 7 bits */ |
char name[I2C_NAME_SIZE]; |
struct i2c_adapter *adapter; /* the adapter we sit on */ |
// struct i2c_driver *driver; /* and our access routines */ |
// struct device dev; /* the device structure */ |
int irq; /* irq issued by device (or -1) */ |
char driver_name[KOBJ_NAME_LEN]; |
struct list_head list; /* DEPRECATED */ |
// struct completion released; |
}; |
#define to_i2c_client(d) container_of(d, struct i2c_client, dev) |
int i2c_bit_add_bus(struct i2c_adapter *); |
int i2c_bit_add_numbered_bus(struct i2c_adapter *); |
struct i2c_msg { |
u16 addr; /* slave address */ |
u16 flags; |
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ |
#define I2C_M_RD 0x0001 /* read data, from slave to master */ |
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ |
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ |
u16 len; /* msg length */ |
u8 *buf; /* pointer to msg data */ |
}; |
#endif /* __DRM_EDID_H__ */ |
/drivers/video/drm/include/pci.h |
---|
1,6 → 1,6 |
#include <types.h> |
#include <link.h> |
#include <list.h> |
#ifndef __PCI_H__ |
#define __PCI_H__ |
545,7 → 545,7 |
typedef struct |
{ |
link_t link; |
struct list_head link; |
struct pci_dev pci_dev; |
}dev_t; |
/drivers/video/drm/include/types.h |
---|
38,6 → 38,9 |
typedef unsigned int u32_t; |
typedef unsigned long long u64_t; |
typedef signed char int8_t; |
typedef signed long long int64_t; |
#define NULL (void*)0 |
typedef uint32_t dma_addr_t; |
55,8 → 58,16 |
#define likely(x) __builtin_expect(!!(x), 1) |
#define unlikely(x) __builtin_expect(!!(x), 0) |
#define BITS_PER_LONG 32 |
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) |
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG) |
#define DECLARE_BITMAP(name,bits) \ |
unsigned long name[BITS_TO_LONGS(bits)] |
#define KERN_EMERG "<0>" /* system is unusable */ |
#define KERN_ALERT "<1>" /* action must be taken immediately */ |
#define KERN_CRIT "<2>" /* critical conditions */ |
115,14 → 126,21 |
#define GFP_KERNEL 0 |
//#include <stdio.h> |
int snprintf(char *str, size_t size, const char *format, ...); |
//#include <string.h> |
void* memcpy(void *s1, const void *s2, size_t n); |
void* memset(void *s, int c, size_t n); |
size_t strlen(const char *s); |
char *strncpy (char *dst, const char *src, size_t len); |
void *malloc(size_t size); |
#define kmalloc(s,f) malloc((s)) |
#define kfree free |
static inline void *kzalloc(size_t size, u32_t flags) |
200,5 → 218,81 |
#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) |
static inline void bitmap_zero(unsigned long *dst, int nbits) |
{ |
if (nbits <= BITS_PER_LONG) |
*dst = 0UL; |
else { |
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); |
memset(dst, 0, len); |
} |
} |
#define EXPORT_SYMBOL(x) |
#define IDR_BITS 5 |
#define IDR_FULL 0xfffffffful |
struct idr_layer { |
unsigned long bitmap; /* A zero bit means "space here" */ |
struct idr_layer *ary[1<<IDR_BITS]; |
int count; /* When zero, we can release it */ |
}; |
struct idr { |
struct idr_layer *top; |
struct idr_layer *id_free; |
int layers; |
int id_free_cnt; |
// spinlock_t lock; |
}; |
#define min(x,y) ({ \ |
typeof(x) _x = (x); \ |
typeof(y) _y = (y); \ |
(void) (&_x == &_y); \ |
_x < _y ? _x : _y; }) |
#define max(x,y) ({ \ |
typeof(x) _x = (x); \ |
typeof(y) _y = (y); \ |
(void) (&_x == &_y); \ |
_x > _y ? _x : _y; }) |
extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); |
# define do_div(n,base) ({ \ |
uint32_t __base = (base); \ |
uint32_t __rem; \ |
(void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ |
if (likely(((n) >> 32) == 0)) { \ |
__rem = (uint32_t)(n) % __base; \ |
(n) = (uint32_t)(n) / __base; \ |
} else \ |
__rem = __div64_32(&(n), __base); \ |
__rem; \ |
}) |
#define lower_32_bits(n) ((u32)(n)) |
#define INT_MAX ((int)(~0U>>1)) |
#define INT_MIN (-INT_MAX - 1) |
#define UINT_MAX (~0U) |
#define LONG_MAX ((long)(~0UL>>1)) |
#define LONG_MIN (-LONG_MAX - 1) |
#define ULONG_MAX (~0UL) |
#define LLONG_MAX ((long long)(~0ULL>>1)) |
#define LLONG_MIN (-LLONG_MAX - 1) |
#define ULLONG_MAX (~0ULL) |
static inline void *kcalloc(size_t n, size_t size, u32_t flags) |
{ |
if (n != 0 && size > ULONG_MAX / n) |
return NULL; |
return kmalloc(n * size, 0); |
} |
#endif //__TYPES_H__ |
/drivers/video/drm/radeon/atombios_crtc.c |
---|
0,0 → 1,697 |
/* |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include <drmP.h> |
#include <drm_crtc_helper.h> |
#include "radeon_drm.h" |
#include "radeon_fixed.h" |
#include "radeon.h" |
#include "atom.h" |
#include "atom-bits.h" |
static void atombios_lock_crtc(struct drm_crtc *crtc, int lock) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
int index = |
GetIndexIntoMasterTable(COMMAND, UpdateCRTC_DoubleBufferRegisters); |
ENABLE_CRTC_PS_ALLOCATION args; |
memset(&args, 0, sizeof(args)); |
args.ucCRTC = radeon_crtc->crtc_id; |
args.ucEnable = lock; |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
static void atombios_enable_crtc(struct drm_crtc *crtc, int state) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
int index = GetIndexIntoMasterTable(COMMAND, EnableCRTC); |
ENABLE_CRTC_PS_ALLOCATION args; |
memset(&args, 0, sizeof(args)); |
args.ucCRTC = radeon_crtc->crtc_id; |
args.ucEnable = state; |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
static void atombios_enable_crtc_memreq(struct drm_crtc *crtc, int state) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
int index = GetIndexIntoMasterTable(COMMAND, EnableCRTCMemReq); |
ENABLE_CRTC_PS_ALLOCATION args; |
memset(&args, 0, sizeof(args)); |
args.ucCRTC = radeon_crtc->crtc_id; |
args.ucEnable = state; |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
static void atombios_blank_crtc(struct drm_crtc *crtc, int state) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
int index = GetIndexIntoMasterTable(COMMAND, BlankCRTC); |
BLANK_CRTC_PS_ALLOCATION args; |
memset(&args, 0, sizeof(args)); |
args.ucCRTC = radeon_crtc->crtc_id; |
args.ucBlanking = state; |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
if (ASIC_IS_DCE3(rdev)) |
atombios_enable_crtc_memreq(crtc, 1); |
atombios_enable_crtc(crtc, 1); |
atombios_blank_crtc(crtc, 0); |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
atombios_blank_crtc(crtc, 1); |
atombios_enable_crtc(crtc, 0); |
if (ASIC_IS_DCE3(rdev)) |
atombios_enable_crtc_memreq(crtc, 0); |
break; |
} |
if (mode != DRM_MODE_DPMS_OFF) { |
radeon_crtc_load_lut(crtc); |
} |
} |
static void |
atombios_set_crtc_dtd_timing(struct drm_crtc *crtc, |
SET_CRTC_USING_DTD_TIMING_PARAMETERS * crtc_param) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
SET_CRTC_USING_DTD_TIMING_PARAMETERS conv_param; |
int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_UsingDTDTiming); |
conv_param.usH_Size = cpu_to_le16(crtc_param->usH_Size); |
conv_param.usH_Blanking_Time = |
cpu_to_le16(crtc_param->usH_Blanking_Time); |
conv_param.usV_Size = cpu_to_le16(crtc_param->usV_Size); |
conv_param.usV_Blanking_Time = |
cpu_to_le16(crtc_param->usV_Blanking_Time); |
conv_param.usH_SyncOffset = cpu_to_le16(crtc_param->usH_SyncOffset); |
conv_param.usH_SyncWidth = cpu_to_le16(crtc_param->usH_SyncWidth); |
conv_param.usV_SyncOffset = cpu_to_le16(crtc_param->usV_SyncOffset); |
conv_param.usV_SyncWidth = cpu_to_le16(crtc_param->usV_SyncWidth); |
conv_param.susModeMiscInfo.usAccess = |
cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); |
conv_param.ucCRTC = crtc_param->ucCRTC; |
printk("executing set crtc dtd timing\n"); |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&conv_param); |
} |
void atombios_crtc_set_timing(struct drm_crtc *crtc, |
SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION * |
crtc_param) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION conv_param; |
int index = GetIndexIntoMasterTable(COMMAND, SetCRTC_Timing); |
conv_param.usH_Total = cpu_to_le16(crtc_param->usH_Total); |
conv_param.usH_Disp = cpu_to_le16(crtc_param->usH_Disp); |
conv_param.usH_SyncStart = cpu_to_le16(crtc_param->usH_SyncStart); |
conv_param.usH_SyncWidth = cpu_to_le16(crtc_param->usH_SyncWidth); |
conv_param.usV_Total = cpu_to_le16(crtc_param->usV_Total); |
conv_param.usV_Disp = cpu_to_le16(crtc_param->usV_Disp); |
conv_param.usV_SyncStart = cpu_to_le16(crtc_param->usV_SyncStart); |
conv_param.usV_SyncWidth = cpu_to_le16(crtc_param->usV_SyncWidth); |
conv_param.susModeMiscInfo.usAccess = |
cpu_to_le16(crtc_param->susModeMiscInfo.usAccess); |
conv_param.ucCRTC = crtc_param->ucCRTC; |
conv_param.ucOverscanRight = crtc_param->ucOverscanRight; |
conv_param.ucOverscanLeft = crtc_param->ucOverscanLeft; |
conv_param.ucOverscanBottom = crtc_param->ucOverscanBottom; |
conv_param.ucOverscanTop = crtc_param->ucOverscanTop; |
conv_param.ucReserved = crtc_param->ucReserved; |
printk("executing set crtc timing\n"); |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&conv_param); |
} |
void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder = NULL; |
struct radeon_encoder *radeon_encoder = NULL; |
uint8_t frev, crev; |
int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); |
SET_PIXEL_CLOCK_PS_ALLOCATION args; |
PIXEL_CLOCK_PARAMETERS *spc1_ptr; |
PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr; |
PIXEL_CLOCK_PARAMETERS_V3 *spc3_ptr; |
uint32_t sclock = mode->clock; |
uint32_t ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0; |
struct radeon_pll *pll; |
int pll_flags = 0; |
memset(&args, 0, sizeof(args)); |
if (ASIC_IS_AVIVO(rdev)) { |
uint32_t ss_cntl; |
if (ASIC_IS_DCE32(rdev) && mode->clock > 200000) /* range limits??? */ |
pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; |
else |
pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; |
/* disable spread spectrum clocking for now -- thanks Hedy Lamarr */ |
if (radeon_crtc->crtc_id == 0) { |
ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); |
WREG32(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl & ~1); |
} else { |
ss_cntl = RREG32(AVIVO_P2PLL_INT_SS_CNTL); |
WREG32(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl & ~1); |
} |
} else { |
pll_flags |= RADEON_PLL_LEGACY; |
if (mode->clock > 200000) /* range limits??? */ |
pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; |
else |
pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; |
} |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc == crtc) { |
if (!ASIC_IS_AVIVO(rdev)) { |
if (encoder->encoder_type != |
DRM_MODE_ENCODER_DAC) |
pll_flags |= RADEON_PLL_NO_ODD_POST_DIV; |
if (!ASIC_IS_AVIVO(rdev) |
&& (encoder->encoder_type == |
DRM_MODE_ENCODER_LVDS)) |
pll_flags |= RADEON_PLL_USE_REF_DIV; |
} |
radeon_encoder = to_radeon_encoder(encoder); |
} |
} |
if (radeon_crtc->crtc_id == 0) |
pll = &rdev->clock.p1pll; |
else |
pll = &rdev->clock.p2pll; |
radeon_compute_pll(pll, mode->clock, &sclock, &fb_div, &frac_fb_div, |
&ref_div, &post_div, pll_flags); |
atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, |
&crev); |
switch (frev) { |
case 1: |
switch (crev) { |
case 1: |
spc1_ptr = (PIXEL_CLOCK_PARAMETERS *) & args.sPCLKInput; |
spc1_ptr->usPixelClock = cpu_to_le16(sclock); |
spc1_ptr->usRefDiv = cpu_to_le16(ref_div); |
spc1_ptr->usFbDiv = cpu_to_le16(fb_div); |
spc1_ptr->ucFracFbDiv = frac_fb_div; |
spc1_ptr->ucPostDiv = post_div; |
spc1_ptr->ucPpll = |
radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; |
spc1_ptr->ucCRTC = radeon_crtc->crtc_id; |
spc1_ptr->ucRefDivSrc = 1; |
break; |
case 2: |
spc2_ptr = |
(PIXEL_CLOCK_PARAMETERS_V2 *) & args.sPCLKInput; |
spc2_ptr->usPixelClock = cpu_to_le16(sclock); |
spc2_ptr->usRefDiv = cpu_to_le16(ref_div); |
spc2_ptr->usFbDiv = cpu_to_le16(fb_div); |
spc2_ptr->ucFracFbDiv = frac_fb_div; |
spc2_ptr->ucPostDiv = post_div; |
spc2_ptr->ucPpll = |
radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; |
spc2_ptr->ucCRTC = radeon_crtc->crtc_id; |
spc2_ptr->ucRefDivSrc = 1; |
break; |
case 3: |
if (!encoder) |
return; |
spc3_ptr = |
(PIXEL_CLOCK_PARAMETERS_V3 *) & args.sPCLKInput; |
spc3_ptr->usPixelClock = cpu_to_le16(sclock); |
spc3_ptr->usRefDiv = cpu_to_le16(ref_div); |
spc3_ptr->usFbDiv = cpu_to_le16(fb_div); |
spc3_ptr->ucFracFbDiv = frac_fb_div; |
spc3_ptr->ucPostDiv = post_div; |
spc3_ptr->ucPpll = |
radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; |
spc3_ptr->ucMiscInfo = (radeon_crtc->crtc_id << 2); |
spc3_ptr->ucTransmitterId = radeon_encoder->encoder_id; |
spc3_ptr->ucEncoderMode = |
atombios_get_encoder_mode(encoder); |
break; |
default: |
DRM_ERROR("Unknown table version %d %d\n", frev, crev); |
return; |
} |
break; |
default: |
DRM_ERROR("Unknown table version %d %d\n", frev, crev); |
return; |
} |
printk("executing set pll\n"); |
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
} |
int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_framebuffer *radeon_fb; |
struct drm_gem_object *obj; |
struct drm_radeon_gem_object *obj_priv; |
uint64_t fb_location; |
uint32_t fb_format, fb_pitch_pixels; |
if (!crtc->fb) |
return -EINVAL; |
radeon_fb = to_radeon_framebuffer(crtc->fb); |
obj = radeon_fb->obj; |
obj_priv = obj->driver_private; |
// if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &fb_location)) { |
// return -EINVAL; |
// } |
switch (crtc->fb->bits_per_pixel) { |
case 15: |
fb_format = |
AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | |
AVIVO_D1GRPH_CONTROL_16BPP_ARGB1555; |
break; |
case 16: |
fb_format = |
AVIVO_D1GRPH_CONTROL_DEPTH_16BPP | |
AVIVO_D1GRPH_CONTROL_16BPP_RGB565; |
break; |
case 24: |
case 32: |
fb_format = |
AVIVO_D1GRPH_CONTROL_DEPTH_32BPP | |
AVIVO_D1GRPH_CONTROL_32BPP_ARGB8888; |
break; |
default: |
DRM_ERROR("Unsupported screen depth %d\n", |
crtc->fb->bits_per_pixel); |
return -EINVAL; |
} |
/* TODO tiling */ |
if (radeon_crtc->crtc_id == 0) |
WREG32(AVIVO_D1VGA_CONTROL, 0); |
else |
WREG32(AVIVO_D2VGA_CONTROL, 0); |
WREG32(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, |
(u32) fb_location); |
WREG32(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + |
radeon_crtc->crtc_offset, (u32) fb_location); |
WREG32(AVIVO_D1GRPH_CONTROL + radeon_crtc->crtc_offset, fb_format); |
WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_X + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_SURFACE_OFFSET_Y + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_X_START + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_Y_START + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_D1GRPH_X_END + radeon_crtc->crtc_offset, crtc->fb->width); |
WREG32(AVIVO_D1GRPH_Y_END + radeon_crtc->crtc_offset, crtc->fb->height); |
fb_pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); |
WREG32(AVIVO_D1GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); |
WREG32(AVIVO_D1GRPH_ENABLE + radeon_crtc->crtc_offset, 1); |
WREG32(AVIVO_D1MODE_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, |
crtc->mode.vdisplay); |
x &= ~3; |
y &= ~1; |
WREG32(AVIVO_D1MODE_VIEWPORT_START + radeon_crtc->crtc_offset, |
(x << 16) | y); |
WREG32(AVIVO_D1MODE_VIEWPORT_SIZE + radeon_crtc->crtc_offset, |
(crtc->mode.hdisplay << 16) | crtc->mode.vdisplay); |
if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) |
WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, |
AVIVO_D1MODE_INTERLEAVE_EN); |
else |
WREG32(AVIVO_D1MODE_DATA_FORMAT + radeon_crtc->crtc_offset, 0); |
if (old_fb && old_fb != crtc->fb) { |
radeon_fb = to_radeon_framebuffer(old_fb); |
// radeon_gem_object_unpin(radeon_fb->obj); |
} |
return 0; |
} |
int atombios_crtc_mode_set(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
int x, int y, struct drm_framebuffer *old_fb) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder; |
SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION crtc_timing; |
/* TODO color tiling */ |
memset(&crtc_timing, 0, sizeof(crtc_timing)); |
/* TODO tv */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
} |
crtc_timing.ucCRTC = radeon_crtc->crtc_id; |
crtc_timing.usH_Total = adjusted_mode->crtc_htotal; |
crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay; |
crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start; |
crtc_timing.usH_SyncWidth = |
adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; |
crtc_timing.usV_Total = adjusted_mode->crtc_vtotal; |
crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay; |
crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start; |
crtc_timing.usV_SyncWidth = |
adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; |
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) |
crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) |
crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) |
crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; |
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) |
crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; |
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) |
crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; |
atombios_crtc_set_pll(crtc, adjusted_mode); |
atombios_crtc_set_timing(crtc, &crtc_timing); |
if (ASIC_IS_AVIVO(rdev)) |
atombios_crtc_set_base(crtc, x, y, old_fb); |
else { |
if (radeon_crtc->crtc_id == 0) { |
SET_CRTC_USING_DTD_TIMING_PARAMETERS crtc_dtd_timing; |
memset(&crtc_dtd_timing, 0, sizeof(crtc_dtd_timing)); |
/* setup FP shadow regs on R4xx */ |
crtc_dtd_timing.ucCRTC = radeon_crtc->crtc_id; |
crtc_dtd_timing.usH_Size = adjusted_mode->crtc_hdisplay; |
crtc_dtd_timing.usV_Size = adjusted_mode->crtc_vdisplay; |
crtc_dtd_timing.usH_Blanking_Time = |
adjusted_mode->crtc_hblank_end - |
adjusted_mode->crtc_hdisplay; |
crtc_dtd_timing.usV_Blanking_Time = |
adjusted_mode->crtc_vblank_end - |
adjusted_mode->crtc_vdisplay; |
crtc_dtd_timing.usH_SyncOffset = |
adjusted_mode->crtc_hsync_start - |
adjusted_mode->crtc_hdisplay; |
crtc_dtd_timing.usV_SyncOffset = |
adjusted_mode->crtc_vsync_start - |
adjusted_mode->crtc_vdisplay; |
crtc_dtd_timing.usH_SyncWidth = |
adjusted_mode->crtc_hsync_end - |
adjusted_mode->crtc_hsync_start; |
crtc_dtd_timing.usV_SyncWidth = |
adjusted_mode->crtc_vsync_end - |
adjusted_mode->crtc_vsync_start; |
/* crtc_dtd_timing.ucH_Border = adjusted_mode->crtc_hborder; */ |
/* crtc_dtd_timing.ucV_Border = adjusted_mode->crtc_vborder; */ |
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) |
crtc_dtd_timing.susModeMiscInfo.usAccess |= |
ATOM_VSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) |
crtc_dtd_timing.susModeMiscInfo.usAccess |= |
ATOM_HSYNC_POLARITY; |
if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) |
crtc_dtd_timing.susModeMiscInfo.usAccess |= |
ATOM_COMPOSITESYNC; |
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) |
crtc_dtd_timing.susModeMiscInfo.usAccess |= |
ATOM_INTERLACE; |
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) |
crtc_dtd_timing.susModeMiscInfo.usAccess |= |
ATOM_DOUBLE_CLOCK_MODE; |
atombios_set_crtc_dtd_timing(crtc, &crtc_dtd_timing); |
} |
radeon_crtc_set_base(crtc, x, y, old_fb); |
radeon_legacy_atom_set_surface(crtc); |
} |
return 0; |
} |
static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
return true; |
} |
static void atombios_crtc_prepare(struct drm_crtc *crtc) |
{ |
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
atombios_lock_crtc(crtc, 1); |
} |
static void atombios_crtc_commit(struct drm_crtc *crtc) |
{ |
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON); |
atombios_lock_crtc(crtc, 0); |
} |
static const struct drm_crtc_helper_funcs atombios_helper_funcs = { |
.dpms = atombios_crtc_dpms, |
.mode_fixup = atombios_crtc_mode_fixup, |
.mode_set = atombios_crtc_mode_set, |
.mode_set_base = atombios_crtc_set_base, |
.prepare = atombios_crtc_prepare, |
.commit = atombios_crtc_commit, |
}; |
void radeon_atombios_init_crtc(struct drm_device *dev, |
struct radeon_crtc *radeon_crtc) |
{ |
if (radeon_crtc->crtc_id == 1) |
radeon_crtc->crtc_offset = |
AVIVO_D2CRTC_H_TOTAL - AVIVO_D1CRTC_H_TOTAL; |
drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs); |
} |
void radeon_init_disp_bw_avivo(struct drm_device *dev, |
struct drm_display_mode *mode1, |
uint32_t pixel_bytes1, |
struct drm_display_mode *mode2, |
uint32_t pixel_bytes2) |
{ |
struct radeon_device *rdev = dev->dev_private; |
fixed20_12 min_mem_eff; |
fixed20_12 peak_disp_bw, mem_bw, pix_clk, pix_clk2, temp_ff; |
fixed20_12 sclk_ff, mclk_ff; |
uint32_t dc_lb_memory_split, temp; |
min_mem_eff.full = rfixed_const_8(0); |
if (rdev->disp_priority == 2) { |
uint32_t mc_init_misc_lat_timer = 0; |
if (rdev->family == CHIP_RV515) |
mc_init_misc_lat_timer = |
RREG32_MC(RV515_MC_INIT_MISC_LAT_TIMER); |
else if (rdev->family == CHIP_RS690) |
mc_init_misc_lat_timer = |
RREG32_MC(RS690_MC_INIT_MISC_LAT_TIMER); |
mc_init_misc_lat_timer &= |
~(R300_MC_DISP1R_INIT_LAT_MASK << |
R300_MC_DISP1R_INIT_LAT_SHIFT); |
mc_init_misc_lat_timer &= |
~(R300_MC_DISP0R_INIT_LAT_MASK << |
R300_MC_DISP0R_INIT_LAT_SHIFT); |
if (mode2) |
mc_init_misc_lat_timer |= |
(1 << R300_MC_DISP1R_INIT_LAT_SHIFT); |
if (mode1) |
mc_init_misc_lat_timer |= |
(1 << R300_MC_DISP0R_INIT_LAT_SHIFT); |
if (rdev->family == CHIP_RV515) |
WREG32_MC(RV515_MC_INIT_MISC_LAT_TIMER, |
mc_init_misc_lat_timer); |
else if (rdev->family == CHIP_RS690) |
WREG32_MC(RS690_MC_INIT_MISC_LAT_TIMER, |
mc_init_misc_lat_timer); |
} |
/* |
* determine is there is enough bw for current mode |
*/ |
temp_ff.full = rfixed_const(100); |
mclk_ff.full = rfixed_const(rdev->clock.default_mclk); |
mclk_ff.full = rfixed_div(mclk_ff, temp_ff); |
sclk_ff.full = rfixed_const(rdev->clock.default_sclk); |
sclk_ff.full = rfixed_div(sclk_ff, temp_ff); |
temp = (rdev->mc.vram_width / 8) * (rdev->mc.vram_is_ddr ? 2 : 1); |
temp_ff.full = rfixed_const(temp); |
mem_bw.full = rfixed_mul(mclk_ff, temp_ff); |
mem_bw.full = rfixed_mul(mem_bw, min_mem_eff); |
pix_clk.full = 0; |
pix_clk2.full = 0; |
peak_disp_bw.full = 0; |
if (mode1) { |
temp_ff.full = rfixed_const(1000); |
pix_clk.full = rfixed_const(mode1->clock); /* convert to fixed point */ |
pix_clk.full = rfixed_div(pix_clk, temp_ff); |
temp_ff.full = rfixed_const(pixel_bytes1); |
peak_disp_bw.full += rfixed_mul(pix_clk, temp_ff); |
} |
if (mode2) { |
temp_ff.full = rfixed_const(1000); |
pix_clk2.full = rfixed_const(mode2->clock); /* convert to fixed point */ |
pix_clk2.full = rfixed_div(pix_clk2, temp_ff); |
temp_ff.full = rfixed_const(pixel_bytes2); |
peak_disp_bw.full += rfixed_mul(pix_clk2, temp_ff); |
} |
if (peak_disp_bw.full >= mem_bw.full) { |
DRM_ERROR |
("You may not have enough display bandwidth for current mode\n" |
"If you have flickering problem, try to lower resolution, refresh rate, or color depth\n"); |
printk("peak disp bw %d, mem_bw %d\n", |
rfixed_trunc(peak_disp_bw), rfixed_trunc(mem_bw)); |
} |
/* |
* Line Buffer Setup |
* There is a single line buffer shared by both display controllers. |
* DC_LB_MEMORY_SPLIT controls how that line buffer is shared between the display |
* controllers. The paritioning can either be done manually or via one of four |
* preset allocations specified in bits 1:0: |
* 0 - line buffer is divided in half and shared between each display controller |
* 1 - D1 gets 3/4 of the line buffer, D2 gets 1/4 |
* 2 - D1 gets the whole buffer |
* 3 - D1 gets 1/4 of the line buffer, D2 gets 3/4 |
* Setting bit 2 of DC_LB_MEMORY_SPLIT controls switches to manual allocation mode. |
* In manual allocation mode, D1 always starts at 0, D1 end/2 is specified in bits |
* 14:4; D2 allocation follows D1. |
*/ |
/* is auto or manual better ? */ |
dc_lb_memory_split = |
RREG32(AVIVO_DC_LB_MEMORY_SPLIT) & ~AVIVO_DC_LB_MEMORY_SPLIT_MASK; |
dc_lb_memory_split &= ~AVIVO_DC_LB_MEMORY_SPLIT_SHIFT_MODE; |
#if 1 |
/* auto */ |
if (mode1 && mode2) { |
if (mode1->hdisplay > mode2->hdisplay) { |
if (mode1->hdisplay > 2560) |
dc_lb_memory_split |= |
AVIVO_DC_LB_MEMORY_SPLIT_D1_3Q_D2_1Q; |
else |
dc_lb_memory_split |= |
AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; |
} else if (mode2->hdisplay > mode1->hdisplay) { |
if (mode2->hdisplay > 2560) |
dc_lb_memory_split |= |
AVIVO_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; |
else |
dc_lb_memory_split |= |
AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; |
} else |
dc_lb_memory_split |= |
AVIVO_DC_LB_MEMORY_SPLIT_D1HALF_D2HALF; |
} else if (mode1) { |
dc_lb_memory_split |= AVIVO_DC_LB_MEMORY_SPLIT_D1_ONLY; |
} else if (mode2) { |
dc_lb_memory_split |= AVIVO_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q; |
} |
#else |
/* manual */ |
dc_lb_memory_split |= AVIVO_DC_LB_MEMORY_SPLIT_SHIFT_MODE; |
dc_lb_memory_split &= |
~(AVIVO_DC_LB_DISP1_END_ADR_MASK << |
AVIVO_DC_LB_DISP1_END_ADR_SHIFT); |
if (mode1) { |
dc_lb_memory_split |= |
((((mode1->hdisplay / 2) + 64) & AVIVO_DC_LB_DISP1_END_ADR_MASK) |
<< AVIVO_DC_LB_DISP1_END_ADR_SHIFT); |
} else if (mode2) { |
dc_lb_memory_split |= (0 << AVIVO_DC_LB_DISP1_END_ADR_SHIFT); |
} |
#endif |
WREG32(AVIVO_DC_LB_MEMORY_SPLIT, dc_lb_memory_split); |
} |
/drivers/video/drm/radeon/radeon.h |
---|
50,7 → 50,7 |
#include <pci.h> |
#include <errno-base.h> |
#include "drm_edid.h" |
#include "radeon_mode.h" |
#include "radeon_reg.h" |
#include "r300.h" |
57,11 → 57,12 |
#include <syscall.h> |
extern int radeon_modeset; |
extern int radeon_dynclks; |
extern int radeon_r4xx_atom; |
extern int radeon_gart_size; |
extern int radeon_r4xx_atom; |
extern int radeon_connector_table; |
/* |
* Copy from radeon_drv.h so we don't have to include both and have conflicting |
* symbol; |
212,8 → 213,23 |
unsigned wdomain; |
}; |
int radeon_object_init(struct radeon_device *rdev); |
void radeon_object_fini(struct radeon_device *rdev); |
int radeon_object_create(struct radeon_device *rdev, |
struct drm_gem_object *gobj, |
unsigned long size, |
bool kernel, |
uint32_t domain, |
bool interruptible, |
struct radeon_object **robj_ptr); |
/* |
* GEM objects. |
*/ |
struct radeon_gem { |
struct list_head objects; |
}; |
1040,40 → 1056,6 |
}; |
/** |
* DRM device structure. This structure represent a complete card that |
* may contain multiple heads. |
*/ |
struct drm_device { |
int irq_enabled; /**< True if irq handler is enabled */ |
__volatile__ long context_flag; /**< Context swapping flag */ |
__volatile__ long interrupt_flag; /**< Interruption handler flag */ |
__volatile__ long dma_flag; /**< DMA dispatch flag */ |
int last_checked; /**< Last context checked for DMA */ |
int last_context; /**< Last current context */ |
unsigned long last_switch; /**< jiffies at last context switch */ |
struct drm_agp_head *agp; /**< AGP data */ |
struct pci_dev *pdev; /**< PCI device structure */ |
int pci_vendor; /**< PCI vendor id */ |
int pci_device; /** PCI device id */ |
int num_crtcs; /**< Number of CRTCs on this device */ |
void *dev_private; /**< device private data */ |
void *mm_private; |
// struct address_space *dev_mapping; |
struct drm_mode_config mode_config; /**< Current mode config */ |
}; |
#define radeon_errata(rdev) (rdev)->asic->errata((rdev)) |
/drivers/video/drm/radeon/radeon_atombios.c |
---|
23,7 → 23,7 |
* Authors: Dave Airlie |
* Alex Deucher |
*/ |
//#include "drmP.h" |
#include "drmP.h" |
#include "radeon_drm.h" |
#include "radeon.h" |
336,11 → 336,11 |
else |
linkb = false; |
// radeon_add_atom_encoder(dev, |
// enc_obj_id, |
// le16_to_cpu |
// (path-> |
// usDeviceTag)); |
radeon_add_atom_encoder(dev, |
enc_obj_id, |
le16_to_cpu |
(path-> |
usDeviceTag)); |
} |
} |
406,18 → 406,18 |
else |
ddc_bus = radeon_lookup_gpio(dev, line_mux); |
// radeon_add_atom_connector(dev, |
// le16_to_cpu(path-> |
// usConnObjectId), |
// le16_to_cpu(path-> |
// usDeviceTag), |
// connector_type, &ddc_bus, |
// linkb, igp_lane_info); |
radeon_add_atom_connector(dev, |
le16_to_cpu(path-> |
usConnObjectId), |
le16_to_cpu(path-> |
usDeviceTag), |
connector_type, &ddc_bus, |
linkb, igp_lane_info); |
} |
} |
// radeon_link_encoder_connector(dev); |
radeon_link_encoder_connector(dev); |
return true; |
} |
532,19 → 532,19 |
bios_connectors[i].valid = true; |
bios_connectors[i].devices = (1 << i); |
// if (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom) |
// radeon_add_atom_encoder(dev, |
// radeon_get_encoder_id(dev, |
// (1 << i), |
// dac), |
// (1 << i)); |
// else |
// radeon_add_legacy_encoder(dev, |
// radeon_get_encoder_id(dev, |
// (1 << |
// i), |
// dac), |
// (1 << i)); |
if (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom) |
radeon_add_atom_encoder(dev, |
radeon_get_encoder_id(dev, |
(1 << i), |
dac), |
(1 << i)); |
else |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
(1 << |
i), |
dac), |
(1 << i)); |
} |
/* combine shared connectors */ |
584,18 → 584,18 |
} |
/* add the connectors */ |
// for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { |
// if (bios_connectors[i].valid) |
// radeon_add_atom_connector(dev, |
// bios_connectors[i].line_mux, |
// bios_connectors[i].devices, |
// bios_connectors[i]. |
// connector_type, |
// &bios_connectors[i].ddc_bus, |
// false, 0); |
// } |
for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { |
if (bios_connectors[i].valid) |
radeon_add_atom_connector(dev, |
bios_connectors[i].line_mux, |
bios_connectors[i].devices, |
bios_connectors[i]. |
connector_type, |
&bios_connectors[i].ddc_bus, |
false, 0); |
} |
// radeon_link_encoder_connector(dev); |
radeon_link_encoder_connector(dev); |
return true; |
} |
/drivers/video/drm/radeon/radeon_bios.c |
---|
25,7 → 25,7 |
* Alex Deucher |
* Jerome Glisse |
*/ |
//#include "drmP.h" |
#include "drmP.h" |
#include "radeon_reg.h" |
#include "radeon.h" |
#include "atom.h" |
/drivers/video/drm/radeon/radeon_clocks.c |
---|
25,7 → 25,7 |
* Alex Deucher |
* Jerome Glisse |
*/ |
//#include "drmP.h" |
#include "drmP.h" |
#include "radeon_drm.h" |
#include "radeon_reg.h" |
#include "radeon.h" |
/drivers/video/drm/radeon/radeon_combios.c |
---|
0,0 → 1,2490 |
/* |
* Copyright 2004 ATI Technologies Inc., Markham, Ontario |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include "drmP.h" |
#include "radeon_drm.h" |
#include "radeon.h" |
#include "atom.h" |
#ifdef CONFIG_PPC_PMAC |
/* not sure which of these are needed */ |
#include <asm/machdep.h> |
#include <asm/pmac_feature.h> |
#include <asm/prom.h> |
#include <asm/pci-bridge.h> |
#endif /* CONFIG_PPC_PMAC */ |
/* from radeon_encoder.c */ |
extern uint32_t |
radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, |
uint8_t dac); |
extern void radeon_link_encoder_connector(struct drm_device *dev); |
/* from radeon_connector.c */ |
extern void |
radeon_add_legacy_connector(struct drm_device *dev, |
uint32_t connector_id, |
uint32_t supported_device, |
int connector_type, |
struct radeon_i2c_bus_rec *i2c_bus); |
/* from radeon_legacy_encoder.c */ |
extern void |
radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, |
uint32_t supported_device); |
/* old legacy ATI BIOS routines */ |
/* COMBIOS table offsets */ |
enum radeon_combios_table_offset { |
/* absolute offset tables */ |
COMBIOS_ASIC_INIT_1_TABLE, |
COMBIOS_BIOS_SUPPORT_TABLE, |
COMBIOS_DAC_PROGRAMMING_TABLE, |
COMBIOS_MAX_COLOR_DEPTH_TABLE, |
COMBIOS_CRTC_INFO_TABLE, |
COMBIOS_PLL_INFO_TABLE, |
COMBIOS_TV_INFO_TABLE, |
COMBIOS_DFP_INFO_TABLE, |
COMBIOS_HW_CONFIG_INFO_TABLE, |
COMBIOS_MULTIMEDIA_INFO_TABLE, |
COMBIOS_TV_STD_PATCH_TABLE, |
COMBIOS_LCD_INFO_TABLE, |
COMBIOS_MOBILE_INFO_TABLE, |
COMBIOS_PLL_INIT_TABLE, |
COMBIOS_MEM_CONFIG_TABLE, |
COMBIOS_SAVE_MASK_TABLE, |
COMBIOS_HARDCODED_EDID_TABLE, |
COMBIOS_ASIC_INIT_2_TABLE, |
COMBIOS_CONNECTOR_INFO_TABLE, |
COMBIOS_DYN_CLK_1_TABLE, |
COMBIOS_RESERVED_MEM_TABLE, |
COMBIOS_EXT_TMDS_INFO_TABLE, |
COMBIOS_MEM_CLK_INFO_TABLE, |
COMBIOS_EXT_DAC_INFO_TABLE, |
COMBIOS_MISC_INFO_TABLE, |
COMBIOS_CRT_INFO_TABLE, |
COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE, |
COMBIOS_COMPONENT_VIDEO_INFO_TABLE, |
COMBIOS_FAN_SPEED_INFO_TABLE, |
COMBIOS_OVERDRIVE_INFO_TABLE, |
COMBIOS_OEM_INFO_TABLE, |
COMBIOS_DYN_CLK_2_TABLE, |
COMBIOS_POWER_CONNECTOR_INFO_TABLE, |
COMBIOS_I2C_INFO_TABLE, |
/* relative offset tables */ |
COMBIOS_ASIC_INIT_3_TABLE, /* offset from misc info */ |
COMBIOS_ASIC_INIT_4_TABLE, /* offset from misc info */ |
COMBIOS_DETECTED_MEM_TABLE, /* offset from misc info */ |
COMBIOS_ASIC_INIT_5_TABLE, /* offset from misc info */ |
COMBIOS_RAM_RESET_TABLE, /* offset from mem config */ |
COMBIOS_POWERPLAY_INFO_TABLE, /* offset from mobile info */ |
COMBIOS_GPIO_INFO_TABLE, /* offset from mobile info */ |
COMBIOS_LCD_DDC_INFO_TABLE, /* offset from mobile info */ |
COMBIOS_TMDS_POWER_TABLE, /* offset from mobile info */ |
COMBIOS_TMDS_POWER_ON_TABLE, /* offset from tmds power */ |
COMBIOS_TMDS_POWER_OFF_TABLE, /* offset from tmds power */ |
}; |
enum radeon_combios_ddc { |
DDC_NONE_DETECTED, |
DDC_MONID, |
DDC_DVI, |
DDC_VGA, |
DDC_CRT2, |
DDC_LCD, |
DDC_GPIO, |
}; |
enum radeon_combios_connector { |
CONNECTOR_NONE_LEGACY, |
CONNECTOR_PROPRIETARY_LEGACY, |
CONNECTOR_CRT_LEGACY, |
CONNECTOR_DVI_I_LEGACY, |
CONNECTOR_DVI_D_LEGACY, |
CONNECTOR_CTV_LEGACY, |
CONNECTOR_STV_LEGACY, |
CONNECTOR_UNSUPPORTED_LEGACY |
}; |
const int legacy_connector_convert[] = { |
DRM_MODE_CONNECTOR_Unknown, |
DRM_MODE_CONNECTOR_DVID, |
DRM_MODE_CONNECTOR_VGA, |
DRM_MODE_CONNECTOR_DVII, |
DRM_MODE_CONNECTOR_DVID, |
DRM_MODE_CONNECTOR_Composite, |
DRM_MODE_CONNECTOR_SVIDEO, |
DRM_MODE_CONNECTOR_Unknown, |
}; |
static uint16_t combios_get_table_offset(struct drm_device *dev, |
enum radeon_combios_table_offset table) |
{ |
struct radeon_device *rdev = dev->dev_private; |
int rev; |
uint16_t offset = 0, check_offset; |
switch (table) { |
/* absolute offset tables */ |
case COMBIOS_ASIC_INIT_1_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0xc); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_BIOS_SUPPORT_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x14); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_DAC_PROGRAMMING_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x2a); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_MAX_COLOR_DEPTH_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x2c); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_CRTC_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x2e); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_PLL_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x30); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_TV_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x32); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_DFP_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x34); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_HW_CONFIG_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x36); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_MULTIMEDIA_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x38); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_TV_STD_PATCH_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x3e); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_LCD_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x40); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_MOBILE_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x42); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_PLL_INIT_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x46); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_MEM_CONFIG_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x48); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_SAVE_MASK_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x4a); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_HARDCODED_EDID_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x4c); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_ASIC_INIT_2_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x4e); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_CONNECTOR_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x50); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_DYN_CLK_1_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x52); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_RESERVED_MEM_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x54); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_EXT_TMDS_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x58); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_MEM_CLK_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x5a); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_EXT_DAC_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x5c); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_MISC_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x5e); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_CRT_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x60); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x62); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_COMPONENT_VIDEO_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x64); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_FAN_SPEED_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x66); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_OVERDRIVE_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x68); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_OEM_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x6a); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_DYN_CLK_2_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x6c); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_POWER_CONNECTOR_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x6e); |
if (check_offset) |
offset = check_offset; |
break; |
case COMBIOS_I2C_INFO_TABLE: |
check_offset = RBIOS16(rdev->bios_header_start + 0x70); |
if (check_offset) |
offset = check_offset; |
break; |
/* relative offset tables */ |
case COMBIOS_ASIC_INIT_3_TABLE: /* offset from misc info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); |
if (check_offset) { |
rev = RBIOS8(check_offset); |
if (rev > 0) { |
check_offset = RBIOS16(check_offset + 0x3); |
if (check_offset) |
offset = check_offset; |
} |
} |
break; |
case COMBIOS_ASIC_INIT_4_TABLE: /* offset from misc info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); |
if (check_offset) { |
rev = RBIOS8(check_offset); |
if (rev > 0) { |
check_offset = RBIOS16(check_offset + 0x5); |
if (check_offset) |
offset = check_offset; |
} |
} |
break; |
case COMBIOS_DETECTED_MEM_TABLE: /* offset from misc info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); |
if (check_offset) { |
rev = RBIOS8(check_offset); |
if (rev > 0) { |
check_offset = RBIOS16(check_offset + 0x7); |
if (check_offset) |
offset = check_offset; |
} |
} |
break; |
case COMBIOS_ASIC_INIT_5_TABLE: /* offset from misc info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MISC_INFO_TABLE); |
if (check_offset) { |
rev = RBIOS8(check_offset); |
if (rev == 2) { |
check_offset = RBIOS16(check_offset + 0x9); |
if (check_offset) |
offset = check_offset; |
} |
} |
break; |
case COMBIOS_RAM_RESET_TABLE: /* offset from mem config */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MEM_CONFIG_TABLE); |
if (check_offset) { |
while (RBIOS8(check_offset++)); |
check_offset += 2; |
if (check_offset) |
offset = check_offset; |
} |
break; |
case COMBIOS_POWERPLAY_INFO_TABLE: /* offset from mobile info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); |
if (check_offset) { |
check_offset = RBIOS16(check_offset + 0x11); |
if (check_offset) |
offset = check_offset; |
} |
break; |
case COMBIOS_GPIO_INFO_TABLE: /* offset from mobile info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); |
if (check_offset) { |
check_offset = RBIOS16(check_offset + 0x13); |
if (check_offset) |
offset = check_offset; |
} |
break; |
case COMBIOS_LCD_DDC_INFO_TABLE: /* offset from mobile info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); |
if (check_offset) { |
check_offset = RBIOS16(check_offset + 0x15); |
if (check_offset) |
offset = check_offset; |
} |
break; |
case COMBIOS_TMDS_POWER_TABLE: /* offset from mobile info */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_MOBILE_INFO_TABLE); |
if (check_offset) { |
check_offset = RBIOS16(check_offset + 0x17); |
if (check_offset) |
offset = check_offset; |
} |
break; |
case COMBIOS_TMDS_POWER_ON_TABLE: /* offset from tmds power */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_TMDS_POWER_TABLE); |
if (check_offset) { |
check_offset = RBIOS16(check_offset + 0x2); |
if (check_offset) |
offset = check_offset; |
} |
break; |
case COMBIOS_TMDS_POWER_OFF_TABLE: /* offset from tmds power */ |
check_offset = |
combios_get_table_offset(dev, COMBIOS_TMDS_POWER_TABLE); |
if (check_offset) { |
check_offset = RBIOS16(check_offset + 0x4); |
if (check_offset) |
offset = check_offset; |
} |
break; |
default: |
break; |
} |
return offset; |
} |
struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line) |
{ |
struct radeon_i2c_bus_rec i2c; |
i2c.mask_clk_mask = RADEON_GPIO_EN_1; |
i2c.mask_data_mask = RADEON_GPIO_EN_0; |
i2c.a_clk_mask = RADEON_GPIO_A_1; |
i2c.a_data_mask = RADEON_GPIO_A_0; |
i2c.put_clk_mask = RADEON_GPIO_EN_1; |
i2c.put_data_mask = RADEON_GPIO_EN_0; |
i2c.get_clk_mask = RADEON_GPIO_Y_1; |
i2c.get_data_mask = RADEON_GPIO_Y_0; |
if ((ddc_line == RADEON_LCD_GPIO_MASK) || |
(ddc_line == RADEON_MDGPIO_EN_REG)) { |
i2c.mask_clk_reg = ddc_line; |
i2c.mask_data_reg = ddc_line; |
i2c.a_clk_reg = ddc_line; |
i2c.a_data_reg = ddc_line; |
i2c.put_clk_reg = ddc_line; |
i2c.put_data_reg = ddc_line; |
i2c.get_clk_reg = ddc_line + 4; |
i2c.get_data_reg = ddc_line + 4; |
} else { |
i2c.mask_clk_reg = ddc_line; |
i2c.mask_data_reg = ddc_line; |
i2c.a_clk_reg = ddc_line; |
i2c.a_data_reg = ddc_line; |
i2c.put_clk_reg = ddc_line; |
i2c.put_data_reg = ddc_line; |
i2c.get_clk_reg = ddc_line; |
i2c.get_data_reg = ddc_line; |
} |
if (ddc_line) |
i2c.valid = true; |
else |
i2c.valid = false; |
return i2c; |
} |
bool radeon_combios_get_clock_info(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint16_t pll_info; |
struct radeon_pll *p1pll = &rdev->clock.p1pll; |
struct radeon_pll *p2pll = &rdev->clock.p2pll; |
struct radeon_pll *spll = &rdev->clock.spll; |
struct radeon_pll *mpll = &rdev->clock.mpll; |
int8_t rev; |
uint16_t sclk, mclk; |
if (rdev->bios == NULL) |
return NULL; |
pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE); |
if (pll_info) { |
rev = RBIOS8(pll_info); |
/* pixel clocks */ |
p1pll->reference_freq = RBIOS16(pll_info + 0xe); |
p1pll->reference_div = RBIOS16(pll_info + 0x10); |
p1pll->pll_out_min = RBIOS32(pll_info + 0x12); |
p1pll->pll_out_max = RBIOS32(pll_info + 0x16); |
if (rev > 9) { |
p1pll->pll_in_min = RBIOS32(pll_info + 0x36); |
p1pll->pll_in_max = RBIOS32(pll_info + 0x3a); |
} else { |
p1pll->pll_in_min = 40; |
p1pll->pll_in_max = 500; |
} |
*p2pll = *p1pll; |
/* system clock */ |
spll->reference_freq = RBIOS16(pll_info + 0x1a); |
spll->reference_div = RBIOS16(pll_info + 0x1c); |
spll->pll_out_min = RBIOS32(pll_info + 0x1e); |
spll->pll_out_max = RBIOS32(pll_info + 0x22); |
if (rev > 10) { |
spll->pll_in_min = RBIOS32(pll_info + 0x48); |
spll->pll_in_max = RBIOS32(pll_info + 0x4c); |
} else { |
/* ??? */ |
spll->pll_in_min = 40; |
spll->pll_in_max = 500; |
} |
/* memory clock */ |
mpll->reference_freq = RBIOS16(pll_info + 0x26); |
mpll->reference_div = RBIOS16(pll_info + 0x28); |
mpll->pll_out_min = RBIOS32(pll_info + 0x2a); |
mpll->pll_out_max = RBIOS32(pll_info + 0x2e); |
if (rev > 10) { |
mpll->pll_in_min = RBIOS32(pll_info + 0x5a); |
mpll->pll_in_max = RBIOS32(pll_info + 0x5e); |
} else { |
/* ??? */ |
mpll->pll_in_min = 40; |
mpll->pll_in_max = 500; |
} |
/* default sclk/mclk */ |
sclk = RBIOS16(pll_info + 0xa); |
mclk = RBIOS16(pll_info + 0x8); |
if (sclk == 0) |
sclk = 200 * 100; |
if (mclk == 0) |
mclk = 200 * 100; |
rdev->clock.default_sclk = sclk; |
rdev->clock.default_mclk = mclk; |
return true; |
} |
return false; |
} |
struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct |
radeon_encoder |
*encoder) |
{ |
struct drm_device *dev = encoder->base.dev; |
struct radeon_device *rdev = dev->dev_private; |
uint16_t dac_info; |
uint8_t rev, bg, dac; |
struct radeon_encoder_primary_dac *p_dac = NULL; |
if (rdev->bios == NULL) |
return NULL; |
/* check CRT table */ |
dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); |
if (dac_info) { |
p_dac = |
kzalloc(sizeof(struct radeon_encoder_primary_dac), |
GFP_KERNEL); |
if (!p_dac) |
return NULL; |
rev = RBIOS8(dac_info) & 0x3; |
if (rev < 2) { |
bg = RBIOS8(dac_info + 0x2) & 0xf; |
dac = (RBIOS8(dac_info + 0x2) >> 4) & 0xf; |
p_dac->ps2_pdac_adj = (bg << 8) | (dac); |
} else { |
bg = RBIOS8(dac_info + 0x2) & 0xf; |
dac = RBIOS8(dac_info + 0x3) & 0xf; |
p_dac->ps2_pdac_adj = (bg << 8) | (dac); |
} |
} |
return p_dac; |
} |
static enum radeon_tv_std |
radeon_combios_get_tv_info(struct radeon_encoder *encoder) |
{ |
struct drm_device *dev = encoder->base.dev; |
struct radeon_device *rdev = dev->dev_private; |
uint16_t tv_info; |
enum radeon_tv_std tv_std = TV_STD_NTSC; |
tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); |
if (tv_info) { |
if (RBIOS8(tv_info + 6) == 'T') { |
switch (RBIOS8(tv_info + 7) & 0xf) { |
case 1: |
tv_std = TV_STD_NTSC; |
DRM_INFO("Default TV standard: NTSC\n"); |
break; |
case 2: |
tv_std = TV_STD_PAL; |
DRM_INFO("Default TV standard: PAL\n"); |
break; |
case 3: |
tv_std = TV_STD_PAL_M; |
DRM_INFO("Default TV standard: PAL-M\n"); |
break; |
case 4: |
tv_std = TV_STD_PAL_60; |
DRM_INFO("Default TV standard: PAL-60\n"); |
break; |
case 5: |
tv_std = TV_STD_NTSC_J; |
DRM_INFO("Default TV standard: NTSC-J\n"); |
break; |
case 6: |
tv_std = TV_STD_SCART_PAL; |
DRM_INFO("Default TV standard: SCART-PAL\n"); |
break; |
default: |
tv_std = TV_STD_NTSC; |
DRM_INFO |
("Unknown TV standard; defaulting to NTSC\n"); |
break; |
} |
switch ((RBIOS8(tv_info + 9) >> 2) & 0x3) { |
case 0: |
DRM_INFO("29.498928713 MHz TV ref clk\n"); |
break; |
case 1: |
DRM_INFO("28.636360000 MHz TV ref clk\n"); |
break; |
case 2: |
DRM_INFO("14.318180000 MHz TV ref clk\n"); |
break; |
case 3: |
DRM_INFO("27.000000000 MHz TV ref clk\n"); |
break; |
default: |
break; |
} |
} |
} |
return tv_std; |
} |
static const uint32_t default_tvdac_adj[CHIP_LAST] = { |
0x00000000, /* r100 */ |
0x00280000, /* rv100 */ |
0x00000000, /* rs100 */ |
0x00880000, /* rv200 */ |
0x00000000, /* rs200 */ |
0x00000000, /* r200 */ |
0x00770000, /* rv250 */ |
0x00290000, /* rs300 */ |
0x00560000, /* rv280 */ |
0x00780000, /* r300 */ |
0x00770000, /* r350 */ |
0x00780000, /* rv350 */ |
0x00780000, /* rv380 */ |
0x01080000, /* r420 */ |
0x01080000, /* r423 */ |
0x01080000, /* rv410 */ |
0x00780000, /* rs400 */ |
0x00780000, /* rs480 */ |
}; |
static struct radeon_encoder_tv_dac |
*radeon_legacy_get_tv_dac_info_from_table(struct radeon_device *rdev) |
{ |
struct radeon_encoder_tv_dac *tv_dac = NULL; |
tv_dac = kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL); |
if (!tv_dac) |
return NULL; |
tv_dac->ps2_tvdac_adj = default_tvdac_adj[rdev->family]; |
if ((rdev->flags & RADEON_IS_MOBILITY) && (rdev->family == CHIP_RV250)) |
tv_dac->ps2_tvdac_adj = 0x00880000; |
tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; |
tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; |
return tv_dac; |
} |
struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct |
radeon_encoder |
*encoder) |
{ |
struct drm_device *dev = encoder->base.dev; |
struct radeon_device *rdev = dev->dev_private; |
uint16_t dac_info; |
uint8_t rev, bg, dac; |
struct radeon_encoder_tv_dac *tv_dac = NULL; |
if (rdev->bios == NULL) |
return radeon_legacy_get_tv_dac_info_from_table(rdev); |
/* first check TV table */ |
dac_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); |
if (dac_info) { |
tv_dac = |
kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL); |
if (!tv_dac) |
return NULL; |
rev = RBIOS8(dac_info + 0x3); |
if (rev > 4) { |
bg = RBIOS8(dac_info + 0xc) & 0xf; |
dac = RBIOS8(dac_info + 0xd) & 0xf; |
tv_dac->ps2_tvdac_adj = (bg << 16) | (dac << 20); |
bg = RBIOS8(dac_info + 0xe) & 0xf; |
dac = RBIOS8(dac_info + 0xf) & 0xf; |
tv_dac->pal_tvdac_adj = (bg << 16) | (dac << 20); |
bg = RBIOS8(dac_info + 0x10) & 0xf; |
dac = RBIOS8(dac_info + 0x11) & 0xf; |
tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); |
} else if (rev > 1) { |
bg = RBIOS8(dac_info + 0xc) & 0xf; |
dac = (RBIOS8(dac_info + 0xc) >> 4) & 0xf; |
tv_dac->ps2_tvdac_adj = (bg << 16) | (dac << 20); |
bg = RBIOS8(dac_info + 0xd) & 0xf; |
dac = (RBIOS8(dac_info + 0xd) >> 4) & 0xf; |
tv_dac->pal_tvdac_adj = (bg << 16) | (dac << 20); |
bg = RBIOS8(dac_info + 0xe) & 0xf; |
dac = (RBIOS8(dac_info + 0xe) >> 4) & 0xf; |
tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); |
} |
tv_dac->tv_std = radeon_combios_get_tv_info(encoder); |
} else { |
/* then check CRT table */ |
dac_info = |
combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); |
if (dac_info) { |
tv_dac = |
kzalloc(sizeof(struct radeon_encoder_tv_dac), |
GFP_KERNEL); |
if (!tv_dac) |
return NULL; |
rev = RBIOS8(dac_info) & 0x3; |
if (rev < 2) { |
bg = RBIOS8(dac_info + 0x3) & 0xf; |
dac = (RBIOS8(dac_info + 0x3) >> 4) & 0xf; |
tv_dac->ps2_tvdac_adj = |
(bg << 16) | (dac << 20); |
tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; |
tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; |
} else { |
bg = RBIOS8(dac_info + 0x4) & 0xf; |
dac = RBIOS8(dac_info + 0x5) & 0xf; |
tv_dac->ps2_tvdac_adj = |
(bg << 16) | (dac << 20); |
tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; |
tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; |
} |
} else { |
DRM_INFO("No TV DAC info found in BIOS\n"); |
return radeon_legacy_get_tv_dac_info_from_table(rdev); |
} |
} |
return tv_dac; |
} |
static struct radeon_encoder_lvds *radeon_legacy_get_lvds_info_from_regs(struct |
radeon_device |
*rdev) |
{ |
struct radeon_encoder_lvds *lvds = NULL; |
uint32_t fp_vert_stretch, fp_horz_stretch; |
uint32_t ppll_div_sel, ppll_val; |
uint32_t lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL); |
lvds = kzalloc(sizeof(struct radeon_encoder_lvds), GFP_KERNEL); |
if (!lvds) |
return NULL; |
fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH); |
fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH); |
/* These should be fail-safe defaults, fingers crossed */ |
lvds->panel_pwr_delay = 200; |
lvds->panel_vcc_delay = 2000; |
lvds->lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); |
lvds->panel_digon_delay = (lvds_ss_gen_cntl >> RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) & 0xf; |
lvds->panel_blon_delay = (lvds_ss_gen_cntl >> RADEON_LVDS_PWRSEQ_DELAY2_SHIFT) & 0xf; |
if (fp_vert_stretch & RADEON_VERT_STRETCH_ENABLE) |
lvds->native_mode.panel_yres = |
((fp_vert_stretch & RADEON_VERT_PANEL_SIZE) >> |
RADEON_VERT_PANEL_SHIFT) + 1; |
else |
lvds->native_mode.panel_yres = |
(RREG32(RADEON_CRTC_V_TOTAL_DISP) >> 16) + 1; |
if (fp_horz_stretch & RADEON_HORZ_STRETCH_ENABLE) |
lvds->native_mode.panel_xres = |
(((fp_horz_stretch & RADEON_HORZ_PANEL_SIZE) >> |
RADEON_HORZ_PANEL_SHIFT) + 1) * 8; |
else |
lvds->native_mode.panel_xres = |
((RREG32(RADEON_CRTC_H_TOTAL_DISP) >> 16) + 1) * 8; |
if ((lvds->native_mode.panel_xres < 640) || |
(lvds->native_mode.panel_yres < 480)) { |
lvds->native_mode.panel_xres = 640; |
lvds->native_mode.panel_yres = 480; |
} |
ppll_div_sel = RREG8(RADEON_CLOCK_CNTL_INDEX + 1) & 0x3; |
ppll_val = RREG32_PLL(RADEON_PPLL_DIV_0 + ppll_div_sel); |
if ((ppll_val & 0x000707ff) == 0x1bb) |
lvds->use_bios_dividers = false; |
else { |
lvds->panel_ref_divider = |
RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff; |
lvds->panel_post_divider = (ppll_val >> 16) & 0x7; |
lvds->panel_fb_divider = ppll_val & 0x7ff; |
if ((lvds->panel_ref_divider != 0) && |
(lvds->panel_fb_divider > 3)) |
lvds->use_bios_dividers = true; |
} |
lvds->panel_vcc_delay = 200; |
DRM_INFO("Panel info derived from registers\n"); |
DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.panel_xres, |
lvds->native_mode.panel_yres); |
return lvds; |
} |
struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder |
*encoder) |
{ |
struct drm_device *dev = encoder->base.dev; |
struct radeon_device *rdev = dev->dev_private; |
uint16_t lcd_info; |
uint32_t panel_setup; |
char stmp[30]; |
int tmp, i; |
struct radeon_encoder_lvds *lvds = NULL; |
if (rdev->bios == NULL) |
return radeon_legacy_get_lvds_info_from_regs(rdev); |
lcd_info = combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE); |
if (lcd_info) { |
lvds = kzalloc(sizeof(struct radeon_encoder_lvds), GFP_KERNEL); |
if (!lvds) |
return NULL; |
for (i = 0; i < 24; i++) |
stmp[i] = RBIOS8(lcd_info + i + 1); |
stmp[24] = 0; |
DRM_INFO("Panel ID String: %s\n", stmp); |
lvds->native_mode.panel_xres = RBIOS16(lcd_info + 0x19); |
lvds->native_mode.panel_yres = RBIOS16(lcd_info + 0x1b); |
DRM_INFO("Panel Size %dx%d\n", lvds->native_mode.panel_xres, |
lvds->native_mode.panel_yres); |
lvds->panel_vcc_delay = RBIOS16(lcd_info + 0x2c); |
if (lvds->panel_vcc_delay > 2000 || lvds->panel_vcc_delay < 0) |
lvds->panel_vcc_delay = 2000; |
lvds->panel_pwr_delay = RBIOS8(lcd_info + 0x24); |
lvds->panel_digon_delay = RBIOS16(lcd_info + 0x38) & 0xf; |
lvds->panel_blon_delay = (RBIOS16(lcd_info + 0x38) >> 4) & 0xf; |
lvds->panel_ref_divider = RBIOS16(lcd_info + 0x2e); |
lvds->panel_post_divider = RBIOS8(lcd_info + 0x30); |
lvds->panel_fb_divider = RBIOS16(lcd_info + 0x31); |
if ((lvds->panel_ref_divider != 0) && |
(lvds->panel_fb_divider > 3)) |
lvds->use_bios_dividers = true; |
panel_setup = RBIOS32(lcd_info + 0x39); |
lvds->lvds_gen_cntl = 0xff00; |
if (panel_setup & 0x1) |
lvds->lvds_gen_cntl |= RADEON_LVDS_PANEL_FORMAT; |
if ((panel_setup >> 4) & 0x1) |
lvds->lvds_gen_cntl |= RADEON_LVDS_PANEL_TYPE; |
switch ((panel_setup >> 8) & 0x7) { |
case 0: |
lvds->lvds_gen_cntl |= RADEON_LVDS_NO_FM; |
break; |
case 1: |
lvds->lvds_gen_cntl |= RADEON_LVDS_2_GREY; |
break; |
case 2: |
lvds->lvds_gen_cntl |= RADEON_LVDS_4_GREY; |
break; |
default: |
break; |
} |
if ((panel_setup >> 16) & 0x1) |
lvds->lvds_gen_cntl |= RADEON_LVDS_FP_POL_LOW; |
if ((panel_setup >> 17) & 0x1) |
lvds->lvds_gen_cntl |= RADEON_LVDS_LP_POL_LOW; |
if ((panel_setup >> 18) & 0x1) |
lvds->lvds_gen_cntl |= RADEON_LVDS_DTM_POL_LOW; |
if ((panel_setup >> 23) & 0x1) |
lvds->lvds_gen_cntl |= RADEON_LVDS_BL_CLK_SEL; |
lvds->lvds_gen_cntl |= (panel_setup & 0xf0000000); |
for (i = 0; i < 32; i++) { |
tmp = RBIOS16(lcd_info + 64 + i * 2); |
if (tmp == 0) |
break; |
if ((RBIOS16(tmp) == lvds->native_mode.panel_xres) && |
(RBIOS16(tmp + 2) == |
lvds->native_mode.panel_yres)) { |
lvds->native_mode.hblank = |
(RBIOS16(tmp + 17) - RBIOS16(tmp + 19)) * 8; |
lvds->native_mode.hoverplus = |
(RBIOS16(tmp + 21) - RBIOS16(tmp + 19) - |
1) * 8; |
lvds->native_mode.hsync_width = |
RBIOS8(tmp + 23) * 8; |
lvds->native_mode.vblank = (RBIOS16(tmp + 24) - |
RBIOS16(tmp + 26)); |
lvds->native_mode.voverplus = |
((RBIOS16(tmp + 28) & 0x7ff) - |
RBIOS16(tmp + 26)); |
lvds->native_mode.vsync_width = |
((RBIOS16(tmp + 28) & 0xf800) >> 11); |
lvds->native_mode.dotclock = |
RBIOS16(tmp + 9) * 10; |
lvds->native_mode.flags = 0; |
} |
} |
encoder->native_mode = lvds->native_mode; |
} else { |
DRM_INFO("No panel info found in BIOS\n"); |
return radeon_legacy_get_lvds_info_from_regs(rdev); |
} |
return lvds; |
} |
static const struct radeon_tmds_pll default_tmds_pll[CHIP_LAST][4] = { |
{{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_R100 */ |
{{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_RV100 */ |
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RS100 */ |
{{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_RV200 */ |
{{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_RS200 */ |
{{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}}, /* CHIP_R200 */ |
{{15500, 0x81b}, {0xffffffff, 0x83f}, {0, 0}, {0, 0}}, /* CHIP_RV250 */ |
{{0, 0}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RS300 */ |
{{13000, 0x400f4}, {15000, 0x400f7}, {0xffffffff, 0x40111}, {0, 0}}, /* CHIP_RV280 */ |
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R300 */ |
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R350 */ |
{{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RV350 */ |
{{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RV380 */ |
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R420 */ |
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R423 */ |
{{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RV410 */ |
{{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RS400 */ |
{{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RS480 */ |
}; |
static struct radeon_encoder_int_tmds |
*radeon_legacy_get_tmds_info_from_table(struct radeon_device *rdev) |
{ |
int i; |
struct radeon_encoder_int_tmds *tmds = NULL; |
tmds = kzalloc(sizeof(struct radeon_encoder_int_tmds), GFP_KERNEL); |
if (!tmds) |
return NULL; |
for (i = 0; i < 4; i++) { |
tmds->tmds_pll[i].value = |
default_tmds_pll[rdev->family][i].value; |
tmds->tmds_pll[i].freq = default_tmds_pll[rdev->family][i].freq; |
} |
return tmds; |
} |
struct radeon_encoder_int_tmds *radeon_combios_get_tmds_info(struct |
radeon_encoder |
*encoder) |
{ |
struct drm_device *dev = encoder->base.dev; |
struct radeon_device *rdev = dev->dev_private; |
uint16_t tmds_info; |
int i, n; |
uint8_t ver; |
struct radeon_encoder_int_tmds *tmds = NULL; |
if (rdev->bios == NULL) |
return radeon_legacy_get_tmds_info_from_table(rdev); |
tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE); |
if (tmds_info) { |
tmds = |
kzalloc(sizeof(struct radeon_encoder_int_tmds), GFP_KERNEL); |
if (!tmds) |
return NULL; |
ver = RBIOS8(tmds_info); |
DRM_INFO("DFP table revision: %d\n", ver); |
if (ver == 3) { |
n = RBIOS8(tmds_info + 5) + 1; |
if (n > 4) |
n = 4; |
for (i = 0; i < n; i++) { |
tmds->tmds_pll[i].value = |
RBIOS32(tmds_info + i * 10 + 0x08); |
tmds->tmds_pll[i].freq = |
RBIOS16(tmds_info + i * 10 + 0x10); |
DRM_DEBUG("TMDS PLL From COMBIOS %u %x\n", |
tmds->tmds_pll[i].freq, |
tmds->tmds_pll[i].value); |
} |
} else if (ver == 4) { |
int stride = 0; |
n = RBIOS8(tmds_info + 5) + 1; |
if (n > 4) |
n = 4; |
for (i = 0; i < n; i++) { |
tmds->tmds_pll[i].value = |
RBIOS32(tmds_info + stride + 0x08); |
tmds->tmds_pll[i].freq = |
RBIOS16(tmds_info + stride + 0x10); |
if (i == 0) |
stride += 10; |
else |
stride += 6; |
DRM_DEBUG("TMDS PLL From COMBIOS %u %x\n", |
tmds->tmds_pll[i].freq, |
tmds->tmds_pll[i].value); |
} |
} |
} else |
DRM_INFO("No TMDS info found in BIOS\n"); |
return tmds; |
} |
void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder) |
{ |
struct drm_device *dev = encoder->base.dev; |
struct radeon_device *rdev = dev->dev_private; |
uint16_t ext_tmds_info; |
uint8_t ver; |
if (rdev->bios == NULL) |
return; |
ext_tmds_info = |
combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE); |
if (ext_tmds_info) { |
ver = RBIOS8(ext_tmds_info); |
DRM_INFO("External TMDS Table revision: %d\n", ver); |
// TODO |
} |
} |
bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_i2c_bus_rec ddc_i2c; |
rdev->mode_info.connector_table = radeon_connector_table; |
if (rdev->mode_info.connector_table == CT_NONE) { |
#ifdef CONFIG_PPC_PMAC |
if (machine_is_compatible("PowerBook3,3")) { |
/* powerbook with VGA */ |
rdev->mode_info.connector_table = CT_POWERBOOK_VGA; |
} else if (machine_is_compatible("PowerBook3,4") || |
machine_is_compatible("PowerBook3,5")) { |
/* powerbook with internal tmds */ |
rdev->mode_info.connector_table = CT_POWERBOOK_INTERNAL; |
} else if (machine_is_compatible("PowerBook5,1") || |
machine_is_compatible("PowerBook5,2") || |
machine_is_compatible("PowerBook5,3") || |
machine_is_compatible("PowerBook5,4") || |
machine_is_compatible("PowerBook5,5")) { |
/* powerbook with external single link tmds (sil164) */ |
rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; |
} else if (machine_is_compatible("PowerBook5,6")) { |
/* powerbook with external dual or single link tmds */ |
rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; |
} else if (machine_is_compatible("PowerBook5,7") || |
machine_is_compatible("PowerBook5,8") || |
machine_is_compatible("PowerBook5,9")) { |
/* PowerBook6,2 ? */ |
/* powerbook with external dual link tmds (sil1178?) */ |
rdev->mode_info.connector_table = CT_POWERBOOK_EXTERNAL; |
} else if (machine_is_compatible("PowerBook4,1") || |
machine_is_compatible("PowerBook4,2") || |
machine_is_compatible("PowerBook4,3") || |
machine_is_compatible("PowerBook6,3") || |
machine_is_compatible("PowerBook6,5") || |
machine_is_compatible("PowerBook6,7")) { |
/* ibook */ |
rdev->mode_info.connector_table = CT_IBOOK; |
} else if (machine_is_compatible("PowerMac4,4")) { |
/* emac */ |
rdev->mode_info.connector_table = CT_EMAC; |
} else if (machine_is_compatible("PowerMac10,1")) { |
/* mini with internal tmds */ |
rdev->mode_info.connector_table = CT_MINI_INTERNAL; |
} else if (machine_is_compatible("PowerMac10,2")) { |
/* mini with external tmds */ |
rdev->mode_info.connector_table = CT_MINI_EXTERNAL; |
} else if (machine_is_compatible("PowerMac12,1")) { |
/* PowerMac8,1 ? */ |
/* imac g5 isight */ |
rdev->mode_info.connector_table = CT_IMAC_G5_ISIGHT; |
} else |
#endif /* CONFIG_PPC_PMAC */ |
rdev->mode_info.connector_table = CT_GENERIC; |
} |
switch (rdev->mode_info.connector_table) { |
case CT_GENERIC: |
DRM_INFO("Connector Table: %d (generic)\n", |
rdev->mode_info.connector_table); |
/* these are the most common settings */ |
if (rdev->flags & RADEON_SINGLE_CRTC) { |
/* VGA - primary dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, |
ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, |
&ddc_i2c); |
} else if (rdev->flags & RADEON_IS_MOBILITY) { |
/* LVDS */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_LCD_GPIO_MASK); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_LCD1_SUPPORT, |
0), |
ATOM_DEVICE_LCD1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, |
ATOM_DEVICE_LCD1_SUPPORT, |
DRM_MODE_CONNECTOR_LVDS, |
&ddc_i2c); |
/* VGA - primary dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, |
ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, |
&ddc_i2c); |
} else { |
/* DVI-I - tv dac, int tmds */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP1_SUPPORT, |
0), |
ATOM_DEVICE_DFP1_SUPPORT); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
radeon_add_legacy_connector(dev, 0, |
ATOM_DEVICE_DFP1_SUPPORT | |
ATOM_DEVICE_CRT2_SUPPORT, |
DRM_MODE_CONNECTOR_DVII, |
&ddc_i2c); |
/* VGA - primary dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, |
ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, |
&ddc_i2c); |
} |
if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) { |
/* TV - tv dac */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, |
ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
} |
break; |
case CT_IBOOK: |
DRM_INFO("Connector Table: %d (ibook)\n", |
rdev->mode_info.connector_table); |
/* LVDS */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_LCD1_SUPPORT, |
0), |
ATOM_DEVICE_LCD1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, |
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); |
/* VGA - TV DAC */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_POWERBOOK_EXTERNAL: |
DRM_INFO("Connector Table: %d (powerbook external tmds)\n", |
rdev->mode_info.connector_table); |
/* LVDS */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_LCD1_SUPPORT, |
0), |
ATOM_DEVICE_LCD1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, |
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); |
/* DVI-I - primary dac, ext tmds */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP2_SUPPORT, |
0), |
ATOM_DEVICE_DFP2_SUPPORT); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, |
ATOM_DEVICE_DFP2_SUPPORT | |
ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_DVII, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_POWERBOOK_INTERNAL: |
DRM_INFO("Connector Table: %d (powerbook internal tmds)\n", |
rdev->mode_info.connector_table); |
/* LVDS */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_LCD1_SUPPORT, |
0), |
ATOM_DEVICE_LCD1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, |
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); |
/* DVI-I - primary dac, int tmds */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP1_SUPPORT, |
0), |
ATOM_DEVICE_DFP1_SUPPORT); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, |
ATOM_DEVICE_DFP1_SUPPORT | |
ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_DVII, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_POWERBOOK_VGA: |
DRM_INFO("Connector Table: %d (powerbook vga)\n", |
rdev->mode_info.connector_table); |
/* LVDS */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_LCD1_SUPPORT, |
0), |
ATOM_DEVICE_LCD1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT, |
DRM_MODE_CONNECTOR_LVDS, &ddc_i2c); |
/* VGA - primary dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_MINI_EXTERNAL: |
DRM_INFO("Connector Table: %d (mini external tmds)\n", |
rdev->mode_info.connector_table); |
/* DVI-I - tv dac, ext tmds */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP2_SUPPORT, |
0), |
ATOM_DEVICE_DFP2_SUPPORT); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
radeon_add_legacy_connector(dev, 0, |
ATOM_DEVICE_DFP2_SUPPORT | |
ATOM_DEVICE_CRT2_SUPPORT, |
DRM_MODE_CONNECTOR_DVII, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_MINI_INTERNAL: |
DRM_INFO("Connector Table: %d (mini internal tmds)\n", |
rdev->mode_info.connector_table); |
/* DVI-I - tv dac, int tmds */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP1_SUPPORT, |
0), |
ATOM_DEVICE_DFP1_SUPPORT); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
radeon_add_legacy_connector(dev, 0, |
ATOM_DEVICE_DFP1_SUPPORT | |
ATOM_DEVICE_CRT2_SUPPORT, |
DRM_MODE_CONNECTOR_DVII, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_IMAC_G5_ISIGHT: |
DRM_INFO("Connector Table: %d (imac g5 isight)\n", |
rdev->mode_info.connector_table); |
/* DVI-D - int tmds */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP1_SUPPORT, |
0), |
ATOM_DEVICE_DFP1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT, |
DRM_MODE_CONNECTOR_DVID, &ddc_i2c); |
/* VGA - tv dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
case CT_EMAC: |
DRM_INFO("Connector Table: %d (emac)\n", |
rdev->mode_info.connector_table); |
/* VGA - primary dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, &ddc_i2c); |
/* VGA - tv dac */ |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT, |
DRM_MODE_CONNECTOR_VGA, &ddc_i2c); |
/* TV - TV DAC */ |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
break; |
default: |
DRM_INFO("Connector table: %d (invalid)\n", |
rdev->mode_info.connector_table); |
return false; |
} |
radeon_link_encoder_connector(dev); |
return true; |
} |
static bool radeon_apply_legacy_quirks(struct drm_device *dev, |
int bios_index, |
enum radeon_combios_connector |
*legacy_connector, |
struct radeon_i2c_bus_rec *ddc_i2c) |
{ |
struct radeon_device *rdev = dev->dev_private; |
/* XPRESS DDC quirks */ |
if ((rdev->family == CHIP_RS400 || |
rdev->family == CHIP_RS480) && |
ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC) |
*ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); |
else if ((rdev->family == CHIP_RS400 || |
rdev->family == CHIP_RS480) && |
ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) { |
ddc_i2c->valid = true; |
ddc_i2c->mask_clk_mask = (0x20 << 8); |
ddc_i2c->mask_data_mask = 0x80; |
ddc_i2c->a_clk_mask = (0x20 << 8); |
ddc_i2c->a_data_mask = 0x80; |
ddc_i2c->put_clk_mask = (0x20 << 8); |
ddc_i2c->put_data_mask = 0x80; |
ddc_i2c->get_clk_mask = (0x20 << 8); |
ddc_i2c->get_data_mask = 0x80; |
ddc_i2c->mask_clk_reg = RADEON_GPIOPAD_MASK; |
ddc_i2c->mask_data_reg = RADEON_GPIOPAD_MASK; |
ddc_i2c->a_clk_reg = RADEON_GPIOPAD_A; |
ddc_i2c->a_data_reg = RADEON_GPIOPAD_A; |
ddc_i2c->put_clk_reg = RADEON_GPIOPAD_EN; |
ddc_i2c->put_data_reg = RADEON_GPIOPAD_EN; |
ddc_i2c->get_clk_reg = RADEON_LCD_GPIO_Y_REG; |
ddc_i2c->get_data_reg = RADEON_LCD_GPIO_Y_REG; |
} |
/* Certain IBM chipset RN50s have a BIOS reporting two VGAs, |
one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */ |
if (dev->pdev->device == 0x515e && |
dev->pdev->subsystem_vendor == 0x1014) { |
if (*legacy_connector == CONNECTOR_CRT_LEGACY && |
ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC) |
return false; |
} |
/* Some RV100 cards with 2 VGA ports show up with DVI+VGA */ |
if (dev->pdev->device == 0x5159 && |
dev->pdev->subsystem_vendor == 0x1002 && |
dev->pdev->subsystem_device == 0x013a) { |
if (*legacy_connector == CONNECTOR_DVI_I_LEGACY) |
*legacy_connector = CONNECTOR_CRT_LEGACY; |
} |
/* X300 card with extra non-existent DVI port */ |
if (dev->pdev->device == 0x5B60 && |
dev->pdev->subsystem_vendor == 0x17af && |
dev->pdev->subsystem_device == 0x201e && bios_index == 2) { |
if (*legacy_connector == CONNECTOR_DVI_I_LEGACY) |
return false; |
} |
return true; |
} |
bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint32_t conn_info, entry, devices; |
uint16_t tmp; |
enum radeon_combios_ddc ddc_type; |
enum radeon_combios_connector connector; |
int i = 0; |
struct radeon_i2c_bus_rec ddc_i2c; |
if (rdev->bios == NULL) |
return false; |
conn_info = combios_get_table_offset(dev, COMBIOS_CONNECTOR_INFO_TABLE); |
if (conn_info) { |
for (i = 0; i < 4; i++) { |
entry = conn_info + 2 + i * 2; |
if (!RBIOS16(entry)) |
break; |
tmp = RBIOS16(entry); |
connector = (tmp >> 12) & 0xf; |
ddc_type = (tmp >> 8) & 0xf; |
switch (ddc_type) { |
case DDC_MONID: |
ddc_i2c = |
combios_setup_i2c_bus(RADEON_GPIO_MONID); |
break; |
case DDC_DVI: |
ddc_i2c = |
combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
break; |
case DDC_VGA: |
ddc_i2c = |
combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); |
break; |
case DDC_CRT2: |
ddc_i2c = |
combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); |
break; |
default: |
break; |
} |
radeon_apply_legacy_quirks(dev, i, &connector, |
&ddc_i2c); |
switch (connector) { |
case CONNECTOR_PROPRIETARY_LEGACY: |
if ((tmp >> 4) & 0x1) |
devices = ATOM_DEVICE_DFP2_SUPPORT; |
else |
devices = ATOM_DEVICE_DFP1_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, devices, 0), |
devices); |
radeon_add_legacy_connector(dev, i, devices, |
legacy_connector_convert |
[connector], |
&ddc_i2c); |
break; |
case CONNECTOR_CRT_LEGACY: |
if (tmp & 0x1) { |
devices = ATOM_DEVICE_CRT2_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
} else { |
devices = ATOM_DEVICE_CRT1_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
} |
radeon_add_legacy_connector(dev, |
i, |
devices, |
legacy_connector_convert |
[connector], |
&ddc_i2c); |
break; |
case CONNECTOR_DVI_I_LEGACY: |
devices = 0; |
if (tmp & 0x1) { |
devices |= ATOM_DEVICE_CRT2_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_CRT2_SUPPORT, |
2), |
ATOM_DEVICE_CRT2_SUPPORT); |
} else { |
devices |= ATOM_DEVICE_CRT1_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
} |
if ((tmp >> 4) & 0x1) { |
devices |= ATOM_DEVICE_DFP2_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_DFP2_SUPPORT, |
0), |
ATOM_DEVICE_DFP2_SUPPORT); |
} else { |
devices |= ATOM_DEVICE_DFP1_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_DFP1_SUPPORT, |
0), |
ATOM_DEVICE_DFP1_SUPPORT); |
} |
radeon_add_legacy_connector(dev, |
i, |
devices, |
legacy_connector_convert |
[connector], |
&ddc_i2c); |
break; |
case CONNECTOR_DVI_D_LEGACY: |
if ((tmp >> 4) & 0x1) |
devices = ATOM_DEVICE_DFP2_SUPPORT; |
else |
devices = ATOM_DEVICE_DFP1_SUPPORT; |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, devices, 0), |
devices); |
radeon_add_legacy_connector(dev, i, devices, |
legacy_connector_convert |
[connector], |
&ddc_i2c); |
break; |
case CONNECTOR_CTV_LEGACY: |
case CONNECTOR_STV_LEGACY: |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, i, |
ATOM_DEVICE_TV1_SUPPORT, |
legacy_connector_convert |
[connector], |
&ddc_i2c); |
break; |
default: |
DRM_ERROR("Unknown connector type: %d\n", |
connector); |
continue; |
} |
} |
} else { |
uint16_t tmds_info = |
combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE); |
if (tmds_info) { |
DRM_DEBUG("Found DFP table, assuming DVI connector\n"); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_CRT1_SUPPORT, |
1), |
ATOM_DEVICE_CRT1_SUPPORT); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_DFP1_SUPPORT, |
0), |
ATOM_DEVICE_DFP1_SUPPORT); |
ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); |
radeon_add_legacy_connector(dev, |
0, |
ATOM_DEVICE_CRT1_SUPPORT | |
ATOM_DEVICE_DFP1_SUPPORT, |
DRM_MODE_CONNECTOR_DVII, |
&ddc_i2c); |
} else { |
DRM_DEBUG("No connector info found\n"); |
return false; |
} |
} |
if (rdev->flags & RADEON_IS_MOBILITY || rdev->flags & RADEON_IS_IGP) { |
uint16_t lcd_info = |
combios_get_table_offset(dev, COMBIOS_LCD_INFO_TABLE); |
if (lcd_info) { |
uint16_t lcd_ddc_info = |
combios_get_table_offset(dev, |
COMBIOS_LCD_DDC_INFO_TABLE); |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id(dev, |
ATOM_DEVICE_LCD1_SUPPORT, |
0), |
ATOM_DEVICE_LCD1_SUPPORT); |
if (lcd_ddc_info) { |
ddc_type = RBIOS8(lcd_ddc_info + 2); |
switch (ddc_type) { |
case DDC_MONID: |
ddc_i2c = |
combios_setup_i2c_bus |
(RADEON_GPIO_MONID); |
break; |
case DDC_DVI: |
ddc_i2c = |
combios_setup_i2c_bus |
(RADEON_GPIO_DVI_DDC); |
break; |
case DDC_VGA: |
ddc_i2c = |
combios_setup_i2c_bus |
(RADEON_GPIO_VGA_DDC); |
break; |
case DDC_CRT2: |
ddc_i2c = |
combios_setup_i2c_bus |
(RADEON_GPIO_CRT2_DDC); |
break; |
case DDC_LCD: |
ddc_i2c = |
combios_setup_i2c_bus |
(RADEON_LCD_GPIO_MASK); |
ddc_i2c.mask_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.mask_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
ddc_i2c.a_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.a_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
ddc_i2c.put_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.put_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
ddc_i2c.get_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.get_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
break; |
case DDC_GPIO: |
ddc_i2c = |
combios_setup_i2c_bus |
(RADEON_MDGPIO_EN_REG); |
ddc_i2c.mask_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.mask_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
ddc_i2c.a_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.a_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
ddc_i2c.put_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.put_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
ddc_i2c.get_clk_mask = |
RBIOS32(lcd_ddc_info + 3); |
ddc_i2c.get_data_mask = |
RBIOS32(lcd_ddc_info + 7); |
break; |
default: |
ddc_i2c.valid = false; |
break; |
} |
DRM_DEBUG("LCD DDC Info Table found!\n"); |
} else |
ddc_i2c.valid = false; |
radeon_add_legacy_connector(dev, |
5, |
ATOM_DEVICE_LCD1_SUPPORT, |
DRM_MODE_CONNECTOR_LVDS, |
&ddc_i2c); |
} |
} |
/* check TV table */ |
if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) { |
uint32_t tv_info = |
combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); |
if (tv_info) { |
if (RBIOS8(tv_info + 6) == 'T') { |
radeon_add_legacy_encoder(dev, |
radeon_get_encoder_id |
(dev, |
ATOM_DEVICE_TV1_SUPPORT, |
2), |
ATOM_DEVICE_TV1_SUPPORT); |
radeon_add_legacy_connector(dev, 6, |
ATOM_DEVICE_TV1_SUPPORT, |
DRM_MODE_CONNECTOR_SVIDEO, |
&ddc_i2c); |
} |
} |
} |
radeon_link_encoder_connector(dev); |
return true; |
} |
static void combios_parse_mmio_table(struct drm_device *dev, uint16_t offset) |
{ |
struct radeon_device *rdev = dev->dev_private; |
if (offset) { |
while (RBIOS16(offset)) { |
uint16_t cmd = ((RBIOS16(offset) & 0xe000) >> 13); |
uint32_t addr = (RBIOS16(offset) & 0x1fff); |
uint32_t val, and_mask, or_mask; |
uint32_t tmp; |
offset += 2; |
switch (cmd) { |
case 0: |
val = RBIOS32(offset); |
offset += 4; |
WREG32(addr, val); |
break; |
case 1: |
val = RBIOS32(offset); |
offset += 4; |
WREG32(addr, val); |
break; |
case 2: |
and_mask = RBIOS32(offset); |
offset += 4; |
or_mask = RBIOS32(offset); |
offset += 4; |
tmp = RREG32(addr); |
tmp &= and_mask; |
tmp |= or_mask; |
WREG32(addr, tmp); |
break; |
case 3: |
and_mask = RBIOS32(offset); |
offset += 4; |
or_mask = RBIOS32(offset); |
offset += 4; |
tmp = RREG32(addr); |
tmp &= and_mask; |
tmp |= or_mask; |
WREG32(addr, tmp); |
break; |
case 4: |
val = RBIOS16(offset); |
offset += 2; |
udelay(val); |
break; |
case 5: |
val = RBIOS16(offset); |
offset += 2; |
switch (addr) { |
case 8: |
while (val--) { |
if (! |
(RREG32_PLL |
(RADEON_CLK_PWRMGT_CNTL) & |
RADEON_MC_BUSY)) |
break; |
} |
break; |
case 9: |
while (val--) { |
if ((RREG32(RADEON_MC_STATUS) & |
RADEON_MC_IDLE)) |
break; |
} |
break; |
default: |
break; |
} |
break; |
default: |
break; |
} |
} |
} |
} |
static void combios_parse_pll_table(struct drm_device *dev, uint16_t offset) |
{ |
struct radeon_device *rdev = dev->dev_private; |
if (offset) { |
while (RBIOS8(offset)) { |
uint8_t cmd = ((RBIOS8(offset) & 0xc0) >> 6); |
uint8_t addr = (RBIOS8(offset) & 0x3f); |
uint32_t val, shift, tmp; |
uint32_t and_mask, or_mask; |
offset++; |
switch (cmd) { |
case 0: |
val = RBIOS32(offset); |
offset += 4; |
WREG32_PLL(addr, val); |
break; |
case 1: |
shift = RBIOS8(offset) * 8; |
offset++; |
and_mask = RBIOS8(offset) << shift; |
and_mask |= ~(0xff << shift); |
offset++; |
or_mask = RBIOS8(offset) << shift; |
offset++; |
tmp = RREG32_PLL(addr); |
tmp &= and_mask; |
tmp |= or_mask; |
WREG32_PLL(addr, tmp); |
break; |
case 2: |
case 3: |
tmp = 1000; |
switch (addr) { |
case 1: |
udelay(150); |
break; |
case 2: |
udelay(1000); |
break; |
case 3: |
while (tmp--) { |
if (! |
(RREG32_PLL |
(RADEON_CLK_PWRMGT_CNTL) & |
RADEON_MC_BUSY)) |
break; |
} |
break; |
case 4: |
while (tmp--) { |
if (RREG32_PLL |
(RADEON_CLK_PWRMGT_CNTL) & |
RADEON_DLL_READY) |
break; |
} |
break; |
case 5: |
tmp = |
RREG32_PLL(RADEON_CLK_PWRMGT_CNTL); |
if (tmp & RADEON_CG_NO1_DEBUG_0) { |
#if 0 |
uint32_t mclk_cntl = |
RREG32_PLL |
(RADEON_MCLK_CNTL); |
mclk_cntl &= 0xffff0000; |
/*mclk_cntl |= 0x00001111;*//* ??? */ |
WREG32_PLL(RADEON_MCLK_CNTL, |
mclk_cntl); |
udelay(10000); |
#endif |
WREG32_PLL |
(RADEON_CLK_PWRMGT_CNTL, |
tmp & |
~RADEON_CG_NO1_DEBUG_0); |
udelay(10000); |
} |
break; |
default: |
break; |
} |
break; |
default: |
break; |
} |
} |
} |
} |
static void combios_parse_ram_reset_table(struct drm_device *dev, |
uint16_t offset) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint32_t tmp; |
if (offset) { |
uint8_t val = RBIOS8(offset); |
while (val != 0xff) { |
offset++; |
if (val == 0x0f) { |
uint32_t channel_complete_mask; |
if (ASIC_IS_R300(rdev)) |
channel_complete_mask = |
R300_MEM_PWRUP_COMPLETE; |
else |
channel_complete_mask = |
RADEON_MEM_PWRUP_COMPLETE; |
tmp = 20000; |
while (tmp--) { |
if ((RREG32(RADEON_MEM_STR_CNTL) & |
channel_complete_mask) == |
channel_complete_mask) |
break; |
} |
} else { |
uint32_t or_mask = RBIOS16(offset); |
offset += 2; |
tmp = RREG32(RADEON_MEM_SDRAM_MODE_REG); |
tmp &= RADEON_SDRAM_MODE_MASK; |
tmp |= or_mask; |
WREG32(RADEON_MEM_SDRAM_MODE_REG, tmp); |
or_mask = val << 24; |
tmp = RREG32(RADEON_MEM_SDRAM_MODE_REG); |
tmp &= RADEON_B3MEM_RESET_MASK; |
tmp |= or_mask; |
WREG32(RADEON_MEM_SDRAM_MODE_REG, tmp); |
} |
val = RBIOS8(offset); |
} |
} |
} |
static uint32_t combios_detect_ram(struct drm_device *dev, int ram, |
int mem_addr_mapping) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint32_t mem_cntl; |
uint32_t mem_size; |
uint32_t addr = 0; |
mem_cntl = RREG32(RADEON_MEM_CNTL); |
if (mem_cntl & RV100_HALF_MODE) |
ram /= 2; |
mem_size = ram; |
mem_cntl &= ~(0xff << 8); |
mem_cntl |= (mem_addr_mapping & 0xff) << 8; |
WREG32(RADEON_MEM_CNTL, mem_cntl); |
RREG32(RADEON_MEM_CNTL); |
/* sdram reset ? */ |
/* something like this???? */ |
while (ram--) { |
addr = ram * 1024 * 1024; |
/* write to each page */ |
WREG32(RADEON_MM_INDEX, (addr) | RADEON_MM_APER); |
WREG32(RADEON_MM_DATA, 0xdeadbeef); |
/* read back and verify */ |
WREG32(RADEON_MM_INDEX, (addr) | RADEON_MM_APER); |
if (RREG32(RADEON_MM_DATA) != 0xdeadbeef) |
return 0; |
} |
return mem_size; |
} |
static void combios_write_ram_size(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint8_t rev; |
uint16_t offset; |
uint32_t mem_size = 0; |
uint32_t mem_cntl = 0; |
/* should do something smarter here I guess... */ |
if (rdev->flags & RADEON_IS_IGP) |
return; |
/* first check detected mem table */ |
offset = combios_get_table_offset(dev, COMBIOS_DETECTED_MEM_TABLE); |
if (offset) { |
rev = RBIOS8(offset); |
if (rev < 3) { |
mem_cntl = RBIOS32(offset + 1); |
mem_size = RBIOS16(offset + 5); |
if (((rdev->flags & RADEON_FAMILY_MASK) < CHIP_R200) && |
((dev->pdev->device != 0x515e) |
&& (dev->pdev->device != 0x5969))) |
WREG32(RADEON_MEM_CNTL, mem_cntl); |
} |
} |
if (!mem_size) { |
offset = |
combios_get_table_offset(dev, COMBIOS_MEM_CONFIG_TABLE); |
if (offset) { |
rev = RBIOS8(offset - 1); |
if (rev < 1) { |
if (((rdev->flags & RADEON_FAMILY_MASK) < |
CHIP_R200) |
&& ((dev->pdev->device != 0x515e) |
&& (dev->pdev->device != 0x5969))) { |
int ram = 0; |
int mem_addr_mapping = 0; |
while (RBIOS8(offset)) { |
ram = RBIOS8(offset); |
mem_addr_mapping = |
RBIOS8(offset + 1); |
if (mem_addr_mapping != 0x25) |
ram *= 2; |
mem_size = |
combios_detect_ram(dev, ram, |
mem_addr_mapping); |
if (mem_size) |
break; |
offset += 2; |
} |
} else |
mem_size = RBIOS8(offset); |
} else { |
mem_size = RBIOS8(offset); |
mem_size *= 2; /* convert to MB */ |
} |
} |
} |
mem_size *= (1024 * 1024); /* convert to bytes */ |
WREG32(RADEON_CONFIG_MEMSIZE, mem_size); |
} |
void radeon_combios_dyn_clk_setup(struct drm_device *dev, int enable) |
{ |
uint16_t dyn_clk_info = |
combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); |
if (dyn_clk_info) |
combios_parse_pll_table(dev, dyn_clk_info); |
} |
void radeon_combios_asic_init(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint16_t table; |
/* port hardcoded mac stuff from radeonfb */ |
if (rdev->bios == NULL) |
return; |
/* ASIC INIT 1 */ |
table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_1_TABLE); |
if (table) |
combios_parse_mmio_table(dev, table); |
/* PLL INIT */ |
table = combios_get_table_offset(dev, COMBIOS_PLL_INIT_TABLE); |
if (table) |
combios_parse_pll_table(dev, table); |
/* ASIC INIT 2 */ |
table = combios_get_table_offset(dev, COMBIOS_ASIC_INIT_2_TABLE); |
if (table) |
combios_parse_mmio_table(dev, table); |
if (!(rdev->flags & RADEON_IS_IGP)) { |
/* ASIC INIT 4 */ |
table = |
combios_get_table_offset(dev, COMBIOS_ASIC_INIT_4_TABLE); |
if (table) |
combios_parse_mmio_table(dev, table); |
/* RAM RESET */ |
table = combios_get_table_offset(dev, COMBIOS_RAM_RESET_TABLE); |
if (table) |
combios_parse_ram_reset_table(dev, table); |
/* ASIC INIT 3 */ |
table = |
combios_get_table_offset(dev, COMBIOS_ASIC_INIT_3_TABLE); |
if (table) |
combios_parse_mmio_table(dev, table); |
/* write CONFIG_MEMSIZE */ |
combios_write_ram_size(dev); |
} |
/* DYN CLK 1 */ |
table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); |
if (table) |
combios_parse_pll_table(dev, table); |
} |
void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
uint32_t bios_0_scratch, bios_6_scratch, bios_7_scratch; |
bios_0_scratch = RREG32(RADEON_BIOS_0_SCRATCH); |
bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); |
bios_7_scratch = RREG32(RADEON_BIOS_7_SCRATCH); |
/* let the bios control the backlight */ |
bios_0_scratch &= ~RADEON_DRIVER_BRIGHTNESS_EN; |
/* tell the bios not to handle mode switching */ |
bios_6_scratch |= (RADEON_DISPLAY_SWITCHING_DIS | |
RADEON_ACC_MODE_CHANGE); |
/* tell the bios a driver is loaded */ |
bios_7_scratch |= RADEON_DRV_LOADED; |
WREG32(RADEON_BIOS_0_SCRATCH, bios_0_scratch); |
WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); |
WREG32(RADEON_BIOS_7_SCRATCH, bios_7_scratch); |
} |
void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t bios_6_scratch; |
bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); |
if (lock) |
bios_6_scratch |= RADEON_DRIVER_CRITICAL; |
else |
bios_6_scratch &= ~RADEON_DRIVER_CRITICAL; |
WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); |
} |
void |
radeon_combios_connected_scratch_regs(struct drm_connector *connector, |
struct drm_encoder *encoder, |
bool connected) |
{ |
struct drm_device *dev = connector->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_connector *radeon_connector = |
to_radeon_connector(connector); |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t bios_4_scratch = RREG32(RADEON_BIOS_4_SCRATCH); |
uint32_t bios_5_scratch = RREG32(RADEON_BIOS_5_SCRATCH); |
if ((radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) && |
(radeon_connector->devices & ATOM_DEVICE_TV1_SUPPORT)) { |
if (connected) { |
DRM_DEBUG("TV1 connected\n"); |
/* fix me */ |
bios_4_scratch |= RADEON_TV1_ATTACHED_SVIDEO; |
/*save->bios_4_scratch |= RADEON_TV1_ATTACHED_COMP; */ |
bios_5_scratch |= RADEON_TV1_ON; |
bios_5_scratch |= RADEON_ACC_REQ_TV1; |
} else { |
DRM_DEBUG("TV1 disconnected\n"); |
bios_4_scratch &= ~RADEON_TV1_ATTACHED_MASK; |
bios_5_scratch &= ~RADEON_TV1_ON; |
bios_5_scratch &= ~RADEON_ACC_REQ_TV1; |
} |
} |
if ((radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) && |
(radeon_connector->devices & ATOM_DEVICE_LCD1_SUPPORT)) { |
if (connected) { |
DRM_DEBUG("LCD1 connected\n"); |
bios_4_scratch |= RADEON_LCD1_ATTACHED; |
bios_5_scratch |= RADEON_LCD1_ON; |
bios_5_scratch |= RADEON_ACC_REQ_LCD1; |
} else { |
DRM_DEBUG("LCD1 disconnected\n"); |
bios_4_scratch &= ~RADEON_LCD1_ATTACHED; |
bios_5_scratch &= ~RADEON_LCD1_ON; |
bios_5_scratch &= ~RADEON_ACC_REQ_LCD1; |
} |
} |
if ((radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) && |
(radeon_connector->devices & ATOM_DEVICE_CRT1_SUPPORT)) { |
if (connected) { |
DRM_DEBUG("CRT1 connected\n"); |
bios_4_scratch |= RADEON_CRT1_ATTACHED_COLOR; |
bios_5_scratch |= RADEON_CRT1_ON; |
bios_5_scratch |= RADEON_ACC_REQ_CRT1; |
} else { |
DRM_DEBUG("CRT1 disconnected\n"); |
bios_4_scratch &= ~RADEON_CRT1_ATTACHED_MASK; |
bios_5_scratch &= ~RADEON_CRT1_ON; |
bios_5_scratch &= ~RADEON_ACC_REQ_CRT1; |
} |
} |
if ((radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) && |
(radeon_connector->devices & ATOM_DEVICE_CRT2_SUPPORT)) { |
if (connected) { |
DRM_DEBUG("CRT2 connected\n"); |
bios_4_scratch |= RADEON_CRT2_ATTACHED_COLOR; |
bios_5_scratch |= RADEON_CRT2_ON; |
bios_5_scratch |= RADEON_ACC_REQ_CRT2; |
} else { |
DRM_DEBUG("CRT2 disconnected\n"); |
bios_4_scratch &= ~RADEON_CRT2_ATTACHED_MASK; |
bios_5_scratch &= ~RADEON_CRT2_ON; |
bios_5_scratch &= ~RADEON_ACC_REQ_CRT2; |
} |
} |
if ((radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) && |
(radeon_connector->devices & ATOM_DEVICE_DFP1_SUPPORT)) { |
if (connected) { |
DRM_DEBUG("DFP1 connected\n"); |
bios_4_scratch |= RADEON_DFP1_ATTACHED; |
bios_5_scratch |= RADEON_DFP1_ON; |
bios_5_scratch |= RADEON_ACC_REQ_DFP1; |
} else { |
DRM_DEBUG("DFP1 disconnected\n"); |
bios_4_scratch &= ~RADEON_DFP1_ATTACHED; |
bios_5_scratch &= ~RADEON_DFP1_ON; |
bios_5_scratch &= ~RADEON_ACC_REQ_DFP1; |
} |
} |
if ((radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) && |
(radeon_connector->devices & ATOM_DEVICE_DFP2_SUPPORT)) { |
if (connected) { |
DRM_DEBUG("DFP2 connected\n"); |
bios_4_scratch |= RADEON_DFP2_ATTACHED; |
bios_5_scratch |= RADEON_DFP2_ON; |
bios_5_scratch |= RADEON_ACC_REQ_DFP2; |
} else { |
DRM_DEBUG("DFP2 disconnected\n"); |
bios_4_scratch &= ~RADEON_DFP2_ATTACHED; |
bios_5_scratch &= ~RADEON_DFP2_ON; |
bios_5_scratch &= ~RADEON_ACC_REQ_DFP2; |
} |
} |
WREG32(RADEON_BIOS_4_SCRATCH, bios_4_scratch); |
WREG32(RADEON_BIOS_5_SCRATCH, bios_5_scratch); |
} |
void |
radeon_combios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t bios_5_scratch = RREG32(RADEON_BIOS_5_SCRATCH); |
if (radeon_encoder->devices & ATOM_DEVICE_TV1_SUPPORT) { |
bios_5_scratch &= ~RADEON_TV1_CRTC_MASK; |
bios_5_scratch |= (crtc << RADEON_TV1_CRTC_SHIFT); |
} |
if (radeon_encoder->devices & ATOM_DEVICE_CRT1_SUPPORT) { |
bios_5_scratch &= ~RADEON_CRT1_CRTC_MASK; |
bios_5_scratch |= (crtc << RADEON_CRT1_CRTC_SHIFT); |
} |
if (radeon_encoder->devices & ATOM_DEVICE_CRT2_SUPPORT) { |
bios_5_scratch &= ~RADEON_CRT2_CRTC_MASK; |
bios_5_scratch |= (crtc << RADEON_CRT2_CRTC_SHIFT); |
} |
if (radeon_encoder->devices & ATOM_DEVICE_LCD1_SUPPORT) { |
bios_5_scratch &= ~RADEON_LCD1_CRTC_MASK; |
bios_5_scratch |= (crtc << RADEON_LCD1_CRTC_SHIFT); |
} |
if (radeon_encoder->devices & ATOM_DEVICE_DFP1_SUPPORT) { |
bios_5_scratch &= ~RADEON_DFP1_CRTC_MASK; |
bios_5_scratch |= (crtc << RADEON_DFP1_CRTC_SHIFT); |
} |
if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT) { |
bios_5_scratch &= ~RADEON_DFP2_CRTC_MASK; |
bios_5_scratch |= (crtc << RADEON_DFP2_CRTC_SHIFT); |
} |
WREG32(RADEON_BIOS_5_SCRATCH, bios_5_scratch); |
} |
void |
radeon_combios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); |
if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) { |
if (on) |
bios_6_scratch |= RADEON_TV_DPMS_ON; |
else |
bios_6_scratch &= ~RADEON_TV_DPMS_ON; |
} |
if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) { |
if (on) |
bios_6_scratch |= RADEON_CRT_DPMS_ON; |
else |
bios_6_scratch &= ~RADEON_CRT_DPMS_ON; |
} |
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { |
if (on) |
bios_6_scratch |= RADEON_LCD_DPMS_ON; |
else |
bios_6_scratch &= ~RADEON_LCD_DPMS_ON; |
} |
if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) { |
if (on) |
bios_6_scratch |= RADEON_DFP_DPMS_ON; |
else |
bios_6_scratch &= ~RADEON_DFP_DPMS_ON; |
} |
WREG32(RADEON_BIOS_6_SCRATCH, bios_6_scratch); |
} |
/drivers/video/drm/radeon/radeon_connectors.c |
---|
0,0 → 1,604 |
/* |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include "drmP.h" |
//#include "drm_edid.h" |
#include "drm_crtc.h" |
#include "drm_crtc_helper.h" |
#include "radeon_drm.h" |
#include "radeon.h" |
extern void |
radeon_combios_connected_scratch_regs(struct drm_connector *connector, |
struct drm_encoder *encoder, |
bool connected); |
extern void |
radeon_atombios_connected_scratch_regs(struct drm_connector *connector, |
struct drm_encoder *encoder, |
bool connected); |
static void |
radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_connector_status status) |
{ |
struct drm_device *dev = connector->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *best_encoder = NULL; |
struct drm_encoder *encoder = NULL; |
struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; |
struct drm_mode_object *obj; |
bool connected; |
int i; |
best_encoder = connector_funcs->best_encoder(connector); |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] == 0) |
break; |
obj = drm_mode_object_find(connector->dev, |
connector->encoder_ids[i], |
DRM_MODE_OBJECT_ENCODER); |
if (!obj) |
continue; |
encoder = obj_to_encoder(obj); |
if ((encoder == best_encoder) && (status == connector_status_connected)) |
connected = true; |
else |
connected = false; |
if (rdev->is_atom_bios) |
radeon_atombios_connected_scratch_regs(connector, encoder, connected); |
else |
radeon_combios_connected_scratch_regs(connector, encoder, connected); |
} |
} |
struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) |
{ |
int enc_id = connector->encoder_ids[0]; |
struct drm_mode_object *obj; |
struct drm_encoder *encoder; |
/* pick the encoder ids */ |
if (enc_id) { |
obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); |
if (!obj) |
return NULL; |
encoder = obj_to_encoder(obj); |
return encoder; |
} |
return NULL; |
} |
static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
struct drm_display_mode *mode = NULL; |
struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; |
if (native_mode->panel_xres != 0 && |
native_mode->panel_yres != 0 && |
native_mode->dotclock != 0) { |
mode = drm_mode_create(dev); |
mode->hdisplay = native_mode->panel_xres; |
mode->vdisplay = native_mode->panel_yres; |
mode->htotal = mode->hdisplay + native_mode->hblank; |
mode->hsync_start = mode->hdisplay + native_mode->hoverplus; |
mode->hsync_end = mode->hsync_start + native_mode->hsync_width; |
mode->vtotal = mode->vdisplay + native_mode->vblank; |
mode->vsync_start = mode->vdisplay + native_mode->voverplus; |
mode->vsync_end = mode->vsync_start + native_mode->vsync_width; |
mode->clock = native_mode->dotclock; |
mode->flags = 0; |
mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; |
drm_mode_set_name(mode); |
DRM_DEBUG("Adding native panel mode %s\n", mode->name); |
} |
return mode; |
} |
int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property, |
uint64_t val) |
{ |
return 0; |
} |
static int radeon_lvds_get_modes(struct drm_connector *connector) |
{ |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct drm_encoder *encoder; |
int ret = 0; |
struct drm_display_mode *mode; |
if (radeon_connector->ddc_bus) { |
ret = radeon_ddc_get_modes(radeon_connector); |
if (ret > 0) { |
return ret; |
} |
} |
encoder = radeon_best_single_encoder(connector); |
if (!encoder) |
return 0; |
/* we have no EDID modes */ |
mode = radeon_fp_native_mode(encoder); |
if (mode) { |
ret = 1; |
drm_mode_probed_add(connector, mode); |
} |
return ret; |
} |
static int radeon_lvds_mode_valid(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
return MODE_OK; |
} |
static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector) |
{ |
enum drm_connector_status ret = connector_status_connected; |
/* check acpi lid status ??? */ |
radeon_connector_update_scratch_regs(connector, ret); |
return ret; |
} |
static void radeon_connector_destroy(struct drm_connector *connector) |
{ |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
if (radeon_connector->ddc_bus) |
radeon_i2c_destroy(radeon_connector->ddc_bus); |
kfree(radeon_connector->con_priv); |
// drm_sysfs_connector_remove(connector); |
drm_connector_cleanup(connector); |
kfree(connector); |
} |
struct drm_connector_helper_funcs radeon_lvds_connector_helper_funcs = { |
.get_modes = radeon_lvds_get_modes, |
.mode_valid = radeon_lvds_mode_valid, |
.best_encoder = radeon_best_single_encoder, |
}; |
struct drm_connector_funcs radeon_lvds_connector_funcs = { |
.dpms = drm_helper_connector_dpms, |
.detect = radeon_lvds_detect, |
.fill_modes = drm_helper_probe_single_connector_modes, |
.destroy = radeon_connector_destroy, |
.set_property = radeon_connector_set_property, |
}; |
static int radeon_vga_get_modes(struct drm_connector *connector) |
{ |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
int ret; |
ret = radeon_ddc_get_modes(radeon_connector); |
return ret; |
} |
static int radeon_vga_mode_valid(struct drm_connector *connector, |
struct drm_display_mode *mode) |
{ |
return MODE_OK; |
} |
static enum drm_connector_status radeon_vga_detect(struct drm_connector *connector) |
{ |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct drm_encoder *encoder; |
struct drm_encoder_helper_funcs *encoder_funcs; |
bool dret; |
enum drm_connector_status ret = connector_status_disconnected; |
radeon_i2c_do_lock(radeon_connector, 1); |
dret = radeon_ddc_probe(radeon_connector); |
radeon_i2c_do_lock(radeon_connector, 0); |
if (dret) |
ret = connector_status_connected; |
else { |
/* if EDID fails to a load detect */ |
encoder = radeon_best_single_encoder(connector); |
if (!encoder) |
ret = connector_status_disconnected; |
else { |
encoder_funcs = encoder->helper_private; |
ret = encoder_funcs->detect(encoder, connector); |
} |
} |
radeon_connector_update_scratch_regs(connector, ret); |
return ret; |
} |
struct drm_connector_helper_funcs radeon_vga_connector_helper_funcs = { |
.get_modes = radeon_vga_get_modes, |
.mode_valid = radeon_vga_mode_valid, |
.best_encoder = radeon_best_single_encoder, |
}; |
struct drm_connector_funcs radeon_vga_connector_funcs = { |
.dpms = drm_helper_connector_dpms, |
.detect = radeon_vga_detect, |
.fill_modes = drm_helper_probe_single_connector_modes, |
.destroy = radeon_connector_destroy, |
.set_property = radeon_connector_set_property, |
}; |
static int radeon_dvi_get_modes(struct drm_connector *connector) |
{ |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
int ret; |
ret = radeon_ddc_get_modes(radeon_connector); |
/* reset scratch regs here since radeon_dvi_detect doesn't check digital bit */ |
radeon_connector_update_scratch_regs(connector, connector_status_connected); |
return ret; |
} |
static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector) |
{ |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct drm_encoder *encoder; |
struct drm_encoder_helper_funcs *encoder_funcs; |
struct drm_mode_object *obj; |
int i; |
enum drm_connector_status ret = connector_status_disconnected; |
bool dret; |
radeon_i2c_do_lock(radeon_connector, 1); |
dret = radeon_ddc_probe(radeon_connector); |
radeon_i2c_do_lock(radeon_connector, 0); |
if (dret) |
ret = connector_status_connected; |
else { |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] == 0) |
break; |
obj = drm_mode_object_find(connector->dev, |
connector->encoder_ids[i], |
DRM_MODE_OBJECT_ENCODER); |
if (!obj) |
continue; |
encoder = obj_to_encoder(obj); |
encoder_funcs = encoder->helper_private; |
if (encoder_funcs->detect) { |
ret = encoder_funcs->detect(encoder, connector); |
if (ret == connector_status_connected) { |
radeon_connector->use_digital = 0; |
break; |
} |
} |
} |
} |
/* updated in get modes as well since we need to know if it's analog or digital */ |
radeon_connector_update_scratch_regs(connector, ret); |
return ret; |
} |
/* okay need to be smart in here about which encoder to pick */ |
struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) |
{ |
int enc_id = connector->encoder_ids[0]; |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
struct drm_mode_object *obj; |
struct drm_encoder *encoder; |
int i; |
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { |
if (connector->encoder_ids[i] == 0) |
break; |
obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER); |
if (!obj) |
continue; |
encoder = obj_to_encoder(obj); |
if (radeon_connector->use_digital) { |
if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) |
return encoder; |
} else { |
if (encoder->encoder_type == DRM_MODE_ENCODER_DAC || |
encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) |
return encoder; |
} |
} |
/* see if we have a default encoder TODO */ |
/* then check use digitial */ |
/* pick the first one */ |
if (enc_id) { |
obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); |
if (!obj) |
return NULL; |
encoder = obj_to_encoder(obj); |
return encoder; |
} |
return NULL; |
} |
struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { |
.get_modes = radeon_dvi_get_modes, |
.mode_valid = radeon_vga_mode_valid, |
.best_encoder = radeon_dvi_encoder, |
}; |
struct drm_connector_funcs radeon_dvi_connector_funcs = { |
.dpms = drm_helper_connector_dpms, |
.detect = radeon_dvi_detect, |
.fill_modes = drm_helper_probe_single_connector_modes, |
.set_property = radeon_connector_set_property, |
.destroy = radeon_connector_destroy, |
}; |
void |
radeon_add_atom_connector(struct drm_device *dev, |
uint32_t connector_id, |
uint32_t supported_device, |
int connector_type, |
struct radeon_i2c_bus_rec *i2c_bus, |
bool linkb, |
uint32_t igp_lane_info) |
{ |
struct drm_connector *connector; |
struct radeon_connector *radeon_connector; |
struct radeon_connector_atom_dig *radeon_dig_connector; |
uint32_t subpixel_order = SubPixelNone; |
/* fixme - tv/cv/din */ |
if ((connector_type == DRM_MODE_CONNECTOR_Unknown) || |
(connector_type == DRM_MODE_CONNECTOR_SVIDEO) || |
(connector_type == DRM_MODE_CONNECTOR_Composite) || |
(connector_type == DRM_MODE_CONNECTOR_9PinDIN)) |
return; |
/* see if we already added it */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
radeon_connector = to_radeon_connector(connector); |
if (radeon_connector->connector_id == connector_id) { |
radeon_connector->devices |= supported_device; |
return; |
} |
} |
radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); |
if (!radeon_connector) |
return; |
connector = &radeon_connector->base; |
radeon_connector->connector_id = connector_id; |
radeon_connector->devices = supported_device; |
switch (connector_type) { |
case DRM_MODE_CONNECTOR_VGA: |
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
break; |
case DRM_MODE_CONNECTOR_DVIA: |
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
break; |
case DRM_MODE_CONNECTOR_DVII: |
case DRM_MODE_CONNECTOR_DVID: |
radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); |
if (!radeon_dig_connector) |
goto failed; |
radeon_dig_connector->linkb = linkb; |
radeon_dig_connector->igp_lane_info = igp_lane_info; |
radeon_connector->con_priv = radeon_dig_connector; |
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
subpixel_order = SubPixelHorizontalRGB; |
break; |
case DRM_MODE_CONNECTOR_HDMIA: |
case DRM_MODE_CONNECTOR_HDMIB: |
radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); |
if (!radeon_dig_connector) |
goto failed; |
radeon_dig_connector->linkb = linkb; |
radeon_dig_connector->igp_lane_info = igp_lane_info; |
radeon_connector->con_priv = radeon_dig_connector; |
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "HDMI"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
subpixel_order = SubPixelHorizontalRGB; |
break; |
case DRM_MODE_CONNECTOR_DisplayPort: |
radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); |
if (!radeon_dig_connector) |
goto failed; |
radeon_dig_connector->linkb = linkb; |
radeon_dig_connector->igp_lane_info = igp_lane_info; |
radeon_connector->con_priv = radeon_dig_connector; |
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
subpixel_order = SubPixelHorizontalRGB; |
break; |
case DRM_MODE_CONNECTOR_SVIDEO: |
case DRM_MODE_CONNECTOR_Composite: |
case DRM_MODE_CONNECTOR_9PinDIN: |
break; |
case DRM_MODE_CONNECTOR_LVDS: |
radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); |
if (!radeon_dig_connector) |
goto failed; |
radeon_dig_connector->linkb = linkb; |
radeon_dig_connector->igp_lane_info = igp_lane_info; |
radeon_connector->con_priv = radeon_dig_connector; |
drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
subpixel_order = SubPixelHorizontalRGB; |
break; |
} |
connector->display_info.subpixel_order = subpixel_order; |
// drm_sysfs_connector_add(connector); |
return; |
failed: |
if (radeon_connector->ddc_bus) |
radeon_i2c_destroy(radeon_connector->ddc_bus); |
drm_connector_cleanup(connector); |
kfree(connector); |
} |
void |
radeon_add_legacy_connector(struct drm_device *dev, |
uint32_t connector_id, |
uint32_t supported_device, |
int connector_type, |
struct radeon_i2c_bus_rec *i2c_bus) |
{ |
struct drm_connector *connector; |
struct radeon_connector *radeon_connector; |
uint32_t subpixel_order = SubPixelNone; |
/* fixme - tv/cv/din */ |
if ((connector_type == DRM_MODE_CONNECTOR_Unknown) || |
(connector_type == DRM_MODE_CONNECTOR_SVIDEO) || |
(connector_type == DRM_MODE_CONNECTOR_Composite) || |
(connector_type == DRM_MODE_CONNECTOR_9PinDIN)) |
return; |
/* see if we already added it */ |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
radeon_connector = to_radeon_connector(connector); |
if (radeon_connector->connector_id == connector_id) { |
radeon_connector->devices |= supported_device; |
return; |
} |
} |
radeon_connector = kzalloc(sizeof(struct radeon_connector), GFP_KERNEL); |
if (!radeon_connector) |
return; |
connector = &radeon_connector->base; |
radeon_connector->connector_id = connector_id; |
radeon_connector->devices = supported_device; |
switch (connector_type) { |
case DRM_MODE_CONNECTOR_VGA: |
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
break; |
case DRM_MODE_CONNECTOR_DVIA: |
drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
break; |
case DRM_MODE_CONNECTOR_DVII: |
case DRM_MODE_CONNECTOR_DVID: |
drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
subpixel_order = SubPixelHorizontalRGB; |
break; |
case DRM_MODE_CONNECTOR_SVIDEO: |
case DRM_MODE_CONNECTOR_Composite: |
case DRM_MODE_CONNECTOR_9PinDIN: |
break; |
case DRM_MODE_CONNECTOR_LVDS: |
drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); |
drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs); |
if (i2c_bus->valid) { |
radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS"); |
if (!radeon_connector->ddc_bus) |
goto failed; |
} |
subpixel_order = SubPixelHorizontalRGB; |
break; |
} |
connector->display_info.subpixel_order = subpixel_order; |
// drm_sysfs_connector_add(connector); |
return; |
failed: |
if (radeon_connector->ddc_bus) |
radeon_i2c_destroy(radeon_connector->ddc_bus); |
drm_connector_cleanup(connector); |
kfree(connector); |
} |
/drivers/video/drm/radeon/radeon_device.c |
---|
26,7 → 26,8 |
* Jerome Glisse |
*/ |
//#include <linux/console.h> |
//#include <drm/drmP.h> |
#include <drmP.h> |
//#include <drm/drm_crtc_helper.h> |
#include "radeon_drm.h" |
#include "radeon_reg.h" |
36,9 → 37,14 |
#include <syscall.h> |
int radeon_modeset = -1; |
int radeon_dynclks = -1; |
int radeon_agpmode = -1; |
int radeon_r4xx_atom = 0; |
int radeon_agpmode = 0; |
int radeon_vram_limit = 0; |
int radeon_gart_size = 512; /* default gart size */ |
int radeon_benchmarking = 0; |
int radeon_connector_table = 0; |
/* |
896,3 → 902,37 |
return pci_resource_len(dev->pdev, resource); |
} |
uint32_t __div64_32(uint64_t *n, uint32_t base) |
{ |
uint64_t rem = *n; |
uint64_t b = base; |
uint64_t res, d = 1; |
uint32_t high = rem >> 32; |
/* Reduce the thing a bit first */ |
res = 0; |
if (high >= base) { |
high /= base; |
res = (uint64_t) high << 32; |
rem -= (uint64_t) (high*base) << 32; |
} |
while ((int64_t)b > 0 && b < rem) { |
b = b+b; |
d = d+d; |
} |
do { |
if (rem >= b) { |
rem -= b; |
res += d; |
} |
b >>= 1; |
d >>= 1; |
} while (d); |
*n = res; |
return rem; |
} |
/drivers/video/drm/radeon/radeon_display.c |
---|
0,0 → 1,697 |
/* |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include "drmP.h" |
#include "radeon_drm.h" |
#include "radeon.h" |
#include "atom.h" |
//#include <asm/div64.h> |
#include "drm_crtc_helper.h" |
#include "drm_edid.h" |
static int radeon_ddc_dump(struct drm_connector *connector); |
static void avivo_crtc_load_lut(struct drm_crtc *crtc) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
int i; |
DRM_DEBUG("%d\n", radeon_crtc->crtc_id); |
WREG32(AVIVO_DC_LUTA_CONTROL + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc->crtc_offset, 0); |
WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc->crtc_offset, 0xffff); |
WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc->crtc_offset, 0xffff); |
WREG32(AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc->crtc_offset, 0xffff); |
WREG32(AVIVO_DC_LUT_RW_SELECT, radeon_crtc->crtc_id); |
WREG32(AVIVO_DC_LUT_RW_MODE, 0); |
WREG32(AVIVO_DC_LUT_WRITE_EN_MASK, 0x0000003f); |
WREG8(AVIVO_DC_LUT_RW_INDEX, 0); |
for (i = 0; i < 256; i++) { |
WREG32(AVIVO_DC_LUT_30_COLOR, |
(radeon_crtc->lut_r[i] << 20) | |
(radeon_crtc->lut_g[i] << 10) | |
(radeon_crtc->lut_b[i] << 0)); |
} |
WREG32(AVIVO_D1GRPH_LUT_SEL + radeon_crtc->crtc_offset, radeon_crtc->crtc_id); |
} |
static void legacy_crtc_load_lut(struct drm_crtc *crtc) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
int i; |
uint32_t dac2_cntl; |
dac2_cntl = RREG32(RADEON_DAC_CNTL2); |
if (radeon_crtc->crtc_id == 0) |
dac2_cntl &= (uint32_t)~RADEON_DAC2_PALETTE_ACC_CTL; |
else |
dac2_cntl |= RADEON_DAC2_PALETTE_ACC_CTL; |
WREG32(RADEON_DAC_CNTL2, dac2_cntl); |
WREG8(RADEON_PALETTE_INDEX, 0); |
for (i = 0; i < 256; i++) { |
WREG32(RADEON_PALETTE_30_DATA, |
(radeon_crtc->lut_r[i] << 20) | |
(radeon_crtc->lut_g[i] << 10) | |
(radeon_crtc->lut_b[i] << 0)); |
} |
} |
void radeon_crtc_load_lut(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
if (!crtc->enabled) |
return; |
if (ASIC_IS_AVIVO(rdev)) |
avivo_crtc_load_lut(crtc); |
else |
legacy_crtc_load_lut(crtc); |
} |
/** Sets the color ramps on behalf of RandR */ |
void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, |
u16 blue, int regno) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
if (regno == 0) |
DRM_DEBUG("gamma set %d\n", radeon_crtc->crtc_id); |
radeon_crtc->lut_r[regno] = red >> 6; |
radeon_crtc->lut_g[regno] = green >> 6; |
radeon_crtc->lut_b[regno] = blue >> 6; |
} |
static void radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, |
u16 *blue, uint32_t size) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
int i, j; |
if (size != 256) { |
return; |
} |
if (crtc->fb == NULL) { |
return; |
} |
if (crtc->fb->depth == 16) { |
for (i = 0; i < 64; i++) { |
if (i <= 31) { |
for (j = 0; j < 8; j++) { |
radeon_crtc->lut_r[i * 8 + j] = red[i] >> 6; |
radeon_crtc->lut_b[i * 8 + j] = blue[i] >> 6; |
} |
} |
for (j = 0; j < 4; j++) |
radeon_crtc->lut_g[i * 4 + j] = green[i] >> 6; |
} |
} else { |
for (i = 0; i < 256; i++) { |
radeon_crtc->lut_r[i] = red[i] >> 6; |
radeon_crtc->lut_g[i] = green[i] >> 6; |
radeon_crtc->lut_b[i] = blue[i] >> 6; |
} |
} |
radeon_crtc_load_lut(crtc); |
} |
static void radeon_crtc_destroy(struct drm_crtc *crtc) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
if (radeon_crtc->mode_set.mode) { |
drm_mode_destroy(crtc->dev, radeon_crtc->mode_set.mode); |
} |
drm_crtc_cleanup(crtc); |
kfree(radeon_crtc); |
} |
static const struct drm_crtc_funcs radeon_crtc_funcs = { |
// .cursor_set = radeon_crtc_cursor_set, |
// .cursor_move = radeon_crtc_cursor_move, |
.gamma_set = radeon_crtc_gamma_set, |
// .set_config = drm_crtc_helper_set_config, |
.destroy = radeon_crtc_destroy, |
}; |
static void radeon_crtc_init(struct drm_device *dev, int index) |
{ |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc; |
int i; |
radeon_crtc = kzalloc(sizeof(struct radeon_crtc) + (RADEONFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); |
if (radeon_crtc == NULL) |
return; |
drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); |
drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); |
radeon_crtc->crtc_id = index; |
radeon_crtc->mode_set.crtc = &radeon_crtc->base; |
radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); |
radeon_crtc->mode_set.num_connectors = 0; |
for (i = 0; i < 256; i++) { |
radeon_crtc->lut_r[i] = i << 2; |
radeon_crtc->lut_g[i] = i << 2; |
radeon_crtc->lut_b[i] = i << 2; |
} |
if (rdev->is_atom_bios && (ASIC_IS_AVIVO(rdev) || radeon_r4xx_atom)) |
radeon_atombios_init_crtc(dev, radeon_crtc); |
else |
radeon_legacy_init_crtc(dev, radeon_crtc); |
} |
static const char *encoder_names[34] = { |
"NONE", |
"INTERNAL_LVDS", |
"INTERNAL_TMDS1", |
"INTERNAL_TMDS2", |
"INTERNAL_DAC1", |
"INTERNAL_DAC2", |
"INTERNAL_SDVOA", |
"INTERNAL_SDVOB", |
"SI170B", |
"CH7303", |
"CH7301", |
"INTERNAL_DVO1", |
"EXTERNAL_SDVOA", |
"EXTERNAL_SDVOB", |
"TITFP513", |
"INTERNAL_LVTM1", |
"VT1623", |
"HDMI_SI1930", |
"HDMI_INTERNAL", |
"INTERNAL_KLDSCP_TMDS1", |
"INTERNAL_KLDSCP_DVO1", |
"INTERNAL_KLDSCP_DAC1", |
"INTERNAL_KLDSCP_DAC2", |
"SI178", |
"MVPU_FPGA", |
"INTERNAL_DDI", |
"VT1625", |
"HDMI_SI1932", |
"DP_AN9801", |
"DP_DP501", |
"INTERNAL_UNIPHY", |
"INTERNAL_KLDSCP_LVTMA", |
"INTERNAL_UNIPHY1", |
"INTERNAL_UNIPHY2", |
}; |
static const char *connector_names[13] = { |
"Unknown", |
"VGA", |
"DVI-I", |
"DVI-D", |
"DVI-A", |
"Composite", |
"S-video", |
"LVDS", |
"Component", |
"DIN", |
"DisplayPort", |
"HDMI-A", |
"HDMI-B", |
}; |
static void radeon_print_display_setup(struct drm_device *dev) |
{ |
struct drm_connector *connector; |
struct radeon_connector *radeon_connector; |
struct drm_encoder *encoder; |
struct radeon_encoder *radeon_encoder; |
uint32_t devices; |
int i = 0; |
DRM_INFO("Radeon Display Connectors\n"); |
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
radeon_connector = to_radeon_connector(connector); |
DRM_INFO("Connector %d:\n", i); |
DRM_INFO(" %s\n", connector_names[connector->connector_type]); |
if (radeon_connector->ddc_bus) |
DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", |
radeon_connector->ddc_bus->rec.mask_clk_reg, |
radeon_connector->ddc_bus->rec.mask_data_reg, |
radeon_connector->ddc_bus->rec.a_clk_reg, |
radeon_connector->ddc_bus->rec.a_data_reg, |
radeon_connector->ddc_bus->rec.put_clk_reg, |
radeon_connector->ddc_bus->rec.put_data_reg, |
radeon_connector->ddc_bus->rec.get_clk_reg, |
radeon_connector->ddc_bus->rec.get_data_reg); |
DRM_INFO(" Encoders:\n"); |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
radeon_encoder = to_radeon_encoder(encoder); |
devices = radeon_encoder->devices & radeon_connector->devices; |
if (devices) { |
if (devices & ATOM_DEVICE_CRT1_SUPPORT) |
DRM_INFO(" CRT1: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_CRT2_SUPPORT) |
DRM_INFO(" CRT2: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_LCD1_SUPPORT) |
DRM_INFO(" LCD1: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_DFP1_SUPPORT) |
DRM_INFO(" DFP1: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_DFP2_SUPPORT) |
DRM_INFO(" DFP2: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_DFP3_SUPPORT) |
DRM_INFO(" DFP3: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_DFP4_SUPPORT) |
DRM_INFO(" DFP4: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_DFP5_SUPPORT) |
DRM_INFO(" DFP5: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_TV1_SUPPORT) |
DRM_INFO(" TV1: %s\n", encoder_names[radeon_encoder->encoder_id]); |
if (devices & ATOM_DEVICE_CV_SUPPORT) |
DRM_INFO(" CV: %s\n", encoder_names[radeon_encoder->encoder_id]); |
} |
} |
i++; |
} |
} |
bool radeon_setup_enc_conn(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
struct drm_connector *drm_connector; |
bool ret = false; |
if (rdev->bios) { |
if (rdev->is_atom_bios) { |
if (rdev->family >= CHIP_R600) |
ret = radeon_get_atom_connector_info_from_object_table(dev); |
else |
ret = radeon_get_atom_connector_info_from_supported_devices_table(dev); |
} else |
ret = radeon_get_legacy_connector_info_from_bios(dev); |
} else { |
if (!ASIC_IS_AVIVO(rdev)) |
ret = radeon_get_legacy_connector_info_from_table(dev); |
} |
if (ret) { |
radeon_print_display_setup(dev); |
list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) |
radeon_ddc_dump(drm_connector); |
} |
return ret; |
} |
int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) |
{ |
struct edid *edid; |
int ret = 0; |
if (!radeon_connector->ddc_bus) |
return -1; |
radeon_i2c_do_lock(radeon_connector, 1); |
edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); |
radeon_i2c_do_lock(radeon_connector, 0); |
if (edid) { |
/* update digital bits here */ |
if (edid->input & DRM_EDID_INPUT_DIGITAL) |
radeon_connector->use_digital = 1; |
else |
radeon_connector->use_digital = 0; |
drm_mode_connector_update_edid_property(&radeon_connector->base, edid); |
ret = drm_add_edid_modes(&radeon_connector->base, edid); |
kfree(edid); |
return ret; |
} |
drm_mode_connector_update_edid_property(&radeon_connector->base, NULL); |
return -1; |
} |
static int radeon_ddc_dump(struct drm_connector *connector) |
{ |
struct edid *edid; |
struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
int ret = 0; |
if (!radeon_connector->ddc_bus) |
return -1; |
radeon_i2c_do_lock(radeon_connector, 1); |
edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter); |
radeon_i2c_do_lock(radeon_connector, 0); |
if (edid) { |
kfree(edid); |
} |
return ret; |
} |
static inline uint32_t radeon_div(uint64_t n, uint32_t d) |
{ |
uint64_t mod; |
n += d / 2; |
mod = do_div(n, d); |
return n; |
} |
void radeon_compute_pll(struct radeon_pll *pll, |
uint64_t freq, |
uint32_t *dot_clock_p, |
uint32_t *fb_div_p, |
uint32_t *frac_fb_div_p, |
uint32_t *ref_div_p, |
uint32_t *post_div_p, |
int flags) |
{ |
uint32_t min_ref_div = pll->min_ref_div; |
uint32_t max_ref_div = pll->max_ref_div; |
uint32_t min_fractional_feed_div = 0; |
uint32_t max_fractional_feed_div = 0; |
uint32_t best_vco = pll->best_vco; |
uint32_t best_post_div = 1; |
uint32_t best_ref_div = 1; |
uint32_t best_feedback_div = 1; |
uint32_t best_frac_feedback_div = 0; |
uint32_t best_freq = -1; |
uint32_t best_error = 0xffffffff; |
uint32_t best_vco_diff = 1; |
uint32_t post_div; |
DRM_DEBUG("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div); |
freq = freq * 1000; |
if (flags & RADEON_PLL_USE_REF_DIV) |
min_ref_div = max_ref_div = pll->reference_div; |
else { |
while (min_ref_div < max_ref_div-1) { |
uint32_t mid = (min_ref_div + max_ref_div) / 2; |
uint32_t pll_in = pll->reference_freq / mid; |
if (pll_in < pll->pll_in_min) |
max_ref_div = mid; |
else if (pll_in > pll->pll_in_max) |
min_ref_div = mid; |
else |
break; |
} |
} |
if (flags & RADEON_PLL_USE_FRAC_FB_DIV) { |
min_fractional_feed_div = pll->min_frac_feedback_div; |
max_fractional_feed_div = pll->max_frac_feedback_div; |
} |
for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) { |
uint32_t ref_div; |
if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) |
continue; |
/* legacy radeons only have a few post_divs */ |
if (flags & RADEON_PLL_LEGACY) { |
if ((post_div == 5) || |
(post_div == 7) || |
(post_div == 9) || |
(post_div == 10) || |
(post_div == 11) || |
(post_div == 13) || |
(post_div == 14) || |
(post_div == 15)) |
continue; |
} |
for (ref_div = min_ref_div; ref_div <= max_ref_div; ++ref_div) { |
uint32_t feedback_div, current_freq = 0, error, vco_diff; |
uint32_t pll_in = pll->reference_freq / ref_div; |
uint32_t min_feed_div = pll->min_feedback_div; |
uint32_t max_feed_div = pll->max_feedback_div + 1; |
if (pll_in < pll->pll_in_min || pll_in > pll->pll_in_max) |
continue; |
while (min_feed_div < max_feed_div) { |
uint32_t vco; |
uint32_t min_frac_feed_div = min_fractional_feed_div; |
uint32_t max_frac_feed_div = max_fractional_feed_div + 1; |
uint32_t frac_feedback_div; |
uint64_t tmp; |
feedback_div = (min_feed_div + max_feed_div) / 2; |
tmp = (uint64_t)pll->reference_freq * feedback_div; |
vco = radeon_div(tmp, ref_div); |
if (vco < pll->pll_out_min) { |
min_feed_div = feedback_div + 1; |
continue; |
} else if (vco > pll->pll_out_max) { |
max_feed_div = feedback_div; |
continue; |
} |
while (min_frac_feed_div < max_frac_feed_div) { |
frac_feedback_div = (min_frac_feed_div + max_frac_feed_div) / 2; |
tmp = (uint64_t)pll->reference_freq * 10000 * feedback_div; |
tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div; |
current_freq = radeon_div(tmp, ref_div * post_div); |
error = abs(current_freq - freq); |
vco_diff = abs(vco - best_vco); |
if ((best_vco == 0 && error < best_error) || |
(best_vco != 0 && |
(error < best_error - 100 || |
(abs(error - best_error) < 100 && vco_diff < best_vco_diff)))) { |
best_post_div = post_div; |
best_ref_div = ref_div; |
best_feedback_div = feedback_div; |
best_frac_feedback_div = frac_feedback_div; |
best_freq = current_freq; |
best_error = error; |
best_vco_diff = vco_diff; |
} else if (current_freq == freq) { |
if (best_freq == -1) { |
best_post_div = post_div; |
best_ref_div = ref_div; |
best_feedback_div = feedback_div; |
best_frac_feedback_div = frac_feedback_div; |
best_freq = current_freq; |
best_error = error; |
best_vco_diff = vco_diff; |
} else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) || |
((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) || |
((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) || |
((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) || |
((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) || |
((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) { |
best_post_div = post_div; |
best_ref_div = ref_div; |
best_feedback_div = feedback_div; |
best_frac_feedback_div = frac_feedback_div; |
best_freq = current_freq; |
best_error = error; |
best_vco_diff = vco_diff; |
} |
} |
if (current_freq < freq) |
min_frac_feed_div = frac_feedback_div + 1; |
else |
max_frac_feed_div = frac_feedback_div; |
} |
if (current_freq < freq) |
min_feed_div = feedback_div + 1; |
else |
max_feed_div = feedback_div; |
} |
} |
} |
*dot_clock_p = best_freq / 10000; |
*fb_div_p = best_feedback_div; |
*frac_fb_div_p = best_frac_feedback_div; |
*ref_div_p = best_ref_div; |
*post_div_p = best_post_div; |
} |
#if 0 |
static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb) |
{ |
struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); |
struct drm_device *dev = fb->dev; |
if (fb->fbdev) |
radeonfb_remove(dev, fb); |
// if (radeon_fb->obj) { |
// radeon_gem_object_unpin(radeon_fb->obj); |
// mutex_lock(&dev->struct_mutex); |
// drm_gem_object_unreference(radeon_fb->obj); |
// mutex_unlock(&dev->struct_mutex); |
// } |
drm_framebuffer_cleanup(fb); |
kfree(radeon_fb); |
} |
static int radeon_user_framebuffer_create_handle(struct drm_framebuffer *fb, |
struct drm_file *file_priv, |
unsigned int *handle) |
{ |
struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb); |
return NULL; |
// return drm_gem_handle_create(file_priv, radeon_fb->obj, handle); |
} |
static const struct drm_framebuffer_funcs radeon_fb_funcs = { |
.destroy = radeon_user_framebuffer_destroy, |
.create_handle = radeon_user_framebuffer_create_handle, |
}; |
struct drm_framebuffer * |
radeon_framebuffer_create(struct drm_device *dev, |
struct drm_mode_fb_cmd *mode_cmd, |
struct drm_gem_object *obj) |
{ |
struct radeon_framebuffer *radeon_fb; |
radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL); |
if (radeon_fb == NULL) { |
return NULL; |
} |
drm_framebuffer_init(dev, &radeon_fb->base, &radeon_fb_funcs); |
drm_helper_mode_fill_fb_struct(&radeon_fb->base, mode_cmd); |
radeon_fb->obj = obj; |
return &radeon_fb->base; |
} |
static struct drm_framebuffer * |
radeon_user_framebuffer_create(struct drm_device *dev, |
struct drm_file *file_priv, |
struct drm_mode_fb_cmd *mode_cmd) |
{ |
struct drm_gem_object *obj; |
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); |
return radeon_framebuffer_create(dev, mode_cmd, obj); |
} |
static const struct drm_mode_config_funcs radeon_mode_funcs = { |
.fb_create = radeon_user_framebuffer_create, |
.fb_changed = radeonfb_probe, |
}; |
#endif |
int radeon_modeset_init(struct radeon_device *rdev) |
{ |
int num_crtc = 2, i; |
int ret; |
drm_mode_config_init(rdev->ddev); |
rdev->mode_info.mode_config_initialized = true; |
// rdev->ddev->mode_config.funcs = (void *)&radeon_mode_funcs; |
if (ASIC_IS_AVIVO(rdev)) { |
rdev->ddev->mode_config.max_width = 8192; |
rdev->ddev->mode_config.max_height = 8192; |
} else { |
rdev->ddev->mode_config.max_width = 4096; |
rdev->ddev->mode_config.max_height = 4096; |
} |
rdev->ddev->mode_config.fb_base = rdev->mc.aper_base; |
/* allocate crtcs - TODO single crtc */ |
for (i = 0; i < num_crtc; i++) { |
radeon_crtc_init(rdev->ddev, i); |
} |
/* okay we should have all the bios connectors */ |
ret = radeon_setup_enc_conn(rdev->ddev); |
if (!ret) { |
return ret; |
} |
drm_helper_initial_config(rdev->ddev); |
return 0; |
} |
void radeon_modeset_fini(struct radeon_device *rdev) |
{ |
if (rdev->mode_info.mode_config_initialized) { |
drm_mode_config_cleanup(rdev->ddev); |
rdev->mode_info.mode_config_initialized = false; |
} |
} |
void radeon_init_disp_bandwidth(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
struct drm_display_mode *modes[2]; |
int pixel_bytes[2]; |
struct drm_crtc *crtc; |
pixel_bytes[0] = pixel_bytes[1] = 0; |
modes[0] = modes[1] = NULL; |
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
if (crtc->enabled && crtc->fb) { |
modes[radeon_crtc->crtc_id] = &crtc->mode; |
pixel_bytes[radeon_crtc->crtc_id] = crtc->fb->bits_per_pixel / 8; |
} |
} |
if (ASIC_IS_AVIVO(rdev)) { |
radeon_init_disp_bw_avivo(dev, |
modes[0], |
pixel_bytes[0], |
modes[1], |
pixel_bytes[1]); |
} else { |
radeon_init_disp_bw_legacy(dev, |
modes[0], |
pixel_bytes[0], |
modes[1], |
pixel_bytes[1]); |
} |
} |
/drivers/video/drm/radeon/radeon_fixed.h |
---|
0,0 → 1,50 |
/* |
* Copyright 2009 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 |
*/ |
#ifndef RADEON_FIXED_H |
#define RADEON_FIXED_H |
typedef union rfixed { |
u32 full; |
} fixed20_12; |
#define rfixed_const(A) (u32)(((A) << 12))/* + ((B + 0.000122)*4096)) */ |
#define rfixed_const_half(A) (u32)(((A) << 12) + 2048) |
#define rfixed_const_666(A) (u32)(((A) << 12) + 2731) |
#define rfixed_const_8(A) (u32)(((A) << 12) + 3277) |
#define rfixed_mul(A, B) ((u64)((u64)(A).full * (B).full + 2048) >> 12) |
#define fixed_init(A) { .full = rfixed_const((A)) } |
#define fixed_init_half(A) { .full = rfixed_const_half((A)) } |
#define rfixed_trunc(A) ((A).full >> 12) |
static inline u32 rfixed_div(fixed20_12 A, fixed20_12 B) |
{ |
u64 tmp = ((u64)A.full << 13); |
do_div(tmp, B.full); |
tmp += 1; |
tmp /= 2; |
return lower_32_bits(tmp); |
} |
#endif |
/drivers/video/drm/radeon/radeon_i2c.c |
---|
0,0 → 1,209 |
/* |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include "drmP.h" |
#include "radeon_drm.h" |
#include "radeon.h" |
/** |
* radeon_ddc_probe |
* |
*/ |
bool radeon_ddc_probe(struct radeon_connector *radeon_connector) |
{ |
u8 out_buf[] = { 0x0, 0x0}; |
u8 buf[2]; |
int ret; |
struct i2c_msg msgs[] = { |
{ |
.addr = 0x50, |
.flags = 0, |
.len = 1, |
.buf = out_buf, |
}, |
{ |
.addr = 0x50, |
.flags = I2C_M_RD, |
.len = 1, |
.buf = buf, |
} |
}; |
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2); |
if (ret == 2) |
return true; |
return false; |
} |
void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state) |
{ |
struct radeon_device *rdev = radeon_connector->base.dev->dev_private; |
uint32_t temp; |
struct radeon_i2c_bus_rec *rec = &radeon_connector->ddc_bus->rec; |
/* RV410 appears to have a bug where the hw i2c in reset |
* holds the i2c port in a bad state - switch hw i2c away before |
* doing DDC - do this for all r200s/r300s/r400s for safety sake |
*/ |
if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) { |
if (rec->a_clk_reg == RADEON_GPIO_MONID) { |
WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST | |
R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1))); |
} else { |
WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST | |
R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3))); |
} |
} |
if (lock_state) { |
temp = RREG32(rec->a_clk_reg); |
temp &= ~(rec->a_clk_mask); |
WREG32(rec->a_clk_reg, temp); |
temp = RREG32(rec->a_data_reg); |
temp &= ~(rec->a_data_mask); |
WREG32(rec->a_data_reg, temp); |
} |
temp = RREG32(rec->mask_clk_reg); |
if (lock_state) |
temp |= rec->mask_clk_mask; |
else |
temp &= ~rec->mask_clk_mask; |
WREG32(rec->mask_clk_reg, temp); |
temp = RREG32(rec->mask_clk_reg); |
temp = RREG32(rec->mask_data_reg); |
if (lock_state) |
temp |= rec->mask_data_mask; |
else |
temp &= ~rec->mask_data_mask; |
WREG32(rec->mask_data_reg, temp); |
temp = RREG32(rec->mask_data_reg); |
} |
static int get_clock(void *i2c_priv) |
{ |
struct radeon_i2c_chan *i2c = i2c_priv; |
struct radeon_device *rdev = i2c->dev->dev_private; |
struct radeon_i2c_bus_rec *rec = &i2c->rec; |
uint32_t val; |
val = RREG32(rec->get_clk_reg); |
val &= rec->get_clk_mask; |
return (val != 0); |
} |
static int get_data(void *i2c_priv) |
{ |
struct radeon_i2c_chan *i2c = i2c_priv; |
struct radeon_device *rdev = i2c->dev->dev_private; |
struct radeon_i2c_bus_rec *rec = &i2c->rec; |
uint32_t val; |
val = RREG32(rec->get_data_reg); |
val &= rec->get_data_mask; |
return (val != 0); |
} |
static void set_clock(void *i2c_priv, int clock) |
{ |
struct radeon_i2c_chan *i2c = i2c_priv; |
struct radeon_device *rdev = i2c->dev->dev_private; |
struct radeon_i2c_bus_rec *rec = &i2c->rec; |
uint32_t val; |
val = RREG32(rec->put_clk_reg) & (uint32_t)~(rec->put_clk_mask); |
val |= clock ? 0 : rec->put_clk_mask; |
WREG32(rec->put_clk_reg, val); |
} |
static void set_data(void *i2c_priv, int data) |
{ |
struct radeon_i2c_chan *i2c = i2c_priv; |
struct radeon_device *rdev = i2c->dev->dev_private; |
struct radeon_i2c_bus_rec *rec = &i2c->rec; |
uint32_t val; |
val = RREG32(rec->put_data_reg) & (uint32_t)~(rec->put_data_mask); |
val |= data ? 0 : rec->put_data_mask; |
WREG32(rec->put_data_reg, val); |
} |
struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, |
struct radeon_i2c_bus_rec *rec, |
const char *name) |
{ |
struct radeon_i2c_chan *i2c; |
int ret; |
i2c = kzalloc(sizeof(struct radeon_i2c_chan), GFP_KERNEL); |
if (i2c == NULL) |
return NULL; |
// i2c->adapter.owner = THIS_MODULE; |
i2c->adapter.algo_data = &i2c->algo; |
i2c->dev = dev; |
i2c->algo.setsda = set_data; |
i2c->algo.setscl = set_clock; |
i2c->algo.getsda = get_data; |
i2c->algo.getscl = get_clock; |
i2c->algo.udelay = 20; |
/* vesa says 2.2 ms is enough, 1 jiffy doesn't seem to always |
* make this, 2 jiffies is a lot more reliable */ |
i2c->algo.timeout = 2; |
i2c->algo.data = i2c; |
i2c->rec = *rec; |
i2c_set_adapdata(&i2c->adapter, i2c); |
ret = i2c_bit_add_bus(&i2c->adapter); |
if (ret) { |
DRM_INFO("Failed to register i2c %s\n", name); |
goto out_free; |
} |
return i2c; |
out_free: |
kfree(i2c); |
return NULL; |
} |
void radeon_i2c_destroy(struct radeon_i2c_chan *i2c) |
{ |
if (!i2c) |
return; |
i2c_del_adapter(&i2c->adapter); |
kfree(i2c); |
} |
struct drm_encoder *radeon_best_encoder(struct drm_connector *connector) |
{ |
return NULL; |
} |
/drivers/video/drm/radeon/radeon_legacy_crtc.c |
---|
0,0 → 1,1276 |
/* |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include <drmP.h> |
#include <drm_crtc_helper.h> |
#include "radeon_drm.h" |
#include "radeon_fixed.h" |
#include "radeon.h" |
void radeon_restore_common_regs(struct drm_device *dev) |
{ |
/* don't need this yet */ |
} |
static void radeon_pll_wait_for_read_update_complete(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
int i = 0; |
/* FIXME: Certain revisions of R300 can't recover here. Not sure of |
the cause yet, but this workaround will mask the problem for now. |
Other chips usually will pass at the very first test, so the |
workaround shouldn't have any effect on them. */ |
for (i = 0; |
(i < 10000 && |
RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); |
i++); |
} |
static void radeon_pll_write_update(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
while (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_ATOMIC_UPDATE_R); |
WREG32_PLL_P(RADEON_PPLL_REF_DIV, |
RADEON_PPLL_ATOMIC_UPDATE_W, |
~(RADEON_PPLL_ATOMIC_UPDATE_W)); |
} |
static void radeon_pll2_wait_for_read_update_complete(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
int i = 0; |
/* FIXME: Certain revisions of R300 can't recover here. Not sure of |
the cause yet, but this workaround will mask the problem for now. |
Other chips usually will pass at the very first test, so the |
workaround shouldn't have any effect on them. */ |
for (i = 0; |
(i < 10000 && |
RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); |
i++); |
} |
static void radeon_pll2_write_update(struct drm_device *dev) |
{ |
struct radeon_device *rdev = dev->dev_private; |
while (RREG32_PLL(RADEON_P2PLL_REF_DIV) & RADEON_P2PLL_ATOMIC_UPDATE_R); |
WREG32_PLL_P(RADEON_P2PLL_REF_DIV, |
RADEON_P2PLL_ATOMIC_UPDATE_W, |
~(RADEON_P2PLL_ATOMIC_UPDATE_W)); |
} |
static uint8_t radeon_compute_pll_gain(uint16_t ref_freq, uint16_t ref_div, |
uint16_t fb_div) |
{ |
unsigned int vcoFreq; |
if (!ref_div) |
return 1; |
vcoFreq = ((unsigned)ref_freq & fb_div) / ref_div; |
/* |
* This is horribly crude: the VCO frequency range is divided into |
* 3 parts, each part having a fixed PLL gain value. |
*/ |
if (vcoFreq >= 30000) |
/* |
* [300..max] MHz : 7 |
*/ |
return 7; |
else if (vcoFreq >= 18000) |
/* |
* [180..300) MHz : 4 |
*/ |
return 4; |
else |
/* |
* [0..180) MHz : 1 |
*/ |
return 1; |
} |
void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) |
{ |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t mask; |
if (radeon_crtc->crtc_id) |
mask = (RADEON_CRTC2_EN | |
RADEON_CRTC2_DISP_DIS | |
RADEON_CRTC2_VSYNC_DIS | |
RADEON_CRTC2_HSYNC_DIS | |
RADEON_CRTC2_DISP_REQ_EN_B); |
else |
mask = (RADEON_CRTC_DISPLAY_DIS | |
RADEON_CRTC_VSYNC_DIS | |
RADEON_CRTC_HSYNC_DIS); |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
if (radeon_crtc->crtc_id) |
WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~mask); |
else { |
WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN | |
RADEON_CRTC_DISP_REQ_EN_B)); |
WREG32_P(RADEON_CRTC_EXT_CNTL, 0, ~mask); |
} |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
if (radeon_crtc->crtc_id) |
WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~mask); |
else { |
WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN | |
RADEON_CRTC_DISP_REQ_EN_B)); |
WREG32_P(RADEON_CRTC_EXT_CNTL, mask, ~mask); |
} |
break; |
} |
if (mode != DRM_MODE_DPMS_OFF) { |
radeon_crtc_load_lut(crtc); |
} |
} |
/* properly set crtc bpp when using atombios */ |
void radeon_legacy_atom_set_surface(struct drm_crtc *crtc) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
int format; |
uint32_t crtc_gen_cntl; |
uint32_t disp_merge_cntl; |
uint32_t crtc_pitch; |
switch (crtc->fb->bits_per_pixel) { |
case 15: /* 555 */ |
format = 3; |
break; |
case 16: /* 565 */ |
format = 4; |
break; |
case 24: /* RGB */ |
format = 5; |
break; |
case 32: /* xRGB */ |
format = 6; |
break; |
default: |
return; |
} |
crtc_pitch = ((((crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8)) * crtc->fb->bits_per_pixel) + |
((crtc->fb->bits_per_pixel * 8) - 1)) / |
(crtc->fb->bits_per_pixel * 8)); |
crtc_pitch |= crtc_pitch << 16; |
WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch); |
switch (radeon_crtc->crtc_id) { |
case 0: |
disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL); |
disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; |
WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); |
crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0xfffff0ff; |
crtc_gen_cntl |= (format << 8); |
crtc_gen_cntl |= RADEON_CRTC_EXT_DISP_EN; |
WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); |
break; |
case 1: |
disp_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL); |
disp_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; |
WREG32(RADEON_DISP2_MERGE_CNTL, disp_merge_cntl); |
crtc_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0xfffff0ff; |
crtc_gen_cntl |= (format << 8); |
WREG32(RADEON_CRTC2_GEN_CNTL, crtc_gen_cntl); |
WREG32(RADEON_FP_H2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_H_SYNC_STRT_WID)); |
WREG32(RADEON_FP_V2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_V_SYNC_STRT_WID)); |
break; |
} |
} |
int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct radeon_framebuffer *radeon_fb; |
struct drm_gem_object *obj; |
uint64_t base; |
uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0; |
uint32_t crtc_pitch, pitch_pixels; |
DRM_DEBUG("\n"); |
radeon_fb = to_radeon_framebuffer(crtc->fb); |
obj = radeon_fb->obj; |
// if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &base)) { |
// return -EINVAL; |
// } |
crtc_offset = (u32)base; |
crtc_offset_cntl = 0; |
pitch_pixels = crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8); |
crtc_pitch = (((pitch_pixels * crtc->fb->bits_per_pixel) + |
((crtc->fb->bits_per_pixel * 8) - 1)) / |
(crtc->fb->bits_per_pixel * 8)); |
crtc_pitch |= crtc_pitch << 16; |
/* TODO tiling */ |
if (0) { |
if (ASIC_IS_R300(rdev)) |
crtc_offset_cntl |= (R300_CRTC_X_Y_MODE_EN | |
R300_CRTC_MICRO_TILE_BUFFER_DIS | |
R300_CRTC_MACRO_TILE_EN); |
else |
crtc_offset_cntl |= RADEON_CRTC_TILE_EN; |
} else { |
if (ASIC_IS_R300(rdev)) |
crtc_offset_cntl &= ~(R300_CRTC_X_Y_MODE_EN | |
R300_CRTC_MICRO_TILE_BUFFER_DIS | |
R300_CRTC_MACRO_TILE_EN); |
else |
crtc_offset_cntl &= ~RADEON_CRTC_TILE_EN; |
} |
/* TODO more tiling */ |
if (0) { |
if (ASIC_IS_R300(rdev)) { |
crtc_tile_x0_y0 = x | (y << 16); |
base &= ~0x7ff; |
} else { |
int byteshift = crtc->fb->bits_per_pixel >> 4; |
int tile_addr = (((y >> 3) * crtc->fb->width + x) >> (8 - byteshift)) << 11; |
base += tile_addr + ((x << byteshift) % 256) + ((y % 8) << 8); |
crtc_offset_cntl |= (y % 16); |
} |
} else { |
int offset = y * pitch_pixels + x; |
switch (crtc->fb->bits_per_pixel) { |
case 15: |
case 16: |
offset *= 2; |
break; |
case 24: |
offset *= 3; |
break; |
case 32: |
offset *= 4; |
break; |
default: |
return false; |
} |
base += offset; |
} |
base &= ~7; |
/* update sarea TODO */ |
crtc_offset = (u32)base; |
WREG32(RADEON_DISPLAY_BASE_ADDR + radeon_crtc->crtc_offset, rdev->mc.vram_location); |
if (ASIC_IS_R300(rdev)) { |
if (radeon_crtc->crtc_id) |
WREG32(R300_CRTC2_TILE_X0_Y0, crtc_tile_x0_y0); |
else |
WREG32(R300_CRTC_TILE_X0_Y0, crtc_tile_x0_y0); |
} |
WREG32(RADEON_CRTC_OFFSET_CNTL + radeon_crtc->crtc_offset, crtc_offset_cntl); |
WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset); |
WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch); |
if (old_fb && old_fb != crtc->fb) { |
radeon_fb = to_radeon_framebuffer(old_fb); |
// radeon_gem_object_unpin(radeon_fb->obj); |
} |
return 0; |
} |
static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mode *mode) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
int format; |
int hsync_start; |
int hsync_wid; |
int vsync_wid; |
uint32_t crtc_h_total_disp; |
uint32_t crtc_h_sync_strt_wid; |
uint32_t crtc_v_total_disp; |
uint32_t crtc_v_sync_strt_wid; |
DRM_DEBUG("\n"); |
switch (crtc->fb->bits_per_pixel) { |
case 15: /* 555 */ |
format = 3; |
break; |
case 16: /* 565 */ |
format = 4; |
break; |
case 24: /* RGB */ |
format = 5; |
break; |
case 32: /* xRGB */ |
format = 6; |
break; |
default: |
return false; |
} |
crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) |
| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); |
hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; |
if (!hsync_wid) |
hsync_wid = 1; |
hsync_start = mode->crtc_hsync_start - 8; |
crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
| ((hsync_wid & 0x3f) << 16) |
| ((mode->flags & DRM_MODE_FLAG_NHSYNC) |
? RADEON_CRTC_H_SYNC_POL |
: 0)); |
/* This works for double scan mode. */ |
crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) |
| ((mode->crtc_vdisplay - 1) << 16)); |
vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; |
if (!vsync_wid) |
vsync_wid = 1; |
crtc_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) |
| ((vsync_wid & 0x1f) << 16) |
| ((mode->flags & DRM_MODE_FLAG_NVSYNC) |
? RADEON_CRTC_V_SYNC_POL |
: 0)); |
/* TODO -> Dell Server */ |
if (0) { |
uint32_t disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG); |
uint32_t tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); |
uint32_t dac2_cntl = RREG32(RADEON_DAC_CNTL2); |
uint32_t crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); |
dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL; |
dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL; |
/* For CRT on DAC2, don't turn it on if BIOS didn't |
enable it, even it's detected. |
*/ |
disp_hw_debug |= RADEON_CRT2_DISP1_SEL; |
tv_dac_cntl &= ~((1<<2) | (3<<8) | (7<<24) | (0xff<<16)); |
tv_dac_cntl |= (0x03 | (2<<8) | (0x58<<16)); |
WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); |
WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug); |
WREG32(RADEON_DAC_CNTL2, dac2_cntl); |
WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); |
} |
if (radeon_crtc->crtc_id) { |
uint32_t crtc2_gen_cntl; |
uint32_t disp2_merge_cntl; |
/* check to see if TV DAC is enabled for another crtc and keep it enabled */ |
if (RREG32(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_CRT2_ON) |
crtc2_gen_cntl = RADEON_CRTC2_CRT2_ON; |
else |
crtc2_gen_cntl = 0; |
crtc2_gen_cntl |= ((format << 8) |
| RADEON_CRTC2_VSYNC_DIS |
| RADEON_CRTC2_HSYNC_DIS |
| RADEON_CRTC2_DISP_DIS |
| RADEON_CRTC2_DISP_REQ_EN_B |
| ((mode->flags & DRM_MODE_FLAG_DBLSCAN) |
? RADEON_CRTC2_DBL_SCAN_EN |
: 0) |
| ((mode->flags & DRM_MODE_FLAG_CSYNC) |
? RADEON_CRTC2_CSYNC_EN |
: 0) |
| ((mode->flags & DRM_MODE_FLAG_INTERLACE) |
? RADEON_CRTC2_INTERLACE_EN |
: 0)); |
disp2_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL); |
disp2_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN; |
WREG32(RADEON_DISP2_MERGE_CNTL, disp2_merge_cntl); |
WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); |
} else { |
uint32_t crtc_gen_cntl; |
uint32_t crtc_ext_cntl; |
uint32_t disp_merge_cntl; |
crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN |
| (format << 8) |
| RADEON_CRTC_DISP_REQ_EN_B |
| ((mode->flags & DRM_MODE_FLAG_DBLSCAN) |
? RADEON_CRTC_DBL_SCAN_EN |
: 0) |
| ((mode->flags & DRM_MODE_FLAG_CSYNC) |
? RADEON_CRTC_CSYNC_EN |
: 0) |
| ((mode->flags & DRM_MODE_FLAG_INTERLACE) |
? RADEON_CRTC_INTERLACE_EN |
: 0)); |
crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); |
crtc_ext_cntl |= (RADEON_XCRT_CNT_EN | |
RADEON_CRTC_VSYNC_DIS | |
RADEON_CRTC_HSYNC_DIS | |
RADEON_CRTC_DISPLAY_DIS); |
disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL); |
disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN; |
WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl); |
WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl); |
WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); |
} |
WREG32(RADEON_CRTC_H_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_h_total_disp); |
WREG32(RADEON_CRTC_H_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_h_sync_strt_wid); |
WREG32(RADEON_CRTC_V_TOTAL_DISP + radeon_crtc->crtc_offset, crtc_v_total_disp); |
WREG32(RADEON_CRTC_V_SYNC_STRT_WID + radeon_crtc->crtc_offset, crtc_v_sync_strt_wid); |
return true; |
} |
static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) |
{ |
struct drm_device *dev = crtc->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
struct drm_encoder *encoder; |
uint32_t feedback_div = 0; |
uint32_t frac_fb_div = 0; |
uint32_t reference_div = 0; |
uint32_t post_divider = 0; |
uint32_t freq = 0; |
uint8_t pll_gain; |
int pll_flags = RADEON_PLL_LEGACY; |
bool use_bios_divs = false; |
/* PLL registers */ |
uint32_t pll_ref_div = 0; |
uint32_t pll_fb_post_div = 0; |
uint32_t htotal_cntl = 0; |
struct radeon_pll *pll; |
struct { |
int divider; |
int bitvalue; |
} *post_div, post_divs[] = { |
/* From RAGE 128 VR/RAGE 128 GL Register |
* Reference Manual (Technical Reference |
* Manual P/N RRG-G04100-C Rev. 0.04), page |
* 3-17 (PLL_DIV_[3:0]). |
*/ |
{ 1, 0 }, /* VCLK_SRC */ |
{ 2, 1 }, /* VCLK_SRC/2 */ |
{ 4, 2 }, /* VCLK_SRC/4 */ |
{ 8, 3 }, /* VCLK_SRC/8 */ |
{ 3, 4 }, /* VCLK_SRC/3 */ |
{ 16, 5 }, /* VCLK_SRC/16 */ |
{ 6, 6 }, /* VCLK_SRC/6 */ |
{ 12, 7 }, /* VCLK_SRC/12 */ |
{ 0, 0 } |
}; |
if (radeon_crtc->crtc_id) |
pll = &rdev->clock.p2pll; |
else |
pll = &rdev->clock.p1pll; |
if (mode->clock > 200000) /* range limits??? */ |
pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; |
else |
pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV; |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
if (encoder->crtc == crtc) { |
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) |
pll_flags |= RADEON_PLL_NO_ODD_POST_DIV; |
if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; |
if (lvds) { |
if (lvds->use_bios_dividers) { |
pll_ref_div = lvds->panel_ref_divider; |
pll_fb_post_div = (lvds->panel_fb_divider | |
(lvds->panel_post_divider << 16)); |
htotal_cntl = 0; |
use_bios_divs = true; |
} |
} |
pll_flags |= RADEON_PLL_USE_REF_DIV; |
} |
} |
} |
DRM_DEBUG("\n"); |
if (!use_bios_divs) { |
radeon_compute_pll(pll, mode->clock, |
&freq, &feedback_div, &frac_fb_div, |
&reference_div, &post_divider, |
pll_flags); |
for (post_div = &post_divs[0]; post_div->divider; ++post_div) { |
if (post_div->divider == post_divider) |
break; |
} |
if (!post_div->divider) |
post_div = &post_divs[0]; |
DRM_DEBUG("dc=%u, fd=%d, rd=%d, pd=%d\n", |
(unsigned)freq, |
feedback_div, |
reference_div, |
post_divider); |
pll_ref_div = reference_div; |
#if defined(__powerpc__) && (0) /* TODO */ |
/* apparently programming this otherwise causes a hang??? */ |
if (info->MacModel == RADEON_MAC_IBOOK) |
pll_fb_post_div = 0x000600ad; |
else |
#endif |
pll_fb_post_div = (feedback_div | (post_div->bitvalue << 16)); |
htotal_cntl = mode->htotal & 0x7; |
} |
pll_gain = radeon_compute_pll_gain(pll->reference_freq, |
pll_ref_div & 0x3ff, |
pll_fb_post_div & 0x7ff); |
if (radeon_crtc->crtc_id) { |
uint32_t pixclks_cntl = ((RREG32_PLL(RADEON_PIXCLKS_CNTL) & |
~(RADEON_PIX2CLK_SRC_SEL_MASK)) | |
RADEON_PIX2CLK_SRC_SEL_P2PLLCLK); |
WREG32_PLL_P(RADEON_PIXCLKS_CNTL, |
RADEON_PIX2CLK_SRC_SEL_CPUCLK, |
~(RADEON_PIX2CLK_SRC_SEL_MASK)); |
WREG32_PLL_P(RADEON_P2PLL_CNTL, |
RADEON_P2PLL_RESET |
| RADEON_P2PLL_ATOMIC_UPDATE_EN |
| ((uint32_t)pll_gain << RADEON_P2PLL_PVG_SHIFT), |
~(RADEON_P2PLL_RESET |
| RADEON_P2PLL_ATOMIC_UPDATE_EN |
| RADEON_P2PLL_PVG_MASK)); |
WREG32_PLL_P(RADEON_P2PLL_REF_DIV, |
pll_ref_div, |
~RADEON_P2PLL_REF_DIV_MASK); |
WREG32_PLL_P(RADEON_P2PLL_DIV_0, |
pll_fb_post_div, |
~RADEON_P2PLL_FB0_DIV_MASK); |
WREG32_PLL_P(RADEON_P2PLL_DIV_0, |
pll_fb_post_div, |
~RADEON_P2PLL_POST0_DIV_MASK); |
radeon_pll2_write_update(dev); |
radeon_pll2_wait_for_read_update_complete(dev); |
WREG32_PLL(RADEON_HTOTAL2_CNTL, htotal_cntl); |
WREG32_PLL_P(RADEON_P2PLL_CNTL, |
0, |
~(RADEON_P2PLL_RESET |
| RADEON_P2PLL_SLEEP |
| RADEON_P2PLL_ATOMIC_UPDATE_EN)); |
DRM_DEBUG("Wrote2: 0x%08x 0x%08x 0x%08x (0x%08x)\n", |
(unsigned)pll_ref_div, |
(unsigned)pll_fb_post_div, |
(unsigned)htotal_cntl, |
RREG32_PLL(RADEON_P2PLL_CNTL)); |
DRM_DEBUG("Wrote2: rd=%u, fd=%u, pd=%u\n", |
(unsigned)pll_ref_div & RADEON_P2PLL_REF_DIV_MASK, |
(unsigned)pll_fb_post_div & RADEON_P2PLL_FB0_DIV_MASK, |
(unsigned)((pll_fb_post_div & |
RADEON_P2PLL_POST0_DIV_MASK) >> 16)); |
mdelay(50); /* Let the clock to lock */ |
WREG32_PLL_P(RADEON_PIXCLKS_CNTL, |
RADEON_PIX2CLK_SRC_SEL_P2PLLCLK, |
~(RADEON_PIX2CLK_SRC_SEL_MASK)); |
WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); |
} else { |
if (rdev->flags & RADEON_IS_MOBILITY) { |
/* A temporal workaround for the occational blanking on certain laptop panels. |
This appears to related to the PLL divider registers (fail to lock?). |
It occurs even when all dividers are the same with their old settings. |
In this case we really don't need to fiddle with PLL registers. |
By doing this we can avoid the blanking problem with some panels. |
*/ |
if ((pll_ref_div == (RREG32_PLL(RADEON_PPLL_REF_DIV) & RADEON_PPLL_REF_DIV_MASK)) && |
(pll_fb_post_div == (RREG32_PLL(RADEON_PPLL_DIV_3) & |
(RADEON_PPLL_POST3_DIV_MASK | RADEON_PPLL_FB3_DIV_MASK)))) { |
WREG32_P(RADEON_CLOCK_CNTL_INDEX, |
RADEON_PLL_DIV_SEL, |
~(RADEON_PLL_DIV_SEL)); |
r100_pll_errata_after_index(rdev); |
return; |
} |
} |
WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, |
RADEON_VCLK_SRC_SEL_CPUCLK, |
~(RADEON_VCLK_SRC_SEL_MASK)); |
WREG32_PLL_P(RADEON_PPLL_CNTL, |
RADEON_PPLL_RESET |
| RADEON_PPLL_ATOMIC_UPDATE_EN |
| RADEON_PPLL_VGA_ATOMIC_UPDATE_EN |
| ((uint32_t)pll_gain << RADEON_PPLL_PVG_SHIFT), |
~(RADEON_PPLL_RESET |
| RADEON_PPLL_ATOMIC_UPDATE_EN |
| RADEON_PPLL_VGA_ATOMIC_UPDATE_EN |
| RADEON_PPLL_PVG_MASK)); |
WREG32_P(RADEON_CLOCK_CNTL_INDEX, |
RADEON_PLL_DIV_SEL, |
~(RADEON_PLL_DIV_SEL)); |
r100_pll_errata_after_index(rdev); |
if (ASIC_IS_R300(rdev) || |
(rdev->family == CHIP_RS300) || |
(rdev->family == CHIP_RS400) || |
(rdev->family == CHIP_RS480)) { |
if (pll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) { |
/* When restoring console mode, use saved PPLL_REF_DIV |
* setting. |
*/ |
WREG32_PLL_P(RADEON_PPLL_REF_DIV, |
pll_ref_div, |
0); |
} else { |
/* R300 uses ref_div_acc field as real ref divider */ |
WREG32_PLL_P(RADEON_PPLL_REF_DIV, |
(pll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), |
~R300_PPLL_REF_DIV_ACC_MASK); |
} |
} else |
WREG32_PLL_P(RADEON_PPLL_REF_DIV, |
pll_ref_div, |
~RADEON_PPLL_REF_DIV_MASK); |
WREG32_PLL_P(RADEON_PPLL_DIV_3, |
pll_fb_post_div, |
~RADEON_PPLL_FB3_DIV_MASK); |
WREG32_PLL_P(RADEON_PPLL_DIV_3, |
pll_fb_post_div, |
~RADEON_PPLL_POST3_DIV_MASK); |
radeon_pll_write_update(dev); |
radeon_pll_wait_for_read_update_complete(dev); |
WREG32_PLL(RADEON_HTOTAL_CNTL, htotal_cntl); |
WREG32_PLL_P(RADEON_PPLL_CNTL, |
0, |
~(RADEON_PPLL_RESET |
| RADEON_PPLL_SLEEP |
| RADEON_PPLL_ATOMIC_UPDATE_EN |
| RADEON_PPLL_VGA_ATOMIC_UPDATE_EN)); |
DRM_DEBUG("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n", |
pll_ref_div, |
pll_fb_post_div, |
(unsigned)htotal_cntl, |
RREG32_PLL(RADEON_PPLL_CNTL)); |
DRM_DEBUG("Wrote: rd=%d, fd=%d, pd=%d\n", |
pll_ref_div & RADEON_PPLL_REF_DIV_MASK, |
pll_fb_post_div & RADEON_PPLL_FB3_DIV_MASK, |
(pll_fb_post_div & RADEON_PPLL_POST3_DIV_MASK) >> 16); |
mdelay(50); /* Let the clock to lock */ |
WREG32_PLL_P(RADEON_VCLK_ECP_CNTL, |
RADEON_VCLK_SRC_SEL_PPLLCLK, |
~(RADEON_VCLK_SRC_SEL_MASK)); |
} |
} |
static bool radeon_crtc_mode_fixup(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
return true; |
} |
static int radeon_crtc_mode_set(struct drm_crtc *crtc, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode, |
int x, int y, struct drm_framebuffer *old_fb) |
{ |
DRM_DEBUG("\n"); |
/* TODO TV */ |
radeon_crtc_set_base(crtc, x, y, old_fb); |
radeon_set_crtc_timing(crtc, adjusted_mode); |
radeon_set_pll(crtc, adjusted_mode); |
radeon_init_disp_bandwidth(crtc->dev); |
return 0; |
} |
static void radeon_crtc_prepare(struct drm_crtc *crtc) |
{ |
radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
} |
static void radeon_crtc_commit(struct drm_crtc *crtc) |
{ |
radeon_crtc_dpms(crtc, DRM_MODE_DPMS_ON); |
} |
static const struct drm_crtc_helper_funcs legacy_helper_funcs = { |
.dpms = radeon_crtc_dpms, |
.mode_fixup = radeon_crtc_mode_fixup, |
.mode_set = radeon_crtc_mode_set, |
.mode_set_base = radeon_crtc_set_base, |
.prepare = radeon_crtc_prepare, |
.commit = radeon_crtc_commit, |
}; |
void radeon_legacy_init_crtc(struct drm_device *dev, |
struct radeon_crtc *radeon_crtc) |
{ |
if (radeon_crtc->crtc_id == 1) |
radeon_crtc->crtc_offset = RADEON_CRTC2_H_TOTAL_DISP - RADEON_CRTC_H_TOTAL_DISP; |
drm_crtc_helper_add(&radeon_crtc->base, &legacy_helper_funcs); |
} |
void radeon_init_disp_bw_legacy(struct drm_device *dev, |
struct drm_display_mode *mode1, |
uint32_t pixel_bytes1, |
struct drm_display_mode *mode2, |
uint32_t pixel_bytes2) |
{ |
struct radeon_device *rdev = dev->dev_private; |
fixed20_12 trcd_ff, trp_ff, tras_ff, trbs_ff, tcas_ff; |
fixed20_12 sclk_ff, mclk_ff, sclk_eff_ff, sclk_delay_ff; |
fixed20_12 peak_disp_bw, mem_bw, pix_clk, pix_clk2, temp_ff, crit_point_ff; |
uint32_t temp, data, mem_trcd, mem_trp, mem_tras; |
fixed20_12 memtcas_ff[8] = { |
fixed_init(1), |
fixed_init(2), |
fixed_init(3), |
fixed_init(0), |
fixed_init_half(1), |
fixed_init_half(2), |
fixed_init(0), |
}; |
fixed20_12 memtcas_rs480_ff[8] = { |
fixed_init(0), |
fixed_init(1), |
fixed_init(2), |
fixed_init(3), |
fixed_init(0), |
fixed_init_half(1), |
fixed_init_half(2), |
fixed_init_half(3), |
}; |
fixed20_12 memtcas2_ff[8] = { |
fixed_init(0), |
fixed_init(1), |
fixed_init(2), |
fixed_init(3), |
fixed_init(4), |
fixed_init(5), |
fixed_init(6), |
fixed_init(7), |
}; |
fixed20_12 memtrbs[8] = { |
fixed_init(1), |
fixed_init_half(1), |
fixed_init(2), |
fixed_init_half(2), |
fixed_init(3), |
fixed_init_half(3), |
fixed_init(4), |
fixed_init_half(4) |
}; |
fixed20_12 memtrbs_r4xx[8] = { |
fixed_init(4), |
fixed_init(5), |
fixed_init(6), |
fixed_init(7), |
fixed_init(8), |
fixed_init(9), |
fixed_init(10), |
fixed_init(11) |
}; |
fixed20_12 min_mem_eff; |
fixed20_12 mc_latency_sclk, mc_latency_mclk, k1; |
fixed20_12 cur_latency_mclk, cur_latency_sclk; |
fixed20_12 disp_latency, disp_latency_overhead, disp_drain_rate, |
disp_drain_rate2, read_return_rate; |
fixed20_12 time_disp1_drop_priority; |
int c; |
int cur_size = 16; /* in octawords */ |
int critical_point = 0, critical_point2; |
/* uint32_t read_return_rate, time_disp1_drop_priority; */ |
int stop_req, max_stop_req; |
min_mem_eff.full = rfixed_const_8(0); |
/* get modes */ |
if ((rdev->disp_priority == 2) && ASIC_IS_R300(rdev)) { |
uint32_t mc_init_misc_lat_timer = RREG32(R300_MC_INIT_MISC_LAT_TIMER); |
mc_init_misc_lat_timer &= ~(R300_MC_DISP1R_INIT_LAT_MASK << R300_MC_DISP1R_INIT_LAT_SHIFT); |
mc_init_misc_lat_timer &= ~(R300_MC_DISP0R_INIT_LAT_MASK << R300_MC_DISP0R_INIT_LAT_SHIFT); |
/* check crtc enables */ |
if (mode2) |
mc_init_misc_lat_timer |= (1 << R300_MC_DISP1R_INIT_LAT_SHIFT); |
if (mode1) |
mc_init_misc_lat_timer |= (1 << R300_MC_DISP0R_INIT_LAT_SHIFT); |
WREG32(R300_MC_INIT_MISC_LAT_TIMER, mc_init_misc_lat_timer); |
} |
/* |
* determine is there is enough bw for current mode |
*/ |
mclk_ff.full = rfixed_const(rdev->clock.default_mclk); |
temp_ff.full = rfixed_const(100); |
mclk_ff.full = rfixed_div(mclk_ff, temp_ff); |
sclk_ff.full = rfixed_const(rdev->clock.default_sclk); |
sclk_ff.full = rfixed_div(sclk_ff, temp_ff); |
temp = (rdev->mc.vram_width / 8) * (rdev->mc.vram_is_ddr ? 2 : 1); |
temp_ff.full = rfixed_const(temp); |
mem_bw.full = rfixed_mul(mclk_ff, temp_ff); |
pix_clk.full = 0; |
pix_clk2.full = 0; |
peak_disp_bw.full = 0; |
if (mode1) { |
temp_ff.full = rfixed_const(1000); |
pix_clk.full = rfixed_const(mode1->clock); /* convert to fixed point */ |
pix_clk.full = rfixed_div(pix_clk, temp_ff); |
temp_ff.full = rfixed_const(pixel_bytes1); |
peak_disp_bw.full += rfixed_mul(pix_clk, temp_ff); |
} |
if (mode2) { |
temp_ff.full = rfixed_const(1000); |
pix_clk2.full = rfixed_const(mode2->clock); /* convert to fixed point */ |
pix_clk2.full = rfixed_div(pix_clk2, temp_ff); |
temp_ff.full = rfixed_const(pixel_bytes2); |
peak_disp_bw.full += rfixed_mul(pix_clk2, temp_ff); |
} |
mem_bw.full = rfixed_mul(mem_bw, min_mem_eff); |
if (peak_disp_bw.full >= mem_bw.full) { |
DRM_ERROR("You may not have enough display bandwidth for current mode\n" |
"If you have flickering problem, try to lower resolution, refresh rate, or color depth\n"); |
} |
/* Get values from the EXT_MEM_CNTL register...converting its contents. */ |
temp = RREG32(RADEON_MEM_TIMING_CNTL); |
if ((rdev->family == CHIP_RV100) || (rdev->flags & RADEON_IS_IGP)) { /* RV100, M6, IGPs */ |
mem_trcd = ((temp >> 2) & 0x3) + 1; |
mem_trp = ((temp & 0x3)) + 1; |
mem_tras = ((temp & 0x70) >> 4) + 1; |
} else if (rdev->family == CHIP_R300 || |
rdev->family == CHIP_R350) { /* r300, r350 */ |
mem_trcd = (temp & 0x7) + 1; |
mem_trp = ((temp >> 8) & 0x7) + 1; |
mem_tras = ((temp >> 11) & 0xf) + 4; |
} else if (rdev->family == CHIP_RV350 || |
rdev->family <= CHIP_RV380) { |
/* rv3x0 */ |
mem_trcd = (temp & 0x7) + 3; |
mem_trp = ((temp >> 8) & 0x7) + 3; |
mem_tras = ((temp >> 11) & 0xf) + 6; |
} else if (rdev->family == CHIP_R420 || |
rdev->family == CHIP_R423 || |
rdev->family == CHIP_RV410) { |
/* r4xx */ |
mem_trcd = (temp & 0xf) + 3; |
if (mem_trcd > 15) |
mem_trcd = 15; |
mem_trp = ((temp >> 8) & 0xf) + 3; |
if (mem_trp > 15) |
mem_trp = 15; |
mem_tras = ((temp >> 12) & 0x1f) + 6; |
if (mem_tras > 31) |
mem_tras = 31; |
} else { /* RV200, R200 */ |
mem_trcd = (temp & 0x7) + 1; |
mem_trp = ((temp >> 8) & 0x7) + 1; |
mem_tras = ((temp >> 12) & 0xf) + 4; |
} |
/* convert to FF */ |
trcd_ff.full = rfixed_const(mem_trcd); |
trp_ff.full = rfixed_const(mem_trp); |
tras_ff.full = rfixed_const(mem_tras); |
/* Get values from the MEM_SDRAM_MODE_REG register...converting its */ |
temp = RREG32(RADEON_MEM_SDRAM_MODE_REG); |
data = (temp & (7 << 20)) >> 20; |
if ((rdev->family == CHIP_RV100) || rdev->flags & RADEON_IS_IGP) { |
if (rdev->family == CHIP_RS480) /* don't think rs400 */ |
tcas_ff = memtcas_rs480_ff[data]; |
else |
tcas_ff = memtcas_ff[data]; |
} else |
tcas_ff = memtcas2_ff[data]; |
if (rdev->family == CHIP_RS400 || |
rdev->family == CHIP_RS480) { |
/* extra cas latency stored in bits 23-25 0-4 clocks */ |
data = (temp >> 23) & 0x7; |
if (data < 5) |
tcas_ff.full += rfixed_const(data); |
} |
if (ASIC_IS_R300(rdev) && !(rdev->flags & RADEON_IS_IGP)) { |
/* on the R300, Tcas is included in Trbs. |
*/ |
temp = RREG32(RADEON_MEM_CNTL); |
data = (R300_MEM_NUM_CHANNELS_MASK & temp); |
if (data == 1) { |
if (R300_MEM_USE_CD_CH_ONLY & temp) { |
temp = RREG32(R300_MC_IND_INDEX); |
temp &= ~R300_MC_IND_ADDR_MASK; |
temp |= R300_MC_READ_CNTL_CD_mcind; |
WREG32(R300_MC_IND_INDEX, temp); |
temp = RREG32(R300_MC_IND_DATA); |
data = (R300_MEM_RBS_POSITION_C_MASK & temp); |
} else { |
temp = RREG32(R300_MC_READ_CNTL_AB); |
data = (R300_MEM_RBS_POSITION_A_MASK & temp); |
} |
} else { |
temp = RREG32(R300_MC_READ_CNTL_AB); |
data = (R300_MEM_RBS_POSITION_A_MASK & temp); |
} |
if (rdev->family == CHIP_RV410 || |
rdev->family == CHIP_R420 || |
rdev->family == CHIP_R423) |
trbs_ff = memtrbs_r4xx[data]; |
else |
trbs_ff = memtrbs[data]; |
tcas_ff.full += trbs_ff.full; |
} |
sclk_eff_ff.full = sclk_ff.full; |
// if (rdev->flags & RADEON_IS_AGP) { |
// fixed20_12 agpmode_ff; |
// agpmode_ff.full = rfixed_const(radeon_agpmode); |
// temp_ff.full = rfixed_const_666(16); |
// sclk_eff_ff.full -= rfixed_mul(agpmode_ff, temp_ff); |
// } |
/* TODO PCIE lanes may affect this - agpmode == 16?? */ |
if (ASIC_IS_R300(rdev)) { |
sclk_delay_ff.full = rfixed_const(250); |
} else { |
if ((rdev->family == CHIP_RV100) || |
rdev->flags & RADEON_IS_IGP) { |
if (rdev->mc.vram_is_ddr) |
sclk_delay_ff.full = rfixed_const(41); |
else |
sclk_delay_ff.full = rfixed_const(33); |
} else { |
if (rdev->mc.vram_width == 128) |
sclk_delay_ff.full = rfixed_const(57); |
else |
sclk_delay_ff.full = rfixed_const(41); |
} |
} |
mc_latency_sclk.full = rfixed_div(sclk_delay_ff, sclk_eff_ff); |
if (rdev->mc.vram_is_ddr) { |
if (rdev->mc.vram_width == 32) { |
k1.full = rfixed_const(40); |
c = 3; |
} else { |
k1.full = rfixed_const(20); |
c = 1; |
} |
} else { |
k1.full = rfixed_const(40); |
c = 3; |
} |
temp_ff.full = rfixed_const(2); |
mc_latency_mclk.full = rfixed_mul(trcd_ff, temp_ff); |
temp_ff.full = rfixed_const(c); |
mc_latency_mclk.full += rfixed_mul(tcas_ff, temp_ff); |
temp_ff.full = rfixed_const(4); |
mc_latency_mclk.full += rfixed_mul(tras_ff, temp_ff); |
mc_latency_mclk.full += rfixed_mul(trp_ff, temp_ff); |
mc_latency_mclk.full += k1.full; |
mc_latency_mclk.full = rfixed_div(mc_latency_mclk, mclk_ff); |
mc_latency_mclk.full += rfixed_div(temp_ff, sclk_eff_ff); |
/* |
HW cursor time assuming worst case of full size colour cursor. |
*/ |
temp_ff.full = rfixed_const((2 * (cur_size - (rdev->mc.vram_is_ddr + 1)))); |
temp_ff.full += trcd_ff.full; |
if (temp_ff.full < tras_ff.full) |
temp_ff.full = tras_ff.full; |
cur_latency_mclk.full = rfixed_div(temp_ff, mclk_ff); |
temp_ff.full = rfixed_const(cur_size); |
cur_latency_sclk.full = rfixed_div(temp_ff, sclk_eff_ff); |
/* |
Find the total latency for the display data. |
*/ |
disp_latency_overhead.full = rfixed_const(80); |
disp_latency_overhead.full = rfixed_div(disp_latency_overhead, sclk_ff); |
mc_latency_mclk.full += disp_latency_overhead.full + cur_latency_mclk.full; |
mc_latency_sclk.full += disp_latency_overhead.full + cur_latency_sclk.full; |
if (mc_latency_mclk.full > mc_latency_sclk.full) |
disp_latency.full = mc_latency_mclk.full; |
else |
disp_latency.full = mc_latency_sclk.full; |
/* setup Max GRPH_STOP_REQ default value */ |
if (ASIC_IS_RV100(rdev)) |
max_stop_req = 0x5c; |
else |
max_stop_req = 0x7c; |
if (mode1) { |
/* CRTC1 |
Set GRPH_BUFFER_CNTL register using h/w defined optimal values. |
GRPH_STOP_REQ <= MIN[ 0x7C, (CRTC_H_DISP + 1) * (bit depth) / 0x10 ] |
*/ |
stop_req = mode1->hdisplay * pixel_bytes1 / 16; |
if (stop_req > max_stop_req) |
stop_req = max_stop_req; |
/* |
Find the drain rate of the display buffer. |
*/ |
temp_ff.full = rfixed_const((16/pixel_bytes1)); |
disp_drain_rate.full = rfixed_div(pix_clk, temp_ff); |
/* |
Find the critical point of the display buffer. |
*/ |
crit_point_ff.full = rfixed_mul(disp_drain_rate, disp_latency); |
crit_point_ff.full += rfixed_const_half(0); |
critical_point = rfixed_trunc(crit_point_ff); |
if (rdev->disp_priority == 2) { |
critical_point = 0; |
} |
/* |
The critical point should never be above max_stop_req-4. Setting |
GRPH_CRITICAL_CNTL = 0 will thus force high priority all the time. |
*/ |
if (max_stop_req - critical_point < 4) |
critical_point = 0; |
if (critical_point == 0 && mode2 && rdev->family == CHIP_R300) { |
/* some R300 cards have problem with this set to 0, when CRTC2 is enabled.*/ |
critical_point = 0x10; |
} |
temp = RREG32(RADEON_GRPH_BUFFER_CNTL); |
temp &= ~(RADEON_GRPH_STOP_REQ_MASK); |
temp |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); |
temp &= ~(RADEON_GRPH_START_REQ_MASK); |
if ((rdev->family == CHIP_R350) && |
(stop_req > 0x15)) { |
stop_req -= 0x10; |
} |
temp |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); |
temp |= RADEON_GRPH_BUFFER_SIZE; |
temp &= ~(RADEON_GRPH_CRITICAL_CNTL | |
RADEON_GRPH_CRITICAL_AT_SOF | |
RADEON_GRPH_STOP_CNTL); |
/* |
Write the result into the register. |
*/ |
WREG32(RADEON_GRPH_BUFFER_CNTL, ((temp & ~RADEON_GRPH_CRITICAL_POINT_MASK) | |
(critical_point << RADEON_GRPH_CRITICAL_POINT_SHIFT))); |
#if 0 |
if ((rdev->family == CHIP_RS400) || |
(rdev->family == CHIP_RS480)) { |
/* attempt to program RS400 disp regs correctly ??? */ |
temp = RREG32(RS400_DISP1_REG_CNTL); |
temp &= ~(RS400_DISP1_START_REQ_LEVEL_MASK | |
RS400_DISP1_STOP_REQ_LEVEL_MASK); |
WREG32(RS400_DISP1_REQ_CNTL1, (temp | |
(critical_point << RS400_DISP1_START_REQ_LEVEL_SHIFT) | |
(critical_point << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); |
temp = RREG32(RS400_DMIF_MEM_CNTL1); |
temp &= ~(RS400_DISP1_CRITICAL_POINT_START_MASK | |
RS400_DISP1_CRITICAL_POINT_STOP_MASK); |
WREG32(RS400_DMIF_MEM_CNTL1, (temp | |
(critical_point << RS400_DISP1_CRITICAL_POINT_START_SHIFT) | |
(critical_point << RS400_DISP1_CRITICAL_POINT_STOP_SHIFT))); |
} |
#endif |
DRM_DEBUG("GRPH_BUFFER_CNTL from to %x\n", |
/* (unsigned int)info->SavedReg->grph_buffer_cntl, */ |
(unsigned int)RREG32(RADEON_GRPH_BUFFER_CNTL)); |
} |
if (mode2) { |
u32 grph2_cntl; |
stop_req = mode2->hdisplay * pixel_bytes2 / 16; |
if (stop_req > max_stop_req) |
stop_req = max_stop_req; |
/* |
Find the drain rate of the display buffer. |
*/ |
temp_ff.full = rfixed_const((16/pixel_bytes2)); |
disp_drain_rate2.full = rfixed_div(pix_clk2, temp_ff); |
grph2_cntl = RREG32(RADEON_GRPH2_BUFFER_CNTL); |
grph2_cntl &= ~(RADEON_GRPH_STOP_REQ_MASK); |
grph2_cntl |= (stop_req << RADEON_GRPH_STOP_REQ_SHIFT); |
grph2_cntl &= ~(RADEON_GRPH_START_REQ_MASK); |
if ((rdev->family == CHIP_R350) && |
(stop_req > 0x15)) { |
stop_req -= 0x10; |
} |
grph2_cntl |= (stop_req << RADEON_GRPH_START_REQ_SHIFT); |
grph2_cntl |= RADEON_GRPH_BUFFER_SIZE; |
grph2_cntl &= ~(RADEON_GRPH_CRITICAL_CNTL | |
RADEON_GRPH_CRITICAL_AT_SOF | |
RADEON_GRPH_STOP_CNTL); |
if ((rdev->family == CHIP_RS100) || |
(rdev->family == CHIP_RS200)) |
critical_point2 = 0; |
else { |
temp = (rdev->mc.vram_width * rdev->mc.vram_is_ddr + 1)/128; |
temp_ff.full = rfixed_const(temp); |
temp_ff.full = rfixed_mul(mclk_ff, temp_ff); |
if (sclk_ff.full < temp_ff.full) |
temp_ff.full = sclk_ff.full; |
read_return_rate.full = temp_ff.full; |
if (mode1) { |
temp_ff.full = read_return_rate.full - disp_drain_rate.full; |
time_disp1_drop_priority.full = rfixed_div(crit_point_ff, temp_ff); |
} else { |
time_disp1_drop_priority.full = 0; |
} |
crit_point_ff.full = disp_latency.full + time_disp1_drop_priority.full + disp_latency.full; |
crit_point_ff.full = rfixed_mul(crit_point_ff, disp_drain_rate2); |
crit_point_ff.full += rfixed_const_half(0); |
critical_point2 = rfixed_trunc(crit_point_ff); |
if (rdev->disp_priority == 2) { |
critical_point2 = 0; |
} |
if (max_stop_req - critical_point2 < 4) |
critical_point2 = 0; |
} |
if (critical_point2 == 0 && rdev->family == CHIP_R300) { |
/* some R300 cards have problem with this set to 0 */ |
critical_point2 = 0x10; |
} |
WREG32(RADEON_GRPH2_BUFFER_CNTL, ((grph2_cntl & ~RADEON_GRPH_CRITICAL_POINT_MASK) | |
(critical_point2 << RADEON_GRPH_CRITICAL_POINT_SHIFT))); |
if ((rdev->family == CHIP_RS400) || |
(rdev->family == CHIP_RS480)) { |
#if 0 |
/* attempt to program RS400 disp2 regs correctly ??? */ |
temp = RREG32(RS400_DISP2_REQ_CNTL1); |
temp &= ~(RS400_DISP2_START_REQ_LEVEL_MASK | |
RS400_DISP2_STOP_REQ_LEVEL_MASK); |
WREG32(RS400_DISP2_REQ_CNTL1, (temp | |
(critical_point2 << RS400_DISP1_START_REQ_LEVEL_SHIFT) | |
(critical_point2 << RS400_DISP1_STOP_REQ_LEVEL_SHIFT))); |
temp = RREG32(RS400_DISP2_REQ_CNTL2); |
temp &= ~(RS400_DISP2_CRITICAL_POINT_START_MASK | |
RS400_DISP2_CRITICAL_POINT_STOP_MASK); |
WREG32(RS400_DISP2_REQ_CNTL2, (temp | |
(critical_point2 << RS400_DISP2_CRITICAL_POINT_START_SHIFT) | |
(critical_point2 << RS400_DISP2_CRITICAL_POINT_STOP_SHIFT))); |
#endif |
WREG32(RS400_DISP2_REQ_CNTL1, 0x105DC1CC); |
WREG32(RS400_DISP2_REQ_CNTL2, 0x2749D000); |
WREG32(RS400_DMIF_MEM_CNTL1, 0x29CA71DC); |
WREG32(RS400_DISP1_REQ_CNTL1, 0x28FBC3AC); |
} |
DRM_DEBUG("GRPH2_BUFFER_CNTL from to %x\n", |
(unsigned int)RREG32(RADEON_GRPH2_BUFFER_CNTL)); |
} |
} |
/drivers/video/drm/radeon/radeon_legacy_encoders.c |
---|
0,0 → 1,1288 |
/* |
* Copyright 2007-8 Advanced Micro Devices, Inc. |
* Copyright 2008 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 |
* Alex Deucher |
*/ |
#include "drmP.h" |
#include "drm_crtc_helper.h" |
#include "radeon_drm.h" |
#include "radeon.h" |
#include "atom.h" |
static void radeon_legacy_rmx_mode_set(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
int xres = mode->hdisplay; |
int yres = mode->vdisplay; |
bool hscale = true, vscale = true; |
int hsync_wid; |
int vsync_wid; |
int hsync_start; |
uint32_t scale, inc; |
uint32_t fp_horz_stretch, fp_vert_stretch, crtc_more_cntl, fp_horz_vert_active; |
uint32_t fp_h_sync_strt_wid, fp_v_sync_strt_wid, fp_crtc_h_total_disp, fp_crtc_v_total_disp; |
struct radeon_native_mode *native_mode = &radeon_encoder->native_mode; |
DRM_DEBUG("\n"); |
fp_vert_stretch = RREG32(RADEON_FP_VERT_STRETCH) & |
(RADEON_VERT_STRETCH_RESERVED | |
RADEON_VERT_AUTO_RATIO_INC); |
fp_horz_stretch = RREG32(RADEON_FP_HORZ_STRETCH) & |
(RADEON_HORZ_FP_LOOP_STRETCH | |
RADEON_HORZ_AUTO_RATIO_INC); |
crtc_more_cntl = 0; |
if ((rdev->family == CHIP_RS100) || |
(rdev->family == CHIP_RS200)) { |
/* This is to workaround the asic bug for RMX, some versions |
of BIOS dosen't have this register initialized correctly. */ |
crtc_more_cntl |= RADEON_CRTC_H_CUTOFF_ACTIVE_EN; |
} |
fp_crtc_h_total_disp = ((((mode->crtc_htotal / 8) - 1) & 0x3ff) |
| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); |
hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; |
if (!hsync_wid) |
hsync_wid = 1; |
hsync_start = mode->crtc_hsync_start - 8; |
fp_h_sync_strt_wid = ((hsync_start & 0x1fff) |
| ((hsync_wid & 0x3f) << 16) |
| ((mode->flags & DRM_MODE_FLAG_NHSYNC) |
? RADEON_CRTC_H_SYNC_POL |
: 0)); |
fp_crtc_v_total_disp = (((mode->crtc_vtotal - 1) & 0xffff) |
| ((mode->crtc_vdisplay - 1) << 16)); |
vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; |
if (!vsync_wid) |
vsync_wid = 1; |
fp_v_sync_strt_wid = (((mode->crtc_vsync_start - 1) & 0xfff) |
| ((vsync_wid & 0x1f) << 16) |
| ((mode->flags & DRM_MODE_FLAG_NVSYNC) |
? RADEON_CRTC_V_SYNC_POL |
: 0)); |
fp_horz_vert_active = 0; |
if (native_mode->panel_xres == 0 || |
native_mode->panel_yres == 0) { |
hscale = false; |
vscale = false; |
} else { |
if (xres > native_mode->panel_xres) |
xres = native_mode->panel_xres; |
if (yres > native_mode->panel_yres) |
yres = native_mode->panel_yres; |
if (xres == native_mode->panel_xres) |
hscale = false; |
if (yres == native_mode->panel_yres) |
vscale = false; |
} |
if (radeon_encoder->flags & RADEON_USE_RMX) { |
if (radeon_encoder->rmx_type != RMX_CENTER) { |
if (!hscale) |
fp_horz_stretch |= ((xres/8-1) << 16); |
else { |
inc = (fp_horz_stretch & RADEON_HORZ_AUTO_RATIO_INC) ? 1 : 0; |
scale = ((xres + inc) * RADEON_HORZ_STRETCH_RATIO_MAX) |
/ native_mode->panel_xres + 1; |
fp_horz_stretch |= (((scale) & RADEON_HORZ_STRETCH_RATIO_MASK) | |
RADEON_HORZ_STRETCH_BLEND | |
RADEON_HORZ_STRETCH_ENABLE | |
((native_mode->panel_xres/8-1) << 16)); |
} |
if (!vscale) |
fp_vert_stretch |= ((yres-1) << 12); |
else { |
inc = (fp_vert_stretch & RADEON_VERT_AUTO_RATIO_INC) ? 1 : 0; |
scale = ((yres + inc) * RADEON_VERT_STRETCH_RATIO_MAX) |
/ native_mode->panel_yres + 1; |
fp_vert_stretch |= (((scale) & RADEON_VERT_STRETCH_RATIO_MASK) | |
RADEON_VERT_STRETCH_ENABLE | |
RADEON_VERT_STRETCH_BLEND | |
((native_mode->panel_yres-1) << 12)); |
} |
} else if (radeon_encoder->rmx_type == RMX_CENTER) { |
int blank_width; |
fp_horz_stretch |= ((xres/8-1) << 16); |
fp_vert_stretch |= ((yres-1) << 12); |
crtc_more_cntl |= (RADEON_CRTC_AUTO_HORZ_CENTER_EN | |
RADEON_CRTC_AUTO_VERT_CENTER_EN); |
blank_width = (mode->crtc_hblank_end - mode->crtc_hblank_start) / 8; |
if (blank_width > 110) |
blank_width = 110; |
fp_crtc_h_total_disp = (((blank_width) & 0x3ff) |
| ((((mode->crtc_hdisplay / 8) - 1) & 0x1ff) << 16)); |
hsync_wid = (mode->crtc_hsync_end - mode->crtc_hsync_start) / 8; |
if (!hsync_wid) |
hsync_wid = 1; |
fp_h_sync_strt_wid = ((((mode->crtc_hsync_start - mode->crtc_hblank_start) / 8) & 0x1fff) |
| ((hsync_wid & 0x3f) << 16) |
| ((mode->flags & DRM_MODE_FLAG_NHSYNC) |
? RADEON_CRTC_H_SYNC_POL |
: 0)); |
fp_crtc_v_total_disp = (((mode->crtc_vblank_end - mode->crtc_vblank_start) & 0xffff) |
| ((mode->crtc_vdisplay - 1) << 16)); |
vsync_wid = mode->crtc_vsync_end - mode->crtc_vsync_start; |
if (!vsync_wid) |
vsync_wid = 1; |
fp_v_sync_strt_wid = ((((mode->crtc_vsync_start - mode->crtc_vblank_start) & 0xfff) |
| ((vsync_wid & 0x1f) << 16) |
| ((mode->flags & DRM_MODE_FLAG_NVSYNC) |
? RADEON_CRTC_V_SYNC_POL |
: 0))); |
fp_horz_vert_active = (((native_mode->panel_yres) & 0xfff) | |
(((native_mode->panel_xres / 8) & 0x1ff) << 16)); |
} |
} else { |
fp_horz_stretch |= ((xres/8-1) << 16); |
fp_vert_stretch |= ((yres-1) << 12); |
} |
WREG32(RADEON_FP_HORZ_STRETCH, fp_horz_stretch); |
WREG32(RADEON_FP_VERT_STRETCH, fp_vert_stretch); |
WREG32(RADEON_CRTC_MORE_CNTL, crtc_more_cntl); |
WREG32(RADEON_FP_HORZ_VERT_ACTIVE, fp_horz_vert_active); |
WREG32(RADEON_FP_H_SYNC_STRT_WID, fp_h_sync_strt_wid); |
WREG32(RADEON_FP_V_SYNC_STRT_WID, fp_v_sync_strt_wid); |
WREG32(RADEON_FP_CRTC_H_TOTAL_DISP, fp_crtc_h_total_disp); |
WREG32(RADEON_FP_CRTC_V_TOTAL_DISP, fp_crtc_v_total_disp); |
} |
static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t lvds_gen_cntl, lvds_pll_cntl, pixclks_cntl, disp_pwr_man; |
int panel_pwr_delay = 2000; |
DRM_DEBUG("\n"); |
if (radeon_encoder->enc_priv) { |
if (rdev->is_atom_bios) { |
struct radeon_encoder_atom_dig *lvds = radeon_encoder->enc_priv; |
panel_pwr_delay = lvds->panel_pwr_delay; |
} else { |
struct radeon_encoder_lvds *lvds = radeon_encoder->enc_priv; |
panel_pwr_delay = lvds->panel_pwr_delay; |
} |
} |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
disp_pwr_man = RREG32(RADEON_DISP_PWR_MAN); |
disp_pwr_man |= RADEON_AUTO_PWRUP_EN; |
WREG32(RADEON_DISP_PWR_MAN, disp_pwr_man); |
lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); |
lvds_pll_cntl |= RADEON_LVDS_PLL_EN; |
WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); |
udelay(1000); |
lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); |
lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET; |
WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); |
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); |
lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN | RADEON_LVDS_DIGON | RADEON_LVDS_BLON); |
lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS); |
udelay(panel_pwr_delay * 1000); |
WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); |
WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb); |
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); |
lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; |
lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON); |
udelay(panel_pwr_delay * 1000); |
WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); |
WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); |
break; |
} |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
else |
radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
} |
static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_OFF); |
} |
static void radeon_legacy_lvds_commit(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
radeon_legacy_lvds_dpms(encoder, DRM_MODE_DPMS_ON); |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, false); |
else |
radeon_combios_output_lock(encoder, false); |
} |
static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t lvds_pll_cntl, lvds_gen_cntl, lvds_ss_gen_cntl; |
DRM_DEBUG("\n"); |
if (radeon_crtc->crtc_id == 0) |
radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); |
lvds_pll_cntl = RREG32(RADEON_LVDS_PLL_CNTL); |
lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN; |
lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL); |
if ((!rdev->is_atom_bios)) { |
struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv; |
if (lvds) { |
DRM_DEBUG("bios LVDS_GEN_CNTL: 0x%x\n", lvds->lvds_gen_cntl); |
lvds_gen_cntl = lvds->lvds_gen_cntl; |
lvds_ss_gen_cntl &= ~((0xf << RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) | |
(0xf << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT)); |
lvds_ss_gen_cntl |= ((lvds->panel_digon_delay << RADEON_LVDS_PWRSEQ_DELAY1_SHIFT) | |
(lvds->panel_blon_delay << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT)); |
} else |
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); |
} else |
lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL); |
lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS; |
lvds_gen_cntl &= ~(RADEON_LVDS_ON | |
RADEON_LVDS_BLON | |
RADEON_LVDS_EN | |
RADEON_LVDS_RST_FM); |
if (ASIC_IS_R300(rdev)) |
lvds_pll_cntl &= ~(R300_LVDS_SRC_SEL_MASK); |
if (radeon_crtc->crtc_id == 0) { |
if (ASIC_IS_R300(rdev)) { |
if (radeon_encoder->flags & RADEON_USE_RMX) |
lvds_pll_cntl |= R300_LVDS_SRC_SEL_RMX; |
} else |
lvds_gen_cntl &= ~RADEON_LVDS_SEL_CRTC2; |
} else { |
if (ASIC_IS_R300(rdev)) |
lvds_pll_cntl |= R300_LVDS_SRC_SEL_CRTC2; |
else |
lvds_gen_cntl |= RADEON_LVDS_SEL_CRTC2; |
} |
WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl); |
WREG32(RADEON_LVDS_PLL_CNTL, lvds_pll_cntl); |
WREG32(RADEON_LVDS_SS_GEN_CNTL, lvds_ss_gen_cntl); |
if (rdev->family == CHIP_RV410) |
WREG32(RADEON_CLOCK_CNTL_INDEX, 0); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
else |
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
} |
static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
drm_mode_set_crtcinfo(adjusted_mode, 0); |
radeon_encoder->flags &= ~RADEON_USE_RMX; |
if (radeon_encoder->rmx_type != RMX_OFF) |
radeon_rmx_mode_fixup(encoder, mode, adjusted_mode); |
return true; |
} |
static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = { |
.dpms = radeon_legacy_lvds_dpms, |
.mode_fixup = radeon_legacy_lvds_mode_fixup, |
.prepare = radeon_legacy_lvds_prepare, |
.mode_set = radeon_legacy_lvds_mode_set, |
.commit = radeon_legacy_lvds_commit, |
}; |
static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = { |
.destroy = radeon_enc_destroy, |
}; |
static bool radeon_legacy_primary_dac_mode_fixup(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
drm_mode_set_crtcinfo(adjusted_mode, 0); |
return true; |
} |
static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); |
uint32_t dac_cntl = RREG32(RADEON_DAC_CNTL); |
uint32_t dac_macro_cntl = RREG32(RADEON_DAC_MACRO_CNTL); |
DRM_DEBUG("\n"); |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
crtc_ext_cntl |= RADEON_CRTC_CRT_ON; |
dac_cntl &= ~RADEON_DAC_PDWN; |
dac_macro_cntl &= ~(RADEON_DAC_PDWN_R | |
RADEON_DAC_PDWN_G | |
RADEON_DAC_PDWN_B); |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
crtc_ext_cntl &= ~RADEON_CRTC_CRT_ON; |
dac_cntl |= RADEON_DAC_PDWN; |
dac_macro_cntl |= (RADEON_DAC_PDWN_R | |
RADEON_DAC_PDWN_G | |
RADEON_DAC_PDWN_B); |
break; |
} |
WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); |
WREG32(RADEON_DAC_CNTL, dac_cntl); |
WREG32(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
else |
radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
} |
static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_OFF); |
} |
static void radeon_legacy_primary_dac_commit(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
radeon_legacy_primary_dac_dpms(encoder, DRM_MODE_DPMS_ON); |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, false); |
else |
radeon_combios_output_lock(encoder, false); |
} |
static void radeon_legacy_primary_dac_mode_set(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t disp_output_cntl, dac_cntl, dac2_cntl, dac_macro_cntl; |
DRM_DEBUG("\n"); |
if (radeon_crtc->crtc_id == 0) |
radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); |
if (radeon_crtc->crtc_id == 0) { |
if (rdev->family == CHIP_R200 || ASIC_IS_R300(rdev)) { |
disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL) & |
~(RADEON_DISP_DAC_SOURCE_MASK); |
WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); |
} else { |
dac2_cntl = RREG32(RADEON_DAC_CNTL2) & ~(RADEON_DAC2_DAC_CLK_SEL); |
WREG32(RADEON_DAC_CNTL2, dac2_cntl); |
} |
} else { |
if (rdev->family == CHIP_R200 || ASIC_IS_R300(rdev)) { |
disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL) & |
~(RADEON_DISP_DAC_SOURCE_MASK); |
disp_output_cntl |= RADEON_DISP_DAC_SOURCE_CRTC2; |
WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); |
} else { |
dac2_cntl = RREG32(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC_CLK_SEL; |
WREG32(RADEON_DAC_CNTL2, dac2_cntl); |
} |
} |
dac_cntl = (RADEON_DAC_MASK_ALL | |
RADEON_DAC_VGA_ADR_EN | |
/* TODO 6-bits */ |
RADEON_DAC_8BIT_EN); |
WREG32_P(RADEON_DAC_CNTL, |
dac_cntl, |
RADEON_DAC_RANGE_CNTL | |
RADEON_DAC_BLANKING); |
if (radeon_encoder->enc_priv) { |
struct radeon_encoder_primary_dac *p_dac = (struct radeon_encoder_primary_dac *)radeon_encoder->enc_priv; |
dac_macro_cntl = p_dac->ps2_pdac_adj; |
} else |
dac_macro_cntl = RREG32(RADEON_DAC_MACRO_CNTL); |
dac_macro_cntl |= RADEON_DAC_PDWN_R | RADEON_DAC_PDWN_G | RADEON_DAC_PDWN_B; |
WREG32(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
else |
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
} |
static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_encoder *encoder, |
struct drm_connector *connector) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t vclk_ecp_cntl, crtc_ext_cntl; |
uint32_t dac_ext_cntl, dac_cntl, dac_macro_cntl, tmp; |
enum drm_connector_status found = connector_status_disconnected; |
bool color = true; |
/* save the regs we need */ |
vclk_ecp_cntl = RREG32_PLL(RADEON_VCLK_ECP_CNTL); |
crtc_ext_cntl = RREG32(RADEON_CRTC_EXT_CNTL); |
dac_ext_cntl = RREG32(RADEON_DAC_EXT_CNTL); |
dac_cntl = RREG32(RADEON_DAC_CNTL); |
dac_macro_cntl = RREG32(RADEON_DAC_MACRO_CNTL); |
tmp = vclk_ecp_cntl & |
~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); |
WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); |
tmp = crtc_ext_cntl | RADEON_CRTC_CRT_ON; |
WREG32(RADEON_CRTC_EXT_CNTL, tmp); |
tmp = RADEON_DAC_FORCE_BLANK_OFF_EN | |
RADEON_DAC_FORCE_DATA_EN; |
if (color) |
tmp |= RADEON_DAC_FORCE_DATA_SEL_RGB; |
else |
tmp |= RADEON_DAC_FORCE_DATA_SEL_G; |
if (ASIC_IS_R300(rdev)) |
tmp |= (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT); |
else |
tmp |= (0x180 << RADEON_DAC_FORCE_DATA_SHIFT); |
WREG32(RADEON_DAC_EXT_CNTL, tmp); |
tmp = dac_cntl & ~(RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_PDWN); |
tmp |= RADEON_DAC_RANGE_CNTL_PS2 | RADEON_DAC_CMP_EN; |
WREG32(RADEON_DAC_CNTL, tmp); |
tmp &= ~(RADEON_DAC_PDWN_R | |
RADEON_DAC_PDWN_G | |
RADEON_DAC_PDWN_B); |
WREG32(RADEON_DAC_MACRO_CNTL, tmp); |
udelay(2000); |
if (RREG32(RADEON_DAC_CNTL) & RADEON_DAC_CMP_OUTPUT) |
found = connector_status_connected; |
/* restore the regs we used */ |
WREG32(RADEON_DAC_CNTL, dac_cntl); |
WREG32(RADEON_DAC_MACRO_CNTL, dac_macro_cntl); |
WREG32(RADEON_DAC_EXT_CNTL, dac_ext_cntl); |
WREG32(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl); |
WREG32_PLL(RADEON_VCLK_ECP_CNTL, vclk_ecp_cntl); |
return found; |
} |
static const struct drm_encoder_helper_funcs radeon_legacy_primary_dac_helper_funcs = { |
.dpms = radeon_legacy_primary_dac_dpms, |
.mode_fixup = radeon_legacy_primary_dac_mode_fixup, |
.prepare = radeon_legacy_primary_dac_prepare, |
.mode_set = radeon_legacy_primary_dac_mode_set, |
.commit = radeon_legacy_primary_dac_commit, |
.detect = radeon_legacy_primary_dac_detect, |
}; |
static const struct drm_encoder_funcs radeon_legacy_primary_dac_enc_funcs = { |
.destroy = radeon_enc_destroy, |
}; |
static bool radeon_legacy_tmds_int_mode_fixup(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
drm_mode_set_crtcinfo(adjusted_mode, 0); |
return true; |
} |
static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t fp_gen_cntl = RREG32(RADEON_FP_GEN_CNTL); |
DRM_DEBUG("\n"); |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
fp_gen_cntl |= (RADEON_FP_FPON | RADEON_FP_TMDS_EN); |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); |
break; |
} |
WREG32(RADEON_FP_GEN_CNTL, fp_gen_cntl); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
else |
radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
} |
static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_OFF); |
} |
static void radeon_legacy_tmds_int_commit(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
radeon_legacy_tmds_int_dpms(encoder, DRM_MODE_DPMS_ON); |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
} |
static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t tmp, tmds_pll_cntl, tmds_transmitter_cntl, fp_gen_cntl; |
int i; |
DRM_DEBUG("\n"); |
if (radeon_crtc->crtc_id == 0) |
radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); |
tmp = tmds_pll_cntl = RREG32(RADEON_TMDS_PLL_CNTL); |
tmp &= 0xfffff; |
if (rdev->family == CHIP_RV280) { |
/* bit 22 of TMDS_PLL_CNTL is read-back inverted */ |
tmp ^= (1 << 22); |
tmds_pll_cntl ^= (1 << 22); |
} |
if (radeon_encoder->enc_priv) { |
struct radeon_encoder_int_tmds *tmds = (struct radeon_encoder_int_tmds *)radeon_encoder->enc_priv; |
for (i = 0; i < 4; i++) { |
if (tmds->tmds_pll[i].freq == 0) |
break; |
if ((uint32_t)(mode->clock / 10) < tmds->tmds_pll[i].freq) { |
tmp = tmds->tmds_pll[i].value ; |
break; |
} |
} |
} |
if (ASIC_IS_R300(rdev) || (rdev->family == CHIP_RV280)) { |
if (tmp & 0xfff00000) |
tmds_pll_cntl = tmp; |
else { |
tmds_pll_cntl &= 0xfff00000; |
tmds_pll_cntl |= tmp; |
} |
} else |
tmds_pll_cntl = tmp; |
tmds_transmitter_cntl = RREG32(RADEON_TMDS_TRANSMITTER_CNTL) & |
~(RADEON_TMDS_TRANSMITTER_PLLRST); |
if (rdev->family == CHIP_R200 || |
rdev->family == CHIP_R100 || |
ASIC_IS_R300(rdev)) |
tmds_transmitter_cntl &= ~(RADEON_TMDS_TRANSMITTER_PLLEN); |
else /* RV chips got this bit reversed */ |
tmds_transmitter_cntl |= RADEON_TMDS_TRANSMITTER_PLLEN; |
fp_gen_cntl = (RREG32(RADEON_FP_GEN_CNTL) | |
(RADEON_FP_CRTC_DONT_SHADOW_VPAR | |
RADEON_FP_CRTC_DONT_SHADOW_HEND)); |
fp_gen_cntl &= ~(RADEON_FP_FPON | RADEON_FP_TMDS_EN); |
if (1) /* FIXME rgbBits == 8 */ |
fp_gen_cntl |= RADEON_FP_PANEL_FORMAT; /* 24 bit format */ |
else |
fp_gen_cntl &= ~RADEON_FP_PANEL_FORMAT;/* 18 bit format */ |
if (radeon_crtc->crtc_id == 0) { |
if (ASIC_IS_R300(rdev) || rdev->family == CHIP_R200) { |
fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; |
if (radeon_encoder->flags & RADEON_USE_RMX) |
fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX; |
else |
fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1; |
} else |
fp_gen_cntl |= RADEON_FP_SEL_CRTC1; |
} else { |
if (ASIC_IS_R300(rdev) || rdev->family == CHIP_R200) { |
fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; |
fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC2; |
} else |
fp_gen_cntl |= RADEON_FP_SEL_CRTC2; |
} |
WREG32(RADEON_TMDS_PLL_CNTL, tmds_pll_cntl); |
WREG32(RADEON_TMDS_TRANSMITTER_CNTL, tmds_transmitter_cntl); |
WREG32(RADEON_FP_GEN_CNTL, fp_gen_cntl); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
else |
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
} |
static const struct drm_encoder_helper_funcs radeon_legacy_tmds_int_helper_funcs = { |
.dpms = radeon_legacy_tmds_int_dpms, |
.mode_fixup = radeon_legacy_tmds_int_mode_fixup, |
.prepare = radeon_legacy_tmds_int_prepare, |
.mode_set = radeon_legacy_tmds_int_mode_set, |
.commit = radeon_legacy_tmds_int_commit, |
}; |
static const struct drm_encoder_funcs radeon_legacy_tmds_int_enc_funcs = { |
.destroy = radeon_enc_destroy, |
}; |
static bool radeon_legacy_tmds_ext_mode_fixup(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
drm_mode_set_crtcinfo(adjusted_mode, 0); |
return true; |
} |
static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); |
DRM_DEBUG("\n"); |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
fp2_gen_cntl &= ~RADEON_FP2_BLANK_EN; |
fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN); |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
fp2_gen_cntl |= RADEON_FP2_BLANK_EN; |
fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN); |
break; |
} |
WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
else |
radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
} |
static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_OFF); |
} |
static void radeon_legacy_tmds_ext_commit(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
radeon_legacy_tmds_ext_dpms(encoder, DRM_MODE_DPMS_ON); |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, false); |
else |
radeon_combios_output_lock(encoder, false); |
} |
static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t fp2_gen_cntl; |
DRM_DEBUG("\n"); |
if (radeon_crtc->crtc_id == 0) |
radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); |
if (rdev->is_atom_bios) { |
radeon_encoder->pixel_clock = adjusted_mode->clock; |
atombios_external_tmds_setup(encoder, ATOM_ENABLE); |
fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); |
} else { |
fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); |
if (1) /* FIXME rgbBits == 8 */ |
fp2_gen_cntl |= RADEON_FP2_PANEL_FORMAT; /* 24 bit format, */ |
else |
fp2_gen_cntl &= ~RADEON_FP2_PANEL_FORMAT;/* 18 bit format, */ |
fp2_gen_cntl &= ~(RADEON_FP2_ON | |
RADEON_FP2_DVO_EN | |
RADEON_FP2_DVO_RATE_SEL_SDR); |
/* XXX: these are oem specific */ |
if (ASIC_IS_R300(rdev)) { |
if ((dev->pdev->device == 0x4850) && |
(dev->pdev->subsystem_vendor == 0x1028) && |
(dev->pdev->subsystem_device == 0x2001)) /* Dell Inspiron 8600 */ |
fp2_gen_cntl |= R300_FP2_DVO_CLOCK_MODE_SINGLE; |
else |
fp2_gen_cntl |= RADEON_FP2_PAD_FLOP_EN | R300_FP2_DVO_CLOCK_MODE_SINGLE; |
/*if (mode->clock > 165000) |
fp2_gen_cntl |= R300_FP2_DVO_DUAL_CHANNEL_EN;*/ |
} |
} |
if (radeon_crtc->crtc_id == 0) { |
if ((rdev->family == CHIP_R200) || ASIC_IS_R300(rdev)) { |
fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; |
if (radeon_encoder->flags & RADEON_USE_RMX) |
fp2_gen_cntl |= R200_FP2_SOURCE_SEL_RMX; |
else |
fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC1; |
} else |
fp2_gen_cntl &= ~RADEON_FP2_SRC_SEL_CRTC2; |
} else { |
if ((rdev->family == CHIP_R200) || ASIC_IS_R300(rdev)) { |
fp2_gen_cntl &= ~R200_FP2_SOURCE_SEL_MASK; |
fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2; |
} else |
fp2_gen_cntl |= RADEON_FP2_SRC_SEL_CRTC2; |
} |
WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
else |
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
} |
static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs = { |
.dpms = radeon_legacy_tmds_ext_dpms, |
.mode_fixup = radeon_legacy_tmds_ext_mode_fixup, |
.prepare = radeon_legacy_tmds_ext_prepare, |
.mode_set = radeon_legacy_tmds_ext_mode_set, |
.commit = radeon_legacy_tmds_ext_commit, |
}; |
static const struct drm_encoder_funcs radeon_legacy_tmds_ext_enc_funcs = { |
.destroy = radeon_enc_destroy, |
}; |
static bool radeon_legacy_tv_dac_mode_fixup(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
drm_mode_set_crtcinfo(adjusted_mode, 0); |
return true; |
} |
static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t fp2_gen_cntl = 0, crtc2_gen_cntl = 0, tv_dac_cntl = 0; |
/* uint32_t tv_master_cntl = 0; */ |
DRM_DEBUG("\n"); |
if (rdev->family == CHIP_R200) |
fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); |
else { |
crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); |
/* FIXME TV */ |
/* tv_master_cntl = RREG32(RADEON_TV_MASTER_CNTL); */ |
tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); |
} |
switch (mode) { |
case DRM_MODE_DPMS_ON: |
if (rdev->family == CHIP_R200) { |
fp2_gen_cntl |= (RADEON_FP2_ON | RADEON_FP2_DVO_EN); |
} else { |
crtc2_gen_cntl |= RADEON_CRTC2_CRT2_ON; |
/* tv_master_cntl |= RADEON_TV_ON; */ |
if (rdev->family == CHIP_R420 || |
rdev->family == CHIP_R423 || |
rdev->family == CHIP_RV410) |
tv_dac_cntl &= ~(R420_TV_DAC_RDACPD | |
R420_TV_DAC_GDACPD | |
R420_TV_DAC_BDACPD | |
RADEON_TV_DAC_BGSLEEP); |
else |
tv_dac_cntl &= ~(RADEON_TV_DAC_RDACPD | |
RADEON_TV_DAC_GDACPD | |
RADEON_TV_DAC_BDACPD | |
RADEON_TV_DAC_BGSLEEP); |
} |
break; |
case DRM_MODE_DPMS_STANDBY: |
case DRM_MODE_DPMS_SUSPEND: |
case DRM_MODE_DPMS_OFF: |
if (rdev->family == CHIP_R200) |
fp2_gen_cntl &= ~(RADEON_FP2_ON | RADEON_FP2_DVO_EN); |
else { |
crtc2_gen_cntl &= ~RADEON_CRTC2_CRT2_ON; |
/* tv_master_cntl &= ~RADEON_TV_ON; */ |
if (rdev->family == CHIP_R420 || |
rdev->family == CHIP_R423 || |
rdev->family == CHIP_RV410) |
tv_dac_cntl |= (R420_TV_DAC_RDACPD | |
R420_TV_DAC_GDACPD | |
R420_TV_DAC_BDACPD | |
RADEON_TV_DAC_BGSLEEP); |
else |
tv_dac_cntl |= (RADEON_TV_DAC_RDACPD | |
RADEON_TV_DAC_GDACPD | |
RADEON_TV_DAC_BDACPD | |
RADEON_TV_DAC_BGSLEEP); |
} |
break; |
} |
if (rdev->family == CHIP_R200) { |
WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); |
} else { |
WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); |
/* WREG32(RADEON_TV_MASTER_CNTL, tv_master_cntl); */ |
WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); |
} |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
else |
radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); |
} |
static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_OFF); |
} |
static void radeon_legacy_tv_dac_commit(struct drm_encoder *encoder) |
{ |
struct radeon_device *rdev = encoder->dev->dev_private; |
radeon_legacy_tv_dac_dpms(encoder, DRM_MODE_DPMS_ON); |
if (rdev->is_atom_bios) |
radeon_atom_output_lock(encoder, true); |
else |
radeon_combios_output_lock(encoder, true); |
} |
static void radeon_legacy_tv_dac_mode_set(struct drm_encoder *encoder, |
struct drm_display_mode *mode, |
struct drm_display_mode *adjusted_mode) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc); |
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); |
uint32_t tv_dac_cntl, gpiopad_a = 0, dac2_cntl, disp_output_cntl = 0; |
uint32_t disp_hw_debug = 0, fp2_gen_cntl = 0; |
DRM_DEBUG("\n"); |
if (radeon_crtc->crtc_id == 0) |
radeon_legacy_rmx_mode_set(encoder, mode, adjusted_mode); |
if (rdev->family != CHIP_R200) { |
tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); |
if (rdev->family == CHIP_R420 || |
rdev->family == CHIP_R423 || |
rdev->family == CHIP_RV410) { |
tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | |
RADEON_TV_DAC_BGADJ_MASK | |
R420_TV_DAC_DACADJ_MASK | |
R420_TV_DAC_RDACPD | |
R420_TV_DAC_GDACPD | |
R420_TV_DAC_GDACPD | |
R420_TV_DAC_TVENABLE); |
} else { |
tv_dac_cntl &= ~(RADEON_TV_DAC_STD_MASK | |
RADEON_TV_DAC_BGADJ_MASK | |
RADEON_TV_DAC_DACADJ_MASK | |
RADEON_TV_DAC_RDACPD | |
RADEON_TV_DAC_GDACPD | |
RADEON_TV_DAC_GDACPD); |
} |
/* FIXME TV */ |
if (radeon_encoder->enc_priv) { |
struct radeon_encoder_tv_dac *tv_dac = radeon_encoder->enc_priv; |
tv_dac_cntl |= (RADEON_TV_DAC_NBLANK | |
RADEON_TV_DAC_NHOLD | |
RADEON_TV_DAC_STD_PS2 | |
tv_dac->ps2_tvdac_adj); |
} else |
tv_dac_cntl |= (RADEON_TV_DAC_NBLANK | |
RADEON_TV_DAC_NHOLD | |
RADEON_TV_DAC_STD_PS2); |
WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); |
} |
if (ASIC_IS_R300(rdev)) { |
gpiopad_a = RREG32(RADEON_GPIOPAD_A) | 1; |
disp_output_cntl = RREG32(RADEON_DISP_OUTPUT_CNTL); |
} else if (rdev->family == CHIP_R200) |
fp2_gen_cntl = RREG32(RADEON_FP2_GEN_CNTL); |
else |
disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG); |
dac2_cntl = RREG32(RADEON_DAC_CNTL2) | RADEON_DAC2_DAC2_CLK_SEL; |
if (radeon_crtc->crtc_id == 0) { |
if (ASIC_IS_R300(rdev)) { |
disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; |
disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC; |
} else if (rdev->family == CHIP_R200) { |
fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK | |
RADEON_FP2_DVO_RATE_SEL_SDR); |
} else |
disp_hw_debug |= RADEON_CRT2_DISP1_SEL; |
} else { |
if (ASIC_IS_R300(rdev)) { |
disp_output_cntl &= ~RADEON_DISP_TVDAC_SOURCE_MASK; |
disp_output_cntl |= RADEON_DISP_TVDAC_SOURCE_CRTC2; |
} else if (rdev->family == CHIP_R200) { |
fp2_gen_cntl &= ~(R200_FP2_SOURCE_SEL_MASK | |
RADEON_FP2_DVO_RATE_SEL_SDR); |
fp2_gen_cntl |= R200_FP2_SOURCE_SEL_CRTC2; |
} else |
disp_hw_debug &= ~RADEON_CRT2_DISP1_SEL; |
} |
WREG32(RADEON_DAC_CNTL2, dac2_cntl); |
if (ASIC_IS_R300(rdev)) { |
WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1); |
WREG32(RADEON_DISP_TV_OUT_CNTL, disp_output_cntl); |
} else if (rdev->family == CHIP_R200) |
WREG32(RADEON_FP2_GEN_CNTL, fp2_gen_cntl); |
else |
WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug); |
if (rdev->is_atom_bios) |
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
else |
radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id); |
} |
static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder *encoder, |
struct drm_connector *connector) |
{ |
struct drm_device *dev = encoder->dev; |
struct radeon_device *rdev = dev->dev_private; |
uint32_t crtc2_gen_cntl, tv_dac_cntl, dac_cntl2, dac_ext_cntl; |
uint32_t disp_hw_debug, disp_output_cntl, gpiopad_a, pixclks_cntl, tmp; |
enum drm_connector_status found = connector_status_disconnected; |
bool color = true; |
/* FIXME tv */ |
/* save the regs we need */ |
pixclks_cntl = RREG32_PLL(RADEON_PIXCLKS_CNTL); |
gpiopad_a = ASIC_IS_R300(rdev) ? RREG32(RADEON_GPIOPAD_A) : 0; |
disp_output_cntl = ASIC_IS_R300(rdev) ? RREG32(RADEON_DISP_OUTPUT_CNTL) : 0; |
disp_hw_debug = ASIC_IS_R300(rdev) ? 0 : RREG32(RADEON_DISP_HW_DEBUG); |
crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL); |
tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL); |
dac_ext_cntl = RREG32(RADEON_DAC_EXT_CNTL); |
dac_cntl2 = RREG32(RADEON_DAC_CNTL2); |
tmp = pixclks_cntl & ~(RADEON_PIX2CLK_ALWAYS_ONb |
| RADEON_PIX2CLK_DAC_ALWAYS_ONb); |
WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); |
if (ASIC_IS_R300(rdev)) |
WREG32_P(RADEON_GPIOPAD_A, 1, ~1); |
tmp = crtc2_gen_cntl & ~RADEON_CRTC2_PIX_WIDTH_MASK; |
tmp |= RADEON_CRTC2_CRT2_ON | |
(2 << RADEON_CRTC2_PIX_WIDTH_SHIFT); |
WREG32(RADEON_CRTC2_GEN_CNTL, tmp); |
if (ASIC_IS_R300(rdev)) { |
tmp = disp_output_cntl & ~RADEON_DISP_TVDAC_SOURCE_MASK; |
tmp |= RADEON_DISP_TVDAC_SOURCE_CRTC2; |
WREG32(RADEON_DISP_OUTPUT_CNTL, tmp); |
} else { |
tmp = disp_hw_debug & ~RADEON_CRT2_DISP1_SEL; |
WREG32(RADEON_DISP_HW_DEBUG, tmp); |
} |
tmp = RADEON_TV_DAC_NBLANK | |
RADEON_TV_DAC_NHOLD | |
RADEON_TV_MONITOR_DETECT_EN | |
RADEON_TV_DAC_STD_PS2; |
WREG32(RADEON_TV_DAC_CNTL, tmp); |
tmp = RADEON_DAC2_FORCE_BLANK_OFF_EN | |
RADEON_DAC2_FORCE_DATA_EN; |
if (color) |
tmp |= RADEON_DAC_FORCE_DATA_SEL_RGB; |
else |
tmp |= RADEON_DAC_FORCE_DATA_SEL_G; |
if (ASIC_IS_R300(rdev)) |
tmp |= (0x1b6 << RADEON_DAC_FORCE_DATA_SHIFT); |
else |
tmp |= (0x180 << RADEON_DAC_FORCE_DATA_SHIFT); |
WREG32(RADEON_DAC_EXT_CNTL, tmp); |
tmp = dac_cntl2 | RADEON_DAC2_DAC2_CLK_SEL | RADEON_DAC2_CMP_EN; |
WREG32(RADEON_DAC_CNTL2, tmp); |
udelay(10000); |
if (ASIC_IS_R300(rdev)) { |
if (RREG32(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUT_B) |
found = connector_status_connected; |
} else { |
if (RREG32(RADEON_DAC_CNTL2) & RADEON_DAC2_CMP_OUTPUT) |
found = connector_status_connected; |
} |
/* restore regs we used */ |
WREG32(RADEON_DAC_CNTL2, dac_cntl2); |
WREG32(RADEON_DAC_EXT_CNTL, dac_ext_cntl); |
WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl); |
WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl); |
if (ASIC_IS_R300(rdev)) { |
WREG32(RADEON_DISP_OUTPUT_CNTL, disp_output_cntl); |
WREG32_P(RADEON_GPIOPAD_A, gpiopad_a, ~1); |
} else { |
WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug); |
} |
WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl); |
/* return found; */ |
return connector_status_disconnected; |
} |
static const struct drm_encoder_helper_funcs radeon_legacy_tv_dac_helper_funcs = { |
.dpms = radeon_legacy_tv_dac_dpms, |
.mode_fixup = radeon_legacy_tv_dac_mode_fixup, |
.prepare = radeon_legacy_tv_dac_prepare, |
.mode_set = radeon_legacy_tv_dac_mode_set, |
.commit = radeon_legacy_tv_dac_commit, |
.detect = radeon_legacy_tv_dac_detect, |
}; |
static const struct drm_encoder_funcs radeon_legacy_tv_dac_enc_funcs = { |
.destroy = radeon_enc_destroy, |
}; |
void |
radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t supported_device) |
{ |
struct radeon_device *rdev = dev->dev_private; |
struct drm_encoder *encoder; |
struct radeon_encoder *radeon_encoder; |
/* see if we already added it */ |
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
radeon_encoder = to_radeon_encoder(encoder); |
if (radeon_encoder->encoder_id == encoder_id) { |
radeon_encoder->devices |= supported_device; |
return; |
} |
} |
/* add a new one */ |
radeon_encoder = kzalloc(sizeof(struct radeon_encoder), GFP_KERNEL); |
if (!radeon_encoder) |
return; |
encoder = &radeon_encoder->base; |
encoder->possible_crtcs = 0x3; |
encoder->possible_clones = 0; |
radeon_encoder->enc_priv = NULL; |
radeon_encoder->encoder_id = encoder_id; |
radeon_encoder->devices = supported_device; |
switch (radeon_encoder->encoder_id) { |
case ENCODER_OBJECT_ID_INTERNAL_LVDS: |
drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); |
drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs); |
if (rdev->is_atom_bios) |
radeon_encoder->enc_priv = radeon_atombios_get_lvds_info(radeon_encoder); |
else |
radeon_encoder->enc_priv = radeon_combios_get_lvds_info(radeon_encoder); |
radeon_encoder->rmx_type = RMX_FULL; |
break; |
case ENCODER_OBJECT_ID_INTERNAL_TMDS1: |
drm_encoder_init(dev, encoder, &radeon_legacy_tmds_int_enc_funcs, DRM_MODE_ENCODER_TMDS); |
drm_encoder_helper_add(encoder, &radeon_legacy_tmds_int_helper_funcs); |
if (rdev->is_atom_bios) |
radeon_encoder->enc_priv = radeon_atombios_get_tmds_info(radeon_encoder); |
else |
radeon_encoder->enc_priv = radeon_combios_get_tmds_info(radeon_encoder); |
break; |
case ENCODER_OBJECT_ID_INTERNAL_DAC1: |
drm_encoder_init(dev, encoder, &radeon_legacy_primary_dac_enc_funcs, DRM_MODE_ENCODER_DAC); |
drm_encoder_helper_add(encoder, &radeon_legacy_primary_dac_helper_funcs); |
if (rdev->is_atom_bios) |
radeon_encoder->enc_priv = radeon_atombios_get_primary_dac_info(radeon_encoder); |
else |
radeon_encoder->enc_priv = radeon_combios_get_primary_dac_info(radeon_encoder); |
break; |
case ENCODER_OBJECT_ID_INTERNAL_DAC2: |
drm_encoder_init(dev, encoder, &radeon_legacy_tv_dac_enc_funcs, DRM_MODE_ENCODER_TVDAC); |
drm_encoder_helper_add(encoder, &radeon_legacy_tv_dac_helper_funcs); |
if (rdev->is_atom_bios) |
radeon_encoder->enc_priv = radeon_atombios_get_tv_dac_info(radeon_encoder); |
else |
radeon_encoder->enc_priv = radeon_combios_get_tv_dac_info(radeon_encoder); |
break; |
case ENCODER_OBJECT_ID_INTERNAL_DVO1: |
drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, DRM_MODE_ENCODER_TMDS); |
drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs); |
if (!rdev->is_atom_bios) |
radeon_combios_get_ext_tmds_info(radeon_encoder); |
break; |
} |
} |
/drivers/video/drm/radeon/radeon_mode.h |
---|
32,8 → 32,8 |
#include "drm_mode.h" |
#include "drm_crtc.h" |
#include <drm_edid.h> |
//#include <drm_edid.h> |
//#include <linux/i2c.h> |
//#include <linux/i2c-id.h> |
//#include <linux/i2c-algo-bit.h> |
148,8 → 148,8 |
struct radeon_i2c_chan { |
struct drm_device *dev; |
// struct i2c_adapter adapter; |
// struct i2c_algo_bit_data algo; |
struct i2c_adapter adapter; |
struct i2c_algo_bit_data algo; |
struct radeon_i2c_bus_rec rec; |
}; |
174,7 → 174,7 |
}; |
struct radeon_crtc { |
// struct drm_crtc base; |
struct drm_crtc base; |
int crtc_id; |
u16_t lut_r[256], lut_g[256], lut_b[256]; |
bool enabled; |
181,7 → 181,7 |
bool can_tile; |
uint32_t crtc_offset; |
struct radeon_framebuffer *fbdev_fb; |
// struct drm_mode_set mode_set; |
struct drm_mode_set mode_set; |
// struct drm_gem_object *cursor_bo; |
uint64_t cursor_addr; |
int cursor_width; |
272,8 → 272,8 |
}; |
struct radeon_framebuffer { |
// struct drm_framebuffer base; |
// struct drm_gem_object *obj; |
struct drm_framebuffer base; |
struct drm_gem_object *obj; |
}; |
extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev, |
283,7 → 283,7 |
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector); |
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector); |
//extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); |
extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector); |
extern void radeon_compute_pll(struct radeon_pll *pll, |
uint64_t freq, |
302,7 → 302,6 |
extern void atombios_external_tmds_setup(struct drm_encoder *encoder, int action); |
extern int atombios_get_encoder_mode(struct drm_encoder *encoder); |
/* |
extern void radeon_crtc_load_lut(struct drm_crtc *crtc); |
extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
struct drm_framebuffer *old_fb); |
396,5 → 395,5 |
struct drm_display_mode *mode2, |
uint32_t pixel_bytes2); |
void radeon_init_disp_bandwidth(struct drm_device *dev); |
*/ |
#endif |