aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdrm/xf86drmMode.c26
-rw-r--r--libdrm/xf86drmMode.h29
-rw-r--r--linux-core/Makefile2
-rw-r--r--linux-core/Makefile.kernel6
-rw-r--r--linux-core/drm_crtc.c249
-rw-r--r--linux-core/drm_crtc.h54
-rw-r--r--linux-core/drm_crtc_helper.c6
-rw-r--r--linux-core/drm_drv.c2
-rw-r--r--linux-core/drm_edid.c76
-rw-r--r--linux-core/drm_modes.c13
-rw-r--r--linux-core/drm_sysfs.c111
-rw-r--r--linux-core/dvo_ch7017.c6
-rw-r--r--linux-core/dvo_ch7xxx.c6
-rw-r--r--linux-core/dvo_ivch.c6
-rw-r--r--linux-core/dvo_sil164.c2
-rw-r--r--linux-core/dvo_tfp410.c2
-rw-r--r--linux-core/intel_crt.c14
-rw-r--r--linux-core/intel_display.c36
-rw-r--r--linux-core/intel_dvo.c12
-rw-r--r--linux-core/intel_fb.c6
-rw-r--r--linux-core/intel_lvds.c2
-rw-r--r--linux-core/intel_sdvo.c18
-rw-r--r--linux-core/intel_tv.c10
-rw-r--r--linux-core/nouveau_bios.c817
-rw-r--r--linux-core/nouveau_bios.h151
-rw-r--r--linux-core/nouveau_drv.c7
-rw-r--r--linux-core/nv50_connector.c207
-rw-r--r--linux-core/nv50_connector.h59
-rw-r--r--linux-core/nv50_crtc.c527
-rw-r--r--linux-core/nv50_crtc.h69
-rw-r--r--linux-core/nv50_cursor.c193
-rw-r--r--linux-core/nv50_cursor.h52
-rw-r--r--linux-core/nv50_dac.c225
-rw-r--r--linux-core/nv50_display.c353
-rw-r--r--linux-core/nv50_display.h83
-rw-r--r--linux-core/nv50_display_commands.h196
-rw-r--r--linux-core/nv50_fb.c142
-rw-r--r--linux-core/nv50_fb.h57
-rw-r--r--linux-core/nv50_fbcon.c627
-rw-r--r--linux-core/nv50_fbcon.h41
-rw-r--r--linux-core/nv50_i2c.c400
-rw-r--r--linux-core/nv50_i2c.h47
-rw-r--r--linux-core/nv50_kms_wrapper.c1280
-rw-r--r--linux-core/nv50_kms_wrapper.h95
-rw-r--r--linux-core/nv50_lut.c193
-rw-r--r--linux-core/nv50_lut.h45
-rw-r--r--linux-core/nv50_output.c33
-rw-r--r--linux-core/nv50_output.h62
-rw-r--r--linux-core/nv50_sor.c240
-rw-r--r--shared-core/drm.h86
-rw-r--r--shared-core/nouveau_dma.h5
-rw-r--r--shared-core/nouveau_drm.h4
-rw-r--r--shared-core/nouveau_drv.h28
-rw-r--r--shared-core/nouveau_irq.c80
-rw-r--r--shared-core/nouveau_mem.c95
-rw-r--r--shared-core/nouveau_notifier.c2
-rw-r--r--shared-core/nouveau_object.c2
-rw-r--r--shared-core/nouveau_reg.h238
-rw-r--r--shared-core/nouveau_state.c57
-rw-r--r--shared-core/nv50_fifo.c4
-rw-r--r--tests/mode/modetest.c409
-rwxr-xr-xtests/mode/test1
-rw-r--r--tests/modeprint/Makefile (renamed from tests/mode/Makefile)0
-rw-r--r--tests/modeprint/modetest.c371
-rwxr-xr-xtests/modeprint/test1
65 files changed, 7554 insertions, 724 deletions
diff --git a/libdrm/xf86drmMode.c b/libdrm/xf86drmMode.c
index a393f965..c3921ee9 100644
--- a/libdrm/xf86drmMode.c
+++ b/libdrm/xf86drmMode.c
@@ -284,11 +284,6 @@ int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
{
struct drm_mode_crtc crtc;
- crtc.count_connectors = 0;
- crtc.connectors = 0;
- crtc.count_possibles = 0;
- crtc.possibles = 0;
-
crtc.x = x;
crtc.y = y;
crtc.crtc_id = crtcId;
@@ -343,8 +338,8 @@ drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
enc.encoder_id = encoder_id;
enc.encoder_type = 0;
- enc.crtcs = 0;
- enc.clones = 0;
+ enc.possible_crtcs = 0;
+ enc.possible_clones = 0;
if (ioctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc))
return 0;
@@ -353,10 +348,10 @@ drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
return 0;
r->encoder_id = enc.encoder_id;
- r->crtc = enc.crtc;
+ r->crtc_id = enc.crtc_id;
r->encoder_type = enc.encoder_type;
- r->crtcs = enc.crtcs;
- r->clones = enc.clones;
+ r->possible_crtcs = enc.possible_crtcs;
+ r->possible_clones = enc.possible_clones;
return r;
}
@@ -370,7 +365,7 @@ drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
struct drm_mode_get_connector conn;
drmModeConnectorPtr r = NULL;
- conn.connector = connector_id;
+ conn.connector_id = connector_id;
conn.connector_type_id = 0;
conn.connector_type = 0;
conn.count_modes = 0;
@@ -402,8 +397,8 @@ drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
goto err_allocs;
}
- r->connector_id = conn.connector;
- r->encoder = conn.encoder;
+ r->connector_id = conn.connector_id;
+ r->encoder_id = conn.encoder_id;
r->connection = conn.connection;
r->mmWidth = conn.mm_width;
r->mmHeight = conn.mm_height;
@@ -419,6 +414,9 @@ drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
r->connector_type = conn.connector_type;
r->connector_type_id = conn.connector_type_id;
+ if (!r->props || !r->prop_values || !r->modes || !r->encoders)
+ goto err_allocs;
+
err_allocs:
drmFree(U642VOID(conn.prop_values_ptr));
drmFree(U642VOID(conn.props_ptr));
@@ -492,7 +490,7 @@ drmModePropertyPtr drmModeGetProperty(int fd, uint32_t property_id)
if (prop.flags & DRM_MODE_PROP_ENUM) {
r->count_enums = prop.count_enum_blobs;
r->enums = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(struct drm_mode_property_enum));
- } else if (prop.flags & DRM_MODE_PROP_ENUM) {
+ } else if (prop.flags & DRM_MODE_PROP_BLOB) {
r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_enum_blobs, sizeof(uint32_t));
r->blob_ids = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(uint32_t));
r->count_blobs = prop.count_enum_blobs;
diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h
index f1b7c86e..59612a94 100644
--- a/libdrm/xf86drmMode.h
+++ b/libdrm/xf86drmMode.h
@@ -50,16 +50,8 @@
* buffer object interface. This object needs to be pinned.
*/
-/*
- * generation - these are to be read by userspace, and if it notices
- * while calling a get output or get crtc that the generation has changed
- * it should re-call the mode resource functions resync its view of the
- * outputs with current view.
- */
-
typedef struct _drmModeRes {
- uint32_t generation;
int count_fbs;
uint32_t *fbs;
@@ -98,31 +90,23 @@ typedef struct _drmModeProperty {
typedef struct _drmModeCrtc {
unsigned int crtc_id;
- unsigned int buffer_id; /**< FB id to connect to 0 = disconnect*/
- uint32_t generation;
+ unsigned int buffer_id; /**< FB id to connect to 0 = disconnect */
- uint32_t x, y; /**< Position on the frameuffer */
+ uint32_t x, y; /**< Position on the framebuffer */
uint32_t width, height;
int mode_valid;
struct drm_mode_modeinfo mode;
- int count_connectors;
- uint32_t connectors; /**< Connectors that are connected */
-
- int count_possibles;
- uint32_t possibles; /**< Connectors that can be connected */
-
int gamma_size; /**< Number of gamma stops */
} drmModeCrtc, *drmModeCrtcPtr;
typedef struct _drmModeEncoder {
- uint32_t generation;
unsigned int encoder_id;
unsigned int encoder_type;
- uint32_t crtc;
- uint32_t crtcs;
- uint32_t clones;
+ unsigned int crtc_id;
+ uint32_t possible_crtcs;
+ uint32_t possible_clones;
} drmModeEncoder, *drmModeEncoderPtr;
typedef enum {
@@ -141,9 +125,8 @@ typedef enum {
} drmModeSubPixel;
typedef struct _drmModeConnector {
- uint32_t generation;
unsigned int connector_id;
- unsigned int encoder; /**< Crtc currently connected to */
+ unsigned int encoder_id; /**< Encoder currently connected to */
unsigned int connector_type;
unsigned int connector_type_id;
drmModeConnection connection;
diff --git a/linux-core/Makefile b/linux-core/Makefile
index a359f775..1790bdb0 100644
--- a/linux-core/Makefile
+++ b/linux-core/Makefile
@@ -90,7 +90,7 @@ VIAHEADERS = via_drm.h via_drv.h via_3d_reg.h via_verifier.h $(DRMHEADERS)
MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS)
NVHEADERS = nv_drv.h $(DRMHEADERS)
FFBHEADERS = ffb_drv.h $(DRMHEADERS)
-NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h $(DRMHEADERS)
+NOUVEAUHEADERS = nouveau_drv.h nouveau_drm.h nouveau_reg.h nouveau_display.h $(DRMHEADERS)
XGIHEADERS = xgi_cmdlist.h xgi_drv.h xgi_misc.h xgi_regs.h $(DRMHEADERS)
RADEONMSHEADERS = radeon_ms_driver.h $(DRMHEADERS)
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index 29503004..768cd22a 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -35,7 +35,11 @@ nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \
nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
nv04_graph.o nv10_graph.o nv20_graph.o \
nv40_graph.o nv50_graph.o \
- nv04_instmem.o nv50_instmem.o
+ nv04_instmem.o nv50_instmem.o \
+ nouveau_bios.o \
+ nv50_crtc.o nv50_cursor.o nv50_lut.o nv50_fb.o nv50_output.o nv50_sor.o nv50_dac.o nv50_connector.o nv50_i2c.o nv50_display.o \
+ nv50_kms_wrapper.o \
+ nv50_fbcon.o
radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o
radeon_ms-objs := radeon_ms_drv.o radeon_ms_drm.o radeon_ms_family.o \
radeon_ms_state.o radeon_ms_bo.o radeon_ms_irq.o \
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
index e1b371cc..fc8d1fe8 100644
--- a/linux-core/drm_crtc.c
+++ b/linux-core/drm_crtc.c
@@ -43,10 +43,10 @@ struct drm_prop_enum_list {
* Global properties
*/
static struct drm_prop_enum_list drm_dpms_enum_list[] =
-{ { DPMSModeOn, "On" },
- { DPMSModeStandby, "Standby" },
- { DPMSModeSuspend, "Suspend" },
- { DPMSModeOff, "Off" }
+{ { DRM_MODE_DPMS_ON, "On" },
+ { DRM_MODE_DPMS_STANDBY, "Standby" },
+ { DRM_MODE_DPMS_SUSPEND, "Suspend" },
+ { DRM_MODE_DPMS_OFF, "Off" }
};
char *drm_get_dpms_name(int val)
@@ -60,27 +60,69 @@ char *drm_get_dpms_name(int val)
return "unknown";
}
+static struct drm_prop_enum_list drm_select_subconnector_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_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+char *drm_get_select_subconnector_name(int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(drm_select_subconnector_enum_list); i++)
+ if (drm_select_subconnector_enum_list[i].type == val)
+ return drm_select_subconnector_enum_list[i].name;
+
+ return "unknown";
+}
+
+static struct drm_prop_enum_list drm_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_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */
+ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+};
+
+char *drm_get_subconnector_name(int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(drm_subconnector_enum_list); i++)
+ if (drm_subconnector_enum_list[i].type == val)
+ return drm_subconnector_enum_list[i].name;
+
+ return "unknown";
+}
+
static struct drm_prop_enum_list drm_connector_enum_list[] =
-{ { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
- { DRM_MODE_CONNECTOR_VGA, "VGA" },
- { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
- { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
- { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
- { DRM_MODE_CONNECTOR_Composite, "Composite" },
- { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
- { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
- { DRM_MODE_CONNECTOR_Component, "Component" },
- { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
- { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort" },
- { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A" },
- { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B" },
+{ { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
+ { DRM_MODE_CONNECTOR_VGA, "VGA" },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+ { DRM_MODE_CONNECTOR_Composite, "Composite" },
+ { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+ { DRM_MODE_CONNECTOR_Component, "Component" },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+ { DRM_MODE_CONNECTOR_DisplayPort, "DisplayPort" },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A" },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B" },
};
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" },
+{ { 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)
@@ -100,6 +142,7 @@ char *drm_get_connector_name(struct drm_connector *connector)
connector->connector_type_id);
return buf;
}
+EXPORT_SYMBOL(drm_get_connector_name);
char *drm_get_connector_status_name(enum drm_connector_status status)
{
@@ -138,7 +181,7 @@ again:
ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
if (ret == -EAGAIN)
- goto again;
+ goto again;
obj->id = new_id;
obj->type = obj_type;
@@ -163,7 +206,7 @@ static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *
static void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
{
struct drm_mode_object *obj;
-
+
obj = idr_find(&dev->mode_config.crtc_idr, id);
if (!obj || (obj->type != type) || (obj->id != id))
return NULL;
@@ -410,7 +453,7 @@ void drm_encoder_init(struct drm_device *dev,
int encoder_type)
{
encoder->dev = dev;
-
+
drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
@@ -497,6 +540,38 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
}
/**
+ * 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)
+{
+ int i;
+
+ if (dev->mode_config.dvi_i_select_subconnector_property)
+ return 0;
+
+ dev->mode_config.dvi_i_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "select subconnector", 3);
+ /* add enum element 0-2 */
+ for (i = 0; i < 3; i++)
+ drm_property_add_enum(dev->mode_config.dvi_i_select_subconnector_property, i, drm_select_subconnector_enum_list[i].type,
+ drm_select_subconnector_enum_list[i].name);
+
+ /* This is a property which indicates the most likely thing to be connected. */
+ dev->mode_config.dvi_i_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE,
+ "subconnector", 3);
+ /* add enum element 0-2 */
+ for (i = 0; i < 3; i++)
+ drm_property_add_enum(dev->mode_config.dvi_i_subconnector_property, i, drm_subconnector_enum_list[i].type,
+ drm_subconnector_enum_list[i].name);
+
+ 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
@@ -507,14 +582,37 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
* responsible for allocating a list of format names and passing them to
* this routine.
*/
-bool drm_create_tv_properties(struct drm_device *dev, int num_modes,
+int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
char *modes[])
{
int i;
+ if (dev->mode_config.tv_select_subconnector_property) /* already done */
+ return 0;
+
+ dev->mode_config.tv_select_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ "select subconnector", 4);
+ /* add enum element 0 */
+ drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, 0, drm_select_subconnector_enum_list[0].type,
+ drm_select_subconnector_enum_list[0].name);
+ /* add enum element 3-5 */
+ for (i = 1; i < 4; i++)
+ drm_property_add_enum(dev->mode_config.tv_select_subconnector_property, i, drm_select_subconnector_enum_list[i + 2].type,
+ drm_select_subconnector_enum_list[i + 2].name);
+
+ /* This is a property which indicates the most likely thing to be connected. */
+ dev->mode_config.tv_subconnector_property = drm_property_create(dev, DRM_MODE_PROP_ENUM | DRM_MODE_PROP_IMMUTABLE,
+ "subconnector", 4);
+ /* add enum element 0 */
+ drm_property_add_enum(dev->mode_config.tv_subconnector_property, 0, drm_subconnector_enum_list[0].type,
+ drm_subconnector_enum_list[0].name);
+ /* add enum element 3-5 */
+ for (i = 1; i < 4; i++)
+ drm_property_add_enum(dev->mode_config.tv_subconnector_property, i, drm_subconnector_enum_list[i + 2].type,
+ drm_subconnector_enum_list[i + 2].name);
+
dev->mode_config.tv_left_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE |
- DRM_MODE_PROP_IMMUTABLE,
+ 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;
@@ -546,7 +644,7 @@ bool drm_create_tv_properties(struct drm_device *dev, int num_modes,
return 0;
}
-EXPORT_SYMBOL(drm_create_tv_properties);
+EXPORT_SYMBOL(drm_mode_create_tv_properties);
/**
* drm_mode_config_init - initialize DRM mode_configuration structure
@@ -573,7 +671,6 @@ void drm_mode_config_init(struct drm_device *dev)
drm_mode_create_standard_connector_properties(dev);
/* Just to be sure */
- dev->mode_config.current_generation = 0;
dev->mode_config.num_fb = 0;
dev->mode_config.num_connector = 0;
dev->mode_config.num_crtc = 0;
@@ -589,7 +686,7 @@ int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group)
total_objects += dev->mode_config.num_crtc;
total_objects += dev->mode_config.num_connector;
total_objects += dev->mode_config.num_encoder;
-
+
group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
if (!group->id_list)
return -ENOMEM;
@@ -735,7 +832,7 @@ void drm_crtc_convert_umode(struct drm_display_mode *out, struct drm_mode_modein
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
}
-
+
/**
* drm_mode_getresources - get graphics configuration
* @inode: inode from the ioctl
@@ -794,7 +891,7 @@ int drm_mode_getresources(struct drm_device *dev,
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;
@@ -804,7 +901,6 @@ int drm_mode_getresources(struct drm_device *dev,
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;
- card_res->generation = dev->mode_config.current_generation;
/* handle this in 4 parts */
/* FBs */
@@ -899,13 +995,11 @@ int drm_mode_getresources(struct drm_device *dev,
}
}
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:
+out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
@@ -947,18 +1041,16 @@ int drm_mode_getcrtc(struct drm_device *dev,
crtc_resp->x = crtc->x;
crtc_resp->y = crtc->y;
crtc_resp->gamma_size = crtc->gamma_size;
- crtc_resp->generation = dev->mode_config.current_generation;
if (crtc->fb)
crtc_resp->fb_id = crtc->fb->base.id;
else
crtc_resp->fb_id = 0;
- crtc_resp->connectors = 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;
}
@@ -1006,20 +1098,17 @@ int drm_mode_getconnector(struct drm_device *dev,
memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
- DRM_DEBUG("connector id %d:\n", out_resp->connector);
+ 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, DRM_MODE_OBJECT_CONNECTOR);
+ 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);
- list_for_each_entry(mode, &connector->modes, head)
- mode_count++;
-
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
if (connector->property_ids[i] != 0) {
props_count++;
@@ -1036,7 +1125,11 @@ int drm_mode_getconnector(struct drm_device *dev,
connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height);
}
- out_resp->generation = dev->mode_config.current_generation;
+ /* 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;
@@ -1044,10 +1137,11 @@ int drm_mode_getconnector(struct drm_device *dev,
out_resp->subpixel = connector->display_info.subpixel_order;
out_resp->connection = connector->status;
if (connector->encoder)
- out_resp->encoder = connector->encoder->base.id;
+ out_resp->encoder_id = connector->encoder->base.id;
else
- out_resp->encoder = 0;
+ 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;
@@ -1059,7 +1153,6 @@ int drm_mode_getconnector(struct drm_device *dev,
goto out;
}
copied++;
-
}
}
out_resp->count_modes = mode_count;
@@ -1112,7 +1205,7 @@ int drm_mode_getencoder(struct drm_device *dev,
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) {
@@ -1122,15 +1215,14 @@ int drm_mode_getencoder(struct drm_device *dev,
encoder = obj_to_encoder(obj);
if (encoder->crtc)
- enc_resp->crtc = encoder->crtc->base.id;
+ enc_resp->crtc_id = encoder->crtc->base.id;
else
- enc_resp->crtc = 0;
- enc_resp->generation = dev->mode_config.current_generation;
+ enc_resp->crtc_id = 0;
enc_resp->encoder_type = encoder->encoder_type;
enc_resp->encoder_id = encoder->base.id;
- enc_resp->crtcs = encoder->possible_crtcs;
- enc_resp->clones = encoder->possible_clones;
-
+ enc_resp->possible_crtcs = encoder->possible_crtcs;
+ enc_resp->possible_clones = encoder->possible_clones;
+
out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
@@ -1175,7 +1267,7 @@ int drm_mode_setcrtc(struct drm_device *dev,
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 */
@@ -1183,7 +1275,7 @@ int drm_mode_setcrtc(struct drm_device *dev,
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;
+ fb = crtc->fb;
}
}
} else {
@@ -1602,7 +1694,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
ret = -ENOMEM;
goto out;
}
-
+
drm_crtc_convert_umode(mode, umode);
ret = drm_mode_attachmode(dev, connector, mode);
@@ -1642,7 +1734,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
goto out;
}
connector = obj_to_connector(obj);
-
+
drm_crtc_convert_umode(&mode, umode);
ret = drm_mode_detachmode(dev, connector, &mode);
out:
@@ -1726,7 +1818,7 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
kfree(property->values);
drm_mode_object_put(dev, &property->base);
list_del(&property->head);
- kfree(property);
+ kfree(property);
}
EXPORT_SYMBOL(drm_property_destroy);
@@ -1837,17 +1929,16 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
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;
@@ -1864,22 +1955,22 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
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 = enum_count;
+ out_resp->count_enum_blobs = blob_count;
}
done:
mutex_unlock(&dev->mode_config.mutex);
@@ -1904,7 +1995,7 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
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;
}
@@ -1927,7 +2018,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
void *blob_ptr;
mutex_lock(&dev->mode_config.mutex);
- obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_CONNECTOR);
+ obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
if (!obj) {
ret = -EINVAL;
goto done;
@@ -1955,8 +2046,15 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, str
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;
}
@@ -1988,7 +2086,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
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;
@@ -2017,6 +2115,9 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
}
}
+ /* store the property value */
+ drm_connector_property_set_value(connector, property, out_resp->value);
+
if (connector->funcs->set_property)
ret = connector->funcs->set_property(connector, property, out_resp->value);
@@ -2127,7 +2228,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
goto out;
}
crtc = obj_to_crtc(obj);
-
+
/* memcpy into gamma store */
if (crtc_lut->gamma_size != crtc->gamma_size) {
ret = -EINVAL;
@@ -2178,7 +2279,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
goto out;
}
crtc = obj_to_crtc(obj);
-
+
/* memcpy into gamma store */
if (crtc_lut->gamma_size != crtc->gamma_size) {
ret = -EINVAL;
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
index 2b577b93..d4bb8794 100644
--- a/linux-core/drm_crtc.h
+++ b/linux-core/drm_crtc.h
@@ -90,6 +90,8 @@ enum drm_mode_status {
.vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
.vscan = (vs), .flags = (f), .vrefresh = 0
+#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
+
struct drm_display_mode {
/* Header */
struct list_head head;
@@ -147,43 +149,6 @@ struct drm_display_mode {
float hsync;
};
-/* Video mode flags */
-#define V_PHSYNC (1<<0)
-#define V_NHSYNC (1<<1)
-#define V_PVSYNC (1<<2)
-#define V_NVSYNC (1<<3)
-#define V_INTERLACE (1<<4)
-#define V_DBLSCAN (1<<5)
-#define V_CSYNC (1<<6)
-#define V_PCSYNC (1<<7)
-#define V_NCSYNC (1<<8)
-#define V_HSKEW (1<<9) /* hskew provided */
-#define V_BCAST (1<<10)
-#define V_PIXMUX (1<<11)
-#define V_DBLCLK (1<<12)
-#define V_CLKDIV2 (1<<13)
-
-#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
-
-#define DPMSModeOn 0
-#define DPMSModeStandby 1
-#define DPMSModeSuspend 2
-#define DPMSModeOff 3
-
-#define DRM_MODE_CONNECTOR_Unknown 0
-#define DRM_MODE_CONNECTOR_VGA 1
-#define DRM_MODE_CONNECTOR_DVII 2
-#define DRM_MODE_CONNECTOR_DVID 3
-#define DRM_MODE_CONNECTOR_DVIA 4
-#define DRM_MODE_CONNECTOR_Composite 5
-#define DRM_MODE_CONNECTOR_SVIDEO 6
-#define DRM_MODE_CONNECTOR_LVDS 7
-#define DRM_MODE_CONNECTOR_Component 8
-#define DRM_MODE_CONNECTOR_9PinDIN 9
-#define DRM_MODE_CONNECTOR_DisplayPort 10
-#define DRM_MODE_CONNECTOR_HDMIA 11
-#define DRM_MODE_CONNECTOR_HDMIB 12
-
enum drm_connector_status {
connector_status_connected = 1,
connector_status_disconnected = 2,
@@ -561,9 +526,6 @@ struct drm_mode_config {
/* in-kernel framebuffers - hung of filp_head in drm_framebuffer */
struct list_head fb_kernel_list;
- /* currently in use generation id */
- int current_generation;
-
int min_width, min_height;
int max_width, max_height;
struct drm_mode_config_funcs *funcs;
@@ -574,7 +536,13 @@ struct drm_mode_config {
struct drm_property *edid_property;
struct drm_property *dpms_property;
+ /* optional properties */
+ struct drm_property *dvi_i_subconnector_property;
+ struct drm_property *dvi_i_select_subconnector_property;
+
/* TV properties */
+ struct drm_property *tv_subconnector_property;
+ struct drm_property *tv_select_subconnector_property;
struct drm_property *tv_mode_property;
struct drm_property *tv_left_margin_property;
struct drm_property *tv_right_margin_property;
@@ -615,10 +583,13 @@ extern void drm_encoder_cleanup(struct drm_encoder *encoder);
extern char *drm_get_connector_name(struct drm_connector *connector);
extern char *drm_get_dpms_name(int val);
+extern char *drm_get_select_subconnector_name(int val);
+extern char *drm_get_subconnector_name(int val);
extern void drm_fb_release(struct file *filp);
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 unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter);
extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode);
@@ -676,7 +647,8 @@ extern struct drm_property *drm_property_create(struct drm_device *dev, int flag
extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
extern int drm_property_add_enum(struct drm_property *property, int index,
uint64_t value, const char *name);
-extern bool drm_create_tv_properties(struct drm_device *dev, int num_formats,
+extern int drm_mode_create_dvi_i_properties(struct drm_device *dev);
+extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats,
char *formats[]);
extern char *drm_get_encoder_name(struct drm_encoder *encoder);
diff --git a/linux-core/drm_crtc_helper.c b/linux-core/drm_crtc_helper.c
index ffd20342..7e5d8fcc 100644
--- a/linux-core/drm_crtc_helper.c
+++ b/linux-core/drm_crtc_helper.c
@@ -38,7 +38,7 @@
static struct drm_display_mode std_mode[] = {
{ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656,
752, 800, 0, 480, 490, 492, 525, 0,
- V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */
};
/**
@@ -182,14 +182,14 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
encoder_funcs = encoder->helper_private;
if (!encoder->crtc)
- (*encoder_funcs->dpms)(encoder, DPMSModeOff);
+ (*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, DPMSModeOff);
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
crtc->fb = NULL;
}
}
diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c
index 555fdcdf..24a6df2f 100644
--- a/linux-core/drm_drv.c
+++ b/linux-core/drm_drv.c
@@ -142,7 +142,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_WAIT_HOTPLUG, drm_wait_hotplug, 0),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_REPLACEFB, drm_mode_replacefb, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_ROOT_ONLY|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER),
diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c
index 0d600396..07894720 100644
--- a/linux-core/drm_edid.c
+++ b/linux-core/drm_edid.c
@@ -122,19 +122,30 @@ static bool edid_is_valid(struct edid *edid)
if (memcmp(edid->header, edid_header, sizeof(edid_header)))
goto bad;
- if (edid->version != 1)
+ if (edid->version != 1) {
+ DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
goto bad;
- if (edid->revision <= 0 || edid->revision > 3)
+ }
+ if (edid->revision <= 0 || edid->revision > 3) {
+ DRM_ERROR("EDID has minor version %d, which is not between 0-3\n", edid->revision);
goto bad;
+ }
for (i = 0; i < EDID_LENGTH; i++)
csum += raw_edid[i];
- if (csum)
+ 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;
}
@@ -324,15 +335,15 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
drm_mode_set_name(mode);
if (pt->interlaced)
- mode->flags |= V_INTERLACE;
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
pt->hsync_positive = 1;
pt->vsync_positive = 1;
}
- mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC;
- mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC;
+ mode->flags |= pt->hsync_positive ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ mode->flags |= pt->vsync_positive ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
mode->width_mm = pt->width_mm_lo | (pt->width_mm_hi << 8);
mode->height_mm = pt->height_mm_lo | (pt->height_mm_hi << 8);
@@ -356,55 +367,55 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
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,
- V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */
+ 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,
- V_PHSYNC | V_PVSYNC) }, /* 800x600@56Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 640x480@75Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 640x480@72Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 640x480@67Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 720x400@88Hz */
+ 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,
- V_NHSYNC | V_PVSYNC) }, /* 720x400@70Hz */
+ 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,
- V_PHSYNC | V_PVSYNC) }, /* 1280x1024@75Hz */
+ 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,
- V_PHSYNC | V_PVSYNC) }, /* 1024x768@75Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 1024x768@70Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 1024x768@60Hz */
+ 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,
- V_PHSYNC | V_PVSYNC | V_INTERLACE) }, /* 1024x768@43Hz */
+ 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,
- V_NHSYNC | V_NVSYNC) }, /* 832x624@75Hz */
+ 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,
- V_PHSYNC | V_PVSYNC) }, /* 800x600@75Hz */
+ 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,
- V_PHSYNC | V_PVSYNC) }, /* 800x600@72Hz */
+ 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,
- V_PHSYNC | V_PVSYNC) }, /* 1152x864@75Hz */
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */
};
#define EDID_EST_TIMINGS 16
@@ -545,7 +556,7 @@ static int add_detailed_info(struct drm_connector *connector,
#define DDC_ADDR 0x50
-static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
+unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
{
unsigned char start = 0x0;
unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
@@ -576,6 +587,7 @@ static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter)
kfree(buf);
return NULL;
}
+EXPORT_SYMBOL(drm_do_probe_ddc_edid);
static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
{
@@ -583,21 +595,13 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
unsigned char *edid = NULL;
int i, j;
- /*
- * Startup the bus:
- * Set clock line high (but give it time to come up)
- * Then set clock & data low
- */
algo_data->setscl(algo_data->data, 1);
- udelay(550); /* startup delay */
- algo_data->setscl(algo_data->data, 0);
- algo_data->setsda(algo_data->data, 0);
for (i = 0; i < 3; i++) {
/* For some old monitors we need the
* following process to initialize/stop DDC
*/
- algo_data->setsda(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 1);
msleep(13);
algo_data->setscl(algo_data->data, 1);
@@ -632,16 +636,16 @@ static unsigned char *drm_ddc_read(struct i2c_adapter *adapter)
algo_data->setsda(algo_data->data, 1);
msleep(15);
algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 0);
if (edid)
break;
}
/* Release the DDC lines when done or the Apple Cinema HD display
* will switch off
*/
- algo_data->setsda(algo_data->data, 0);
- algo_data->setscl(algo_data->data, 0);
+ algo_data->setsda(algo_data->data, 1);
algo_data->setscl(algo_data->data, 1);
-
+
return edid;
}
diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c
index 3ed427fb..4ee00305 100644
--- a/linux-core/drm_modes.c
+++ b/linux-core/drm_modes.c
@@ -89,6 +89,7 @@ void drm_mode_list_concat(struct list_head *head, struct list_head *new)
list_move_tail(entry, new);
}
}
+EXPORT_SYMBOL(drm_mode_list_concat);
/**
* drm_mode_width - get the width of a mode
@@ -161,9 +162,9 @@ int drm_mode_vrefresh(struct drm_display_mode *mode)
calc_val /= mode->vtotal;
refresh = calc_val;
- if (mode->flags & V_INTERLACE)
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
refresh *= 2;
- if (mode->flags & V_DBLSCAN)
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
refresh /= 2;
if (mode->vscan > 1)
refresh /= mode->vscan;
@@ -197,7 +198,7 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
p->crtc_vsync_end = p->vsync_end;
p->crtc_vtotal = p->vtotal;
- if (p->flags & V_INTERLACE) {
+ if (p->flags & DRM_MODE_FLAG_INTERLACE) {
if (adjust_flags & CRTC_INTERLACE_HALVE_V) {
p->crtc_vdisplay /= 2;
p->crtc_vsync_start /= 2;
@@ -208,7 +209,7 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
p->crtc_vtotal |= 1;
}
- if (p->flags & V_DBLSCAN) {
+ if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
p->crtc_vdisplay *= 2;
p->crtc_vsync_start *= 2;
p->crtc_vsync_end *= 2;
@@ -401,6 +402,7 @@ void drm_mode_prune_invalid(struct drm_device *dev,
}
}
}
+EXPORT_SYMBOL(drm_mode_prune_invalid);
/**
* drm_mode_compare - compare modes for favorability
@@ -525,7 +527,7 @@ 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
@@ -564,3 +566,4 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
}
}
}
+EXPORT_SYMBOL(drm_mode_connector_list_update);
diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c
index 92371c22..5c384a60 100644
--- a/linux-core/drm_sysfs.c
+++ b/linux-core/drm_sysfs.c
@@ -176,6 +176,18 @@ static ssize_t dpms_show(struct device *device,
return snprintf(buf, PAGE_SIZE, "%s", drm_get_dpms_name((int)dpms_status));
}
+static ssize_t enabled_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+
+ if (connector->encoder)
+ return snprintf(buf, PAGE_SIZE, "enabled");
+ else
+ return snprintf(buf, PAGE_SIZE, "disabled");
+}
+
static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
@@ -219,12 +231,91 @@ static ssize_t modes_show(struct device *device,
return written;
}
+static ssize_t subconnector_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop = NULL;
+ uint64_t subconnector;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ prop = dev->mode_config.dvi_i_subconnector_property;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ prop = dev->mode_config.tv_subconnector_property;
+ break;
+ default:
+ DRM_ERROR("Wrong connector type for this property\n");
+ return 0;
+ }
+
+ if (!prop) {
+ DRM_ERROR("Unable to find subconnector property\n");
+ return 0;
+ }
+
+ ret = drm_connector_property_get_value(connector, prop, &subconnector);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", drm_get_subconnector_name((int)subconnector));
+}
+
+static ssize_t select_subconnector_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = container_of(device, struct drm_connector, kdev);
+ struct drm_device *dev = connector->dev;
+ struct drm_property *prop = NULL;
+ uint64_t subconnector;
+ int ret;
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ prop = dev->mode_config.dvi_i_select_subconnector_property;
+ break;
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ prop = dev->mode_config.tv_select_subconnector_property;
+ break;
+ default:
+ DRM_ERROR("Wrong connector type for this property\n");
+ return 0;
+ }
+
+ if (!prop) {
+ DRM_ERROR("Unable to find select subconnector property\n");
+ return 0;
+ }
+
+ ret = drm_connector_property_get_value(connector, prop, &subconnector);
+ if (ret)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", drm_get_select_subconnector_name((int)subconnector));
+}
+
static struct device_attribute connector_attrs[] = {
__ATTR_RO(status),
+ __ATTR_RO(enabled),
__ATTR_RO(dpms),
__ATTR_RO(modes),
};
+/* These attributes are for both DVI-I connectors and all types of tv-out. */
+static struct device_attribute connector_attrs_opt1[] = {
+ __ATTR_RO(subconnector),
+ __ATTR_RO(select_subconnector),
+};
+
static struct bin_attribute edid_attr = {
.attr.name = "edid",
.size = 128,
@@ -269,12 +360,32 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
goto out;
}
+ /* Standard attributes */
+
for (i = 0; i < ARRAY_SIZE(connector_attrs); i++) {
ret = device_create_file(&connector->kdev, &connector_attrs[i]);
if (ret)
goto err_out_files;
}
+ /* Optional attributes */
+ /* On the long run it maybe a good idea to make one set of optionals per connector type. */
+
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_Composite:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Component:
+ for (i = 0; i < ARRAY_SIZE(connector_attrs_opt1); i++) {
+ ret = device_create_file(&connector->kdev, &connector_attrs_opt1[i]);
+ if (ret)
+ goto err_out_files;
+ }
+ break;
+ default:
+ break;
+ }
+
ret = sysfs_create_bin_file(&connector->kdev.kobj, &edid_attr);
if (ret)
goto err_out_files;
diff --git a/linux-core/dvo_ch7017.c b/linux-core/dvo_ch7017.c
index 194a7af1..b10e0388 100644
--- a/linux-core/dvo_ch7017.c
+++ b/linux-core/dvo_ch7017.c
@@ -327,7 +327,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
(mode->hdisplay & 0x0700) >> 8;
- ch7017_dpms(dvo, DPMSModeOff);
+ ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
horizontal_active_pixel_input);
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
@@ -363,7 +363,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
CH7017_DAC3_POWER_DOWN |
CH7017_TV_POWER_DOWN_EN);
- if (mode == DPMSModeOn) {
+ if (mode == DRM_MODE_DPMS_ON) {
/* Turn on the LVDS */
ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
val & ~CH7017_LVDS_POWER_DOWN_EN);
@@ -418,7 +418,7 @@ static void ch7017_restore(struct intel_dvo_device *dvo)
struct ch7017_priv *priv = dvo->dev_priv;
/* Power down before changing mode */
- ch7017_dpms(dvo, DPMSModeOff);
+ ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi);
ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo);
diff --git a/linux-core/dvo_ch7xxx.c b/linux-core/dvo_ch7xxx.c
index f9beac0b..77c86395 100644
--- a/linux-core/dvo_ch7xxx.c
+++ b/linux-core/dvo_ch7xxx.c
@@ -289,10 +289,10 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
- if (mode->flags & V_PHSYNC)
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
idf |= CH7xxx_IDF_HSP;
- if (mode->flags & V_PVSYNC)
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
idf |= CH7xxx_IDF_HSP;
ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
@@ -301,7 +301,7 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
/* set the CH7xxx power state */
static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
{
- if (mode == DPMSModeOn)
+ if (mode == DRM_MODE_DPMS_ON)
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
else
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
diff --git a/linux-core/dvo_ivch.c b/linux-core/dvo_ivch.c
index 5af7a9b1..788b0721 100644
--- a/linux-core/dvo_ivch.c
+++ b/linux-core/dvo_ivch.c
@@ -321,13 +321,13 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
if (!ivch_read(dvo, VR01, &vr01))
return;
- if (mode == DPMSModeOn)
+ if (mode == DRM_MODE_DPMS_ON)
backlight = 1;
else
backlight = 0;
ivch_write(dvo, VR80, backlight);
- if (mode == DPMSModeOn)
+ if (mode == DRM_MODE_DPMS_ON)
vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
else
vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
@@ -339,7 +339,7 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
if (!ivch_read(dvo, VR30, &vr30))
break;
- if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DPMSModeOn))
+ if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON))
break;
udelay(1000);
}
diff --git a/linux-core/dvo_sil164.c b/linux-core/dvo_sil164.c
index d0fa4913..033a4bb0 100644
--- a/linux-core/dvo_sil164.c
+++ b/linux-core/dvo_sil164.c
@@ -226,7 +226,7 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
if (ret == false)
return;
- if (mode == DPMSModeOn)
+ if (mode == DRM_MODE_DPMS_ON)
ch |= SIL164_8_PD;
else
ch &= ~SIL164_8_PD;
diff --git a/linux-core/dvo_tfp410.c b/linux-core/dvo_tfp410.c
index 65b76c86..207fda80 100644
--- a/linux-core/dvo_tfp410.c
+++ b/linux-core/dvo_tfp410.c
@@ -248,7 +248,7 @@ static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
return;
- if (mode == DPMSModeOn)
+ if (mode == DRM_MODE_DPMS_ON)
ctl1 |= TFP410_CTL_1_PD;
else
ctl1 &= ~TFP410_CTL_1_PD;
diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c
index 2e1611e1..2ed1cb79 100644
--- a/linux-core/intel_crt.c
+++ b/linux-core/intel_crt.c
@@ -44,16 +44,16 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
temp &= ~ADPA_DAC_ENABLE;
switch(mode) {
- case DPMSModeOn:
+ case DRM_MODE_DPMS_ON:
temp |= ADPA_DAC_ENABLE;
break;
- case DPMSModeStandby:
+ case DRM_MODE_DPMS_STANDBY:
temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
break;
- case DPMSModeSuspend:
+ case DRM_MODE_DPMS_SUSPEND:
temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
break;
- case DPMSModeOff:
+ case DRM_MODE_DPMS_OFF:
temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
break;
}
@@ -74,7 +74,7 @@ static void intel_crt_restore(struct drm_connector *connector)
static int intel_crt_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- if (mode->flags & V_DBLSCAN)
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (mode->clock > 400000 || mode->clock < 25000)
@@ -118,9 +118,9 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
}
adpa = 0;
- if (adjusted_mode->flags & V_PHSYNC)
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
adpa |= ADPA_HSYNC_ACTIVE_HIGH;
- if (adjusted_mode->flags & V_PVSYNC)
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
adpa |= ADPA_VSYNC_ACTIVE_HIGH;
if (intel_crtc->pipe == 0)
diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c
index 40cbc492..a761b61b 100644
--- a/linux-core/intel_display.c
+++ b/linux-core/intel_display.c
@@ -471,12 +471,12 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
bool enabled;
/* XXX: When our outputs are all unaware of DPMS modes other than off
- * and on, we should map those modes to DPMSModeOff in the CRTC.
+ * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
*/
switch (mode) {
- case DPMSModeOn:
- case DPMSModeStandby:
- case DPMSModeSuspend:
+ case DRM_MODE_DPMS_ON:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
/* Enable the DPLL */
temp = I915_READ(dpll_reg);
if ((temp & DPLL_VCO_ENABLE) == 0) {
@@ -512,7 +512,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
/* Give the overlay scaler a chance to enable if it's on this pipe */
//intel_crtc_dpms_video(crtc, true); TODO
break;
- case DPMSModeOff:
+ case DRM_MODE_DPMS_OFF:
/* Give the overlay scaler a chance to disable if it's on this pipe */
//intel_crtc_dpms_video(crtc, FALSE); TODO
@@ -561,7 +561,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
if (!master_priv->sarea_priv)
return;
- enabled = crtc->enabled && mode != DPMSModeOff;
+ enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
switch (pipe) {
case 0:
@@ -583,27 +583,27 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
static void intel_crtc_prepare (struct drm_crtc *crtc)
{
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
- crtc_funcs->dpms(crtc, DPMSModeOff);
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}
static void intel_crtc_commit (struct drm_crtc *crtc)
{
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
- crtc_funcs->dpms(crtc, DPMSModeOn);
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
}
void intel_encoder_prepare (struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
/* lvds has its own version of prepare see intel_lvds_prepare */
- encoder_funcs->dpms(encoder, DPMSModeOff);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
}
void intel_encoder_commit (struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
/* lvds has its own version of commit see intel_lvds_commit */
- encoder_funcs->dpms(encoder, DPMSModeOn);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}
static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -1115,7 +1115,7 @@ static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
/* VESA 640x480x72Hz mode to set on the pipe */
static struct drm_display_mode load_detect_mode = {
DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,
- 704, 832, 0, 480, 489, 491, 520, 0, V_NHSYNC | V_NVSYNC),
+ 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
};
struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
@@ -1148,10 +1148,10 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
/* Make sure the crtc and connector are running */
intel_crtc = to_intel_crtc(crtc);
*dpms_mode = intel_crtc->dpms_mode;
- if (intel_crtc->dpms_mode != DPMSModeOn) {
+ if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
crtc_funcs = crtc->helper_private;
- crtc_funcs->dpms(crtc, DPMSModeOn);
- encoder_funcs->dpms(encoder, DPMSModeOn);
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}
return crtc;
}
@@ -1187,9 +1187,9 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
mode = &load_detect_mode;
drm_crtc_helper_set_mode(crtc, mode, 0, 0);
} else {
- if (intel_crtc->dpms_mode != DPMSModeOn) {
+ if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
crtc_funcs = crtc->helper_private;
- crtc_funcs->dpms(crtc, DPMSModeOn);
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
}
/* Add this connector to the crtc */
@@ -1218,7 +1218,7 @@ void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_
}
/* Switch crtc and output back off if necessary */
- if (crtc->enabled && dpms_mode != DPMSModeOn) {
+ if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) {
if (encoder->crtc == crtc)
encoder_funcs->dpms(encoder, dpms_mode);
crtc_funcs->dpms(crtc, dpms_mode);
@@ -1381,7 +1381,7 @@ void intel_crtc_init(struct drm_device *dev, int pipe)
}
intel_crtc->cursor_addr = 0;
- intel_crtc->dpms_mode = DPMSModeOff;
+ intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
intel_crtc->mode_set.crtc = &intel_crtc->base;
diff --git a/linux-core/intel_dvo.c b/linux-core/intel_dvo.c
index e6df8fdd..39ec65d2 100644
--- a/linux-core/intel_dvo.c
+++ b/linux-core/intel_dvo.c
@@ -93,7 +93,7 @@ static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
u32 dvo_reg = dvo->dvo_reg;
u32 temp = I915_READ(dvo_reg);
- if (mode == DPMSModeOn) {
+ if (mode == DRM_MODE_DPMS_ON) {
I915_WRITE(dvo_reg, temp | DVO_ENABLE);
I915_READ(dvo_reg);
dvo->dev_ops->dpms(dvo, mode);
@@ -139,7 +139,7 @@ static int intel_dvo_mode_valid(struct drm_connector *connector,
struct intel_output *intel_output = to_intel_output(connector);
struct intel_dvo_device *dvo = intel_output->dev_priv;
- if (mode->flags & V_DBLSCAN)
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
/* XXX: Validate clock range */
@@ -225,9 +225,9 @@ static void intel_dvo_mode_set(struct drm_encoder *encoder,
if (pipe == 1)
dvo_val |= DVO_PIPE_B_SELECT;
dvo_val |= DVO_PIPE_STALL;
- if (adjusted_mode->flags & V_PHSYNC)
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
- if (adjusted_mode->flags & V_PVSYNC)
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
dvo_val |= DVO_VSYNC_ACTIVE_HIGH;
I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED);
@@ -387,9 +387,9 @@ intel_dvo_get_current_mode (struct drm_connector *connector)
if (mode) {
mode->type |= DRM_MODE_TYPE_PREFERRED;
if (dvo_val & DVO_HSYNC_ACTIVE_HIGH)
- mode->flags |= V_PHSYNC;
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
- mode->flags |= V_PVSYNC;
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
}
}
}
diff --git a/linux-core/intel_fb.c b/linux-core/intel_fb.c
index 0780aae7..bc056bc3 100644
--- a/linux-core/intel_fb.c
+++ b/linux-core/intel_fb.c
@@ -263,8 +263,8 @@ static int intelfb_set_par(struct fb_info *info)
drm_mode->clock = PICOS2KHZ(var->pixclock);
drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
drm_mode->flags = 0;
- drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? V_PHSYNC : V_NHSYNC;
- drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? V_PVSYNC : V_NVSYNC;
+ drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
drm_mode_set_name(drm_mode);
drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
@@ -982,7 +982,7 @@ static int intelfb_single_fb_probe(struct drm_device *dev)
struct intel_framebuffer *intel_fb;
struct fb_info *info;
struct intelfb_par *par;
- struct drm_mode_set *modeset;
+ struct drm_mode_set *modeset = NULL;
DRM_DEBUG("\n");
/* first up get a count of crtcs now in use and new min/maxes width/heights */
diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c
index 69d88497..fa8209c7 100644
--- a/linux-core/intel_lvds.c
+++ b/linux-core/intel_lvds.c
@@ -93,7 +93,7 @@ static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
- if (mode == DPMSModeOn)
+ if (mode == DRM_MODE_DPMS_ON)
intel_lvds_set_power(dev, true);
else
intel_lvds_set_power(dev, false);
diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c
index f0a47e2e..6624bde2 100644
--- a/linux-core/intel_sdvo.c
+++ b/linux-core/intel_sdvo.c
@@ -363,16 +363,16 @@ static bool intel_sdvo_set_encoder_power_state(struct intel_output *intel_output
u8 status, state = SDVO_ENCODER_STATE_ON;
switch (mode) {
- case DPMSModeOn:
+ case DRM_MODE_DPMS_ON:
state = SDVO_ENCODER_STATE_ON;
break;
- case DPMSModeStandby:
+ case DRM_MODE_DPMS_STANDBY:
state = SDVO_ENCODER_STATE_STANDBY;
break;
- case DPMSModeSuspend:
+ case DRM_MODE_DPMS_SUSPEND:
state = SDVO_ENCODER_STATE_SUSPEND;
break;
- case DPMSModeOff:
+ case DRM_MODE_DPMS_OFF:
state = SDVO_ENCODER_STATE_OFF;
break;
}
@@ -602,9 +602,9 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
((v_sync_len & 0x30) >> 4);
output_dtd.part2.dtd_flags = 0x18;
- if (mode->flags & V_PHSYNC)
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
output_dtd.part2.dtd_flags |= 0x2;
- if (mode->flags & V_PVSYNC)
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
output_dtd.part2.dtd_flags |= 0x4;
output_dtd.part2.sdvo_flags = 0;
@@ -691,12 +691,12 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
u32 temp;
- if (mode != DPMSModeOn) {
+ if (mode != DRM_MODE_DPMS_ON) {
intel_sdvo_set_active_outputs(intel_output, 0);
if (0)
intel_sdvo_set_encoder_power_state(intel_output, mode);
- if (mode == DPMSModeOff) {
+ if (mode == DRM_MODE_DPMS_OFF) {
temp = I915_READ(sdvo_priv->output_device);
if ((temp & SDVO_ENABLE) != 0) {
intel_sdvo_write_sdvox(intel_output, temp & ~SDVO_ENABLE);
@@ -825,7 +825,7 @@ static int intel_sdvo_mode_valid(struct drm_connector *connector,
struct intel_output *intel_output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
- if (mode->flags & V_DBLSCAN)
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
if (sdvo_priv->pixel_clock_min > mode->clock)
diff --git a/linux-core/intel_tv.c b/linux-core/intel_tv.c
index ba680635..b89ccece 100644
--- a/linux-core/intel_tv.c
+++ b/linux-core/intel_tv.c
@@ -904,12 +904,12 @@ intel_tv_dpms(struct drm_encoder *encoder, int mode)
struct drm_i915_private *dev_priv = dev->dev_private;
switch(mode) {
- case DPMSModeOn:
+ case DRM_MODE_DPMS_ON:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
break;
- case DPMSModeStandby:
- case DPMSModeSuspend:
- case DPMSModeOff:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
break;
}
@@ -1713,7 +1713,7 @@ intel_tv_init(struct drm_device *dev)
goto out;
for (i = 0; i < NUM_TV_MODES; i++)
tv_format_names[i] = tv_modes[i].name;
- drm_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
+ drm_mode_create_tv_properties(dev, NUM_TV_MODES, tv_format_names);
drm_connector_attach_property(connector, dev->mode_config.tv_mode_property,
initial_mode);
diff --git a/linux-core/nouveau_bios.c b/linux-core/nouveau_bios.c
new file mode 100644
index 00000000..3e7fe23f
--- /dev/null
+++ b/linux-core/nouveau_bios.c
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2005-2006 Erik Waling
+ * Copyright (C) 2006 Stephane Marchesin
+ * Copyright (C) 2007-2008 Stuart Bennett
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include <asm/byteorder.h>
+#include "nouveau_bios.h"
+#include "nouveau_drv.h"
+
+/* returns true if it mismatches */
+static bool nv_checksum(const uint8_t *data, unsigned int length)
+{
+ /* there's a few checksums in the BIOS, so here's a generic checking function */
+ int i;
+ uint8_t sum = 0;
+
+ for (i = 0; i < length; i++)
+ sum += data[i];
+
+ if (sum)
+ return true;
+
+ return false;
+}
+
+static int nv_valid_bios(struct drm_device *dev, uint8_t *data)
+{
+ /* check for BIOS signature */
+ if (!(data[0] == 0x55 && data[1] == 0xAA)) {
+ DRM_ERROR("BIOS signature not found.\n");
+ return 0;
+ }
+
+ if (nv_checksum(data, data[2] * 512)) {
+ DRM_ERROR("BIOS checksum invalid.\n");
+ return 1;
+ }
+
+ return 2;
+}
+
+static void nv_shadow_bios_rom(struct drm_device *dev, uint8_t *data)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i;
+
+ /* enable access to rom */
+ NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED);
+
+ /* This is also valid for pre-NV50, it just happened to be the only define already present. */
+ for (i=0; i < NV50_PROM__ESIZE; i++) {
+ /* Appearantly needed for a 6600GT/6800LE bug. */
+ data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i);
+ data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i);
+ data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i);
+ data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i);
+ data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i);
+ }
+
+ /* disable access to rom */
+ NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+}
+
+static void nv_shadow_bios_ramin(struct drm_device *dev, uint8_t *data)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t old_bar0_pramin = 0;
+ int i;
+
+ /* Move the bios copy to the start of ramin? */
+ if (dev_priv->card_type >= NV_50) {
+ uint32_t vbios_vram = (NV_READ(0x619f04) & ~0xff) << 8;
+
+ if (!vbios_vram)
+ vbios_vram = (NV_READ(0x1700) << 16) + 0xf0000;
+
+ old_bar0_pramin = NV_READ(0x1700);
+ NV_WRITE(0x1700, vbios_vram >> 16);
+ }
+
+ for (i=0; i < NV50_PROM__ESIZE; i++)
+ data[i] = DRM_READ8(dev_priv->mmio, NV04_PRAMIN + i);
+
+ if (dev_priv->card_type >= NV_50)
+ NV_WRITE(0x1700, old_bar0_pramin);
+}
+
+static bool nv_shadow_bios(struct drm_device *dev, uint8_t *data)
+{
+ nv_shadow_bios_rom(dev, data);
+ if (nv_valid_bios(dev, data) == 2)
+ return true;
+
+ nv_shadow_bios_ramin(dev, data);
+ if (nv_valid_bios(dev, data))
+ return true;
+
+ return false;
+}
+
+struct bit_entry {
+ uint8_t id[2];
+ uint16_t length;
+ uint16_t offset;
+};
+
+static int parse_bit_C_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry)
+{
+ /* offset + 8 (16 bits): PLL limits table pointer
+ *
+ * There's more in here, but that's unknown.
+ */
+
+ if (bitentry->length < 10) {
+ DRM_ERROR( "Do not understand BIT C table\n");
+ return 0;
+ }
+
+ bios->pll_limit_tbl_ptr = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset + 8])));
+
+ return 1;
+}
+
+static void parse_bit_structure(struct drm_device *dev, struct bios *bios, const uint16_t bitoffset)
+{
+ int entries = bios->data[bitoffset + 4];
+ /* parse i first, I next (which needs C & M before it), and L before D */
+ char parseorder[] = "iCMILDT";
+ struct bit_entry bitentry;
+ int i, j, offset;
+
+ for (i = 0; i < sizeof(parseorder); i++) {
+ for (j = 0, offset = bitoffset + 6; j < entries; j++, offset += 6) {
+ bitentry.id[0] = bios->data[offset];
+ bitentry.id[1] = bios->data[offset + 1];
+ bitentry.length = le16_to_cpu(*((uint16_t *)&bios->data[offset + 2]));
+ bitentry.offset = le16_to_cpu(*((uint16_t *)&bios->data[offset + 4]));
+
+ if (bitentry.id[0] != parseorder[i])
+ continue;
+
+ switch (bitentry.id[0]) {
+ case 'C':
+ parse_bit_C_tbl_entry(dev, bios, &bitentry);
+ break;
+ }
+ }
+ }
+}
+
+static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
+{
+ int i, j;
+
+ for (i = 0; i <= (n - len); i++) {
+ for (j = 0; j < len; j++)
+ if (data[i + j] != str[j])
+ break;
+ if (j == len)
+ return i;
+ }
+
+ return 0;
+}
+
+static void
+read_dcb_i2c_entry(struct drm_device *dev, uint8_t dcb_version, uint16_t i2ctabptr, int index)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct bios *bios = &dev_priv->bios;
+ uint8_t *i2ctable = &bios->data[i2ctabptr];
+ uint8_t headerlen = 0;
+ int i2c_entries = MAX_NUM_DCB_ENTRIES;
+ int recordoffset = 0, rdofs = 1, wrofs = 0;
+
+ if (!i2ctabptr)
+ return;
+
+ if (dcb_version >= 0x30) {
+ if (i2ctable[0] != dcb_version) /* necessary? */
+ DRM_ERROR(
+ "DCB I2C table version mismatch (%02X vs %02X)\n",
+ i2ctable[0], dcb_version);
+ headerlen = i2ctable[1];
+ i2c_entries = i2ctable[2];
+
+ /* same address offset used for read and write for C51 and G80 */
+ if (bios->chip_version == 0x51)
+ rdofs = wrofs = 1;
+ if (i2ctable[0] >= 0x40)
+ rdofs = wrofs = 0;
+ }
+ /* it's your own fault if you call this function on a DCB 1.1 BIOS --
+ * the test below is for DCB 1.2
+ */
+ if (dcb_version < 0x14) {
+ recordoffset = 2;
+ rdofs = 0;
+ wrofs = 1;
+ }
+
+ if (index == 0xf)
+ return;
+ if (index > i2c_entries) {
+ DRM_ERROR(
+ "DCB I2C index too big (%d > %d)\n",
+ index, i2ctable[2]);
+ return;
+ }
+ if (i2ctable[headerlen + 4 * index + 3] == 0xff) {
+ DRM_ERROR(
+ "DCB I2C entry invalid\n");
+ return;
+ }
+
+ if (bios->chip_version == 0x51) {
+ int port_type = i2ctable[headerlen + 4 * index + 3];
+
+ if (port_type != 4)
+ DRM_ERROR(
+ "DCB I2C table has port type %d\n", port_type);
+ }
+ if (i2ctable[0] >= 0x40) {
+ int port_type = i2ctable[headerlen + 4 * index + 3];
+
+ if (port_type != 5)
+ DRM_ERROR(
+ "DCB I2C table has port type %d\n", port_type);
+ }
+
+ dev_priv->dcb_table.i2c_read[index] = i2ctable[headerlen + recordoffset + rdofs + 4 * index];
+ dev_priv->dcb_table.i2c_write[index] = i2ctable[headerlen + recordoffset + wrofs + 4 * index];
+}
+
+static bool
+parse_dcb_entry(struct drm_device *dev, int index, uint8_t dcb_version, uint16_t i2ctabptr, uint32_t conn, uint32_t conf)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_entry *entry = &dev_priv->dcb_table.entry[index];
+
+ memset(entry, 0, sizeof (struct dcb_entry));
+
+ entry->index = index;
+ /* safe defaults for a crt */
+ entry->type = 0;
+ entry->i2c_index = 0;
+ entry->heads = 1;
+ entry->bus = 0;
+ entry->location = LOC_ON_CHIP;
+ entry->or = 1;
+ entry->duallink_possible = false;
+
+ if (dcb_version >= 0x20) {
+ entry->type = conn & 0xf;
+ entry->i2c_index = (conn >> 4) & 0xf;
+ entry->heads = (conn >> 8) & 0xf;
+ entry->bus = (conn >> 16) & 0xf;
+ entry->location = (conn >> 20) & 0xf;
+ entry->or = (conn >> 24) & 0xf;
+ /* Normal entries consist of a single bit, but dual link has the
+ * adjacent more significant bit set too
+ */
+ if ((1 << (ffs(entry->or) - 1)) * 3 == entry->or)
+ entry->duallink_possible = true;
+
+ switch (entry->type) {
+ case DCB_OUTPUT_LVDS:
+ {
+ uint32_t mask;
+ if (conf & 0x1)
+ entry->lvdsconf.use_straps_for_mode = true;
+ if (dcb_version < 0x22) {
+ mask = ~0xd;
+ /* both 0x4 and 0x8 show up in v2.0 tables; assume they mean
+ * the same thing, which is probably wrong, but might work */
+ if (conf & 0x4 || conf & 0x8)
+ entry->lvdsconf.use_power_scripts = true;
+ } else {
+ mask = ~0x5;
+ if (conf & 0x4)
+ entry->lvdsconf.use_power_scripts = true;
+ }
+ if (conf & mask) {
+ DRM_ERROR(
+ "Unknown LVDS configuration bits, please report\n");
+ /* cause output setting to fail, so message is seen */
+ dev_priv->dcb_table.entries = 0;
+ return false;
+ }
+ break;
+ }
+ case 0xe:
+ /* weird type that appears on g80 mobile bios; nv driver treats it as a terminator */
+ return false;
+ }
+ read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index);
+ } else if (dcb_version >= 0x14 ) {
+ if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 && conn != 0xf2204301 && conn != 0xf2244311 && conn != 0xf2045f14 && conn != 0xf2205004 && conn != 0xf2208001 && conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011) {
+ DRM_ERROR(
+ "Unknown DCB 1.4 / 1.5 entry, please report\n");
+ /* cause output setting to fail, so message is seen */
+ dev_priv->dcb_table.entries = 0;
+ return false;
+ }
+ /* most of the below is a "best guess" atm */
+ entry->type = conn & 0xf;
+ if (entry->type == 4) { /* digital */
+ if (conn & 0x10)
+ entry->type = DCB_OUTPUT_LVDS;
+ else
+ entry->type = DCB_OUTPUT_TMDS;
+ }
+ /* what's in bits 5-13? could be some brooktree/chrontel/philips thing, in tv case */
+ entry->i2c_index = (conn >> 14) & 0xf;
+ /* raw heads field is in range 0-1, so move to 1-2 */
+ entry->heads = ((conn >> 18) & 0x7) + 1;
+ entry->location = (conn >> 21) & 0xf;
+ entry->bus = (conn >> 25) & 0x7;
+ /* set or to be same as heads -- hopefully safe enough */
+ entry->or = entry->heads;
+
+ switch (entry->type) {
+ case DCB_OUTPUT_LVDS:
+ /* this is probably buried in conn's unknown bits */
+ entry->lvdsconf.use_power_scripts = true;
+ break;
+ case DCB_OUTPUT_TMDS:
+ /* invent a DVI-A output, by copying the fields of the DVI-D output
+ * reported to work by math_b on an NV20(!) */
+ memcpy(&entry[1], &entry[0], sizeof(struct dcb_entry));
+ entry[1].type = DCB_OUTPUT_ANALOG;
+ dev_priv->dcb_table.entries++;
+ }
+ read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index);
+ } else if (dcb_version >= 0x12) {
+ /* v1.2 tables normally have the same 5 entries, which are not
+ * specific to the card, so use the defaults for a crt */
+ /* DCB v1.2 does have an I2C table that read_dcb_i2c_table can handle, but cards
+ * exist (seen on nv11) where the pointer to the table points to the wrong
+ * place, so for now, we rely on the indices parsed in parse_bmp_structure
+ */
+ entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt;
+ } else { /* pre DCB / v1.1 - use the safe defaults for a crt */
+ DRM_ERROR(
+ "No information in BIOS output table; assuming a CRT output exists\n");
+ entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt;
+ }
+
+ if (entry->type == DCB_OUTPUT_LVDS && dev_priv->bios.fp.strapping != 0xff)
+ entry->lvdsconf.use_straps_for_mode = true;
+
+ dev_priv->dcb_table.entries++;
+
+ return true;
+}
+
+static void merge_like_dcb_entries(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ /* DCB v2.0 lists each output combination separately.
+ * Here we merge compatible entries to have fewer outputs, with more options
+ */
+ int i, newentries = 0;
+
+ for (i = 0; i < dev_priv->dcb_table.entries; i++) {
+ struct dcb_entry *ient = &dev_priv->dcb_table.entry[i];
+ int j;
+
+ for (j = i + 1; j < dev_priv->dcb_table.entries; j++) {
+ struct dcb_entry *jent = &dev_priv->dcb_table.entry[j];
+
+ if (jent->type == 100) /* already merged entry */
+ continue;
+
+ /* merge heads field when all other fields the same */
+ if (jent->i2c_index == ient->i2c_index && jent->type == ient->type && jent->location == ient->location && jent->or == ient->or) {
+ DRM_INFO(
+ "Merging DCB entries %d and %d\n", i, j);
+ ient->heads |= jent->heads;
+ jent->type = 100; /* dummy value */
+ }
+ }
+ }
+
+ /* Compact entries merged into others out of dcb_table */
+ for (i = 0; i < dev_priv->dcb_table.entries; i++) {
+ if ( dev_priv->dcb_table.entry[i].type == 100 )
+ continue;
+
+ if (newentries != i)
+ memcpy(&dev_priv->dcb_table.entry[newentries], &dev_priv->dcb_table.entry[i], sizeof(struct dcb_entry));
+ newentries++;
+ }
+
+ dev_priv->dcb_table.entries = newentries;
+}
+
+static unsigned int parse_dcb_table(struct drm_device *dev, struct bios *bios)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint16_t dcbptr, i2ctabptr = 0;
+ uint8_t *dcbtable;
+ uint8_t dcb_version, headerlen = 0x4, entries = MAX_NUM_DCB_ENTRIES;
+ bool configblock = true;
+ int recordlength = 8, confofs = 4;
+ int i;
+
+ dev_priv->dcb_table.entries = 0;
+
+ /* get the offset from 0x36 */
+ dcbptr = le16_to_cpu(*(uint16_t *)&bios->data[0x36]);
+
+ if (dcbptr == 0x0) {
+ DRM_ERROR(
+ "No Display Configuration Block pointer found\n");
+ /* this situation likely means a really old card, pre DCB, so we'll add the safe CRT entry */
+ parse_dcb_entry(dev, 0, 0, 0, 0, 0);
+ return 1;
+ }
+
+ dcbtable = &bios->data[dcbptr];
+
+ /* get DCB version */
+ dcb_version = dcbtable[0];
+ DRM_INFO(
+ "Display Configuration Block version %d.%d found\n",
+ dcb_version >> 4, dcb_version & 0xf);
+
+ if (dcb_version >= 0x20) { /* NV17+ */
+ uint32_t sig;
+
+ if (dcb_version >= 0x30) { /* NV40+ */
+ headerlen = dcbtable[1];
+ entries = dcbtable[2];
+ recordlength = dcbtable[3];
+ i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[4]);
+ sig = le32_to_cpu(*(uint32_t *)&dcbtable[6]);
+
+ DRM_INFO(
+ "DCB header length %d, with %d possible entries\n",
+ headerlen, entries);
+ } else {
+ i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]);
+ sig = le32_to_cpu(*(uint32_t *)&dcbtable[4]);
+ headerlen = 8;
+ }
+
+ if (sig != 0x4edcbdcb) {
+ DRM_ERROR(
+ "Bad Display Configuration Block signature (%08X)\n", sig);
+ return 0;
+ }
+ } else if (dcb_version >= 0x14) { /* some NV15/16, and NV11+ */
+ char sig[8];
+
+ memset(sig, 0, 8);
+ strncpy(sig, (char *)&dcbtable[-7], 7);
+ i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]);
+ recordlength = 10;
+ confofs = 6;
+
+ if (strcmp(sig, "DEV_REC")) {
+ DRM_ERROR(
+ "Bad Display Configuration Block signature (%s)\n", sig);
+ return 0;
+ }
+ } else if (dcb_version >= 0x12) { /* some NV6/10, and NV15+ */
+ i2ctabptr = le16_to_cpu(*(uint16_t *)&dcbtable[2]);
+ configblock = false;
+ } else { /* NV5+, maybe NV4 */
+ /* DCB 1.1 seems to be quite unhelpful - we'll just add the safe CRT entry */
+ parse_dcb_entry(dev, 0, dcb_version, 0, 0, 0);
+ return 1;
+ }
+
+ if (entries >= MAX_NUM_DCB_ENTRIES)
+ entries = MAX_NUM_DCB_ENTRIES;
+
+ for (i = 0; i < entries; i++) {
+ uint32_t connection, config = 0;
+
+ connection = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + recordlength * i]);
+ if (configblock)
+ config = le32_to_cpu(*(uint32_t *)&dcbtable[headerlen + confofs + recordlength * i]);
+
+ /* Should we allow discontinuous DCBs? Certainly DCB I2C tables can be discontinuous */
+ if ((connection & 0x0000000f) == 0x0000000f) /* end of records */
+ break;
+ if (connection == 0x00000000) /* seen on an NV11 with DCB v1.5 */
+ break;
+
+ DRM_INFO("Raw DCB entry %d: %08x %08x\n", i, connection, config);
+ if (!parse_dcb_entry(dev, dev_priv->dcb_table.entries, dcb_version, i2ctabptr, connection, config))
+ break;
+ }
+
+ merge_like_dcb_entries(dev);
+
+ return dev_priv->dcb_table.entries;
+}
+
+int nouveau_parse_bios(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ const uint8_t bit_signature[] = { 'B', 'I', 'T' };
+ int offset;
+
+ dev_priv->bios.data = kzalloc(NV50_PROM__ESIZE, GFP_KERNEL);
+ if (!dev_priv->bios.data)
+ return -ENOMEM;
+
+ if (!nv_shadow_bios(dev, dev_priv->bios.data))
+ return -EINVAL;
+
+ dev_priv->bios.length = dev_priv->bios.data[2] * 512;
+ if (dev_priv->bios.length > NV50_PROM__ESIZE)
+ dev_priv->bios.length = NV50_PROM__ESIZE;
+
+ if ((offset = findstr(dev_priv->bios.data, dev_priv->bios.length, bit_signature, sizeof(bit_signature)))) {
+ DRM_INFO("BIT BIOS found\n");
+ parse_bit_structure(dev, &dev_priv->bios, offset + 4);
+ } else {
+ DRM_ERROR("BIT BIOS not found\n");
+ return -EINVAL;
+ }
+
+ if (parse_dcb_table(dev, &dev_priv->bios))
+ DRM_INFO("Found %d entries in DCB\n", dev_priv->dcb_table.entries);
+
+ return 0;
+}
+
+/* temporary */
+#define NV_RAMDAC_NVPLL 0x00680500
+#define NV_RAMDAC_MPLL 0x00680504
+#define NV_RAMDAC_VPLL 0x00680508
+# define NV_RAMDAC_PLL_COEFF_MDIV 0x000000FF
+# define NV_RAMDAC_PLL_COEFF_NDIV 0x0000FF00
+# define NV_RAMDAC_PLL_COEFF_PDIV 0x00070000
+# define NV30_RAMDAC_ENABLE_VCO2 (1 << 7)
+#define NV_RAMDAC_VPLL2 0x00680520
+
+bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
+{
+ /* PLL limits table
+ *
+ * Version 0x10: NV31
+ * One byte header (version), one record of 24 bytes
+ * Version 0x11: NV36 - Not implemented
+ * Seems to have same record style as 0x10, but 3 records rather than 1
+ * Version 0x20: Found on Geforce 6 cards
+ * Trivial 4 byte BIT header. 31 (0x1f) byte record length
+ * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards
+ * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record
+ * length in general, some (integrated) have an extra configuration byte
+ */
+
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct bios *bios = &dev_priv->bios;
+ uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
+ int pllindex = 0;
+ uint32_t crystal_strap_mask, crystal_straps;
+
+ if (!bios->pll_limit_tbl_ptr) {
+ if (bios->chip_version >= 0x40 || bios->chip_version == 0x31 || bios->chip_version == 0x36) {
+ DRM_ERROR("Pointer to PLL limits table invalid\n");
+ return false;
+ }
+ } else {
+ pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr];
+
+ DRM_INFO("Found PLL limits table version 0x%X\n", pll_lim_ver);
+ }
+
+ crystal_strap_mask = 1 << 6;
+ /* open coded pNv->twoHeads test */
+ if (bios->chip_version > 0x10 && bios->chip_version != 0x15 &&
+ bios->chip_version != 0x1a && bios->chip_version != 0x20)
+ crystal_strap_mask |= 1 << 22;
+ crystal_straps = NV_READ(NV50_PEXTDEV + 0x0) & crystal_strap_mask;
+
+ switch (pll_lim_ver) {
+ /* we use version 0 to indicate a pre limit table bios (single stage pll)
+ * and load the hard coded limits instead */
+ case 0:
+ break;
+ case 0x10:
+ case 0x11: /* strictly v0x11 has 3 entries, but the last two don't seem to get used */
+ headerlen = 1;
+ recordlen = 0x18;
+ entries = 1;
+ pllindex = 0;
+ break;
+ case 0x20:
+ case 0x21:
+ headerlen = bios->data[bios->pll_limit_tbl_ptr + 1];
+ recordlen = bios->data[bios->pll_limit_tbl_ptr + 2];
+ entries = bios->data[bios->pll_limit_tbl_ptr + 3];
+ break;
+ default:
+ DRM_ERROR("PLL limits table revision not currently supported\n");
+ return false;
+ }
+
+ /* initialize all members to zero */
+ memset(pll_lim, 0, sizeof(struct pll_lims));
+
+ if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
+ uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex;
+
+ pll_lim->vco1.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs])));
+ pll_lim->vco1.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 4])));
+ pll_lim->vco2.minfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 8])));
+ pll_lim->vco2.maxfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 12])));
+ pll_lim->vco1.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 16])));
+ pll_lim->vco2.min_inputfreq = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + 20])));
+ pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX;
+
+ /* these values taken from nv30/31/36 */
+ pll_lim->vco1.min_n = 0x1;
+ if (bios->chip_version == 0x36)
+ pll_lim->vco1.min_n = 0x5;
+ pll_lim->vco1.max_n = 0xff;
+ pll_lim->vco1.min_m = 0x1;
+ pll_lim->vco1.max_m = 0xd;
+ pll_lim->vco2.min_n = 0x4;
+ /* on nv30, 31, 36 (i.e. all cards with two stage PLLs with this
+ * table version (apart from nv35)), N2 is compared to
+ * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
+ * save a comparison
+ */
+ pll_lim->vco2.max_n = 0x28;
+ if (bios->chip_version == 0x30 || bios->chip_version == 0x35)
+ /* only 5 bits available for N2 on nv30/35 */
+ pll_lim->vco2.max_n = 0x1f;
+ pll_lim->vco2.min_m = 0x1;
+ pll_lim->vco2.max_m = 0x4;
+ } else if (pll_lim_ver) { /* ver 0x20, 0x21 */
+ uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
+ uint32_t reg = 0; /* default match */
+ int i;
+
+ /* first entry is default match, if nothing better. warn if reg field nonzero */
+ if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs])))
+ DRM_ERROR("Default PLL limit entry has non-zero register field\n");
+
+ if (limit_match > MAX_PLL_TYPES)
+ /* we've been passed a reg as the match */
+ reg = limit_match;
+ else /* limit match is a pll type */
+ for (i = 1; i < entries && !reg; i++) {
+ uint32_t cmpreg = le32_to_cpu(*((uint32_t *)(&bios->data[plloffs + recordlen * i])));
+
+ if (limit_match == NVPLL && (cmpreg == NV_RAMDAC_NVPLL || cmpreg == 0x4000))
+ reg = cmpreg;
+ if (limit_match == MPLL && (cmpreg == NV_RAMDAC_MPLL || cmpreg == 0x4020))
+ reg = cmpreg;
+ if (limit_match == VPLL1 && (cmpreg == NV_RAMDAC_VPLL || cmpreg == 0x4010))
+ reg = cmpreg;
+ if (limit_match == VPLL2 && (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018))
+ reg = cmpreg;
+ }
+
+ for (i = 1; i < entries; i++)
+ if (le32_to_cpu(*((uint32_t *)&bios->data[plloffs + recordlen * i])) == reg) {
+ pllindex = i;
+ break;
+ }
+
+ plloffs += recordlen * pllindex;
+
+ DRM_INFO("Loading PLL limits for reg 0x%08x\n", pllindex ? reg : 0);
+
+ /* frequencies are stored in tables in MHz, kHz are more useful, so we convert */
+
+ /* What output frequencies can each VCO generate? */
+ pll_lim->vco1.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 4]))) * 1000;
+ pll_lim->vco1.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 6]))) * 1000;
+ pll_lim->vco2.minfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 8]))) * 1000;
+ pll_lim->vco2.maxfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 10]))) * 1000;
+
+ /* What input frequencies do they accept (past the m-divider)? */
+ pll_lim->vco1.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 12]))) * 1000;
+ pll_lim->vco2.min_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 14]))) * 1000;
+ pll_lim->vco1.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 16]))) * 1000;
+ pll_lim->vco2.max_inputfreq = le16_to_cpu(*((uint16_t *)(&bios->data[plloffs + 18]))) * 1000;
+
+ /* What values are accepted as multiplier and divider? */
+ pll_lim->vco1.min_n = bios->data[plloffs + 20];
+ pll_lim->vco1.max_n = bios->data[plloffs + 21];
+ pll_lim->vco1.min_m = bios->data[plloffs + 22];
+ pll_lim->vco1.max_m = bios->data[plloffs + 23];
+ pll_lim->vco2.min_n = bios->data[plloffs + 24];
+ pll_lim->vco2.max_n = bios->data[plloffs + 25];
+ pll_lim->vco2.min_m = bios->data[plloffs + 26];
+ pll_lim->vco2.max_m = bios->data[plloffs + 27];
+
+ pll_lim->unk1c = bios->data[plloffs + 28];
+ pll_lim->max_log2p_bias = bios->data[plloffs + 29];
+ pll_lim->log2p_bias = bios->data[plloffs + 30];
+
+ if (recordlen > 0x22)
+ pll_lim->refclk = le32_to_cpu(*((uint32_t *)&bios->data[plloffs + 31]));
+
+ if (recordlen > 0x23)
+ if (bios->data[plloffs + 35])
+ DRM_ERROR("Bits set in PLL configuration byte (%x)\n", bios->data[plloffs + 35]);
+
+ /* C51 special not seen elsewhere */
+ /*if (bios->chip_version == 0x51 && !pll_lim->refclk) {
+ uint32_t sel_clk = nv32_rd(pScrn, NV_RAMDAC_SEL_CLK);
+
+ if (((limit_match == NV_RAMDAC_VPLL || limit_match == VPLL1) && sel_clk & 0x20) ||
+ ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) {
+ if (nv_idx_port_rd(pScrn, CRTC_INDEX_COLOR, NV_VGA_CRTCX_REVISION) < 0xa3)
+ pll_lim->refclk = 200000;
+ else
+ pll_lim->refclk = 25000;
+ }
+ }*/
+ }
+
+ /* By now any valid limit table ought to have set a max frequency for
+ * vco1, so if it's zero it's either a pre limit table bios, or one
+ * with an empty limit table (seen on nv18)
+ */
+ if (!pll_lim->vco1.maxfreq) {
+ pll_lim->vco1.minfreq = bios->fminvco;
+ pll_lim->vco1.maxfreq = bios->fmaxvco;
+ pll_lim->vco1.min_inputfreq = 0;
+ pll_lim->vco1.max_inputfreq = INT_MAX;
+ pll_lim->vco1.min_n = 0x1;
+ pll_lim->vco1.max_n = 0xff;
+ pll_lim->vco1.min_m = 0x1;
+ if (crystal_straps == 0) {
+ /* nv05 does this, nv11 doesn't, nv10 unknown */
+ if (bios->chip_version < 0x11)
+ pll_lim->vco1.min_m = 0x7;
+ pll_lim->vco1.max_m = 0xd;
+ } else {
+ if (bios->chip_version < 0x11)
+ pll_lim->vco1.min_m = 0x8;
+ pll_lim->vco1.max_m = 0xe;
+ }
+ }
+
+ if (!pll_lim->refclk)
+ switch (crystal_straps) {
+ case 0:
+ pll_lim->refclk = 13500;
+ break;
+ case (1 << 6):
+ pll_lim->refclk = 14318;
+ break;
+ case (1 << 22):
+ pll_lim->refclk = 27000;
+ break;
+ case (1 << 22 | 1 << 6):
+ pll_lim->refclk = 25000;
+ break;
+ }
+
+#if 1 /* for easy debugging */
+ DRM_INFO("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
+ DRM_INFO("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
+ DRM_INFO("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
+ DRM_INFO("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
+
+ DRM_INFO("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
+ DRM_INFO("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
+ DRM_INFO("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
+ DRM_INFO("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
+
+ DRM_INFO("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
+ DRM_INFO("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
+ DRM_INFO("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
+ DRM_INFO("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
+ DRM_INFO("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
+ DRM_INFO("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
+ DRM_INFO("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
+ DRM_INFO("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
+
+ DRM_INFO("pll.unk1c: %d\n", pll_lim->unk1c);
+ DRM_INFO("pll.max_log2p_bias: %d\n", pll_lim->max_log2p_bias);
+ DRM_INFO("pll.log2p_bias: %d\n", pll_lim->log2p_bias);
+
+ DRM_INFO("pll.refclk: %d\n", pll_lim->refclk);
+#endif
+
+ return true;
+}
diff --git a/linux-core/nouveau_bios.h b/linux-core/nouveau_bios.h
new file mode 100644
index 00000000..e33ecd0f
--- /dev/null
+++ b/linux-core/nouveau_bios.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2005-2006 Erik Waling
+ * Copyright (C) 2006 Stephane Marchesin
+ * Copyright (C) 2007-2008 Stuart Bennett
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NOUVEAU_BIOS_H__
+#define __NOUVEAU_BIOS_H__
+
+#include "drmP.h"
+#include "drm.h"
+
+#define LOC_ON_CHIP 0
+
+enum dcb_output_type {/* matches DCB types */
+ DCB_OUTPUT_NONE = 4,
+ DCB_OUTPUT_ANALOG = 0,
+ DCB_OUTPUT_TMDS = 2,
+ DCB_OUTPUT_LVDS = 3,
+ DCB_OUTPUT_TV = 1,
+};
+
+struct bios {
+ uint8_t *data;
+ unsigned int length;
+ bool execute;
+
+ uint8_t major_version, chip_version;
+ uint8_t feature_byte;
+
+ uint32_t fmaxvco, fminvco;
+
+ uint32_t dactestval;
+
+ uint16_t init_script_tbls_ptr;
+ uint16_t extra_init_script_tbl_ptr;
+ uint16_t macro_index_tbl_ptr;
+ uint16_t macro_tbl_ptr;
+ uint16_t condition_tbl_ptr;
+ uint16_t io_condition_tbl_ptr;
+ uint16_t io_flag_condition_tbl_ptr;
+ uint16_t init_function_tbl_ptr;
+
+ uint16_t pll_limit_tbl_ptr;
+ uint16_t ram_restrict_tbl_ptr;
+
+ struct {
+ struct nouveau_hw_mode *native_mode;
+ uint8_t *edid;
+ uint16_t lvdsmanufacturerpointer;
+ uint16_t xlated_entry;
+ bool power_off_for_reset;
+ bool reset_after_pclk_change;
+ bool dual_link;
+ bool link_c_increment;
+ bool if_is_24bit;
+ bool BITbit1;
+ int duallink_transition_clk;
+ /* lower nibble stores PEXTDEV_BOOT_0 strap
+ * upper nibble stores xlated display strap */
+ uint8_t strapping;
+ } fp;
+
+ struct {
+ uint16_t output0_script_ptr;
+ uint16_t output1_script_ptr;
+ } tmds;
+
+ struct {
+ uint16_t mem_init_tbl_ptr;
+ uint16_t sdr_seq_tbl_ptr;
+ uint16_t ddr_seq_tbl_ptr;
+
+ struct {
+ uint8_t crt, tv, panel;
+ } i2c_indices;
+ } legacy;
+};
+
+struct dcb_entry {
+ int index;
+ uint8_t type;
+ uint8_t i2c_index;
+ uint8_t heads;
+ uint8_t bus;
+ uint8_t location;
+ uint8_t or;
+ bool duallink_possible;
+ union {
+ struct {
+ bool use_straps_for_mode;
+ bool use_power_scripts;
+ } lvdsconf;
+ };
+};
+
+/* changing these requires matching changes to reg tables in nv_get_clock */
+#define MAX_PLL_TYPES 4
+enum pll_types {
+ NVPLL,
+ MPLL,
+ VPLL1,
+ VPLL2
+};
+
+struct pll_lims {
+ struct {
+ int minfreq;
+ int maxfreq;
+ int min_inputfreq;
+ int max_inputfreq;
+
+ uint8_t min_m;
+ uint8_t max_m;
+ uint8_t min_n;
+ uint8_t max_n;
+ } vco1, vco2;
+
+ uint8_t unk1c;
+ uint8_t max_log2p_bias;
+ uint8_t log2p_bias;
+ int refclk;
+};
+
+bool get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim);
+int nouveau_parse_bios(struct drm_device *dev);
+
+#endif /* __NOUVEAU_BIOS_H__ */
diff --git a/linux-core/nouveau_drv.c b/linux-core/nouveau_drv.c
index c8f57dff..04f002f2 100644
--- a/linux-core/nouveau_drv.c
+++ b/linux-core/nouveau_drv.c
@@ -28,6 +28,9 @@
#include "drm_pciids.h"
+unsigned int nouveau_modeset = 0; /* kms */
+module_param_named(modeset, nouveau_modeset, int, 0400);
+
static struct pci_device_id pciidlist[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
@@ -104,6 +107,10 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent)
static int __init nouveau_init(void)
{
driver.num_ioctls = nouveau_max_ioctl;
+
+ if (nouveau_modeset == 1)
+ driver.driver_features |= DRIVER_MODESET;
+
return drm_init(&driver, pciidlist);
}
diff --git a/linux-core/nv50_connector.c b/linux-core/nv50_connector.c
new file mode 100644
index 00000000..ac5194c0
--- /dev/null
+++ b/linux-core/nv50_connector.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_connector.h"
+
+static struct nv50_output *nv50_connector_to_output(struct nv50_connector *connector, bool digital)
+{
+ struct nv50_display *display = nv50_get_display(connector->dev);
+ struct nv50_output *output = NULL;
+ bool digital_possible = false;
+ bool analog_possible = false;
+
+ switch (connector->type) {
+ case CONNECTOR_VGA:
+ case CONNECTOR_TV:
+ analog_possible = true;
+ break;
+ case CONNECTOR_DVI_I:
+ analog_possible = true;
+ digital_possible = true;
+ break;
+ case CONNECTOR_DVI_D:
+ case CONNECTOR_LVDS:
+ digital_possible = true;
+ break;
+ default:
+ break;
+ }
+
+ /* Return early on bad situations. */
+ if (!analog_possible && !digital_possible)
+ return NULL;
+
+ if (!analog_possible && !digital)
+ return NULL;
+
+ if (!digital_possible && digital)
+ return NULL;
+
+ list_for_each_entry(output, &display->outputs, item) {
+ if (connector->bus != output->bus)
+ continue;
+ if (digital && output->type == OUTPUT_TMDS)
+ return output;
+ if (digital && output->type == OUTPUT_LVDS)
+ return output;
+ if (!digital && output->type == OUTPUT_DAC)
+ return output;
+ if (!digital && output->type == OUTPUT_TV)
+ return output;
+ }
+
+ return NULL;
+}
+
+static bool nv50_connector_detect(struct nv50_connector *connector)
+{
+ /* kindly borrrowed from the intel driver, hope it works. */
+ uint8_t out_buf[] = { 0x0, 0x0};
+ uint8_t 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,
+ }
+ };
+
+ NV50_DEBUG("\n");
+
+ if (!connector->i2c_chan)
+ return false;
+
+ ret = i2c_transfer(&connector->i2c_chan->adapter, msgs, 2);
+ DRM_INFO("I2C detect returned %d\n", ret);
+
+ if (ret == 2)
+ return true;
+
+ return false;
+}
+
+static int nv50_connector_destroy(struct nv50_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = nv50_get_display(dev);
+
+ NV50_DEBUG("\n");
+
+ if (!display || !connector)
+ return -EINVAL;
+
+ list_del(&connector->item);
+
+ if (connector->i2c_chan)
+ nv50_i2c_channel_destroy(connector->i2c_chan);
+
+ if (dev_priv->free_connector)
+ dev_priv->free_connector(connector);
+
+ return 0;
+}
+
+int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type)
+{
+ struct nv50_connector *connector = NULL;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = NULL;
+
+ NV50_DEBUG("\n");
+
+ /* This allows the public layer to do it's thing. */
+ if (dev_priv->alloc_connector)
+ connector = dev_priv->alloc_connector(dev);
+
+ if (!connector)
+ return -ENOMEM;
+
+ connector->dev = dev;
+
+ display = nv50_get_display(dev);
+ if (!display)
+ goto out;
+
+ if (type == CONNECTOR_UNKNOWN)
+ goto out;
+
+ list_add_tail(&connector->item, &display->connectors);
+
+ connector->bus = bus;
+ connector->type = type;
+
+ switch (type) {
+ case CONNECTOR_VGA:
+ DRM_INFO("Detected a VGA connector\n");
+ break;
+ case CONNECTOR_DVI_D:
+ DRM_INFO("Detected a DVI-D connector\n");
+ break;
+ case CONNECTOR_DVI_I:
+ DRM_INFO("Detected a DVI-I connector\n");
+ break;
+ case CONNECTOR_LVDS:
+ DRM_INFO("Detected a LVDS connector\n");
+ break;
+ case CONNECTOR_TV:
+ DRM_INFO("Detected a TV connector\n");
+ break;
+ default:
+ DRM_ERROR("Unknown connector, this is not good.\n");
+ break;
+ }
+
+ /* some reasonable defaults */
+ if (type == CONNECTOR_DVI_D || type == CONNECTOR_DVI_I || type == CONNECTOR_LVDS)
+ connector->scaling_mode = SCALE_FULLSCREEN;
+ else
+ connector->scaling_mode = SCALE_PANEL;
+
+ if (i2c_index < 0xf)
+ connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index);
+
+ /* set function pointers */
+ connector->detect = nv50_connector_detect;
+ connector->destroy = nv50_connector_destroy;
+ connector->to_output = nv50_connector_to_output;
+
+ return 0;
+
+out:
+ if (dev_priv->free_connector)
+ dev_priv->free_connector(connector);
+
+ return -EINVAL;
+}
diff --git a/linux-core/nv50_connector.h b/linux-core/nv50_connector.h
new file mode 100644
index 00000000..ebd6eac6
--- /dev/null
+++ b/linux-core/nv50_connector.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_CONNECTOR_H__
+#define __NV50_CONNECTOR_H__
+
+#include "nv50_output.h"
+#include "nv50_i2c.h"
+
+#define CONNECTOR_UNKNOWN 0
+#define CONNECTOR_VGA 1
+#define CONNECTOR_DVI_D 2
+#define CONNECTOR_DVI_I 3
+#define CONNECTOR_LVDS 4
+#define CONNECTOR_TV 5
+
+struct nv50_connector {
+ struct list_head item;
+
+ struct drm_device *dev;
+ int type;
+
+ int bus;
+ struct nv50_i2c_channel *i2c_chan;
+ struct nv50_output *output;
+
+ int scaling_mode;
+
+ bool (*detect) (struct nv50_connector *connector);
+ int (*destroy) (struct nv50_connector *connector);
+ struct nv50_output *(*to_output) (struct nv50_connector *connector, bool digital);
+};
+
+int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int type);
+
+#endif /* __NV50_CONNECTOR_H__ */
diff --git a/linux-core/nv50_crtc.c b/linux-core/nv50_crtc.c
new file mode 100644
index 00000000..6c3d404f
--- /dev/null
+++ b/linux-core/nv50_crtc.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_crtc.h"
+#include "nv50_cursor.h"
+#include "nv50_lut.h"
+#include "nv50_fb.h"
+
+static int nv50_crtc_validate_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode)
+{
+ NV50_DEBUG("\n");
+
+ if (mode->clock > 400000)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock < 25000)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static int nv50_crtc_set_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode)
+{
+ struct nouveau_hw_mode *hw_mode = crtc->mode;
+ uint8_t rval;
+
+ NV50_DEBUG("index %d\n", crtc->index);
+
+ if (!mode) {
+ DRM_ERROR("No mode\n");
+ return MODE_NOMODE;
+ }
+
+ if ((rval = crtc->validate_mode(crtc, mode))) {
+ DRM_ERROR("Mode invalid\n");
+ return rval;
+ }
+
+ /* copy values to mode */
+ *hw_mode = *mode;
+
+ return 0;
+}
+
+static int nv50_crtc_execute_mode(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ struct nouveau_hw_mode *hw_mode;
+ uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end;
+ uint32_t hunk1, vunk1, vunk2a, vunk2b;
+ uint32_t offset = crtc->index * 0x400;
+
+ NV50_DEBUG("index %d\n", crtc->index);
+ NV50_DEBUG("%s native mode\n", crtc->use_native_mode ? "using" : "not using");
+
+ if (crtc->use_native_mode)
+ hw_mode = crtc->native_mode;
+ else
+ hw_mode = crtc->mode;
+
+ hsync_dur = hw_mode->hsync_end - hw_mode->hsync_start;
+ vsync_dur = hw_mode->vsync_end - hw_mode->vsync_start;
+ hsync_start_to_end = hw_mode->hblank_end - hw_mode->hsync_start;
+ vsync_start_to_end = hw_mode->vblank_end - hw_mode->vsync_start;
+ /* I can't give this a proper name, anyone else can? */
+ hunk1 = hw_mode->htotal - hw_mode->hsync_start + hw_mode->hblank_start;
+ vunk1 = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start;
+ /* Another strange value, this time only for interlaced modes. */
+ vunk2a = 2*hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start;
+ vunk2b = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_end;
+
+ if (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ vsync_dur /= 2;
+ vsync_start_to_end /= 2;
+ vunk1 /= 2;
+ vunk2a /= 2;
+ vunk2b /= 2;
+ /* magic */
+ if (hw_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+ vsync_start_to_end -= 1;
+ vunk1 -= 1;
+ vunk2a -= 1;
+ vunk2b -= 1;
+ }
+ }
+
+ OUT_MODE(NV50_CRTC0_CLOCK + offset, hw_mode->clock | 0x800000);
+ OUT_MODE(NV50_CRTC0_INTERLACE + offset, (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0);
+ OUT_MODE(NV50_CRTC0_DISPLAY_START + offset, 0);
+ OUT_MODE(NV50_CRTC0_UNK82C + offset, 0);
+ OUT_MODE(NV50_CRTC0_DISPLAY_TOTAL + offset, hw_mode->vtotal << 16 | hw_mode->htotal);
+ OUT_MODE(NV50_CRTC0_SYNC_DURATION + offset, (vsync_dur - 1) << 16 | (hsync_dur - 1));
+ OUT_MODE(NV50_CRTC0_SYNC_START_TO_BLANK_END + offset, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1));
+ OUT_MODE(NV50_CRTC0_MODE_UNK1 + offset, (vunk1 - 1) << 16 | (hunk1 - 1));
+ if (hw_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ OUT_MODE(NV50_CRTC0_MODE_UNK2 + offset, (vunk2b - 1) << 16 | (vunk2a - 1));
+ }
+
+ crtc->set_fb(crtc);
+ crtc->set_dither(crtc);
+
+ /* This is the actual resolution of the mode. */
+ OUT_MODE(NV50_CRTC0_REAL_RES + offset, (crtc->mode->vdisplay << 16) | crtc->mode->hdisplay);
+ OUT_MODE(NV50_CRTC0_SCALE_CENTER_OFFSET + offset, NV50_CRTC_SCALE_CENTER_OFFSET_VAL(0,0));
+
+ /* Maybe move this as well? */
+ crtc->blank(crtc, FALSE);
+
+ return 0;
+}
+
+static int nv50_crtc_set_fb(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ uint32_t offset = crtc->index * 0x400;
+
+ NV50_DEBUG("\n");
+
+ OUT_MODE(NV50_CRTC0_FB_SIZE + offset, crtc->fb->height << 16 | crtc->fb->width);
+
+ /* I suspect this flag indicates a linear fb. */
+ OUT_MODE(NV50_CRTC0_FB_PITCH + offset, crtc->fb->pitch | 0x100000);
+
+ switch (crtc->fb->depth) {
+ case 8:
+ OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_8BPP);
+ break;
+ case 15:
+ OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_15BPP);
+ break;
+ case 16:
+ OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_16BPP);
+ break;
+ case 24:
+ OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_24BPP);
+ break;
+ }
+
+ OUT_MODE(NV50_CRTC0_COLOR_CTRL + offset, NV50_CRTC_COLOR_CTRL_MODE_COLOR);
+ OUT_MODE(NV50_CRTC0_FB_POS + offset, (crtc->fb->y << 16) | (crtc->fb->x));
+
+ return 0;
+}
+
+static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ uint32_t offset = crtc->index * 0x400;
+
+ NV50_DEBUG("index %d\n", crtc->index);
+ NV50_DEBUG("%s\n", blanked ? "blanked" : "unblanked");
+
+ /* We really need a framebuffer. */
+ if (!crtc->fb->block && !blanked) {
+ DRM_ERROR("No framebuffer available on crtc %d\n", crtc->index);
+ return -EINVAL;
+ }
+
+ if (blanked) {
+ crtc->cursor->hide(crtc);
+
+ OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, NV50_CRTC0_CLUT_MODE_BLANK);
+ OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, 0);
+ if (dev_priv->chipset != 0x50)
+ OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_BLANK);
+ OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_BLANK);
+ if (dev_priv->chipset != 0x50)
+ OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_BLANK);
+ } else {
+ OUT_MODE(NV50_CRTC0_FB_OFFSET + offset, crtc->fb->block->start >> 8);
+ OUT_MODE(0x864 + offset, 0);
+
+ crtc->cursor->set_offset(crtc);
+
+ if (dev_priv->chipset != 0x50)
+ OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_UNBLANK);
+
+ if (crtc->cursor->visible)
+ crtc->cursor->show(crtc);
+ else
+ crtc->cursor->hide(crtc);
+
+ OUT_MODE(NV50_CRTC0_CLUT_MODE + offset,
+ crtc->fb->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON);
+ OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, crtc->lut->block->start >> 8);
+ if (dev_priv->chipset != 0x50)
+ OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_UNBLANK);
+ OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_UNBLANK);
+ }
+
+ /* sometimes you need to know if a screen is already blanked. */
+ crtc->blanked = blanked;
+
+ return 0;
+}
+
+static int nv50_crtc_set_dither(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ uint32_t offset = crtc->index * 0x400;
+
+ NV50_DEBUG("\n");
+
+ OUT_MODE(NV50_CRTC0_DITHERING_CTRL + offset, crtc->use_dithering ?
+ NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF);
+
+ return 0;
+}
+
+static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_t *outY)
+{
+ uint32_t hor_scale, ver_scale;
+
+ /* max res is 8192, which is 2^13, which leaves 19 bits */
+ hor_scale = (crtc->native_mode->hdisplay << 19)/crtc->mode->hdisplay;
+ ver_scale = (crtc->native_mode->vdisplay << 19)/crtc->mode->vdisplay;
+
+ if (ver_scale > hor_scale) {
+ *outX = (crtc->mode->hdisplay * hor_scale) >> 19;
+ *outY = (crtc->mode->vdisplay * hor_scale) >> 19;
+ } else {
+ *outX = (crtc->mode->hdisplay * ver_scale) >> 19;
+ *outY = (crtc->mode->vdisplay * ver_scale) >> 19;
+ }
+}
+
+static int nv50_crtc_set_scale(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ uint32_t offset = crtc->index * 0x400;
+ uint32_t outX, outY;
+
+ NV50_DEBUG("\n");
+
+ switch (crtc->scaling_mode) {
+ case SCALE_ASPECT:
+ nv50_crtc_calc_scale(crtc, &outX, &outY);
+ break;
+ case SCALE_FULLSCREEN:
+ outX = crtc->native_mode->hdisplay;
+ outY = crtc->native_mode->vdisplay;
+ break;
+ case SCALE_NOSCALE:
+ case SCALE_PANEL:
+ default:
+ outX = crtc->mode->hdisplay;
+ outY = crtc->mode->vdisplay;
+ break;
+ }
+
+ /* Got a better name for SCALER_ACTIVE? */
+ /* One day i've got to really figure out why this is needed. */
+ if ((crtc->mode->flags & DRM_MODE_FLAG_DBLSCAN) || (crtc->mode->flags & DRM_MODE_FLAG_INTERLACE) ||
+ crtc->mode->hdisplay != outX || crtc->mode->vdisplay != outY) {
+ OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE);
+ } else {
+ OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE);
+ }
+
+ OUT_MODE(NV50_CRTC0_SCALE_RES1 + offset, outY << 16 | outX);
+ OUT_MODE(NV50_CRTC0_SCALE_RES2 + offset, outY << 16 | outX);
+
+ return 0;
+}
+
+static int nv50_crtc_calc_clock(struct nv50_crtc *crtc,
+ uint32_t *bestN1, uint32_t *bestN2, uint32_t *bestM1, uint32_t *bestM2, uint32_t *bestlog2P)
+{
+ struct nouveau_hw_mode *hw_mode;
+ struct pll_lims limits;
+ int clk, vco2, crystal;
+ int minvco1, minvco2, minU1, maxU1, minU2, maxU2, minM1, maxM1;
+ int maxvco1, maxvco2, minN1, maxN1, minM2, maxM2, minN2, maxN2;
+ bool fixedgain2;
+ int M1, N1, M2, N2, log2P;
+ int clkP, calcclk1, calcclk2, calcclkout;
+ int delta, bestdelta = INT_MAX;
+ int bestclk = 0;
+
+ NV50_DEBUG("\n");
+
+ if (crtc->use_native_mode)
+ hw_mode = crtc->native_mode;
+ else
+ hw_mode = crtc->mode;
+
+ clk = hw_mode->clock;
+
+ /* These are in the g80 bios tables, at least in mine. */
+ if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits))
+ return -EINVAL;
+
+ minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq;
+ minvco2 = limits.vco2.minfreq, maxvco2 = limits.vco2.maxfreq;
+ minU1 = limits.vco1.min_inputfreq, minU2 = limits.vco2.min_inputfreq;
+ maxU1 = limits.vco1.max_inputfreq, maxU2 = limits.vco2.max_inputfreq;
+ minM1 = limits.vco1.min_m, maxM1 = limits.vco1.max_m;
+ minN1 = limits.vco1.min_n, maxN1 = limits.vco1.max_n;
+ minM2 = limits.vco2.min_m, maxM2 = limits.vco2.max_m;
+ minN2 = limits.vco2.min_n, maxN2 = limits.vco2.max_n;
+ crystal = limits.refclk;
+ fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
+
+ vco2 = (maxvco2 - maxvco2/200) / 2;
+ for (log2P = 0; clk && log2P < 6 && clk <= (vco2 >> log2P); log2P++) /* log2P is maximum of 6 */
+ ;
+ clkP = clk << log2P;
+
+ if (maxvco2 < clk + clk/200) /* +0.5% */
+ maxvco2 = clk + clk/200;
+
+ for (M1 = minM1; M1 <= maxM1; M1++) {
+ if (crystal/M1 < minU1)
+ return bestclk;
+ if (crystal/M1 > maxU1)
+ continue;
+
+ for (N1 = minN1; N1 <= maxN1; N1++) {
+ calcclk1 = crystal * N1 / M1;
+ if (calcclk1 < minvco1)
+ continue;
+ if (calcclk1 > maxvco1)
+ break;
+
+ for (M2 = minM2; M2 <= maxM2; M2++) {
+ if (calcclk1/M2 < minU2)
+ break;
+ if (calcclk1/M2 > maxU2)
+ continue;
+
+ /* add calcclk1/2 to round better */
+ N2 = (clkP * M2 + calcclk1/2) / calcclk1;
+ if (N2 < minN2)
+ continue;
+ if (N2 > maxN2)
+ break;
+
+ if (!fixedgain2) {
+ calcclk2 = calcclk1 * N2 / M2;
+ if (calcclk2 < minvco2)
+ break;
+ if (calcclk2 > maxvco2)
+ continue;
+ } else
+ calcclk2 = calcclk1;
+
+ calcclkout = calcclk2 >> log2P;
+ delta = abs(calcclkout - clk);
+ /* we do an exhaustive search rather than terminating
+ * on an optimality condition...
+ */
+ if (delta < bestdelta) {
+ bestdelta = delta;
+ bestclk = calcclkout;
+ *bestN1 = N1;
+ *bestN2 = N2;
+ *bestM1 = M1;
+ *bestM2 = M2;
+ *bestlog2P = log2P;
+ if (delta == 0) /* except this one */
+ return bestclk;
+ }
+ }
+ }
+ }
+
+ return bestclk;
+}
+
+static int nv50_crtc_set_clock(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index);
+
+ uint32_t N1 = 0, N2 = 0, M1 = 0, M2 = 0, log2P = 0;
+
+ uint32_t reg1 = NV_READ(pll_reg + 4);
+ uint32_t reg2 = NV_READ(pll_reg + 8);
+
+ NV50_DEBUG("\n");
+
+ NV_WRITE(pll_reg, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED | 0x10000011);
+
+ /* The other bits are typically empty, but let's be on the safe side. */
+ reg1 &= 0xff00ff00;
+ reg2 &= 0x8000ff00;
+
+ if (!nv50_crtc_calc_clock(crtc, &N1, &N2, &M1, &M2, &log2P))
+ return -EINVAL;
+
+ NV50_DEBUG("N1 %d N2 %d M1 %d M2 %d log2P %d\n", N1, N2, M1, M2, log2P);
+
+ reg1 |= (M1 << 16) | N1;
+ reg2 |= (log2P << 28) | (M2 << 16) | N2;
+
+ NV_WRITE(pll_reg + 4, reg1);
+ NV_WRITE(pll_reg + 8, reg2);
+
+ return 0;
+}
+
+static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ NV50_DEBUG("\n");
+
+ /* This acknowledges a clock request. */
+ NV_WRITE(NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(crtc->index), 0);
+
+ return 0;
+}
+
+static int nv50_crtc_destroy(struct nv50_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = nv50_get_display(dev);
+
+ NV50_DEBUG("\n");
+
+ if (!display || !crtc)
+ return -EINVAL;
+
+ list_del(&crtc->item);
+
+ nv50_fb_destroy(crtc);
+ nv50_lut_destroy(crtc);
+ nv50_cursor_destroy(crtc);
+
+ kfree(crtc->mode);
+ kfree(crtc->native_mode);
+
+ if (dev_priv->free_crtc)
+ dev_priv->free_crtc(crtc);
+
+ return 0;
+}
+
+int nv50_crtc_create(struct drm_device *dev, int index)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_crtc *crtc = NULL;
+ struct nv50_display *display = NULL;
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ /* This allows the public layer to do it's thing. */
+ if (dev_priv->alloc_crtc)
+ crtc = dev_priv->alloc_crtc(dev);
+
+ if (!crtc)
+ return -ENOMEM;
+
+ crtc->dev = dev;
+
+ display = nv50_get_display(dev);
+ if (!display) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ list_add_tail(&crtc->item, &display->crtcs);
+
+ crtc->index = index;
+
+ crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
+ crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
+
+ if (!crtc->mode || !crtc->native_mode) {
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ nv50_fb_create(crtc);
+ nv50_lut_create(crtc);
+ nv50_cursor_create(crtc);
+
+ /* set function pointers */
+ crtc->validate_mode = nv50_crtc_validate_mode;
+ crtc->set_mode = nv50_crtc_set_mode;
+ crtc->execute_mode = nv50_crtc_execute_mode;
+ crtc->set_fb = nv50_crtc_set_fb;
+ crtc->blank = nv50_crtc_blank;
+ crtc->set_dither = nv50_crtc_set_dither;
+ crtc->set_scale = nv50_crtc_set_scale;
+ crtc->set_clock = nv50_crtc_set_clock;
+ crtc->set_clock_mode = nv50_crtc_set_clock_mode;
+ crtc->destroy = nv50_crtc_destroy;
+
+ return 0;
+
+out:
+ if (crtc->mode)
+ kfree(crtc->mode);
+ if (crtc->native_mode)
+ kfree(crtc->native_mode);
+ if (dev_priv->free_crtc)
+ dev_priv->free_crtc(crtc);
+
+ return rval;
+}
diff --git a/linux-core/nv50_crtc.h b/linux-core/nv50_crtc.h
new file mode 100644
index 00000000..8235c9d6
--- /dev/null
+++ b/linux-core/nv50_crtc.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_CRTC_H__
+#define __NV50_CRTC_H__
+
+#include "nv50_display.h"
+
+struct nv50_cursor;
+struct nv50_lut;
+struct nv50_fb;
+
+struct nv50_crtc {
+ struct list_head item;
+
+ struct drm_device *dev;
+ int index;
+ bool enabled;
+ bool blanked;
+
+ struct nouveau_hw_mode *mode;
+ struct nouveau_hw_mode *native_mode;
+
+ bool use_native_mode;
+ bool use_dithering;
+ int scaling_mode;
+
+ struct nv50_cursor *cursor;
+ struct nv50_lut *lut;
+ struct nv50_fb *fb;
+
+ int (*validate_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode);
+ int (*set_mode) (struct nv50_crtc *crtc, struct nouveau_hw_mode *mode);
+ int (*execute_mode) (struct nv50_crtc *crtc);
+ int (*set_fb) (struct nv50_crtc *crtc);
+ int (*blank) (struct nv50_crtc *crtc, bool blanked);
+ int (*set_dither) (struct nv50_crtc *crtc);
+ int (*set_scale) (struct nv50_crtc *crtc);
+ int (*set_clock) (struct nv50_crtc *crtc);
+ int (*set_clock_mode) (struct nv50_crtc *crtc);
+ int (*destroy) (struct nv50_crtc *crtc);
+};
+
+int nv50_crtc_create(struct drm_device *dev, int index);
+
+#endif /* __NV50_CRTC_H__ */
diff --git a/linux-core/nv50_cursor.c b/linux-core/nv50_cursor.c
new file mode 100644
index 00000000..091c94fe
--- /dev/null
+++ b/linux-core/nv50_cursor.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_cursor.h"
+#include "nv50_crtc.h"
+#include "nv50_display.h"
+
+static int nv50_cursor_enable(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ NV50_DEBUG("\n");
+
+ NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0x2000);
+ while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK);
+
+ NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON);
+ while((NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK)
+ != NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE);
+
+ crtc->cursor->enabled = true;
+
+ return 0;
+}
+
+static int nv50_cursor_disable(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ NV50_DEBUG("\n");
+
+ NV_WRITE(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index), 0);
+ while(NV_READ(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(crtc->index)) & NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK);
+
+ crtc->cursor->enabled = false;
+
+ return 0;
+}
+
+/* Calling update or changing the stored cursor state is left to the higher level ioctl's. */
+static int nv50_cursor_show(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ uint32_t offset = crtc->index * 0x400;
+
+ NV50_DEBUG("\n");
+
+ /* Better not show the cursor when we have none. */
+ /* TODO: is cursor offset actually set? */
+ if (!crtc->cursor->block) {
+ DRM_ERROR("No cursor available on crtc %d\n", crtc->index);
+ return -EINVAL;
+ }
+
+ OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_SHOW);
+
+ return 0;
+}
+
+static int nv50_cursor_hide(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ uint32_t offset = crtc->index * 0x400;
+
+ NV50_DEBUG("\n");
+
+ OUT_MODE(NV50_CRTC0_CURSOR_CTRL + offset, NV50_CRTC0_CURSOR_CTRL_HIDE);
+
+ return 0;
+}
+
+static int nv50_cursor_set_pos(struct nv50_crtc *crtc, int x, int y)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ NV_WRITE(NV50_HW_CURSOR_POS(crtc->index), ((y & 0xFFFF) << 16) | (x & 0xFFFF));
+ /* Needed to make the cursor move. */
+ NV_WRITE(NV50_HW_CURSOR_POS_CTRL(crtc->index), 0);
+
+ return 0;
+}
+
+static int nv50_cursor_set_offset(struct nv50_crtc *crtc)
+{
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ NV50_DEBUG("\n");
+
+ if (crtc->cursor->block) {
+ OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, crtc->cursor->block->start >> 8);
+ } else {
+ OUT_MODE(NV50_CRTC0_CURSOR_OFFSET + crtc->index * 0x400, 0);
+ }
+
+ return 0;
+}
+
+static int nv50_cursor_set_bo(struct nv50_crtc *crtc, drm_handle_t handle)
+{
+ struct mem_block *block = NULL;
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+
+ NV50_DEBUG("\n");
+
+ block = find_block_by_handle(dev_priv->fb_heap, handle);
+
+ if (block) {
+ bool first_time = false;
+ if (!crtc->cursor->block)
+ first_time = true;
+
+ crtc->cursor->block = block;
+
+ /* set the cursor offset cursor */
+ if (first_time) {
+ crtc->cursor->set_offset(crtc);
+ if (crtc->cursor->visible)
+ crtc->cursor->show(crtc);
+ }
+ } else {
+ DRM_ERROR("Unable to find cursor bo with handle 0x%X\n", handle);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int nv50_cursor_create(struct nv50_crtc *crtc)
+{
+ NV50_DEBUG("\n");
+
+ if (!crtc)
+ return -EINVAL;
+
+ crtc->cursor = kzalloc(sizeof(struct nv50_cursor), GFP_KERNEL);
+ if (!crtc->cursor)
+ return -ENOMEM;
+
+ /* function pointers */
+ crtc->cursor->show = nv50_cursor_show;
+ crtc->cursor->hide = nv50_cursor_hide;
+ crtc->cursor->set_pos = nv50_cursor_set_pos;
+ crtc->cursor->set_offset = nv50_cursor_set_offset;
+ crtc->cursor->set_bo = nv50_cursor_set_bo;
+ crtc->cursor->enable = nv50_cursor_enable;
+ crtc->cursor->disable = nv50_cursor_disable;
+
+ return 0;
+}
+
+int nv50_cursor_destroy(struct nv50_crtc *crtc)
+{
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ if (!crtc)
+ return -EINVAL;
+
+ if (crtc->cursor->enabled) {
+ rval = crtc->cursor->disable(crtc);
+ if (rval != 0)
+ return rval;
+ }
+
+ kfree(crtc->cursor);
+ crtc->cursor = NULL;
+
+ return 0;
+}
diff --git a/linux-core/nv50_cursor.h b/linux-core/nv50_cursor.h
new file mode 100644
index 00000000..4fd0d39a
--- /dev/null
+++ b/linux-core/nv50_cursor.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_CURSOR_H__
+#define __NV50_CURSOR_H__
+
+#include "nv50_display.h"
+
+struct nv50_crtc;
+
+struct nv50_cursor {
+ struct mem_block *block;
+ int x, y;
+ bool visible;
+ bool enabled;
+
+ int (*show) (struct nv50_crtc *crtc);
+ int (*hide) (struct nv50_crtc *crtc);
+ int (*set_pos) (struct nv50_crtc *crtc, int x, int y);
+ int (*set_offset) (struct nv50_crtc *crtc);
+ int (*set_bo) (struct nv50_crtc *crtc, drm_handle_t handle);
+ int (*enable) (struct nv50_crtc *crtc);
+ int (*disable) (struct nv50_crtc *crtc);
+};
+
+int nv50_cursor_create(struct nv50_crtc *crtc);
+int nv50_cursor_destroy(struct nv50_crtc *crtc);
+
+#endif /* __NV50_CURSOR_H__ */
diff --git a/linux-core/nv50_dac.c b/linux-core/nv50_dac.c
new file mode 100644
index 00000000..ca4bb5e1
--- /dev/null
+++ b/linux-core/nv50_dac.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_output.h"
+
+static int nv50_dac_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode)
+{
+ NV50_DEBUG("\n");
+
+ if (mode->clock > 400000)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock < 25000)
+ return MODE_CLOCK_LOW;
+
+ return MODE_OK;
+}
+
+static int nv50_dac_execute_mode(struct nv50_output *output, bool disconnect)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+ struct nv50_crtc *crtc = output->crtc;
+ struct nouveau_hw_mode *desired_mode = NULL;
+
+ uint32_t offset = nv50_output_or_offset(output) * 0x80;
+
+ uint32_t mode_ctl = NV50_DAC_MODE_CTRL_OFF;
+ uint32_t mode_ctl2 = 0;
+
+ NV50_DEBUG("or %d\n", nv50_output_or_offset(output));
+
+ if (disconnect) {
+ NV50_DEBUG("Disconnecting DAC\n");
+ OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl);
+ return 0;
+ }
+
+ desired_mode = (crtc->use_native_mode ? crtc->native_mode :
+ crtc->mode);
+
+ if (crtc->index == 1)
+ mode_ctl |= NV50_DAC_MODE_CTRL_CRTC1;
+ else
+ mode_ctl |= NV50_DAC_MODE_CTRL_CRTC0;
+
+ /* Lacking a working tv-out, this is not a 100% sure. */
+ if (output->type == OUTPUT_DAC) {
+ mode_ctl |= 0x40;
+ } else if (output->type == OUTPUT_TV) {
+ mode_ctl |= 0x100;
+ }
+
+ if (desired_mode->flags & DRM_MODE_FLAG_NHSYNC)
+ mode_ctl2 |= NV50_DAC_MODE_CTRL2_NHSYNC;
+
+ if (desired_mode->flags & DRM_MODE_FLAG_NVSYNC)
+ mode_ctl2 |= NV50_DAC_MODE_CTRL2_NVSYNC;
+
+ OUT_MODE(NV50_DAC0_MODE_CTRL + offset, mode_ctl);
+ OUT_MODE(NV50_DAC0_MODE_CTRL2 + offset, mode_ctl2);
+
+ return 0;
+}
+
+static int nv50_dac_set_clock_mode(struct nv50_output *output)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+
+ NV50_DEBUG("or %d\n", nv50_output_or_offset(output));
+
+ NV_WRITE(NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(nv50_output_or_offset(output)), 0);
+
+ return 0;
+}
+
+static int nv50_dac_set_power_mode(struct nv50_output *output, int mode)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+ uint32_t val;
+ int or = nv50_output_or_offset(output);
+
+ NV50_DEBUG("or %d\n", or);
+
+ /* wait for it to be done */
+ while (NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING);
+
+ val = NV_READ(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or)) & ~0x7F;
+
+ if (mode != DRM_MODE_DPMS_ON)
+ val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED;
+
+ switch (mode) {
+ case DRM_MODE_DPMS_STANDBY:
+ val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF;
+ break;
+ case DRM_MODE_DPMS_SUSPEND:
+ val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF;
+ break;
+ case DRM_MODE_DPMS_OFF:
+ val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF;
+ val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF;
+ val |= NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF;
+ break;
+ default:
+ break;
+ }
+
+ NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(or), val | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING);
+
+ return 0;
+}
+
+static int nv50_dac_destroy(struct nv50_output *output)
+{
+ struct drm_device *dev = output->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = nv50_get_display(dev);
+
+ NV50_DEBUG("\n");
+
+ if (!display || !output)
+ return -EINVAL;
+
+ list_del(&output->item);
+
+ kfree(output->native_mode);
+ if (dev_priv->free_output)
+ dev_priv->free_output(output);
+
+ return 0;
+}
+
+int nv50_dac_create(struct drm_device *dev, int dcb_entry)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_output *output = NULL;
+ struct nv50_display *display = NULL;
+ struct dcb_entry *entry = NULL;
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ /* This allows the public layer to do it's thing. */
+ if (dev_priv->alloc_output)
+ output = dev_priv->alloc_output(dev);
+
+ if (!output)
+ return -ENOMEM;
+
+ output->dev = dev;
+
+ display = nv50_get_display(dev);
+ if (!display) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ entry = &dev_priv->dcb_table.entry[dcb_entry];
+ if (!entry) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ switch (entry->type) {
+ case DCB_OUTPUT_ANALOG:
+ output->type = OUTPUT_DAC;
+ DRM_INFO("Detected a DAC output\n");
+ break;
+ default:
+ rval = -EINVAL;
+ goto out;
+ }
+
+ output->dcb_entry = dcb_entry;
+ output->bus = entry->bus;
+
+ list_add_tail(&output->item, &display->outputs);
+
+ output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
+ if (!output->native_mode) {
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ /* Set function pointers. */
+ output->validate_mode = nv50_dac_validate_mode;
+ output->execute_mode = nv50_dac_execute_mode;
+ output->set_clock_mode = nv50_dac_set_clock_mode;
+ output->set_power_mode = nv50_dac_set_power_mode;
+ output->detect = NULL; /* TODO */
+ output->destroy = nv50_dac_destroy;
+
+ return 0;
+
+out:
+ if (output->native_mode)
+ kfree(output->native_mode);
+ if (dev_priv->free_output)
+ dev_priv->free_output(output);
+ return rval;
+}
+
diff --git a/linux-core/nv50_display.c b/linux-core/nv50_display.c
new file mode 100644
index 00000000..b68c4e28
--- /dev/null
+++ b/linux-core/nv50_display.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_display.h"
+#include "nv50_crtc.h"
+#include "nv50_output.h"
+#include "nv50_connector.h"
+
+static int nv50_display_pre_init(struct nv50_display *display)
+{
+ struct drm_device *dev = display->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ int i;
+ uint32_t ram_amount;
+
+ NV50_DEBUG("\n");
+
+ NV_WRITE(0x00610184, NV_READ(0x00614004));
+ /*
+ * I think the 0x006101XX range is some kind of main control area that enables things.
+ */
+ /* CRTC? */
+ NV_WRITE(0x00610190 + 0 * 0x10, NV_READ(0x00616100 + 0 * 0x800));
+ NV_WRITE(0x00610190 + 1 * 0x10, NV_READ(0x00616100 + 1 * 0x800));
+ NV_WRITE(0x00610194 + 0 * 0x10, NV_READ(0x00616104 + 0 * 0x800));
+ NV_WRITE(0x00610194 + 1 * 0x10, NV_READ(0x00616104 + 1 * 0x800));
+ NV_WRITE(0x00610198 + 0 * 0x10, NV_READ(0x00616108 + 0 * 0x800));
+ NV_WRITE(0x00610198 + 1 * 0x10, NV_READ(0x00616108 + 1 * 0x800));
+ NV_WRITE(0x0061019c + 0 * 0x10, NV_READ(0x0061610c + 0 * 0x800));
+ NV_WRITE(0x0061019c + 1 * 0x10, NV_READ(0x0061610c + 1 * 0x800));
+ /* DAC */
+ NV_WRITE(0x006101d0 + 0 * 0x4, NV_READ(0x0061a000 + 0 * 0x800));
+ NV_WRITE(0x006101d0 + 1 * 0x4, NV_READ(0x0061a000 + 1 * 0x800));
+ NV_WRITE(0x006101d0 + 2 * 0x4, NV_READ(0x0061a000 + 2 * 0x800));
+ /* SOR */
+ NV_WRITE(0x006101e0 + 0 * 0x4, NV_READ(0x0061c000 + 0 * 0x800));
+ NV_WRITE(0x006101e0 + 1 * 0x4, NV_READ(0x0061c000 + 1 * 0x800));
+ /* Something not yet in use, tv-out maybe. */
+ NV_WRITE(0x006101f0 + 0 * 0x4, NV_READ(0x0061e000 + 0 * 0x800));
+ NV_WRITE(0x006101f0 + 1 * 0x4, NV_READ(0x0061e000 + 1 * 0x800));
+ NV_WRITE(0x006101f0 + 2 * 0x4, NV_READ(0x0061e000 + 2 * 0x800));
+
+ for (i = 0; i < 3; i++) {
+ NV_WRITE(NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i), 0x00550000 | NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING);
+ NV_WRITE(NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i), 0x00000001);
+ }
+
+ /* This used to be in crtc unblank, but seems out of place there. */
+ NV_WRITE(NV50_PDISPLAY_UNK_380, 0);
+ /* RAM is clamped to 256 MiB. */
+ ram_amount = nouveau_mem_fb_amount(display->dev);
+ NV50_DEBUG("ram_amount %d\n", ram_amount);
+ if (ram_amount > 256*1024*1024)
+ ram_amount = 256*1024*1024;
+ NV_WRITE(NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1);
+ NV_WRITE(NV50_PDISPLAY_UNK_388, 0x150000);
+ NV_WRITE(NV50_PDISPLAY_UNK_38C, 0);
+
+ display->preinit_done = true;
+
+ return 0;
+}
+
+static int nv50_display_init(struct nv50_display *display)
+{
+ struct drm_device *dev = display->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t val;
+
+ NV50_DEBUG("\n");
+
+ /* The precise purpose is unknown, i suspect it has something to do with text mode. */
+ if (NV_READ(NV50_PDISPLAY_SUPERVISOR) & 0x100) {
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR, 0x100);
+ NV_WRITE(0x006194e8, NV_READ(0x006194e8) & ~1);
+ while (NV_READ(0x006194e8) & 2);
+ }
+
+ /* taken from nv bug #12637 */
+ NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x2b00);
+ do {
+ val = NV_READ(NV50_PDISPLAY_UNK200_CTRL);
+ if ((val & 0x9f0000) == 0x20000)
+ NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x800000);
+
+ if ((val & 0x3f0000) == 0x30000)
+ NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, val | 0x200000);
+ } while (val & 0x1e0000);
+
+ NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE);
+ NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0x1000b03);
+ while (!(NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x40000000));
+
+ /* For the moment this is just a wrapper, which should be replaced with a real fifo at some point. */
+ OUT_MODE(NV50_UNK84, 0);
+ OUT_MODE(NV50_UNK88, 0);
+ OUT_MODE(NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK);
+ OUT_MODE(NV50_CRTC0_UNK800, 0);
+ OUT_MODE(NV50_CRTC0_DISPLAY_START, 0);
+ OUT_MODE(NV50_CRTC0_UNK82C, 0);
+
+ /* enable clock change interrupts. */
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) | 0x70);
+
+ display->init_done = true;
+
+ return 0;
+}
+
+static int nv50_display_disable(struct nv50_display *display)
+{
+ struct drm_device *dev = display->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_crtc *crtc = NULL;
+ int i;
+
+ NV50_DEBUG("\n");
+
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ crtc->blank(crtc, true);
+ }
+
+ display->update(display);
+
+ /* Almost like ack'ing a vblank interrupt, maybe in the spirit of cleaning up? */
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ if (crtc->enabled) {
+ uint32_t mask;
+
+ if (crtc->index == 1)
+ mask = NV50_PDISPLAY_SUPERVISOR_CRTC1;
+ else
+ mask = NV50_PDISPLAY_SUPERVISOR_CRTC0;
+
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR, mask);
+ while (!(NV_READ(NV50_PDISPLAY_SUPERVISOR) & mask));
+ }
+ }
+
+ NV_WRITE(NV50_PDISPLAY_UNK200_CTRL, 0);
+ NV_WRITE(NV50_PDISPLAY_CTRL_STATE, 0);
+ while ((NV_READ(NV50_PDISPLAY_UNK200_CTRL) & 0x1e0000) != 0);
+
+ for (i = 0; i < 2; i++) {
+ while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i)) & NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT);
+ }
+
+ /* disable clock change interrupts. */
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR_INTR, NV_READ(NV50_PDISPLAY_SUPERVISOR_INTR) & ~0x70);
+
+ display->init_done = FALSE;
+
+ return 0;
+}
+
+static int nv50_display_update(struct nv50_display *display)
+{
+ struct drm_device *dev = display->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ NV50_DEBUG("\n");
+
+ OUT_MODE(NV50_UPDATE_DISPLAY, 0);
+
+ return 0;
+}
+
+int nv50_display_create(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = kzalloc(sizeof(struct nv50_display), GFP_KERNEL);
+ int i, type, output_index, bus;
+ /* DAC0, DAC1, DAC2, SOR0, SOR1*/
+ int or_counter[5] = {0, 0, 0, 0, 0};
+ int i2c_index[5] = {0, 0, 0, 0, 0};
+ uint32_t bus_mask = 0;
+ uint32_t bus_digital = 0, bus_analog = 0;
+
+ NV50_DEBUG("\n");
+
+ if (!display)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&display->crtcs);
+ INIT_LIST_HEAD(&display->outputs);
+ INIT_LIST_HEAD(&display->connectors);
+
+ dev_priv->display_priv = display;
+
+ for (i = 0; i < 2; i++) {
+ nv50_crtc_create(dev, i);
+ }
+
+ /* we setup the outputs up from the BIOS table */
+ for (i = 0 ; i < dev_priv->dcb_table.entries; i++) {
+ type = dev_priv->dcb_table.entry[i].type;
+ output_index = ffs(dev_priv->dcb_table.entry[i].or) - 1;
+ bus = dev_priv->dcb_table.entry[i].bus;
+
+ switch (type) {
+ case DCB_OUTPUT_TMDS:
+ case DCB_OUTPUT_LVDS:
+ or_counter[output_index + 3] += 1;
+ i2c_index[output_index + 3] = dev_priv->dcb_table.entry[i].i2c_index;
+ bus_digital |= (1 << bus);
+ nv50_sor_create(dev, i);
+ break;
+ case DCB_OUTPUT_ANALOG:
+ or_counter[output_index] += 1;
+ i2c_index[output_index] = dev_priv->dcb_table.entry[i].i2c_index;
+ bus_analog |= (1 << bus);
+ nv50_dac_create(dev, i);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ /* setup the connectors based on the output tables. */
+ for (i = 0 ; i < dev_priv->dcb_table.entries; i++) {
+ int connector_type = 0;
+ type = dev_priv->dcb_table.entry[i].type;
+ bus = dev_priv->dcb_table.entry[i].bus;
+
+ /* already done? */
+ if (bus_mask & (1 << bus))
+ continue;
+
+ /* only do it for supported outputs */
+ if (type != DCB_OUTPUT_ANALOG && type != DCB_OUTPUT_TMDS
+ && type != DCB_OUTPUT_LVDS)
+ continue;
+
+ switch (type) {
+ case DCB_OUTPUT_TMDS:
+ case DCB_OUTPUT_ANALOG:
+ if ((bus_digital & (1 << bus)) && (bus_analog & (1 << bus)))
+ connector_type = CONNECTOR_DVI_I;
+ else if (bus_digital & (1 << bus))
+ connector_type = CONNECTOR_DVI_D;
+ else if (bus_analog & (1 << bus))
+ connector_type = CONNECTOR_VGA;
+ break;
+ case DCB_OUTPUT_LVDS:
+ connector_type = CONNECTOR_LVDS;
+ break;
+ default:
+ connector_type = CONNECTOR_UNKNOWN;
+ break;
+ }
+
+ if (connector_type == CONNECTOR_UNKNOWN)
+ continue;
+
+ nv50_connector_create(dev, bus, dev_priv->dcb_table.entry[i].i2c_index, connector_type);
+
+ bus_mask |= (1 << bus);
+ }
+
+ display->dev = dev;
+
+ /* function pointers */
+ display->init = nv50_display_init;
+ display->pre_init = nv50_display_pre_init;
+ display->disable = nv50_display_disable;
+ display->update = nv50_display_update;
+
+ return 0;
+}
+
+int nv50_display_destroy(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = nv50_get_display(dev);
+ struct nv50_crtc *crtc = NULL;
+ struct nv50_output *output = NULL;
+ struct nv50_connector *connector = NULL;
+
+ NV50_DEBUG("\n");
+
+ if (display->init_done)
+ display->disable(display);
+
+ list_for_each_entry(connector, &display->connectors, item) {
+ connector->destroy(connector);
+ }
+
+ list_for_each_entry(output, &display->outputs, item) {
+ output->destroy(output);
+ }
+
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ crtc->destroy(crtc);
+ }
+
+ kfree(display);
+ dev_priv->display_priv = NULL;
+
+ return 0;
+}
+
+/* This can be replaced with a real fifo in the future. */
+void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val)
+{
+ uint32_t counter = 0;
+
+#if 1
+ DRM_INFO("mthd 0x%03X val 0x%08X\n", mthd, val);
+#endif
+
+ NV_WRITE(NV50_PDISPLAY_CTRL_VAL, val);
+ NV_WRITE(NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_PENDING | 0x10000 | mthd | NV50_PDISPLAY_CTRL_STATE_ENABLE);
+
+ while (NV_READ(NV50_PDISPLAY_CTRL_STATE) & NV50_PDISPLAY_CTRL_STATE_PENDING) {
+ counter++;
+ if (counter > 1000000) {
+ DRM_ERROR("You probably need a reboot now\n");
+ break;
+ }
+ udelay(1);
+ }
+}
+
+struct nv50_display *nv50_get_display(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ return (struct nv50_display *) dev_priv->display_priv;
+}
diff --git a/linux-core/nv50_display.h b/linux-core/nv50_display.h
new file mode 100644
index 00000000..f20e67da
--- /dev/null
+++ b/linux-core/nv50_display.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_DISPLAY_H__
+#define __NV50_DISPLAY_H__
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_dma.h"
+#include "nouveau_drv.h"
+#include "nouveau_reg.h"
+#include "nv50_display_commands.h"
+
+/* for convience, so you can see through the trees. */
+#define NV50_DEBUG DRM_ERROR
+
+struct nouveau_hw_mode {
+ unsigned int clock;
+ unsigned short hdisplay, hblank_start, hsync_start, hsync_end, hblank_end, htotal;
+ unsigned short vdisplay, vblank_start, vsync_start, vsync_end, vblank_end, vtotal;
+
+ unsigned int flags;
+};
+
+struct nv50_crtc;
+struct nv50_output;
+struct nv50_connector;
+
+struct nv50_display {
+ struct drm_device *dev;
+
+ bool preinit_done;
+ bool init_done;
+
+ int last_crtc; /* crtc used for last mode set */
+
+ int (*pre_init) (struct nv50_display *display);
+ int (*init) (struct nv50_display *display);
+ int (*disable) (struct nv50_display *display);
+ int (*update) (struct nv50_display *display);
+
+ struct list_head crtcs;
+ struct list_head outputs;
+ struct list_head connectors;
+};
+
+enum scaling_modes {
+ SCALE_PANEL,
+ SCALE_FULLSCREEN,
+ SCALE_ASPECT,
+ SCALE_NOSCALE,
+ SCALE_INVALID
+};
+
+void nv50_display_command(struct drm_nouveau_private *dev_priv, uint32_t mthd, uint32_t val);
+struct nv50_display *nv50_get_display(struct drm_device *dev);
+int nv50_display_create(struct drm_device *dev);
+int nv50_display_destroy(struct drm_device *dev);
+
+#endif /* __NV50_DISPLAY_H__ */
diff --git a/linux-core/nv50_display_commands.h b/linux-core/nv50_display_commands.h
new file mode 100644
index 00000000..97b3d3c1
--- /dev/null
+++ b/linux-core/nv50_display_commands.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+/* copied from ddx definitions, until rules-ng can handle this */
+
+#define NV50_UPDATE_DISPLAY 0x80
+#define NV50_UNK84 0x84
+#define NV50_UNK88 0x88
+
+#define NV50_DAC0_MODE_CTRL 0x400
+ #define NV50_DAC_MODE_CTRL_OFF (0 << 0)
+ #define NV50_DAC_MODE_CTRL_CRTC0 (1 << 0)
+ #define NV50_DAC_MODE_CTRL_CRTC1 (1 << 1)
+#define NV50_DAC1_MODE_CTRL 0x480
+#define NV50_DAC2_MODE_CTRL 0x500
+
+#define NV50_DAC0_MODE_CTRL2 0x404
+ #define NV50_DAC_MODE_CTRL2_NHSYNC (1 << 0)
+ #define NV50_DAC_MODE_CTRL2_NVSYNC (2 << 0)
+#define NV50_DAC1_MODE_CTRL2 0x484
+#define NV50_DAC2_MODE_CTRL2 0x504
+
+#define NV50_SOR0_MODE_CTRL 0x600
+ #define NV50_SOR_MODE_CTRL_OFF (0 << 0)
+ #define NV50_SOR_MODE_CTRL_CRTC0 (1 << 0)
+ #define NV50_SOR_MODE_CTRL_CRTC1 (1 << 1)
+ #define NV50_SOR_MODE_CTRL_LVDS (0 << 8)
+ #define NV50_SOR_MODE_CTRL_TMDS (1 << 8)
+ #define NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK (4 << 8)
+ #define NV50_SOR_MODE_CTRL_NHSYNC (1 << 12)
+ #define NV50_SOR_MODE_CTRL_NVSYNC (2 << 12)
+#define NV50_SOR1_MODE_CTRL 0x640
+
+#define NV50_CRTC0_UNK800 0x800
+#define NV50_CRTC0_CLOCK 0x804
+#define NV50_CRTC0_INTERLACE 0x808
+
+/* 0x810 is a reasonable guess, nothing more. */
+#define NV50_CRTC0_DISPLAY_START 0x810
+#define NV50_CRTC0_DISPLAY_TOTAL 0x814
+#define NV50_CRTC0_SYNC_DURATION 0x818
+#define NV50_CRTC0_SYNC_START_TO_BLANK_END 0x81C
+#define NV50_CRTC0_MODE_UNK1 0x820
+#define NV50_CRTC0_MODE_UNK2 0x824
+
+#define NV50_CRTC0_UNK82C 0x82C
+
+/* You can't have a palette in 8 bit mode (=OFF) */
+#define NV50_CRTC0_CLUT_MODE 0x840
+ #define NV50_CRTC0_CLUT_MODE_BLANK 0x00000000
+ #define NV50_CRTC0_CLUT_MODE_OFF 0x80000000
+ #define NV50_CRTC0_CLUT_MODE_ON 0xC0000000
+#define NV50_CRTC0_CLUT_OFFSET 0x844
+
+/* Anyone know what part of the chip is triggered here precisely? */
+#define NV84_CRTC0_BLANK_UNK1 0x85C
+ #define NV84_CRTC0_BLANK_UNK1_BLANK 0x0
+ #define NV84_CRTC0_BLANK_UNK1_UNBLANK 0x1
+
+#define NV50_CRTC0_FB_OFFSET 0x860
+
+#define NV50_CRTC0_FB_SIZE 0x868
+#define NV50_CRTC0_FB_PITCH 0x86C
+
+#define NV50_CRTC0_DEPTH 0x870
+ #define NV50_CRTC0_DEPTH_8BPP 0x1E00
+ #define NV50_CRTC0_DEPTH_15BPP 0xE900
+ #define NV50_CRTC0_DEPTH_16BPP 0xE800
+ #define NV50_CRTC0_DEPTH_24BPP 0xCF00
+
+/* I'm openminded to better interpretations. */
+/* This is an educated guess. */
+/* NV50 has RAMDAC and TMDS offchip, so it's unlikely to be that. */
+#define NV50_CRTC0_BLANK_CTRL 0x874
+ #define NV50_CRTC0_BLANK_CTRL_BLANK 0x0
+ #define NV50_CRTC0_BLANK_CTRL_UNBLANK 0x1
+
+#define NV50_CRTC0_CURSOR_CTRL 0x880
+ #define NV50_CRTC0_CURSOR_CTRL_SHOW 0x85000000
+ #define NV50_CRTC0_CURSOR_CTRL_HIDE 0x05000000
+
+#define NV50_CRTC0_CURSOR_OFFSET 0x884
+
+/* Anyone know what part of the chip is triggered here precisely? */
+#define NV84_CRTC0_BLANK_UNK2 0x89C
+ #define NV84_CRTC0_BLANK_UNK2_BLANK 0x0
+ #define NV84_CRTC0_BLANK_UNK2_UNBLANK 0x1
+
+#define NV50_CRTC0_DITHERING_CTRL 0x8A0
+ #define NV50_CRTC0_DITHERING_CTRL_ON 0x11
+ #define NV50_CRTC0_DITHERING_CTRL_OFF 0x0
+
+#define NV50_CRTC0_SCALE_CTRL 0x8A4
+ #define NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE (0 << 0)
+ /* It doesn't seem to be needed, hence i wonder what it does precisely. */
+ #define NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE (9 << 0)
+#define NV50_CRTC0_COLOR_CTRL 0x8A8
+ #define NV50_CRTC_COLOR_CTRL_MODE_COLOR (4 << 16)
+
+#define NV50_CRTC0_FB_POS 0x8C0
+#define NV50_CRTC0_REAL_RES 0x8C8
+
+/* Added a macro, because the signed stuff can cause you problems very quickly. */
+#define NV50_CRTC0_SCALE_CENTER_OFFSET 0x8D4
+ #define NV50_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF))
+/* Both of these are needed, otherwise nothing happens. */
+#define NV50_CRTC0_SCALE_RES1 0x8D8
+#define NV50_CRTC0_SCALE_RES2 0x8DC
+
+#define NV50_CRTC1_UNK800 0xC00
+#define NV50_CRTC1_CLOCK 0xC04
+#define NV50_CRTC1_INTERLACE 0xC08
+
+/* 0xC10 is a reasonable guess, nothing more. */
+#define NV50_CRTC1_DISPLAY_START 0xC10
+#define NV50_CRTC1_DISPLAY_TOTAL 0xC14
+#define NV50_CRTC1_SYNC_DURATION 0xC18
+#define NV50_CRTC1_SYNC_START_TO_BLANK_END 0xC1C
+#define NV50_CRTC1_MODE_UNK1 0xC20
+#define NV50_CRTC1_MODE_UNK2 0xC24
+
+#define NV50_CRTC1_CLUT_MODE 0xC40
+ #define NV50_CRTC1_CLUT_MODE_BLANK 0x00000000
+ #define NV50_CRTC1_CLUT_MODE_OFF 0x80000000
+ #define NV50_CRTC1_CLUT_MODE_ON 0xC0000000
+#define NV50_CRTC1_CLUT_OFFSET 0xC44
+
+/* Anyone know what part of the chip is triggered here precisely? */
+#define NV84_CRTC1_BLANK_UNK1 0xC5C
+ #define NV84_CRTC1_BLANK_UNK1_BLANK 0x0
+ #define NV84_CRTC1_BLANK_UNK1_UNBLANK 0x1
+
+#define NV50_CRTC1_FB_OFFSET 0xC60
+
+#define NV50_CRTC1_FB_SIZE 0xC68
+#define NV50_CRTC1_FB_PITCH 0xC6C
+
+#define NV50_CRTC1_DEPTH 0xC70
+ #define NV50_CRTC1_DEPTH_8BPP 0x1E00
+ #define NV50_CRTC1_DEPTH_15BPP 0xE900
+ #define NV50_CRTC1_DEPTH_16BPP 0xE800
+ #define NV50_CRTC1_DEPTH_24BPP 0xCF00
+
+/* I'm openminded to better interpretations. */
+#define NV50_CRTC1_BLANK_CTRL 0xC74
+ #define NV50_CRTC1_BLANK_CTRL_BLANK 0x0
+ #define NV50_CRTC1_BLANK_CTRL_UNBLANK 0x1
+
+#define NV50_CRTC1_CURSOR_CTRL 0xC80
+ #define NV50_CRTC1_CURSOR_CTRL_SHOW 0x85000000
+ #define NV50_CRTC1_CURSOR_CTRL_HIDE 0x05000000
+
+#define NV50_CRTC1_CURSOR_OFFSET 0xC84
+
+/* Anyone know what part of the chip is triggered here precisely? */
+#define NV84_CRTC1_BLANK_UNK2 0xC9C
+ #define NV84_CRTC1_BLANK_UNK2_BLANK 0x0
+ #define NV84_CRTC1_BLANK_UNK2_UNBLANK 0x1
+
+#define NV50_CRTC1_DITHERING_CTRL 0xCA0
+ #define NV50_CRTC1_DITHERING_CTRL_ON 0x11
+ #define NV50_CRTC1_DITHERING_CTRL_OFF 0x0
+
+#define NV50_CRTC1_SCALE_CTRL 0xCA4
+#define NV50_CRTC1_COLOR_CTRL 0xCA8
+
+#define NV50_CRTC1_FB_POS 0xCC0
+#define NV50_CRTC1_REAL_RES 0xCC8
+
+#define NV50_CRTC1_SCALE_CENTER_OFFSET 0xCD4
+/* Both of these are needed, otherwise nothing happens. */
+#define NV50_CRTC1_SCALE_RES1 0xCD8
+#define NV50_CRTC1_SCALE_RES2 0xCDC
diff --git a/linux-core/nv50_fb.c b/linux-core/nv50_fb.c
new file mode 100644
index 00000000..153899da
--- /dev/null
+++ b/linux-core/nv50_fb.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_fb.h"
+#include "nv50_lut.h"
+#include "nv50_crtc.h"
+#include "nv50_display.h"
+
+static int nv50_fb_bind(struct nv50_crtc *crtc, struct nv50_fb_info *info)
+{
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ if (!crtc || !info) {
+ DRM_ERROR("crtc %p info %p\n",crtc, info);
+ return -EINVAL;
+ }
+
+ if (!info->block || !info->width || !info->height || !info->depth || !info->bpp || !info->pitch) {
+ DRM_ERROR("block %p width %d height %d depth %d bpp %d pitch %d\n", info->block, info->width,
+ info->height, info->depth, info->bpp, info->pitch);
+ return -EINVAL;
+ }
+
+ crtc->fb->block = info->block;
+ crtc->fb->width = info->width;
+ crtc->fb->height = info->height;
+
+ crtc->fb->y = info->x;
+ crtc->fb->x = info->y;
+
+ crtc->fb->depth = info->depth;
+ crtc->fb->bpp = info->bpp;
+
+ crtc->fb->pitch = info->pitch;
+
+ /* update lut if needed */
+ if (crtc->fb->depth != crtc->lut->depth) {
+ int r_size = 0, g_size = 0, b_size = 0;
+ uint16_t *r_val, *g_val, *b_val;
+ int i;
+
+ switch (crtc->fb->depth) {
+ case 15:
+ r_size = 32;
+ g_size = 32;
+ b_size = 32;
+ break;
+ case 16:
+ r_size = 32;
+ g_size = 64;
+ b_size = 32;
+ break;
+ case 24:
+ default:
+ r_size = 256;
+ g_size = 256;
+ b_size = 256;
+ break;
+ }
+
+ r_val = kmalloc(r_size * sizeof(uint16_t), GFP_KERNEL);
+ g_val = kmalloc(g_size * sizeof(uint16_t), GFP_KERNEL);
+ b_val = kmalloc(b_size * sizeof(uint16_t), GFP_KERNEL);
+
+ if (!r_val || !g_val || !b_val)
+ return -ENOMEM;
+
+ /* Set the color indices. */
+ for (i = 0; i < r_size; i++) {
+ r_val[i] = i << 8;
+ }
+ for (i = 0; i < g_size; i++) {
+ g_val[i] = i << 8;
+ }
+ for (i = 0; i < b_size; i++) {
+ b_val[i] = i << 8;
+ }
+
+ rval = crtc->lut->set(crtc, r_val, g_val, b_val);
+
+ /* free before returning */
+ kfree(r_val);
+ kfree(g_val);
+ kfree(b_val);
+
+ if (rval != 0)
+ return rval;
+ }
+
+ return 0;
+}
+
+int nv50_fb_create(struct nv50_crtc *crtc)
+{
+ if (!crtc)
+ return -EINVAL;
+
+ crtc->fb = kzalloc(sizeof(struct nv50_fb), GFP_KERNEL);
+
+ if (!crtc->fb)
+ return -ENOMEM;
+
+ crtc->fb->bind = nv50_fb_bind;
+
+ return 0;
+}
+
+int nv50_fb_destroy(struct nv50_crtc *crtc)
+{
+ if (!crtc)
+ return -EINVAL;
+
+ kfree(crtc->fb);
+ crtc->fb = NULL;
+
+ return 0;
+}
diff --git a/linux-core/nv50_fb.h b/linux-core/nv50_fb.h
new file mode 100644
index 00000000..3051dc5c
--- /dev/null
+++ b/linux-core/nv50_fb.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_FB_H__
+#define __NV50_FB_H__
+
+#include "nv50_display.h"
+
+struct nv50_crtc;
+
+struct nv50_fb_info {
+ struct mem_block *block;
+ int width, height;
+ int bpp, depth;
+ int pitch;
+ int x,y;
+};
+
+struct nv50_fb {
+ struct mem_block *block;
+ int width, height;
+ int bpp, depth;
+ int pitch;
+
+ int x,y;
+
+ /* function points */
+ int (*bind) (struct nv50_crtc *crtc, struct nv50_fb_info *info);
+};
+
+int nv50_fb_create(struct nv50_crtc *crtc);
+int nv50_fb_destroy(struct nv50_crtc *crtc);
+
+#endif /* __NV50_FB_H__ */
diff --git a/linux-core/nv50_fbcon.c b/linux-core/nv50_fbcon.c
new file mode 100644
index 00000000..3dd73062
--- /dev/null
+++ b/linux-core/nv50_fbcon.c
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+
+#include "nv50_fbcon.h"
+
+static int nv50_fbcon_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct nv50_fbcon_par *par = info->par;
+ struct drm_device *dev = par->dev;
+ struct drm_framebuffer *drm_fb;
+
+ list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
+ if (regno > 255)
+ return 1;
+
+ /* TODO: 8 bit support */
+ if (regno < 16) {
+ switch (drm_fb->depth) {
+ case 15:
+ drm_fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) |
+ ((green & 0xf800) >> 6) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 16:
+ drm_fb->pseudo_palette[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) |
+ ((blue & 0xf800) >> 11);
+ break;
+ case 24:
+ case 32:
+ drm_fb->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+ (green & 0xff00) |
+ ((blue & 0xff00) >> 8);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nv50_fbcon_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct nv50_fbcon_par *par = info->par;
+ struct drm_framebuffer *drm_fb = par->fb;
+ int depth;
+
+ NV50_DEBUG("\n");
+
+ if (!var || !drm_fb || !info) {
+ DRM_ERROR("No var, drm_fb or info\n");
+ }
+
+ par->use_preferred_mode = false;
+
+ if (var->pixclock == -1 || !var->pixclock) {
+ DRM_INFO("Using preferred mode.\n");
+ par->use_preferred_mode = true;
+ }
+
+ /* Need to resize the fb object !!! */
+ if (var->xres > drm_fb->width || var->yres > drm_fb->height) {
+ DRM_ERROR("Requested width/height is greater than current fb object %dx%d > %dx%d\n", var->xres,var->yres, drm_fb->width, drm_fb->height);
+ DRM_ERROR("Need resizing code.\n");
+ return -EINVAL;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ depth = var->bits_per_pixel;
+ break;
+ }
+
+ switch (depth) {
+ case 15:
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 1;
+ var->transp.offset = 15;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 24:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 32:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ break;
+ default:
+ DRM_ERROR("Invalid depth %d\n", depth);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nv50_fbcon_set_par(struct fb_info *info)
+{
+ struct nv50_fbcon_par *par;
+ struct drm_framebuffer *drm_fb;
+ struct drm_connector *drm_connector;
+ struct drm_crtc *drm_crtc;
+ struct fb_var_screeninfo *var;
+ struct drm_display_mode *drm_mode = NULL, *t;
+ struct drm_device *dev;
+ int rval;
+ bool crtc_used[2] = {false, false};
+
+ NV50_DEBUG("\n");
+
+ if (!info) {
+ DRM_ERROR("No fb_info\n");
+ return -EINVAL;
+ }
+
+ par = info->par;
+
+ if (!par) {
+ DRM_ERROR("No nv50_fbcon_par\n");
+ return -EINVAL;
+ }
+
+ drm_fb = par->fb;
+ var = &info->var;
+ dev = par->dev;
+
+ if (!drm_fb || !var || !dev) {
+ DRM_ERROR("No drm_fb, var or dev\n");
+ return -EINVAL;
+ }
+
+ par->use_preferred_mode = false;
+
+ if (var->pixclock == -1 || !var->pixclock) {
+ DRM_INFO("Using preferred mode.\n");
+ par->use_preferred_mode = true;
+ }
+
+ /* FB setup */
+ switch (var->bits_per_pixel) {
+ case 16:
+ drm_fb->depth = (var->green.length == 6) ? 16 : 15;
+ break;
+ case 32:
+ drm_fb->depth = (var->transp.length > 0) ? 32 : 24;
+ break;
+ default:
+ drm_fb->depth = var->bits_per_pixel;
+ break;
+ }
+
+ drm_fb->bits_per_pixel = var->bits_per_pixel;
+
+ info->fix.line_length = drm_fb->pitch;
+ info->fix.smem_len = info->fix.line_length * drm_fb->height;
+ /* ignoring 8bpp for the moment */
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ info->screen_size = info->fix.smem_len; /* ??? */
+
+ /* create a drm mode */
+ if (!par->use_preferred_mode) {
+ drm_mode = drm_mode_create(dev);
+ drm_mode->hdisplay = var->xres;
+ drm_mode->hsync_start = drm_mode->hdisplay + var->right_margin;
+ drm_mode->hsync_end = drm_mode->hsync_start + var->hsync_len;
+ drm_mode->htotal = drm_mode->hsync_end + var->left_margin;
+ drm_mode->vdisplay = var->yres;
+ drm_mode->vsync_start = drm_mode->vdisplay + var->lower_margin;
+ drm_mode->vsync_end = drm_mode->vsync_start + var->vsync_len;
+ drm_mode->vtotal = drm_mode->vsync_end + var->upper_margin;
+ drm_mode->clock = PICOS2KHZ(var->pixclock);
+ drm_mode->vrefresh = drm_mode_vrefresh(drm_mode);
+ drm_mode->flags = 0;
+ drm_mode->flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+ drm_mode->flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
+
+ drm_mode_set_name(drm_mode);
+ drm_mode_set_crtcinfo(drm_mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ /* hook up crtc's */
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
+ enum drm_connector_status status;
+ struct drm_mode_set mode_set;
+ int crtc_count = 0;
+
+ status = drm_connector->funcs->detect(drm_connector);
+
+ if (status != connector_status_connected)
+ continue;
+
+ memset(&mode_set, 0, sizeof(struct drm_mode_set));
+
+ /* set connector */
+ mode_set.num_connectors = 1;
+ mode_set.connectors = kzalloc(sizeof(struct drm_connector *), GFP_KERNEL);
+ if (!mode_set.connectors) {
+ rval = -ENOMEM;
+ goto out;
+ }
+ mode_set.connectors[0] = drm_connector;
+
+ /* set fb */
+ list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
+ break; /* first entry is the only entry */
+ }
+ mode_set.fb = drm_fb;
+
+ /* set mode */
+ if (par->use_preferred_mode) {
+ /* find preferred mode */
+ list_for_each_entry_safe(drm_mode, t, &drm_connector->modes, head) {
+ if (drm_mode->type & DRM_MODE_TYPE_PREFERRED)
+ break;
+ }
+ }
+ mode_set.mode = drm_mode;
+
+ /* choose crtc it already has, if possible */
+ if (drm_connector->encoder) {
+ struct drm_encoder *drm_encoder = drm_connector->encoder;
+
+ if (drm_encoder->crtc) {
+ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
+ if (drm_crtc == drm_encoder->crtc) {
+ if (!crtc_used[crtc_count]) /* still available? */
+ mode_set.crtc = drm_crtc;
+ break;
+ }
+
+ crtc_count++;
+ }
+ }
+ }
+
+ /* proceed as planned */
+ if (mode_set.crtc) {
+ mode_set.crtc->funcs->set_config(&mode_set);
+ crtc_used[crtc_count] = true;
+ }
+
+ if (!mode_set.crtc) {
+ crtc_count = 0; /* reset */
+
+ /* choose a "random" crtc */
+ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
+ if (crtc_used[crtc_count]) {
+ crtc_count++;
+ continue;
+ }
+
+ /* found a crtc */
+ mode_set.crtc = drm_crtc;
+
+ break;
+ }
+
+ /* proceed as planned */
+ if (mode_set.crtc) {
+ mode_set.crtc->funcs->set_config(&mode_set);
+ crtc_used[crtc_count] = true;
+ }
+ }
+
+ kfree(mode_set.connectors);
+ }
+
+ return 0;
+
+out:
+ return rval;
+}
+
+static struct fb_ops nv50_fb_ops = {
+ .owner = THIS_MODULE,
+ //.fb_open = nv50_fb_open,
+ //.fb_read = nv50_fb_read,
+ //.fb_write = nv50_fb_write,
+ //.fb_release = nv50_fb_release,
+ //.fb_ioctl = nv50_fb_ioctl,
+ .fb_check_var = nv50_fbcon_check_var,
+ .fb_set_par = nv50_fbcon_set_par,
+ .fb_setcolreg = nv50_fbcon_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ //.fb_pan_display = nv50_fb_pan_display,
+};
+
+static int nv50_fbcon_initial_config(struct drm_device *dev)
+{
+ struct drm_connector *drm_connector;
+
+ struct drm_framebuffer *drm_fb = NULL;
+ struct drm_mode_fb_cmd drm_fb_cmd;
+ enum drm_connector_status status;
+ uint32_t max_width = 0, max_height = 0, pitch = 0;
+ struct mem_block *block;
+ struct drm_file *file_priv;
+ uint32_t flags;
+ int rval = 0;
+
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
+ status = drm_connector->funcs->detect(drm_connector);
+
+ /* find the framebuffer size */
+ if (status == connector_status_connected) {
+ struct drm_display_mode *mode, *t;
+ list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
+ if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+ if (mode->hdisplay > max_width)
+ max_width = mode->hdisplay;
+ if (mode->vdisplay > max_height)
+ max_height = mode->vdisplay;
+ }
+ }
+ }
+ }
+
+ /* allocate framebuffer */
+ file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL);
+ if (!file_priv) {
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ pitch = (max_width + 63) & ~63;
+ pitch *= 4; /* TODO */
+
+ flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED;
+
+ /* Any file_priv should do as it's pointer is used as identification. */
+ block = nouveau_mem_alloc(dev, 0, pitch * max_height, flags, file_priv);
+ if (!block) {
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ memset(&drm_fb_cmd, 0, sizeof(struct drm_mode_fb_cmd));
+
+ drm_fb_cmd.width = max_width;
+ drm_fb_cmd.height = max_height;
+ drm_fb_cmd.pitch = pitch;
+ drm_fb_cmd.bpp = 32; /* TODO */
+ drm_fb_cmd.handle = block->map_handle;
+ drm_fb_cmd.depth = 24; /* TODO */
+
+ drm_fb = dev->mode_config.funcs->fb_create(dev, file_priv, &drm_fb_cmd);
+ if (!drm_fb) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ list_add(&drm_fb->filp_head, &dev->mode_config.fb_kernel_list);
+
+ return 0;
+
+out:
+ if (file_priv)
+ kfree(file_priv);
+ if (drm_fb)
+ drm_fb->funcs->destroy(drm_fb);
+
+ return rval;
+}
+
+/*
+ * Single framebuffer, ideally operating in clone mode across various connectors.
+ */
+int nv50_fbcon_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct fb_info *info;
+ struct nv50_fbcon_par *par;
+ struct device *device = &dev->pdev->dev;
+ struct drm_framebuffer *drm_fb;
+ struct mem_block *block;
+ void __iomem *fb = NULL;
+ int rval;
+
+ rval = nv50_fbcon_initial_config(dev);
+ if (rval != 0) {
+ DRM_ERROR("nv50_fbcon_initial_config failed\n");
+ return rval;
+ }
+
+ list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
+ break; /* first entry is the only entry */
+ }
+
+ if (!drm_fb) {
+ DRM_ERROR("no drm_fb found\n");
+ return -EINVAL;
+ }
+
+ block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle);
+ if (!block) {
+ DRM_ERROR("can't find mem_block\n");
+ return -EINVAL;
+ }
+
+ info = framebuffer_alloc(sizeof(struct nv50_fbcon_par), device);
+ if (!info) {
+ DRM_ERROR("framebuffer_alloc failed\n");
+ return -EINVAL;
+ }
+
+ par = info->par;
+
+ strcpy(info->fix.id, "nv50drmfb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 0; /* 1 is doing it in hw */
+ info->fix.ypanstep = 0;
+ info->fix.ywrapstep = 0;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.type_aux = 0;
+
+ info->flags = FBINFO_DEFAULT;
+
+ info->fbops = &nv50_fb_ops;
+
+ info->fix.line_length = drm_fb->pitch;
+ info->fix.smem_start = dev_priv->fb_phys + block->start;
+ info->fix.smem_len = info->fix.line_length * drm_fb->height;
+
+ info->flags = FBINFO_DEFAULT;
+
+ fb = ioremap(dev_priv->fb_phys + block->start, block->size);
+ if (!fb) {
+ DRM_ERROR("Unable to ioremap framebuffer\n");
+ return -EINVAL;
+ }
+
+ info->screen_base = fb;
+ info->screen_size = info->fix.smem_len; /* FIXME */
+
+ info->pseudo_palette = drm_fb->pseudo_palette;
+ info->var.xres_virtual = drm_fb->width;
+ info->var.yres_virtual = drm_fb->height;
+ info->var.bits_per_pixel = drm_fb->bits_per_pixel;
+ info->var.xoffset = 0;
+ info->var.yoffset = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = -1;
+ info->var.width = -1;
+
+ /* TODO: improve this */
+ info->var.xres = drm_fb->width;
+ info->var.yres = drm_fb->height;
+
+ info->fix.mmio_start = drm_get_resource_start(dev, 0);
+ info->fix.mmio_len = drm_get_resource_len(dev, 0);
+
+ DRM_DEBUG("fb depth is %d\n", drm_fb->depth);
+ DRM_DEBUG(" pitch is %d\n", drm_fb->pitch);
+
+ switch(drm_fb->depth) {
+ case 15:
+ info->var.red.offset = 10;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 5;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 15;
+ info->var.transp.length = 1;
+ break;
+ case 16:
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ break;
+ case 24:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ break;
+ case 32:
+ info->var.red.offset = 16;
+ info->var.green.offset = 8;
+ info->var.blue.offset = 0;
+ info->var.red.length = 8;
+ info->var.green.length = 8;
+ info->var.blue.length = 8;
+ info->var.transp.offset = 24;
+ info->var.transp.length = 8;
+ break;
+ default:
+ break;
+ }
+
+ drm_fb->fbdev = info;
+ par->dev = dev;
+ par->fb = drm_fb;
+
+ register_framebuffer(info);
+
+ DRM_INFO("nv50drmfb initialised\n");
+
+ return 0;
+}
+
+int nv50_fbcon_destroy(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_framebuffer *drm_fb;
+ struct fb_info *info;
+ struct mem_block *block;
+ struct drm_file *file_priv;
+
+ list_for_each_entry(drm_fb, &dev->mode_config.fb_kernel_list, filp_head) {
+ break; /* first entry is the only entry */
+ }
+
+ if (!drm_fb) {
+ DRM_ERROR("No framebuffer to destroy\n");
+ return -EINVAL;
+ }
+
+ info = drm_fb->fbdev;
+ if (!info) {
+ DRM_ERROR("No fb_info\n");
+ return -EINVAL;
+ }
+
+ unregister_framebuffer(info);
+
+ block = find_block_by_handle(dev_priv->fb_heap, drm_fb->mm_handle);
+ if (!block) {
+ DRM_ERROR("can't find mem_block\n");
+ return -EINVAL;
+ }
+
+ /* we need to free this after memory is freed */
+ file_priv = block->file_priv;
+
+ /* free memory */
+ nouveau_mem_free(dev, block);
+
+ if (file_priv) {
+ kfree(file_priv);
+ file_priv = NULL;
+ }
+
+ framebuffer_release(info);
+
+ return 0;
+}
diff --git a/linux-core/nv50_fbcon.h b/linux-core/nv50_fbcon.h
new file mode 100644
index 00000000..98e7101a
--- /dev/null
+++ b/linux-core/nv50_fbcon.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_FBCON_H__
+#define __NV50_FBCON_H__
+
+#include "nv50_kms_wrapper.h"
+
+struct nv50_fbcon_par {
+ struct drm_framebuffer *fb;
+ struct drm_device *dev;
+ bool use_preferred_mode;
+};
+
+int nv50_fbcon_init(struct drm_device *dev);
+int nv50_fbcon_destroy(struct drm_device *dev);
+
+#endif /* __NV50_FBCON_H__ */ \ No newline at end of file
diff --git a/linux-core/nv50_i2c.c b/linux-core/nv50_i2c.c
new file mode 100644
index 00000000..30e317c5
--- /dev/null
+++ b/linux-core/nv50_i2c.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+/* This is largely a clone from xorg i2c functions, as i had serious trouble getting an i2c_bit_algo adaptor running. */
+
+#include "nv50_i2c.h"
+
+static uint32_t nv50_i2c_port(int index)
+{
+ uint32_t port = 0;
+
+ switch (index) {
+ case 0:
+ port = NV50_PCONNECTOR_I2C_PORT_0;
+ break;
+ case 1:
+ port = NV50_PCONNECTOR_I2C_PORT_1;
+ break;
+ case 2:
+ port = NV50_PCONNECTOR_I2C_PORT_2;
+ break;
+ case 3:
+ port = NV50_PCONNECTOR_I2C_PORT_3;
+ break;
+ case 4:
+ port = NV50_PCONNECTOR_I2C_PORT_4;
+ break;
+ case 5:
+ port = NV50_PCONNECTOR_I2C_PORT_5;
+ break;
+ default:
+ break;
+ }
+
+ if (!port) {
+ DRM_ERROR("Invalid i2c port, returning 0.\n");
+ BUG();
+ }
+
+ return port;
+}
+
+static void nv50_i2c_set_bits(struct nv50_i2c_channel *chan, int clock_high, int data_high)
+{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+ uint32_t port = nv50_i2c_port(chan->index);
+
+ if (!port)
+ return;
+
+ NV_WRITE(port, 4 | (data_high << 1) | clock_high);
+}
+
+static void nv50_i2c_get_bits(struct nv50_i2c_channel *chan, int *clock_high, int *data_high)
+{
+ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+ uint32_t port = nv50_i2c_port(chan->index);
+ uint32_t val;
+
+ if (!port)
+ return;
+
+ val = NV_READ(port);
+
+ if (val & 1)
+ *clock_high = 1;
+ else
+ *clock_high = 0;
+
+ if (val & 2)
+ *data_high = 1;
+ else
+ *data_high = 0;
+}
+
+static bool nv50_i2c_raise_clock(struct nv50_i2c_channel *chan, int data)
+{
+ int i, clock;
+
+ nv50_i2c_set_bits(chan, 1, data);
+ udelay(2);
+
+ for (i = 2200; i > 0; i -= 2) {
+ nv50_i2c_get_bits(chan, &clock, &data);
+ if (clock)
+ return true;
+ udelay(2);
+ }
+
+ printk("a timeout occured in nv50_i2c_raise_clock\n");
+
+ return false;
+}
+
+static bool nv50_i2c_start(struct nv50_i2c_channel *chan)
+{
+ if (!nv50_i2c_raise_clock(chan, 1))
+ return false;
+
+ nv50_i2c_set_bits(chan, 1, 0);
+ udelay(5);
+
+ nv50_i2c_set_bits(chan, 0, 0);
+ udelay(5);
+
+ return true;
+}
+
+static void nv50_i2c_stop(struct nv50_i2c_channel *chan)
+{
+ nv50_i2c_set_bits(chan, 0, 0);
+ udelay(2);
+
+ nv50_i2c_set_bits(chan, 1, 0);
+ udelay(5);
+
+ nv50_i2c_set_bits(chan, 1, 1);
+ udelay(5);
+}
+
+static bool nv50_i2c_write_bit(struct nv50_i2c_channel *chan, int data)
+{
+ bool rval;
+
+ nv50_i2c_set_bits(chan, 0, data);
+ udelay(2);
+
+ rval = nv50_i2c_raise_clock(chan, data);
+ udelay(5);
+
+ nv50_i2c_set_bits(chan, 0, data);
+ udelay(5);
+
+ return rval;
+}
+
+static bool nv50_i2c_read_bit(struct nv50_i2c_channel *chan, int *data)
+{
+ bool rval;
+ int clock;
+
+ rval = nv50_i2c_raise_clock(chan, 1);
+ udelay(5);
+
+ nv50_i2c_get_bits(chan, &clock, data);
+ udelay(5);
+
+ nv50_i2c_set_bits(chan, 0, 1);
+ udelay(5);
+
+ return rval;
+}
+
+static bool nv50_i2c_write_byte(struct nv50_i2c_channel *chan, uint8_t byte)
+{
+ bool rval;
+ int i, clock, data;
+
+ for (i = 7; i >= 0; i--)
+ if (!nv50_i2c_write_bit(chan, (byte >> i) & 1))
+ return false;
+
+ nv50_i2c_set_bits(chan, 0, 1);
+ udelay(5);
+
+ rval = nv50_i2c_raise_clock(chan, 1);
+
+ if (rval) {
+ for (i = 40; i > 0; i -= 2) {
+ udelay(2);
+ nv50_i2c_get_bits(chan, &clock, &data);
+ if (data == 0)
+ break;
+ }
+
+ if (i <= 0) {
+ printk("a timeout occured in nv50_i2c_write_byte\n");
+ rval = false;
+ }
+ }
+
+ nv50_i2c_set_bits(chan, 0, 1);
+ udelay(5);
+
+ return rval;
+}
+
+static bool nv50_i2c_read_byte(struct nv50_i2c_channel *chan, uint8_t *byte, bool last)
+{
+ int i, bit;
+
+ nv50_i2c_set_bits(chan, 0, 1);
+ udelay(5);
+
+ *byte = 0;
+
+ for (i = 7; i >= 0; i--) {
+ if (nv50_i2c_read_bit(chan, &bit)) {
+ if (bit)
+ *byte |= (1 << i);
+ } else {
+ return false;
+ }
+ }
+
+ if (!nv50_i2c_write_bit(chan, last ? 1 : 0))
+ return false;
+
+ return true;
+}
+
+/* only 7 bits addresses. */
+static bool nv50_i2c_address(struct nv50_i2c_channel *chan, uint8_t address, bool write)
+{
+ if (nv50_i2c_start(chan)) {
+ uint8_t real_addr = (address << 1);
+ if (!write)
+ real_addr |= 1;
+
+ if (nv50_i2c_write_byte(chan, real_addr))
+ return true;
+
+ /* failure, so issue stop */
+ nv50_i2c_stop(chan);
+ }
+
+ return false;
+}
+
+static bool nv50_i2c_read(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length)
+{
+ int i, j;
+ bool rval, last;
+
+ /* retries */
+ for (i = 0; i < 4; i++) {
+ rval = nv50_i2c_address(chan, address, false);
+ if (!rval)
+ return false;
+
+ for (j = 0; j < length; j++) {
+ last = false;
+ if (j == (length - 1))
+ last = true;
+ rval = nv50_i2c_read_byte(chan, &buffer[j], last);
+ if (!rval) {
+ nv50_i2c_stop(chan);
+ break;
+ }
+ }
+
+ nv50_i2c_stop(chan);
+
+ /* done */
+ if (rval)
+ break;
+ }
+
+ if (!rval)
+ printk("nv50_i2c_read failed\n");
+
+ return rval;
+}
+
+static bool nv50_i2c_write(struct nv50_i2c_channel *chan, uint8_t address, uint8_t *buffer, uint32_t length)
+{
+ int i, j;
+ bool rval;
+
+ /* retries */
+ for (i = 0; i < 4; i++) {
+ rval = nv50_i2c_address(chan, address, true);
+ if (!rval)
+ return false;
+
+ for (j = 0; j < length; j++) {
+ rval = nv50_i2c_write_byte(chan, buffer[j]);
+ if (!rval) {
+ break;
+ }
+ }
+
+ nv50_i2c_stop(chan);
+
+ /* done */
+ if (rval)
+ break;
+ }
+
+ if (!rval)
+ printk("nv50_i2c_write failed\n");
+
+ return rval;
+}
+
+static int nv50_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct nv50_i2c_channel *chan = i2c_get_adapdata(i2c_adap);
+ bool rval;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) { /* read */
+ rval = nv50_i2c_read(chan, msgs[i].addr, msgs[i].buf, msgs[i].len);
+ } else { /* write */
+ rval = nv50_i2c_write(chan, msgs[i].addr, msgs[i].buf, msgs[i].len);
+ }
+
+ if (!rval)
+ break;
+ }
+
+ if (rval)
+ return i;
+ else
+ return -EINVAL;
+}
+
+static u32 nv50_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm nv50_i2c_algo = {
+ .master_xfer = nv50_i2c_xfer,
+ .functionality = nv50_i2c_functionality,
+};
+
+static int nv50_i2c_register_bus(struct i2c_adapter *adap)
+{
+ adap->algo = &nv50_i2c_algo;
+
+ adap->timeout = 40;
+ adap->retries = 4;
+
+ return i2c_add_adapter(adap);
+}
+
+#define I2C_HW_B_NOUVEAU 0x010030
+struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index)
+{
+ struct nv50_i2c_channel *chan;
+
+ chan = kzalloc(sizeof(struct nv50_i2c_channel), GFP_KERNEL);
+
+ if (!chan)
+ goto out;
+
+ DRM_INFO("Creating i2c bus with index %d\n", index);
+
+ chan->dev = dev;
+ chan->index = index;
+ snprintf(chan->adapter.name, I2C_NAME_SIZE, "nv50 i2c %d", index);
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.id = I2C_HW_B_NOUVEAU;
+ chan->adapter.dev.parent = &dev->pdev->dev;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ if (nv50_i2c_register_bus(&chan->adapter))
+ goto out;
+
+ return chan;
+
+out:
+ kfree(chan);
+ return NULL;
+}
+
+void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan)
+{
+ if (!chan)
+ return;
+
+ i2c_del_adapter(&chan->adapter);
+ kfree(chan);
+}
diff --git a/linux-core/nv50_i2c.h b/linux-core/nv50_i2c.h
new file mode 100644
index 00000000..1740f8ec
--- /dev/null
+++ b/linux-core/nv50_i2c.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_I2C_H__
+#define __NV50_I2C_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm.h"
+#include "nv50_display.h"
+
+struct nv50_i2c_channel {
+ struct drm_device *dev;
+
+ uint32_t index;
+ struct i2c_adapter adapter;
+};
+
+struct nv50_i2c_channel *nv50_i2c_channel_create(struct drm_device *dev, uint32_t index);
+void nv50_i2c_channel_destroy(struct nv50_i2c_channel *chan);
+
+#endif /* __NV50_I2C_H__ */
diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c
new file mode 100644
index 00000000..a7966e9a
--- /dev/null
+++ b/linux-core/nv50_kms_wrapper.c
@@ -0,0 +1,1280 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_kms_wrapper.h"
+#include "drm_crtc_helper.h" /* be careful what you use from this */
+
+/* This file serves as the interface between the common kernel modesetting code and the device dependent implementation. */
+
+/*
+ * Get private functions.
+ */
+
+struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ return dev_priv->kms_priv;
+}
+
+/*
+ * Allocation functions.
+ */
+
+static void *nv50_kms_alloc_crtc(struct drm_device *dev)
+{
+ struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev);
+ struct nv50_kms_crtc *crtc = kzalloc(sizeof(struct nv50_kms_crtc), GFP_KERNEL);
+
+ if (!crtc)
+ return NULL;
+
+ list_add_tail(&crtc->item, &kms_priv->crtcs);
+
+ return &(crtc->priv);
+}
+
+static void *nv50_kms_alloc_output(struct drm_device *dev)
+{
+ struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev);
+ struct nv50_kms_encoder *encoder = kzalloc(sizeof(struct nv50_kms_encoder), GFP_KERNEL);
+
+ if (!encoder)
+ return NULL;
+
+ list_add_tail(&encoder->item, &kms_priv->encoders);
+
+ return &(encoder->priv);
+}
+
+static void *nv50_kms_alloc_connector(struct drm_device *dev)
+{
+ struct nv50_kms_priv *kms_priv = nv50_get_kms_priv(dev);
+ struct nv50_kms_connector *connector = kzalloc(sizeof(struct nv50_kms_connector), GFP_KERNEL);
+
+ if (!connector)
+ return NULL;
+
+ list_add_tail(&connector->item, &kms_priv->connectors);
+
+ return &(connector->priv);
+}
+
+static void nv50_kms_free_crtc(void *crtc)
+{
+ struct nv50_kms_crtc *kms_crtc = from_nv50_crtc(crtc);
+
+ list_del(&kms_crtc->item);
+
+ kfree(kms_crtc);
+}
+
+static void nv50_kms_free_output(void *output)
+{
+ struct nv50_kms_encoder *kms_encoder = from_nv50_output(output);
+
+ list_del(&kms_encoder->item);
+
+ kfree(kms_encoder);
+}
+
+static void nv50_kms_free_connector(void *connector)
+{
+ struct nv50_kms_connector *kms_connector = from_nv50_connector(connector);
+
+ list_del(&kms_connector->item);
+
+ kfree(kms_connector);
+}
+
+/*
+ * Mode conversion functions.
+ */
+
+static struct nouveau_hw_mode *nv50_kms_to_hw_mode(struct drm_display_mode *mode)
+{
+ struct nouveau_hw_mode *hw_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
+ if (!hw_mode)
+ return NULL;
+
+ /* create hw values. */
+ hw_mode->clock = mode->clock;
+ hw_mode->flags = hw_mode->flags;
+
+ hw_mode->hdisplay = mode->hdisplay;
+ hw_mode->hsync_start = mode->hsync_start;
+ hw_mode->hsync_end = mode->hsync_end;
+ hw_mode->htotal = mode->htotal;
+
+ hw_mode->hblank_start = mode->hdisplay + 1;
+ hw_mode->hblank_end = mode->htotal;
+
+ hw_mode->vdisplay = mode->vdisplay;
+ hw_mode->vsync_start = mode->vsync_start;
+ hw_mode->vsync_end = mode->vsync_end;
+ hw_mode->vtotal = mode->vtotal;
+
+ hw_mode->vblank_start = mode->vdisplay + 1;
+ hw_mode->vblank_end = mode->vtotal;
+
+ return hw_mode;
+}
+
+/*
+ * State mirroring functions.
+ */
+
+static void nv50_kms_mirror_routing(struct drm_device *dev)
+{
+ struct nv50_display *display = nv50_get_display(dev);
+ struct nv50_crtc *crtc = NULL;
+ struct nv50_output *output = NULL;
+ struct nv50_connector *connector = NULL;
+ struct drm_connector *drm_connector = NULL;
+ struct drm_crtc *drm_crtc = NULL;
+
+ /* Wipe all previous connections. */
+ list_for_each_entry(connector, &display->connectors, item) {
+ connector->output = NULL;
+ }
+
+ list_for_each_entry(output, &display->outputs, item) {
+ output->crtc = NULL;
+ }
+
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
+ if (drm_connector->encoder) {
+ output = to_nv50_output(drm_connector->encoder);
+ connector = to_nv50_connector(drm_connector);
+
+ /* hook up output to connector. */
+ connector->output = output;
+
+ if (drm_connector->encoder->crtc) {
+ crtc = to_nv50_crtc(drm_connector->encoder->crtc);
+
+ /* hook up output to crtc. */
+ output->crtc = crtc;
+ }
+ }
+ }
+
+ /* mirror crtc active state */
+ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
+ crtc = to_nv50_crtc(drm_crtc);
+
+ crtc->enabled = drm_crtc->enabled;
+ }
+}
+
+/*
+ * FB functions.
+ */
+
+static void nv50_kms_framebuffer_destroy(struct drm_framebuffer *drm_framebuffer)
+{
+ drm_framebuffer_cleanup(drm_framebuffer);
+
+ kfree(drm_framebuffer);
+}
+
+static const struct drm_framebuffer_funcs nv50_kms_fb_funcs = {
+ .destroy = nv50_kms_framebuffer_destroy,
+};
+
+/*
+ * Mode config functions.
+ */
+
+static struct drm_framebuffer *nv50_kms_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd)
+{
+ struct drm_framebuffer *drm_framebuffer = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
+ if (!drm_framebuffer)
+ return NULL;
+
+ drm_framebuffer_init(dev, drm_framebuffer, &nv50_kms_fb_funcs);
+ drm_helper_mode_fill_fb_struct(drm_framebuffer, mode_cmd);
+
+ return drm_framebuffer;
+}
+
+static int nv50_kms_fb_changed(struct drm_device *dev)
+{
+ return 0; /* not needed until nouveaufb? */
+}
+
+static const struct drm_mode_config_funcs nv50_kms_mode_funcs = {
+ .resize_fb = NULL,
+ .fb_create = nv50_kms_framebuffer_create,
+ .fb_changed = nv50_kms_fb_changed,
+};
+
+/*
+ * CRTC functions.
+ */
+
+static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc, uint32_t buffer_handle,
+ uint32_t width, uint32_t height)
+{
+ struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
+ struct nv50_display *display = nv50_get_display(crtc->dev);
+ int rval = 0;
+
+ if (width != 64 || height != 64)
+ return -EINVAL;
+
+ /* set bo before doing show cursor */
+ if (buffer_handle) {
+ rval = crtc->cursor->set_bo(crtc, (drm_handle_t) buffer_handle);
+ if (rval != 0)
+ goto out;
+ }
+
+ crtc->cursor->visible = buffer_handle ? true : false;
+
+ if (buffer_handle) {
+ rval = crtc->cursor->show(crtc);
+ if (rval != 0)
+ goto out;
+ } else { /* no handle implies hiding the cursor */
+ rval = crtc->cursor->hide(crtc);
+ goto out;
+ }
+
+ if (rval != 0)
+ return rval;
+
+out:
+ /* in case this triggers any other cursor changes */
+ display->update(display);
+
+ return rval;
+}
+
+static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y)
+{
+ struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
+
+ return crtc->cursor->set_pos(crtc, x, y);
+}
+
+void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b,
+ uint32_t size)
+{
+ struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
+
+ if (size != 256)
+ return;
+
+ crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b);
+}
+
+int nv50_kms_crtc_set_config(struct drm_mode_set *set)
+{
+ int rval = 0, i;
+ uint32_t crtc_mask = 0;
+ struct drm_device *dev = NULL;
+ struct drm_nouveau_private *dev_priv = NULL;
+ struct nv50_display *display = NULL;
+ struct drm_connector *drm_connector = NULL;
+ struct drm_encoder *drm_encoder = NULL;
+ struct drm_crtc *drm_crtc = NULL;
+
+ struct nv50_crtc *crtc = NULL;
+ struct nv50_output *output = NULL;
+ struct nv50_connector *connector = NULL;
+ struct nouveau_hw_mode *hw_mode = NULL;
+ struct nv50_fb_info fb_info;
+
+ bool blank = false;
+ bool switch_fb = false;
+ bool modeset = false;
+
+ NV50_DEBUG("\n");
+
+ /*
+ * Supported operations:
+ * - Switch mode.
+ * - Switch framebuffer.
+ * - Blank screen.
+ */
+
+ /* Sanity checking */
+ if (!set) {
+ DRM_ERROR("Sanity check failed\n");
+ goto out;
+ }
+
+ if (!set->crtc) {
+ DRM_ERROR("Sanity check failed\n");
+ goto out;
+ }
+
+ if (set->mode) {
+ if (set->fb) {
+ if (!drm_mode_equal(set->mode, &set->crtc->mode))
+ modeset = true;
+
+ if (set->fb != set->crtc->fb)
+ switch_fb = true;
+
+ if (set->x != set->crtc->x || set->y != set->crtc->y)
+ switch_fb = true;
+ }
+ } else {
+ blank = true;
+ }
+
+ if (!set->connectors && !blank) {
+ DRM_ERROR("Sanity check failed\n");
+ goto out;
+ }
+
+ /* Basic variable setting */
+ dev = set->crtc->dev;
+ dev_priv = dev->dev_private;
+ display = nv50_get_display(dev);
+ crtc = to_nv50_crtc(set->crtc);
+
+ /**
+ * Wiring up the encoders and connectors.
+ */
+
+ /* for switch_fb we verify if any important changes happened */
+ if (!blank) {
+ /* Mode validation */
+ hw_mode = nv50_kms_to_hw_mode(set->mode);
+
+ rval = crtc->validate_mode(crtc, hw_mode);
+
+ if (rval != MODE_OK) {
+ DRM_ERROR("Mode not ok\n");
+ goto out;
+ }
+
+ for (i = 0; i < set->num_connectors; i++) {
+ drm_connector = set->connectors[i];
+ if (!drm_connector) {
+ DRM_ERROR("No connector\n");
+ goto out;
+ }
+ connector = to_nv50_connector(drm_connector);
+
+ /* This is to ensure it knows the connector subtype. */
+ drm_connector->funcs->fill_modes(drm_connector, 0, 0);
+
+ output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+ if (!output) {
+ DRM_ERROR("No output\n");
+ goto out;
+ }
+
+ rval = output->validate_mode(output, hw_mode);
+ if (rval != MODE_OK) {
+ DRM_ERROR("Mode not ok\n");
+ goto out;
+ }
+
+ /* verify if any "sneaky" changes happened */
+ if (output != connector->output)
+ modeset = true;
+
+ if (output->crtc != crtc)
+ modeset = true;
+ }
+ }
+
+ /* Now we verified if anything changed, fail if nothing has. */
+ if (!modeset && !switch_fb && !blank)
+ DRM_INFO("A seemingly empty modeset encountered, this could be a bug.\n");
+
+ /* Validation done, move on to cleaning of existing structures. */
+ if (modeset) {
+ /* find encoders that use this crtc. */
+ list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
+ if (drm_encoder->crtc == set->crtc) {
+ /* find the connector that goes with it */
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
+ if (drm_connector->encoder == drm_encoder) {
+ drm_connector->encoder = NULL;
+ break;
+ }
+ }
+ drm_encoder->crtc = NULL;
+ }
+ }
+
+ /* now find if our desired encoders or connectors are in use already. */
+ for (i = 0; i < set->num_connectors; i++) {
+ drm_connector = set->connectors[i];
+ if (!drm_connector) {
+ DRM_ERROR("No connector\n");
+ goto out;
+ }
+
+ if (!drm_connector->encoder)
+ continue;
+
+ drm_encoder = drm_connector->encoder;
+ drm_connector->encoder = NULL;
+
+ if (!drm_encoder->crtc)
+ continue;
+
+ drm_crtc = drm_encoder->crtc;
+ drm_encoder->crtc = NULL;
+
+ drm_crtc->enabled = false;
+ }
+
+ /* Time to wire up the public encoder, the private one will be handled later. */
+ for (i = 0; i < set->num_connectors; i++) {
+ drm_connector = set->connectors[i];
+ if (!drm_connector) {
+ DRM_ERROR("No connector\n");
+ goto out;
+ }
+
+ output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+ if (!output) {
+ DRM_ERROR("No output\n");
+ goto out;
+ }
+
+ /* find the encoder public structure that matches out output structure. */
+ drm_encoder = to_nv50_kms_encoder(output);
+
+ if (!drm_encoder) {
+ DRM_ERROR("No encoder\n");
+ goto out;
+ }
+
+ drm_encoder->crtc = set->crtc;
+ set->crtc->enabled = true;
+ drm_connector->encoder = drm_encoder;
+ }
+ }
+
+ /**
+ * Disable crtc.
+ */
+
+ if (blank) {
+ crtc = to_nv50_crtc(set->crtc);
+
+ set->crtc->enabled = false;
+
+ /* disconnect encoders and connectors */
+ for (i = 0; i < set->num_connectors; i++) {
+ drm_connector = set->connectors[i];
+
+ if (!drm_connector->encoder)
+ continue;
+
+ drm_connector->encoder->crtc = NULL;
+ drm_connector->encoder = NULL;
+ }
+ }
+
+ /**
+ * All state should now be updated, now onto the real work.
+ */
+
+ /* mirror everything to the private structs */
+ nv50_kms_mirror_routing(dev);
+
+ /**
+ * Bind framebuffer.
+ */
+
+ if (switch_fb) {
+ crtc = to_nv50_crtc(set->crtc);
+
+ /* set framebuffer */
+ set->crtc->fb = set->fb;
+
+ /* set private framebuffer */
+ crtc = to_nv50_crtc(set->crtc);
+ fb_info.block = find_block_by_handle(dev_priv->fb_heap, set->fb->mm_handle);
+ fb_info.width = set->fb->width;
+ fb_info.height = set->fb->height;
+ fb_info.depth = set->fb->depth;
+ fb_info.bpp = set->fb->bits_per_pixel;
+ fb_info.pitch = set->fb->pitch;
+ fb_info.x = set->x;
+ fb_info.y = set->y;
+
+ rval = crtc->fb->bind(crtc, &fb_info);
+ if (rval != 0) {
+ DRM_ERROR("fb_bind failed\n");
+ goto out;
+ }
+ }
+
+ /* this is !cursor_show */
+ if (!crtc->cursor->enabled) {
+ rval = crtc->cursor->enable(crtc);
+ if (rval != 0) {
+ DRM_ERROR("cursor_enable failed\n");
+ goto out;
+ }
+ }
+
+ /**
+ * Blanking.
+ */
+
+ if (blank) {
+ crtc = to_nv50_crtc(set->crtc);
+
+ rval = crtc->blank(crtc, true);
+ if (rval != 0) {
+ DRM_ERROR("blanking failed\n");
+ goto out;
+ }
+
+ /* detach any outputs that are currently unused */
+ list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) {
+ if (!drm_encoder->crtc) {
+ output = to_nv50_output(drm_encoder);
+
+ rval = output->execute_mode(output, true);
+ if (rval != 0) {
+ DRM_ERROR("detaching output failed\n");
+ goto out;
+ }
+ }
+ }
+ }
+
+ /**
+ * Change framebuffer, without changing mode.
+ */
+
+ if (switch_fb && !modeset && !blank) {
+ crtc = to_nv50_crtc(set->crtc);
+
+ rval = crtc->set_fb(crtc);
+ if (rval != 0) {
+ DRM_ERROR("set_fb failed\n");
+ goto out;
+ }
+
+ /* this also sets the fb offset */
+ rval = crtc->blank(crtc, false);
+ if (rval != 0) {
+ DRM_ERROR("unblanking failed\n");
+ goto out;
+ }
+ }
+
+ /**
+ * Normal modesetting.
+ */
+
+ if (modeset) {
+ crtc = to_nv50_crtc(set->crtc);
+
+ /* disconnect unused outputs */
+ list_for_each_entry(output, &display->outputs, item) {
+ if (output->crtc) {
+ crtc_mask |= 1 << output->crtc->index;
+ } else {
+ rval = output->execute_mode(output, true);
+ if (rval != 0) {
+ DRM_ERROR("detaching output failed\n");
+ goto out;
+ }
+ }
+ }
+
+ /* blank any unused crtcs */
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ if (!(crtc_mask & (1 << crtc->index)))
+ crtc->blank(crtc, true);
+ }
+
+ crtc = to_nv50_crtc(set->crtc);
+
+ rval = crtc->set_mode(crtc, hw_mode);
+ if (rval != 0) {
+ DRM_ERROR("crtc mode set failed\n");
+ goto out;
+ }
+
+ /* find native mode. */
+ list_for_each_entry(output, &display->outputs, item) {
+ if (output->crtc != crtc)
+ continue;
+
+ *crtc->native_mode = *output->native_mode;
+ list_for_each_entry(connector, &display->connectors, item) {
+ if (connector->output != output)
+ continue;
+
+ crtc->scaling_mode = connector->scaling_mode;
+ break;
+ }
+
+ if (crtc->scaling_mode == SCALE_PANEL)
+ crtc->use_native_mode = false;
+ else
+ crtc->use_native_mode = true;
+
+ break; /* no use in finding more than one mode */
+ }
+
+ rval = crtc->execute_mode(crtc);
+ if (rval != 0) {
+ DRM_ERROR("crtc execute mode failed\n");
+ goto out;
+ }
+
+ list_for_each_entry(output, &display->outputs, item) {
+ if (output->crtc != crtc)
+ continue;
+
+ rval = output->execute_mode(output, false);
+ if (rval != 0) {
+ DRM_ERROR("output execute mode failed\n");
+ goto out;
+ }
+ }
+
+ rval = crtc->set_scale(crtc);
+ if (rval != 0) {
+ DRM_ERROR("crtc set scale failed\n");
+ goto out;
+ }
+
+ /* next line changes crtc, so putting it here is important */
+ display->last_crtc = crtc->index;
+ }
+
+ /* always reset dpms, regardless if any other modesetting is done. */
+ if (!blank) {
+ /* this is executed immediately */
+ list_for_each_entry(output, &display->outputs, item) {
+ if (output->crtc != crtc)
+ continue;
+
+ rval = output->set_power_mode(output, DRM_MODE_DPMS_ON);
+ if (rval != 0) {
+ DRM_ERROR("output set power mode failed\n");
+ goto out;
+ }
+ }
+
+ /* update dpms state to DPMSModeOn */
+ for (i = 0; i < set->num_connectors; i++) {
+ drm_connector = set->connectors[i];
+ if (!drm_connector) {
+ DRM_ERROR("No connector\n");
+ goto out;
+ }
+
+ rval = drm_connector_property_set_value(drm_connector,
+ dev->mode_config.dpms_property,
+ DRM_MODE_DPMS_ON);
+ if (rval != 0) {
+ DRM_ERROR("failed to update dpms state\n");
+ goto out;
+ }
+ }
+ }
+
+ display->update(display);
+
+ /* Update the current mode, now that all has gone well. */
+ if (modeset) {
+ set->crtc->mode = *(set->mode);
+ set->crtc->x = set->x;
+ set->crtc->y = set->y;
+ }
+
+ kfree(hw_mode);
+
+ return 0;
+
+out:
+ kfree(hw_mode);
+
+ if (rval != 0)
+ return rval;
+ else
+ return -EINVAL;
+}
+
+static void nv50_kms_crtc_destroy(struct drm_crtc *drm_crtc)
+{
+ struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc);
+
+ drm_crtc_cleanup(drm_crtc);
+
+ /* this will even destroy the public structure. */
+ crtc->destroy(crtc);
+}
+
+static const struct drm_crtc_funcs nv50_kms_crtc_funcs = {
+ .save = NULL,
+ .restore = NULL,
+ .cursor_set = nv50_kms_crtc_cursor_set,
+ .cursor_move = nv50_kms_crtc_cursor_move,
+ .gamma_set = nv50_kms_crtc_gamma_set,
+ .set_config = nv50_kms_crtc_set_config,
+ .destroy = nv50_kms_crtc_destroy,
+};
+
+static int nv50_kms_crtcs_init(struct drm_device *dev)
+{
+ struct nv50_display *display = nv50_get_display(dev);
+ struct nv50_crtc *crtc = NULL;
+
+ /*
+ * This may look a bit confusing, but:
+ * The internal structure is already allocated and so is the public one.
+ * Just a matter of getting to the memory and register it.
+ */
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ struct drm_crtc *drm_crtc = to_nv50_kms_crtc(crtc);
+
+ drm_crtc_init(dev, drm_crtc, &nv50_kms_crtc_funcs);
+
+ /* init lut storage */
+ drm_mode_crtc_set_gamma_size(drm_crtc, 256);
+ }
+
+ return 0;
+}
+
+/*
+ * Encoder functions
+ */
+
+static void nv50_kms_encoder_destroy(struct drm_encoder *drm_encoder)
+{
+ struct nv50_output *output = to_nv50_output(drm_encoder);
+
+ drm_encoder_cleanup(drm_encoder);
+
+ /* this will even destroy the public structure. */
+ output->destroy(output);
+}
+
+static const struct drm_encoder_funcs nv50_kms_encoder_funcs = {
+ .destroy = nv50_kms_encoder_destroy,
+};
+
+static int nv50_kms_encoders_init(struct drm_device *dev)
+{
+ struct nv50_display *display = nv50_get_display(dev);
+ struct nv50_output *output = NULL;
+
+ list_for_each_entry(output, &display->outputs, item) {
+ struct drm_encoder *drm_encoder = to_nv50_kms_encoder(output);
+ uint32_t type = DRM_MODE_ENCODER_NONE;
+
+ switch (output->type) {
+ case OUTPUT_DAC:
+ type = DRM_MODE_ENCODER_DAC;
+ break;
+ case OUTPUT_TMDS:
+ type = DRM_MODE_ENCODER_TMDS;
+ break;
+ case OUTPUT_LVDS:
+ type = DRM_MODE_ENCODER_LVDS;
+ break;
+ case OUTPUT_TV:
+ type = DRM_MODE_ENCODER_TVDAC;
+ break;
+ default:
+ type = DRM_MODE_ENCODER_NONE;
+ break;
+ }
+
+ if (type == DRM_MODE_ENCODER_NONE) {
+ DRM_ERROR("DRM_MODE_ENCODER_NONE encountered\n");
+ continue;
+ }
+
+ drm_encoder_init(dev, drm_encoder, &nv50_kms_encoder_funcs, type);
+
+ /* I've never seen possible crtc's restricted. */
+ drm_encoder->possible_crtcs = 3;
+ drm_encoder->possible_clones = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Connector functions
+ */
+
+bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector)
+{
+ struct drm_device *dev = drm_connector->dev;
+
+ switch (drm_connector->connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ return false;
+ case DRM_MODE_CONNECTOR_DVID:
+ case DRM_MODE_CONNECTOR_LVDS:
+ return true;
+ default:
+ break;
+ }
+
+ if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+ int rval;
+ uint64_t prop_val;
+
+ rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, &prop_val);
+ if (rval) {
+ DRM_ERROR("Unable to find select subconnector property, defaulting to DVI-D\n");
+ return true;
+ }
+
+ /* Is a subconnector explicitly selected? */
+ switch (prop_val) {
+ case DRM_MODE_SUBCONNECTOR_DVID:
+ return true;
+ case DRM_MODE_SUBCONNECTOR_DVIA:
+ return false;
+ default:
+ break;
+ }
+
+ rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &prop_val);
+ if (rval) {
+ DRM_ERROR("Unable to find subconnector property, defaulting to DVI-D\n");
+ return true;
+ }
+
+ /* Do we know what subconnector we currently have connected? */
+ switch (prop_val) {
+ case DRM_MODE_SUBCONNECTOR_DVID:
+ return true;
+ case DRM_MODE_SUBCONNECTOR_DVIA:
+ return false;
+ default:
+ DRM_ERROR("Unknown subconnector value, defaulting to DVI-D\n");
+ return true;
+ }
+ }
+
+ DRM_ERROR("Unknown connector type, defaulting to analog\n");
+ return false;
+}
+
+void nv50_kms_connector_detect_all(struct drm_device *dev)
+{
+ struct drm_connector *drm_connector = NULL;
+
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
+ drm_connector->funcs->detect(drm_connector);
+ }
+}
+
+static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector)
+{
+ struct nv50_connector *connector = to_nv50_connector(drm_connector);
+ struct drm_device *dev = drm_connector->dev;
+ bool connected;
+ int old_status;
+
+ connected = connector->detect(connector);
+
+ old_status = drm_connector->status;
+
+ if (connected)
+ drm_connector->status = connector_status_connected;
+ else
+ drm_connector->status = connector_status_disconnected;
+
+ /* update our modes whenever there is reason to */
+ if (old_status != drm_connector->status) {
+ drm_connector->funcs->fill_modes(drm_connector, 0, 0);
+
+ /* notify fb of changes */
+ dev->mode_config.funcs->fb_changed(dev);
+ }
+
+ return drm_connector->status;
+}
+
+static void nv50_kms_connector_destroy(struct drm_connector *drm_connector)
+{
+ struct nv50_connector *connector = to_nv50_connector(drm_connector);
+
+ drm_sysfs_connector_remove(drm_connector);
+ drm_connector_cleanup(drm_connector);
+
+ /* this will even destroy the public structure. */
+ connector->destroy(connector);
+}
+
+/*
+ * Detailed mode info for a standard 640x480@60Hz monitor
+ */
+static struct drm_display_mode std_mode[] = {
+ /*{ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656,
+ 752, 800, 0, 480, 490, 492, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },*/ /* 640x480@60Hz */
+ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DEFAULT, 135000, 1280, 1296,
+ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */
+};
+
+static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, uint32_t maxX, uint32_t maxY)
+{
+ struct nv50_connector *connector = to_nv50_connector(drm_connector);
+ struct drm_device *dev = drm_connector->dev;
+ int rval = 0;
+ bool connected;
+ struct drm_display_mode *mode, *t;
+ struct edid *edid = NULL;
+
+ NV50_DEBUG("%s\n", drm_get_connector_name(drm_connector));
+ /* set all modes to the unverified state */
+ list_for_each_entry_safe(mode, t, &drm_connector->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ connected = connector->detect(connector);
+
+ if (connected)
+ drm_connector->status = connector_status_connected;
+ else
+ drm_connector->status = connector_status_disconnected;
+
+ if (!connected) {
+ NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector));
+ }
+
+ /* Not all connnectors have an i2c channel. */
+ if (connected && connector->i2c_chan)
+ edid = (struct edid *) drm_do_probe_ddc_edid(&connector->i2c_chan->adapter);
+
+ /* This will remove edid if needed. */
+ drm_mode_connector_update_edid_property(drm_connector, edid);
+
+ if (edid) {
+ rval = drm_add_edid_modes(drm_connector, edid);
+
+ /* 2 encoders per connector */
+ /* eventually do this based on load detect and hot plug detect */
+ if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+ uint64_t subtype = 0;
+ if (edid->digital)
+ subtype = DRM_MODE_SUBCONNECTOR_DVID;
+ else
+ subtype = DRM_MODE_SUBCONNECTOR_DVIA;
+ drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, subtype);
+ }
+
+ kfree(edid);
+ }
+
+ if (rval) /* number of modes > 1 */
+ drm_mode_connector_list_update(drm_connector);
+
+ if (maxX && maxY)
+ drm_mode_validate_size(dev, &drm_connector->modes, maxX, maxY, 0);
+
+ list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
+ if (mode->status == MODE_OK) {
+ struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
+ struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+
+ mode->status = output->validate_mode(output, hw_mode);
+ /* find native mode, TODO: also check if we actually found one */
+ if (mode->status == MODE_OK) {
+ if (mode->type & DRM_MODE_TYPE_PREFERRED)
+ *output->native_mode = *hw_mode;
+ }
+ kfree(hw_mode);
+ }
+ }
+
+ /* revalidate now that we have native mode */
+ list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
+ if (mode->status == MODE_OK) {
+ struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode);
+ struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+
+ mode->status = output->validate_mode(output, hw_mode);
+ kfree(hw_mode);
+ }
+ }
+
+ drm_mode_prune_invalid(dev, &drm_connector->modes, true);
+
+ /* pruning is done, so bail out. */
+ if (!connected)
+ return;
+
+ if (list_empty(&drm_connector->modes)) {
+ struct drm_display_mode *stdmode;
+ struct nouveau_hw_mode *hw_mode;
+ struct nv50_output *output;
+
+ NV50_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector));
+
+ /* Should we do this here ???
+ * When no valid EDID modes are available we end up
+ * here and bailed in the past, now we add a standard
+ * 640x480@60Hz mode and carry on.
+ */
+ stdmode = drm_mode_duplicate(dev, &std_mode[0]);
+ drm_mode_probed_add(drm_connector, stdmode);
+ drm_mode_list_concat(&drm_connector->probed_modes,
+ &drm_connector->modes);
+
+ /* also add it as native mode */
+ hw_mode = nv50_kms_to_hw_mode(mode);
+ output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector));
+
+ if (hw_mode)
+ *output->native_mode = *hw_mode;
+
+ DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n",
+ drm_get_connector_name(drm_connector));
+ }
+
+ drm_mode_sort(&drm_connector->modes);
+
+ NV50_DEBUG("Probed modes for %s\n", drm_get_connector_name(drm_connector));
+
+ list_for_each_entry_safe(mode, t, &drm_connector->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ /* is this needed, as it's unused by the driver? */
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(mode);
+ }
+}
+
+static bool nv50_kms_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct drm_device *dev = connector->dev;
+
+ if (property == dev->mode_config.dpms_property && connector->encoder) {
+ struct nv50_output *output = to_nv50_output(connector->encoder);
+
+ if (!output->set_power_mode(output, (int) value))
+ return true;
+ else
+ return false;
+ }
+
+ return false;
+}
+
+static const struct drm_connector_funcs nv50_kms_connector_funcs = {
+ .save = NULL,
+ .restore = NULL,
+ .detect = nv50_kms_connector_detect,
+ .destroy = nv50_kms_connector_destroy,
+ .fill_modes = nv50_kms_connector_fill_modes,
+ .set_property = nv50_kms_connector_set_property
+};
+
+static int nv50_kms_connectors_init(struct drm_device *dev)
+{
+ struct nv50_display *display = nv50_get_display(dev);
+ struct nv50_connector *connector = NULL;
+ int i;
+
+ list_for_each_entry(connector, &display->connectors, item) {
+ struct drm_connector *drm_connector = to_nv50_kms_connector(connector);
+ uint32_t type = DRM_MODE_CONNECTOR_Unknown;
+
+ switch (connector->type) {
+ case CONNECTOR_VGA:
+ type = DRM_MODE_CONNECTOR_VGA;
+ break;
+ case CONNECTOR_DVI_D:
+ type = DRM_MODE_CONNECTOR_DVID;
+ break;
+ case CONNECTOR_DVI_I:
+ type = DRM_MODE_CONNECTOR_DVII;
+ break;
+ case CONNECTOR_LVDS:
+ type = DRM_MODE_CONNECTOR_LVDS;
+ break;
+ case CONNECTOR_TV:
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ break;
+ default:
+ type = DRM_MODE_CONNECTOR_Unknown;
+ break;
+ }
+
+ if (type == DRM_MODE_CONNECTOR_Unknown) {
+ DRM_ERROR("DRM_MODE_CONNECTOR_Unknown encountered\n");
+ continue;
+ }
+
+ /* It should be allowed sometimes, but let's be safe for the moment. */
+ drm_connector->interlace_allowed = false;
+ drm_connector->doublescan_allowed = false;
+
+ drm_connector_init(dev, drm_connector, &nv50_kms_connector_funcs, type);
+
+ /* Init DVI-I specific properties */
+ if (type == DRM_MODE_CONNECTOR_DVII) {
+ drm_mode_create_dvi_i_properties(dev);
+ drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_subconnector_property, 0);
+ drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
+ }
+
+ /* attach encoders, possibilities are analog + digital */
+ for (i = 0; i < 2; i++) {
+ struct drm_encoder *drm_encoder = NULL;
+ struct nv50_output *output = connector->to_output(connector, i);
+ if (!output)
+ continue;
+
+ drm_encoder = to_nv50_kms_encoder(output);
+ if (!drm_encoder) {
+ DRM_ERROR("No struct drm_connector to match struct nv50_output\n");
+ continue;
+ }
+
+ drm_mode_connector_attach_encoder(drm_connector, drm_encoder);
+ }
+
+ drm_sysfs_connector_add(drm_connector);
+ }
+
+ return 0;
+}
+
+/*
+ * Main functions
+ */
+
+int nv50_kms_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_kms_priv *kms_priv = kzalloc(sizeof(struct nv50_kms_priv), GFP_KERNEL);
+ struct nv50_display *display = NULL;
+ int rval = 0;
+
+ if (!kms_priv)
+ return -ENOMEM;
+
+ dev_priv->kms_priv = kms_priv;
+
+ /* function pointers */
+ /* an allocation interface that deals with the outside world, without polluting the core. */
+ dev_priv->alloc_crtc = nv50_kms_alloc_crtc;
+ dev_priv->alloc_output = nv50_kms_alloc_output;
+ dev_priv->alloc_connector = nv50_kms_alloc_connector;
+
+ dev_priv->free_crtc = nv50_kms_free_crtc;
+ dev_priv->free_output = nv50_kms_free_output;
+ dev_priv->free_connector = nv50_kms_free_connector;
+
+ /* bios is needed for tables. */
+ rval = nouveau_parse_bios(dev);
+ if (rval != 0)
+ goto out;
+
+ /* init basic kernel modesetting */
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ dev->mode_config.funcs = (void *)&nv50_kms_mode_funcs;
+
+ dev->mode_config.max_width = 8192;
+ dev->mode_config.max_height = 8192;
+
+ dev->mode_config.fb_base = dev_priv->fb_phys;
+
+ /* init kms lists */
+ INIT_LIST_HEAD(&kms_priv->crtcs);
+ INIT_LIST_HEAD(&kms_priv->encoders);
+ INIT_LIST_HEAD(&kms_priv->connectors);
+
+ /* init the internal core, must be done first. */
+ rval = nv50_display_create(dev);
+ if (rval != 0)
+ goto out;
+
+ display = nv50_get_display(dev);
+ if (!display) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ /* pre-init now */
+ rval = display->pre_init(display);
+ if (rval != 0)
+ goto out;
+
+ /* init external layer */
+ rval = nv50_kms_crtcs_init(dev);
+ if (rval != 0)
+ goto out;
+
+ rval = nv50_kms_encoders_init(dev);
+ if (rval != 0)
+ goto out;
+
+ rval = nv50_kms_connectors_init(dev);
+ if (rval != 0)
+ goto out;
+
+ /* init now, this'll kill the textmode */
+ rval = display->init(display);
+ if (rval != 0)
+ goto out;
+
+ /* process cmdbuffer */
+ display->update(display);
+
+ return 0;
+
+out:
+ kfree(kms_priv);
+ dev_priv->kms_priv = NULL;
+
+ return rval;
+}
+
+int nv50_kms_destroy(struct drm_device *dev)
+{
+ drm_mode_config_cleanup(dev);
+
+ return 0;
+}
+
diff --git a/linux-core/nv50_kms_wrapper.h b/linux-core/nv50_kms_wrapper.h
new file mode 100644
index 00000000..5ac66522
--- /dev/null
+++ b/linux-core/nv50_kms_wrapper.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_KMS_WRAPPER_H__
+#define __NV50_KMS_WRAPPER_H__
+
+#include "drmP.h"
+#include "drm.h"
+
+#include "nv50_display.h"
+#include "nv50_crtc.h"
+#include "nv50_cursor.h"
+#include "nv50_lut.h"
+#include "nv50_fb.h"
+#include "nv50_output.h"
+#include "nv50_connector.h"
+
+#include "drm_crtc.h"
+#include "drm_edid.h"
+
+/* Link internal modesetting structure to interface. */
+
+struct nv50_kms_crtc {
+ struct list_head item;
+
+ struct nv50_crtc priv;
+ struct drm_crtc pub;
+};
+
+struct nv50_kms_encoder {
+ struct list_head item;
+
+ struct nv50_output priv;
+ struct drm_encoder pub;
+};
+
+struct nv50_kms_connector {
+ struct list_head item;
+
+ struct nv50_connector priv;
+ struct drm_connector pub;
+};
+
+struct nv50_kms_priv {
+ struct list_head crtcs;
+ struct list_head encoders;
+ struct list_head connectors;
+};
+
+/* Get private functions. */
+#define from_nv50_kms_crtc(x) container_of(x, struct nv50_kms_crtc, pub)
+#define from_nv50_crtc(x) container_of(x, struct nv50_kms_crtc, priv)
+#define from_nv50_kms_encoder(x) container_of(x, struct nv50_kms_encoder, pub)
+#define from_nv50_output(x) container_of(x, struct nv50_kms_encoder, priv)
+#define from_nv50_kms_connector(x) container_of(x, struct nv50_kms_connector, pub)
+#define from_nv50_connector(x) container_of(x, struct nv50_kms_connector, priv)
+
+#define to_nv50_crtc(x) (&(from_nv50_kms_crtc(x)->priv))
+#define to_nv50_kms_crtc(x) (&(from_nv50_crtc(x)->pub))
+#define to_nv50_output(x) (&(from_nv50_kms_encoder(x)->priv))
+#define to_nv50_kms_encoder(x) (&(from_nv50_output(x)->pub))
+#define to_nv50_connector(x) (&(from_nv50_kms_connector(x)->priv))
+#define to_nv50_kms_connector(x) (&(from_nv50_connector(x)->pub))
+
+struct nv50_kms_priv *nv50_get_kms_priv(struct drm_device *dev);
+void nv50_kms_connector_detect_all(struct drm_device *dev);
+bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector);
+
+int nv50_kms_init(struct drm_device *dev);
+int nv50_kms_destroy(struct drm_device *dev);
+
+#endif /* __NV50_KMS_WRAPPER_H__ */
diff --git a/linux-core/nv50_lut.c b/linux-core/nv50_lut.c
new file mode 100644
index 00000000..7982a926
--- /dev/null
+++ b/linux-core/nv50_lut.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_lut.h"
+#include "nv50_fb.h"
+#include "nv50_crtc.h"
+#include "nv50_display.h"
+
+static int nv50_lut_alloc(struct nv50_crtc *crtc)
+{
+ struct mem_block *block;
+ struct drm_file *file_priv = kzalloc(sizeof(struct drm_file), GFP_KERNEL);
+ uint32_t flags = NOUVEAU_MEM_FB | NOUVEAU_MEM_MAPPED;
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ if (!file_priv)
+ return -ENOMEM;
+
+ /* Any file_priv should do as it's pointer is used as identification. */
+ block = nouveau_mem_alloc(crtc->dev, 0, 4096, flags, file_priv);
+
+ if (!block) {
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ crtc->lut->block = block;
+
+ return 0;
+
+out:
+ if (file_priv)
+ kfree(file_priv);
+
+ return rval;
+}
+
+static int nv50_lut_free(struct nv50_crtc *crtc)
+{
+ struct drm_file *file_priv = crtc->lut->block->file_priv;
+
+ NV50_DEBUG("\n");
+
+ nouveau_mem_free(crtc->dev, crtc->lut->block);
+
+ kfree(file_priv);
+
+ return 0;
+}
+
+#define NV50_LUT_INDEX(val, w) ((val << (8 - w)) | (val >> ((w << 1) - 8)))
+static int nv50_lut_set(struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue)
+{
+ uint32_t index = 0, i;
+ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ void __iomem *lut = NULL;
+
+ NV50_DEBUG("\n");
+
+ if (!crtc->lut || !crtc->lut->block) {
+ DRM_ERROR("Something wrong with the LUT\n");
+ return -EINVAL;
+ }
+
+ /* 16 bits, red, green, blue, unused, total of 64 bits per index */
+ /* maybe switch to ioremap_wc once it becomes available. */
+ lut = ioremap(dev_priv->fb_phys + crtc->lut->block->start, crtc->lut->block->size);
+ if (!lut) {
+ DRM_ERROR("ioremap failed on LUT\n");
+ return -EINVAL;
+ }
+
+ /* 10 bits lut, with 14 bits values. */
+ switch (crtc->fb->depth) {
+ case 15:
+ /* R5G5B5 */
+ for (i = 0; i < 32; i++) {
+ index = NV50_LUT_INDEX(i, 5);
+ writew(red[i] >> 2, lut + 8*index + 0);
+ writew(green[i] >> 2, lut + 8*index + 2);
+ writew(blue[i] >> 2, lut + 8*index + 4);
+ }
+ break;
+ case 16:
+ /* R5G6B5 */
+ for (i = 0; i < 32; i++) {
+ index = NV50_LUT_INDEX(i, 5);
+ writew(red[i] >> 2, lut + 8*index + 0);
+ writew(blue[i] >> 2, lut + 8*index + 4);
+ }
+
+ /* Green has an extra bit. */
+ for (i = 0; i < 64; i++) {
+ index = NV50_LUT_INDEX(i, 6);
+ writew(green[i] >> 2, lut + 8*index + 2);
+ }
+ break;
+ default:
+ /* R8G8B8 */
+ for (i = 0; i < 256; i++) {
+ writew(red[i] >> 2, lut + 8*i + 0);
+ writew(green[i] >> 2, lut + 8*i + 2);
+ writew(blue[i] >> 2, lut + 8*i + 4);
+ }
+ break;
+ }
+
+ crtc->lut->depth = crtc->fb->depth;
+
+ /* Cleaning time. */
+ iounmap(lut);
+
+ return 0;
+}
+
+int nv50_lut_create(struct nv50_crtc *crtc)
+{
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ if (!crtc)
+ return -EINVAL;
+
+ crtc->lut = kzalloc(sizeof(struct nv50_lut), GFP_KERNEL);
+
+ if (!crtc->lut)
+ return -ENOMEM;
+
+ rval = nv50_lut_alloc(crtc);
+ if (rval != 0) {
+ goto out;
+ }
+
+ /* lut will be inited when fb is bound */
+ crtc->lut->depth = 0;
+
+ /* function pointers */
+ crtc->lut->set = nv50_lut_set;
+
+ return 0;
+
+out:
+ if (crtc->lut)
+ kfree(crtc->lut);
+
+ return rval;
+}
+
+int nv50_lut_destroy(struct nv50_crtc *crtc)
+{
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ if (!crtc)
+ return -EINVAL;
+
+ rval = nv50_lut_free(crtc);
+
+ kfree(crtc->lut);
+ crtc->lut = NULL;
+
+ if (rval != 0)
+ return rval;
+
+ return 0;
+}
diff --git a/linux-core/nv50_lut.h b/linux-core/nv50_lut.h
new file mode 100644
index 00000000..06704830
--- /dev/null
+++ b/linux-core/nv50_lut.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_LUT_H__
+#define __NV50_LUT_H__
+
+#include "nv50_display.h"
+
+struct nv50_crtc;
+
+struct nv50_lut {
+ struct mem_block *block;
+
+ int depth; /* check against fb to see if it needs to be reuploaded */
+
+ int (*set) (struct nv50_crtc *crtc, uint16_t *red, uint16_t *green, uint16_t *blue);
+};
+
+int nv50_lut_create(struct nv50_crtc *crtc);
+int nv50_lut_destroy(struct nv50_crtc *crtc);
+
+#endif /* __NV50_LUT_H__ */
diff --git a/linux-core/nv50_output.c b/linux-core/nv50_output.c
new file mode 100644
index 00000000..de0017b9
--- /dev/null
+++ b/linux-core/nv50_output.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_output.h"
+
+int nv50_output_or_offset(struct nv50_output *output)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+ return ffs(dev_priv->dcb_table.entry[output->dcb_entry].or) - 1;
+}
diff --git a/linux-core/nv50_output.h b/linux-core/nv50_output.h
new file mode 100644
index 00000000..a5faf050
--- /dev/null
+++ b/linux-core/nv50_output.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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 __NV50_OUTPUT_H__
+#define __NV50_OUTPUT_H__
+
+#include "nv50_crtc.h"
+
+#define OUTPUT_UNKNOWN 0
+#define OUTPUT_DAC 1
+#define OUTPUT_TMDS 2
+#define OUTPUT_LVDS 3
+#define OUTPUT_TV 4
+
+struct nv50_output {
+ struct list_head item;
+
+ struct drm_device *dev;
+ int bus;
+ int dcb_entry;
+ int type;
+
+ struct nv50_crtc *crtc;
+ struct nouveau_hw_mode *native_mode;
+
+ int (*validate_mode) (struct nv50_output *output, struct nouveau_hw_mode *mode);
+ int (*execute_mode) (struct nv50_output *output, bool disconnect);
+ int (*set_clock_mode) (struct nv50_output *output);
+ /* this is not a normal modeset call, it is a direct register write, so it's executed immediately */
+ int (*set_power_mode) (struct nv50_output *output, int mode);
+ bool (*detect) (struct nv50_output *output);
+ int (*destroy) (struct nv50_output *output);
+};
+
+int nv50_output_or_offset(struct nv50_output *output);
+int nv50_sor_create(struct drm_device *dev, int dcb_entry);
+int nv50_dac_create(struct drm_device *dev, int dcb_entry);
+
+#endif /* __NV50_OUTPUT_H__ */
diff --git a/linux-core/nv50_sor.c b/linux-core/nv50_sor.c
new file mode 100644
index 00000000..41116923
--- /dev/null
+++ b/linux-core/nv50_sor.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * 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 THE COPYRIGHT OWNER(S) 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.
+ *
+ */
+
+#include "nv50_output.h"
+
+static int nv50_sor_validate_mode(struct nv50_output *output, struct nouveau_hw_mode *mode)
+{
+ NV50_DEBUG("\n");
+
+ if (mode->clock > 165000) /* no dual link until we figure it out completely */
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock < 25000)
+ return MODE_CLOCK_LOW;
+
+ if (output->native_mode->hdisplay > 0 && output->native_mode->vdisplay > 0) {
+ if (mode->hdisplay > output->native_mode->hdisplay || mode->vdisplay > output->native_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static int nv50_sor_execute_mode(struct nv50_output *output, bool disconnect)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+ struct nv50_crtc *crtc = output->crtc;
+ struct nouveau_hw_mode *desired_mode = NULL;
+
+ uint32_t offset = nv50_output_or_offset(output) * 0x40;
+
+ uint32_t mode_ctl = NV50_SOR_MODE_CTRL_OFF;
+
+ NV50_DEBUG("or %d\n", nv50_output_or_offset(output));
+
+ if (disconnect) {
+ NV50_DEBUG("Disconnecting SOR\n");
+ OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl);
+ return 0;
+ }
+
+ desired_mode = (crtc->use_native_mode ? crtc->native_mode : crtc->mode);
+
+ if (output->type == OUTPUT_LVDS) {
+ mode_ctl |= NV50_SOR_MODE_CTRL_LVDS;
+ } else {
+ mode_ctl |= NV50_SOR_MODE_CTRL_TMDS;
+ if (desired_mode->clock > 165000)
+ mode_ctl |= NV50_SOR_MODE_CTRL_TMDS_DUAL_LINK;
+ }
+
+ if (crtc->index == 1)
+ mode_ctl |= NV50_SOR_MODE_CTRL_CRTC1;
+ else
+ mode_ctl |= NV50_SOR_MODE_CTRL_CRTC0;
+
+ if (desired_mode->flags & DRM_MODE_FLAG_NHSYNC)
+ mode_ctl |= NV50_SOR_MODE_CTRL_NHSYNC;
+
+ if (desired_mode->flags & DRM_MODE_FLAG_NVSYNC)
+ mode_ctl |= NV50_SOR_MODE_CTRL_NVSYNC;
+
+ OUT_MODE(NV50_SOR0_MODE_CTRL + offset, mode_ctl);
+
+ return 0;
+}
+
+static int nv50_sor_set_clock_mode(struct nv50_output *output)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+ struct nv50_crtc *crtc = output->crtc;
+
+ uint32_t limit = 165000;
+ struct nouveau_hw_mode *hw_mode;
+
+ NV50_DEBUG("or %d\n", nv50_output_or_offset(output));
+
+ /* We don't yet know what to do, if anything at all. */
+ if (output->type == OUTPUT_LVDS)
+ return 0;
+
+ if (crtc->use_native_mode)
+ hw_mode = crtc->native_mode;
+ else
+ hw_mode = crtc->mode;
+
+ /* 0x70000 was a late addition to nv, mentioned as fixing tmds initialisation on certain gpu's. */
+ /* I presume it's some kind of clock setting, but what precisely i do not know. */
+ NV_WRITE(NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(nv50_output_or_offset(output)), 0x70000 | ((hw_mode->clock > limit) ? 0x101 : 0));
+
+ return 0;
+}
+
+static int nv50_sor_set_power_mode(struct nv50_output *output, int mode)
+{
+ struct drm_nouveau_private *dev_priv = output->dev->dev_private;
+ uint32_t val;
+ int or = nv50_output_or_offset(output);
+
+ NV50_DEBUG("or %d\n", nv50_output_or_offset(output));
+
+ /* wait for it to be done */
+ while (NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or)) & NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING);
+
+ val = NV_READ(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or));
+
+ if (mode == DRM_MODE_DPMS_ON)
+ val |= NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON;
+ else
+ val &= ~NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON;
+
+ NV_WRITE(NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(or), val | NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING);
+
+ return 0;
+}
+
+static int nv50_sor_destroy(struct nv50_output *output)
+{
+ struct drm_device *dev = output->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_display *display = nv50_get_display(dev);
+
+ NV50_DEBUG("\n");
+
+ if (!display || !output)
+ return -EINVAL;
+
+ list_del(&output->item);
+
+ kfree(output->native_mode);
+ if (dev_priv->free_output)
+ dev_priv->free_output(output);
+
+ return 0;
+}
+
+int nv50_sor_create(struct drm_device *dev, int dcb_entry)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_output *output = NULL;
+ struct nv50_display *display = NULL;
+ struct dcb_entry *entry = NULL;
+ int rval = 0;
+
+ NV50_DEBUG("\n");
+
+ /* This allows the public layer to do it's thing. */
+ if (dev_priv->alloc_output)
+ output = dev_priv->alloc_output(dev);
+
+ if (!output)
+ return -ENOMEM;
+
+ output->dev = dev;
+
+ display = nv50_get_display(dev);
+ if (!display) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ entry = &dev_priv->dcb_table.entry[dcb_entry];
+ if (!entry) {
+ rval = -EINVAL;
+ goto out;
+ }
+
+ switch (entry->type) {
+ case DCB_OUTPUT_TMDS:
+ output->type = OUTPUT_TMDS;
+ DRM_INFO("Detected a TMDS output\n");
+ break;
+ case DCB_OUTPUT_LVDS:
+ output->type = OUTPUT_LVDS;
+ DRM_INFO("Detected a LVDS output\n");
+ break;
+ default:
+ rval = -EINVAL;
+ goto out;
+ }
+
+ output->dcb_entry = dcb_entry;
+ output->bus = entry->bus;
+
+ list_add_tail(&output->item, &display->outputs);
+
+ output->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
+ if (!output->native_mode) {
+ rval = -ENOMEM;
+ goto out;
+ }
+
+ /* Set function pointers. */
+ output->validate_mode = nv50_sor_validate_mode;
+ output->execute_mode = nv50_sor_execute_mode;
+ output->set_clock_mode = nv50_sor_set_clock_mode;
+ output->set_power_mode = nv50_sor_set_power_mode;
+ output->detect = NULL;
+ output->destroy = nv50_sor_destroy;
+
+ /* Some default state, unknown what it precisely means. */
+ if (output->type == OUTPUT_TMDS) {
+ NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_00C(nv50_output_or_offset(output)), 0x03010700);
+ NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_010(nv50_output_or_offset(output)), 0x0000152f);
+ NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_014(nv50_output_or_offset(output)), 0x00000000);
+ NV_WRITE(NV50_PDISPLAY_SOR_REGS_UNK_018(nv50_output_or_offset(output)), 0x00245af8);
+ }
+
+ return 0;
+
+out:
+ if (output->native_mode)
+ kfree(output->native_mode);
+ if (dev_priv->free_output)
+ dev_priv->free_output(output);
+ return rval;
+}
diff --git a/shared-core/drm.h b/shared-core/drm.h
index 382e3fa1..017c012b 100644
--- a/shared-core/drm.h
+++ b/shared-core/drm.h
@@ -1034,6 +1034,30 @@ struct drm_gem_open {
#define DRM_MODE_TYPE_USERDEF (1<<5)
#define DRM_MODE_TYPE_DRIVER (1<<6)
+/* Video mode flags */
+/* bit compatible with the xorg definitions. */
+#define DRM_MODE_FLAG_PHSYNC (1<<0)
+#define DRM_MODE_FLAG_NHSYNC (1<<1)
+#define DRM_MODE_FLAG_PVSYNC (1<<2)
+#define DRM_MODE_FLAG_NVSYNC (1<<3)
+#define DRM_MODE_FLAG_INTERLACE (1<<4)
+#define DRM_MODE_FLAG_DBLSCAN (1<<5)
+#define DRM_MODE_FLAG_CSYNC (1<<6)
+#define DRM_MODE_FLAG_PCSYNC (1<<7)
+#define DRM_MODE_FLAG_NCSYNC (1<<8)
+#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */
+#define DRM_MODE_FLAG_BCAST (1<<10)
+#define DRM_MODE_FLAG_PIXMUX (1<<11)
+#define DRM_MODE_FLAG_DBLCLK (1<<12)
+#define DRM_MODE_FLAG_CLKDIV2 (1<<13)
+
+/* DPMS flags */
+/* bit compatible with the xorg definitions. */
+#define DRM_MODE_DPMS_ON 0
+#define DRM_MODE_DPMS_STANDBY 1
+#define DRM_MODE_DPMS_SUSPEND 2
+#define DRM_MODE_DPMS_OFF 3
+
struct drm_mode_modeinfo {
unsigned int clock;
unsigned short hdisplay, hsync_start, hsync_end, htotal, hskew;
@@ -1057,47 +1081,63 @@ struct drm_mode_card_res {
int count_encoders;
int min_width, max_width;
int min_height, max_height;
- uint32_t generation;
};
struct drm_mode_crtc {
uint64_t set_connectors_ptr;
+ int count_connectors;
unsigned int crtc_id; /**< Id */
unsigned int fb_id; /**< Id of framebuffer */
int x, y; /**< Position on the frameuffer */
- uint32_t generation;
-
- int count_connectors;
- unsigned int connectors; /**< Connectors that are connected */
-
- int count_possibles;
- unsigned int possibles; /**< Connectors that can be connected */
uint32_t gamma_size;
int mode_valid;
struct drm_mode_modeinfo mode;
};
-struct drm_mode_get_encoder {
-
- uint32_t generation;
-
- uint32_t encoder_type;
- uint32_t encoder_id;
-
- unsigned int crtc; /**< Id of crtc */
- uint32_t crtcs;
- uint32_t clones;
-};
-
#define DRM_MODE_ENCODER_NONE 0
#define DRM_MODE_ENCODER_DAC 1
#define DRM_MODE_ENCODER_TMDS 2
#define DRM_MODE_ENCODER_LVDS 3
#define DRM_MODE_ENCODER_TVDAC 4
+struct drm_mode_get_encoder {
+
+ unsigned int encoder_type;
+ unsigned int encoder_id;
+
+ unsigned int crtc_id; /**< Id of crtc */
+
+ uint32_t possible_crtcs;
+ uint32_t possible_clones;
+};
+
+/* This is for connectors with multiple signal types. */
+/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */
+#define DRM_MODE_SUBCONNECTOR_Automatic 0
+#define DRM_MODE_SUBCONNECTOR_Unknown 0
+#define DRM_MODE_SUBCONNECTOR_DVID 3
+#define DRM_MODE_SUBCONNECTOR_DVIA 4
+#define DRM_MODE_SUBCONNECTOR_Composite 5
+#define DRM_MODE_SUBCONNECTOR_SVIDEO 6
+#define DRM_MODE_SUBCONNECTOR_Component 8
+
+#define DRM_MODE_CONNECTOR_Unknown 0
+#define DRM_MODE_CONNECTOR_VGA 1
+#define DRM_MODE_CONNECTOR_DVII 2
+#define DRM_MODE_CONNECTOR_DVID 3
+#define DRM_MODE_CONNECTOR_DVIA 4
+#define DRM_MODE_CONNECTOR_Composite 5
+#define DRM_MODE_CONNECTOR_SVIDEO 6
+#define DRM_MODE_CONNECTOR_LVDS 7
+#define DRM_MODE_CONNECTOR_Component 8
+#define DRM_MODE_CONNECTOR_9PinDIN 9
+#define DRM_MODE_CONNECTOR_DisplayPort 10
+#define DRM_MODE_CONNECTOR_HDMIA 11
+#define DRM_MODE_CONNECTOR_HDMIB 12
+
struct drm_mode_get_connector {
uint64_t encoders_ptr;
@@ -1109,13 +1149,11 @@ struct drm_mode_get_connector {
int count_props;
int count_encoders;
- unsigned int encoder; /**< Current Encoder */
- unsigned int connector; /**< Id */
+ unsigned int encoder_id; /**< Current Encoder */
+ unsigned int connector_id; /**< Id */
unsigned int connector_type;
unsigned int connector_type_id;
- uint32_t generation;
-
unsigned int connection;
unsigned int mm_width, mm_height; /**< HxW in millimeters */
unsigned int subpixel;
diff --git a/shared-core/nouveau_dma.h b/shared-core/nouveau_dma.h
index ce3c58cb..07652c2b 100644
--- a/shared-core/nouveau_dma.h
+++ b/shared-core/nouveau_dma.h
@@ -93,4 +93,9 @@ typedef enum {
} \
} while(0)
+/* This should allow easy switching to a real fifo in the future. */
+#define OUT_MODE(mthd, val) do { \
+ nv50_display_command(dev_priv, mthd, val); \
+} while(0)
+
#endif
diff --git a/shared-core/nouveau_drm.h b/shared-core/nouveau_drm.h
index cf762052..bbb51bc4 100644
--- a/shared-core/nouveau_drm.h
+++ b/shared-core/nouveau_drm.h
@@ -88,9 +88,11 @@ struct drm_nouveau_gpuobj_free {
#define NOUVEAU_MEM_INSTANCE 0x00000200 /* internal */
#define NOUVEAU_MEM_NOTIFIER 0x00000400 /* internal */
#define NOUVEAU_MEM_NOVM 0x00000800 /* internal */
+#define NOUVEAU_MEM_USER 0x00001000 /* internal */
#define NOUVEAU_MEM_INTERNAL (NOUVEAU_MEM_INSTANCE | \
NOUVEAU_MEM_NOTIFIER | \
- NOUVEAU_MEM_NOVM)
+ NOUVEAU_MEM_NOVM | \
+ NOUVEAU_MEM_USER)
struct drm_nouveau_mem_alloc {
int flags;
diff --git a/shared-core/nouveau_drv.h b/shared-core/nouveau_drv.h
index a51e552c..33e2a5b6 100644
--- a/shared-core/nouveau_drv.h
+++ b/shared-core/nouveau_drv.h
@@ -41,6 +41,9 @@
#include "nouveau_drm.h"
#include "nouveau_reg.h"
+#include "nouveau_bios.h"
+
+#define MAX_NUM_DCB_ENTRIES 16
struct mem_block {
struct mem_block *next;
@@ -310,6 +313,28 @@ struct drm_nouveau_private {
struct nouveau_config config;
struct list_head gpuobj_list;
+
+ void *display_priv; /* internal modesetting */
+ void *kms_priv; /* related to public interface */
+
+ /* Hook these up to the "public interface" to accomodate a certain allocation style. */
+ /* This is to avoid polluting the internal interface. */
+ void *(*alloc_crtc) (struct drm_device *dev);
+ void *(*alloc_output) (struct drm_device *dev);
+ void *(*alloc_connector) (struct drm_device *dev);
+
+ void (*free_crtc) (void *crtc);
+ void (*free_output) (void *output);
+ void (*free_connector) (void *connector);
+
+ struct bios bios;
+
+ struct {
+ int entries;
+ struct dcb_entry entry[MAX_NUM_DCB_ENTRIES];
+ unsigned char i2c_read[MAX_NUM_DCB_ENTRIES];
+ unsigned char i2c_write[MAX_NUM_DCB_ENTRIES];
+ } dcb_table;
};
#define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do { \
@@ -350,9 +375,10 @@ extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start,
uint64_t size);
extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
uint64_t size, int align2,
- struct drm_file *);
+ struct drm_file *, int tail);
extern void nouveau_mem_takedown(struct mem_block **heap);
extern void nouveau_mem_free_block(struct mem_block *);
+extern struct mem_block* find_block_by_handle(struct mem_block *heap, drm_handle_t handle);
extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
extern int nouveau_ioctl_mem_alloc(struct drm_device *, void *data,
diff --git a/shared-core/nouveau_irq.c b/shared-core/nouveau_irq.c
index 2a3d8a0b..4c46da8d 100644
--- a/shared-core/nouveau_irq.c
+++ b/shared-core/nouveau_irq.c
@@ -37,6 +37,11 @@
#include "nouveau_reg.h"
#include "nouveau_swmthd.h"
+/* needed for interrupt based vpll changes */
+#include "nv50_display.h"
+#include "nv50_crtc.h"
+#include "nv50_output.h"
+
void
nouveau_irq_preinstall(struct drm_device *dev)
{
@@ -503,11 +508,82 @@ static void
nouveau_nv50_display_irq_handler(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t val = NV_READ(NV50_DISPLAY_SUPERVISOR);
+ uint32_t val = NV_READ(NV50_PDISPLAY_SUPERVISOR);
DRM_INFO("NV50_DISPLAY_INTR - 0x%08X\n", val);
- NV_WRITE(NV50_DISPLAY_SUPERVISOR, val);
+ /* vblank interrupts */
+ if (val & NV50_PDISPLAY_SUPERVISOR_CRTCn) {
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CRTCn);
+ val &= ~NV50_PDISPLAY_SUPERVISOR_CRTCn;
+ }
+
+ /* clock setting amongst other things. */
+ if (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) {
+ uint32_t state = (val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK) >> NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT;
+
+ NV50_DEBUG("state %d\n", state);
+
+ /* Set pll */
+ if (state == 2) {
+ struct nv50_display *display = nv50_get_display(dev);
+ struct nv50_output *output = NULL;
+ struct nv50_crtc *crtc = NULL;
+ int crtc_index;
+
+ uint32_t unk30 = NV_READ(NV50_PDISPLAY_UNK30_CTRL);
+
+ for (crtc_index = 0; crtc_index < 2; crtc_index++) {
+ bool clock_change = false;
+ bool clock_ack = false;
+
+ if (crtc_index == 0 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0))
+ clock_change = true;
+
+ if (crtc_index == 1 && (unk30 & NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1))
+ clock_change = true;
+
+ if (clock_change)
+ clock_ack = true;
+
+ if (display->last_crtc == crtc_index)
+ clock_ack = true;
+
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ if (crtc->index == crtc_index)
+ break;
+ }
+
+ if (clock_change)
+ crtc->set_clock(crtc);
+
+ NV50_DEBUG("index %d clock_change %d clock_ack %d\n", crtc_index, clock_change, clock_ack);
+
+ if (!clock_ack)
+ continue;
+
+ crtc->set_clock_mode(crtc);
+
+ list_for_each_entry(output, &display->outputs, item) {
+ if (!output->crtc)
+ continue;
+
+ if (output->crtc == crtc)
+ output->set_clock_mode(output);
+ }
+ }
+ }
+
+ NV_WRITE(NV50_PDISPLAY_UNK30_CTRL, NV50_PDISPLAY_UNK30_CTRL_PENDING);
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val & NV50_PDISPLAY_SUPERVISOR_CLK_MASK);
+
+ val &= ~NV50_PDISPLAY_SUPERVISOR_CLK_MASK;
+ }
+
+ if (val)
+ DRM_ERROR("unsupported NV50_DISPLAY_INTR - 0x%08X\n", val);
+
+ NV_WRITE(NV50_PDISPLAY_SUPERVISOR, val);
}
static void
diff --git a/shared-core/nouveau_mem.c b/shared-core/nouveau_mem.c
index 2cf8807d..861d699f 100644
--- a/shared-core/nouveau_mem.c
+++ b/shared-core/nouveau_mem.c
@@ -34,9 +34,12 @@
#include "drm.h"
#include "drm_sarea.h"
#include "nouveau_drv.h"
+#include "nv50_kms_wrapper.h"
-static struct mem_block *split_block(struct mem_block *p, uint64_t start, uint64_t size,
- struct drm_file *file_priv)
+
+static struct mem_block *
+split_block(struct mem_block *p, uint64_t start, uint64_t size,
+ struct drm_file *file_priv)
{
/* Maybe cut off the start of an existing block */
if (start > p->start) {
@@ -77,10 +80,9 @@ out:
return p;
}
-struct mem_block *nouveau_mem_alloc_block(struct mem_block *heap,
- uint64_t size,
- int align2,
- struct drm_file *file_priv)
+struct mem_block *
+nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
+ int align2, struct drm_file *file_priv, int tail)
{
struct mem_block *p;
uint64_t mask = (1 << align2) - 1;
@@ -88,10 +90,22 @@ struct mem_block *nouveau_mem_alloc_block(struct mem_block *heap,
if (!heap)
return NULL;
- list_for_each(p, heap) {
- uint64_t start = (p->start + mask) & ~mask;
- if (p->file_priv == 0 && start + size <= p->start + p->size)
- return split_block(p, start, size, file_priv);
+ if (tail) {
+ list_for_each_prev(p, heap) {
+ uint64_t start = ((p->start + p->size) - size) & ~mask;
+
+ if (p->file_priv == 0 && start >= p->start &&
+ start + size <= p->start + p->size)
+ return split_block(p, start, size, file_priv);
+ }
+ } else {
+ list_for_each(p, heap) {
+ uint64_t start = (p->start + mask) & ~mask;
+
+ if (p->file_priv == 0 &&
+ start + size <= p->start + p->size)
+ return split_block(p, start, size, file_priv);
+ }
}
return NULL;
@@ -108,6 +122,17 @@ static struct mem_block *find_block(struct mem_block *heap, uint64_t start)
return NULL;
}
+struct mem_block *find_block_by_handle(struct mem_block *heap, drm_handle_t handle)
+{
+ struct mem_block *p;
+
+ list_for_each(p, heap)
+ if (p->map_handle == handle)
+ return p;
+
+ return NULL;
+}
+
void nouveau_mem_free_block(struct mem_block *p)
{
p->file_priv = NULL;
@@ -563,13 +588,13 @@ int nouveau_mem_init(struct drm_device *dev)
return 0;
}
-struct mem_block* nouveau_mem_alloc(struct drm_device *dev, int alignment,
- uint64_t size, int flags,
- struct drm_file *file_priv)
+struct mem_block *
+nouveau_mem_alloc(struct drm_device *dev, int alignment, uint64_t size,
+ int flags, struct drm_file *file_priv)
{
- struct mem_block *block;
- int type;
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct mem_block *block;
+ int type, tail = !(flags & NOUVEAU_MEM_USER);
/*
* Make things easier on ourselves: all allocations are page-aligned.
@@ -600,14 +625,14 @@ struct mem_block* nouveau_mem_alloc(struct drm_device *dev, int alignment,
#define NOUVEAU_MEM_ALLOC_AGP {\
type=NOUVEAU_MEM_AGP;\
block = nouveau_mem_alloc_block(dev_priv->agp_heap, size,\
- alignment, file_priv); \
+ alignment, file_priv, tail); \
if (block) goto alloc_ok;\
}
#define NOUVEAU_MEM_ALLOC_PCI {\
type = NOUVEAU_MEM_PCI;\
block = nouveau_mem_alloc_block(dev_priv->pci_heap, size, \
- alignment, file_priv); \
+ alignment, file_priv, tail); \
if ( block ) goto alloc_ok;\
}
@@ -616,11 +641,11 @@ struct mem_block* nouveau_mem_alloc(struct drm_device *dev, int alignment,
if (!(flags&NOUVEAU_MEM_MAPPED)) {\
block = nouveau_mem_alloc_block(dev_priv->fb_nomap_heap,\
size, alignment, \
- file_priv); \
+ file_priv, tail); \
if (block) goto alloc_ok;\
}\
block = nouveau_mem_alloc_block(dev_priv->fb_heap, size,\
- alignment, file_priv);\
+ alignment, file_priv, tail);\
if (block) goto alloc_ok;\
}
@@ -707,6 +732,30 @@ void nouveau_mem_free(struct drm_device* dev, struct mem_block* block)
DRM_DEBUG("freeing 0x%llx type=0x%08x\n", block->start, block->flags);
+ /* Check if the deallocations cause problems for our modesetting system. */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ if (dev_priv->card_type >= NV_50) {
+ struct nv50_crtc *crtc = NULL;
+ struct nv50_display *display = nv50_get_display(dev);
+
+ list_for_each_entry(crtc, &display->crtcs, item) {
+ if (crtc->fb->block == block) {
+ crtc->fb->block = NULL;
+
+ if (!crtc->blanked)
+ crtc->blank(crtc, true);
+ }
+
+ if (crtc->cursor->block == block) {
+ crtc->cursor->block = NULL;
+
+ if (crtc->cursor->visible)
+ crtc->cursor->hide(crtc);
+ }
+ }
+ }
+ }
+
if (block->flags&NOUVEAU_MEM_MAPPED)
drm_rmmap(dev, block->map);
@@ -738,7 +787,9 @@ out_free:
* Ioctls
*/
-int nouveau_ioctl_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int
+nouveau_ioctl_mem_alloc(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_nouveau_mem_alloc *alloc = data;
struct mem_block *block;
@@ -748,8 +799,8 @@ int nouveau_ioctl_mem_alloc(struct drm_device *dev, void *data, struct drm_file
if (alloc->flags & NOUVEAU_MEM_INTERNAL)
return -EINVAL;
- block=nouveau_mem_alloc(dev, alloc->alignment, alloc->size,
- alloc->flags, file_priv);
+ block = nouveau_mem_alloc(dev, alloc->alignment, alloc->size,
+ alloc->flags | NOUVEAU_MEM_USER, file_priv);
if (!block)
return -ENOMEM;
alloc->map_handle=block->map_handle;
diff --git a/shared-core/nouveau_notifier.c b/shared-core/nouveau_notifier.c
index 82c8ab7d..edece4da 100644
--- a/shared-core/nouveau_notifier.c
+++ b/shared-core/nouveau_notifier.c
@@ -94,7 +94,7 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
}
mem = nouveau_mem_alloc_block(chan->notifier_heap, count*32, 0,
- (struct drm_file *)-2);
+ (struct drm_file *)-2, 0);
if (!mem) {
DRM_ERROR("Channel %d notifier block full\n", chan->id);
return -ENOMEM;
diff --git a/shared-core/nouveau_object.c b/shared-core/nouveau_object.c
index 09f9027a..5664bfc8 100644
--- a/shared-core/nouveau_object.c
+++ b/shared-core/nouveau_object.c
@@ -248,7 +248,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
/* Allocate a chunk of the PRAMIN aperture */
gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size,
drm_order(align),
- (struct drm_file *)-2);
+ (struct drm_file *)-2, 0);
if (!gpuobj->im_pramin) {
nouveau_gpuobj_del(dev, &gpuobj);
return -ENOMEM;
diff --git a/shared-core/nouveau_reg.h b/shared-core/nouveau_reg.h
index 1ae0177c..6ed23e26 100644
--- a/shared-core/nouveau_reg.h
+++ b/shared-core/nouveau_reg.h
@@ -116,6 +116,9 @@
#define NV04_PBUS_PCI_NV_1 0x00001804
#define NV04_PBUS_PCI_NV_19 0x0000184C
+#define NV04_PBUS_PCI_NV_20 0x00001850
+# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
+# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
#define NV04_PTIMER_INTR_0 0x00009100
#define NV04_PTIMER_INTR_EN_0 0x00009140
@@ -542,6 +545,8 @@
/* This name is a partial guess. */
#define NV50_DISPLAY_SUPERVISOR 0x00610024
+#define NV04_PRAMIN 0x00700000
+
/* Fifo commands. These are not regs, neither masks */
#define NV03_FIFO_CMD_JUMP 0x20000000
#define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc
@@ -591,3 +596,236 @@
#define NV40_RAMFC_UNK_48 0x48
#define NV40_RAMFC_UNK_4C 0x4C
#define NV40_RAMFC_UNK_50 0x50
+
+/* This is a partial import from rules-ng, a few things may be duplicated.
+ * Eventually we should completely import everything from rules-ng.
+ * For the moment check rules-ng for docs.
+ */
+
+#define NV50_PMC 0x00000000
+#define NV50_PMC__LEN 0x1
+#define NV50_PMC__ESIZE 0x2000
+# define NV50_PMC_BOOT_0 0x00000000
+# define NV50_PMC_BOOT_0_REVISION 0x000000ff
+# define NV50_PMC_BOOT_0_REVISION__SHIFT 0
+# define NV50_PMC_BOOT_0_ARCH 0x0ff00000
+# define NV50_PMC_BOOT_0_ARCH__SHIFT 20
+# define NV50_PMC_INTR_0 0x00000100
+# define NV50_PMC_INTR_0_PFIFO (1<<8)
+# define NV50_PMC_INTR_0_PGRAPH (1<<12)
+# define NV50_PMC_INTR_0_PTIMER (1<<20)
+# define NV50_PMC_INTR_0_HOTPLUG (1<<21)
+# define NV50_PMC_INTR_0_DISPLAY (1<<26)
+# define NV50_PMC_INTR_EN_0 0x00000140
+# define NV50_PMC_INTR_EN_0_MASTER (1<<0)
+# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0)
+# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0)
+# define NV50_PMC_ENABLE 0x00000200
+# define NV50_PMC_ENABLE_PFIFO (1<<8)
+# define NV50_PMC_ENABLE_PGRAPH (1<<12)
+
+#define NV50_PCONNECTOR 0x0000e000
+#define NV50_PCONNECTOR__LEN 0x1
+#define NV50_PCONNECTOR__ESIZE 0x1000
+# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18)
+# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18)
+# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19)
+# define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2)
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6)
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10)
+# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14)
+# define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138
+# define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150
+# define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168
+# define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180
+# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240
+# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258
+
+#define NV50_PBUS 0x00088000
+#define NV50_PBUS__LEN 0x1
+#define NV50_PBUS__ESIZE 0x1000
+# define NV50_PBUS_PCI_ID 0x00088000
+# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff
+# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0
+# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000
+# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16
+
+#define NV50_PFB 0x00100000
+#define NV50_PFB__LEN 0x1
+#define NV50_PFB__ESIZE 0x1000
+
+#define NV50_PEXTDEV 0x00101000
+#define NV50_PEXTDEV__LEN 0x1
+#define NV50_PEXTDEV__ESIZE 0x1000
+
+#define NV50_PROM 0x00300000
+#define NV50_PROM__LEN 0x1
+#define NV50_PROM__ESIZE 0x10000
+
+#define NV50_PGRAPH 0x00400000
+#define NV50_PGRAPH__LEN 0x1
+#define NV50_PGRAPH__ESIZE 0x10000
+
+#define NV50_PDISPLAY 0x00610000
+#define NV50_PDISPLAY__LEN 0x1
+#define NV50_PDISPLAY__ESIZE 0x10000
+# define NV50_PDISPLAY_SUPERVISOR 0x00610024
+# define NV50_PDISPLAY_SUPERVISOR_CRTCn 0x0000000c
+# define NV50_PDISPLAY_SUPERVISOR_CRTCn__SHIFT 2
+# define NV50_PDISPLAY_SUPERVISOR_CRTC0 (1<<2)
+# define NV50_PDISPLAY_SUPERVISOR_CRTC1 (1<<3)
+# define NV50_PDISPLAY_SUPERVISOR_CLK_MASK 0x00000070
+# define NV50_PDISPLAY_SUPERVISOR_CLK_MASK__SHIFT 4
+# define NV50_PDISPLAY_SUPERVISOR_CLK_UPDATE (1<<5)
+# define NV50_PDISPLAY_SUPERVISOR_INTR 0x0061002c
+# define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC0 (1<<2)
+# define NV50_PDISPLAY_SUPERVISOR_INTR_VBLANK_CRTC1 (1<<3)
+# define NV50_PDISPLAY_SUPERVISOR_INTR_UNK1 (1<<4)
+# define NV50_PDISPLAY_SUPERVISOR_INTR_CLK_UPDATE (1<<5)
+# define NV50_PDISPLAY_SUPERVISOR_INTR_UNK4 (1<<6)
+# define NV50_PDISPLAY_UNK30_CTRL 0x00610030
+# define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 (1<<9)
+# define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 (1<<10)
+# define NV50_PDISPLAY_UNK30_CTRL_PENDING (1<<31)
+# define NV50_PDISPLAY_UNK50_CTRL 0x00610050
+# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE (1<<1)
+# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK 0x00000003
+# define NV50_PDISPLAY_UNK50_CTRL_CRTC0_ACTIVE_MASK__SHIFT 0
+# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE (1<<9)
+# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK 0x00000300
+# define NV50_PDISPLAY_UNK50_CTRL_CRTC1_ACTIVE_MASK__SHIFT 8
+# define NV50_PDISPLAY_UNK200_CTRL 0x00610200
+# define NV50_PDISPLAY_CURSOR 0x00610270
+# define NV50_PDISPLAY_CURSOR__LEN 0x2
+# define NV50_PDISPLAY_CURSOR__ESIZE 0x10
+# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) (0x00610270+(i)*0x10)
+# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON (1<<0)
+# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK 0x00030000
+# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_MASK__SHIFT 16
+# define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE (1<<16)
+
+# define NV50_PDISPLAY_CTRL_STATE 0x00610300
+# define NV50_PDISPLAY_CTRL_STATE_ENABLE (1<<0)
+# define NV50_PDISPLAY_CTRL_STATE_PENDING (1<<31)
+# define NV50_PDISPLAY_CTRL_VAL 0x00610304
+# define NV50_PDISPLAY_UNK_380 0x00610380
+# define NV50_PDISPLAY_RAM_AMOUNT 0x00610384
+# define NV50_PDISPLAY_UNK_388 0x00610388
+# define NV50_PDISPLAY_UNK_38C 0x0061038c
+# define NV50_PDISPLAY_CRTC_VAL 0x00610a00
+# define NV50_PDISPLAY_CRTC_VAL__LEN 0x2
+# define NV50_PDISPLAY_CRTC_VAL_UNK_900(i,j) (0x00610a18+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_CLUT_MODE(i,j) (0x00610a24+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_INTERLACE(i,j) (0x00610a48+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_SCALE_CTRL(i,j) (0x00610a50+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_CURSOR_CTRL(i,j) (0x00610a58+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_UNK_904(i,j) (0x00610ab8+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_DEPTH(i,j) (0x00610ac8+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_CLOCK(i,j) (0x00610ad0+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_COLOR_CTRL(i,j) (0x00610ae0+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_SYNC_START_TO_BLANK_END(i,j) (0x00610ae8+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_MODE_UNK1(i,j) (0x00610af0+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_DISPLAY_TOTAL(i,j) (0x00610af8+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_SYNC_DURATION(i,j) (0x00610b00+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_MODE_UNK2(i,j) (0x00610b08+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_UNK_828(i,j) (0x00610b10+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_FB_SIZE(i,j) (0x00610b18+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_FB_PITCH(i,j) (0x00610b20+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_FB_PITCH_LINEAR_FB (1<<20)
+# define NV50_PDISPLAY_CRTC_VAL_FB_POS(i,j) (0x00610b28+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_SCALE_CENTER_OFFSET(i,j) (0x00610b38+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_REAL_RES(i,j) (0x00610b40+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_SCALE_RES1(i,j) (0x00610b48+(i)*0x540+(j)*0x4)
+# define NV50_PDISPLAY_CRTC_VAL_SCALE_RES2(i,j) (0x00610b50+(i)*0x540+(j)*0x4)
+
+
+# define NV50_PDISPLAY_DAC_VAL_MODE_CTRL(i,j) (0x00610b58+(i)*0x8+(j)*0x4)
+
+
+# define NV50_PDISPLAY_SOR_VAL_MODE_CTRL(i,j) (0x00610b70+(i)*0x8+(j)*0x4)
+
+
+# define NV50_PDISPLAY_DAC_VAL_MODE_CTRL2(i,j) (0x00610bdc+(i)*0x8+(j)*0x4)
+
+
+# define NV50_PDISPLAY_CRTC_CLK 0x00614000
+# define NV50_PDISPLAY_CRTC_CLK__LEN 0x2
+# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(i) (0x00614100+(i)*0x800)
+# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED 0x00000600
+# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED__SHIFT 9
+# define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) (0x00614104+(i)*0x800)
+# define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) (0x00614108+(i)*0x800)
+# define NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(i) (0x00614200+(i)*0x800)
+
+# define NV50_PDISPLAY_DAC_CLK 0x00614000
+# define NV50_PDISPLAY_DAC_CLK__LEN 0x3
+# define NV50_PDISPLAY_DAC_CLK_CLK_CTRL2(i) (0x00614280+(i)*0x800)
+
+# define NV50_PDISPLAY_SOR_CLK 0x00614000
+# define NV50_PDISPLAY_SOR_CLK__LEN 0x3
+# define NV50_PDISPLAY_SOR_CLK_CLK_CTRL2(i) (0x00614300+(i)*0x800)
+
+# define NV50_PDISPLAY_DAC_REGS 0x0061a000
+# define NV50_PDISPLAY_DAC_REGS__LEN 0x3
+# define NV50_PDISPLAY_DAC_REGS__ESIZE 0x800
+# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL(i) (0x0061a004+(i)*0x800)
+# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_HSYNC_OFF (1<<0)
+# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_VSYNC_OFF (1<<2)
+# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_BLANKED (1<<4)
+# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_OFF (1<<6)
+# define NV50_PDISPLAY_DAC_REGS_DPMS_CTRL_PENDING (1<<31)
+# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL(i) (0x0061a00c+(i)*0x800)
+# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_ACTIVE (1<<20)
+# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT 0x38000000
+# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_PRESENT__SHIFT 29
+# define NV50_PDISPLAY_DAC_REGS_LOAD_CTRL_DONE (1<<31)
+# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1(i) (0x0061a010+(i)*0x800)
+# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED 0x00000600
+# define NV50_PDISPLAY_DAC_REGS_CLK_CTRL1_CONNECTED__SHIFT 9
+
+# define NV50_PDISPLAY_SOR_REGS 0x0061c000
+# define NV50_PDISPLAY_SOR_REGS__LEN 0x2
+# define NV50_PDISPLAY_SOR_REGS__ESIZE 0x800
+# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL(i) (0x0061c004+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_ON (1<<0)
+# define NV50_PDISPLAY_SOR_REGS_DPMS_CTRL_PENDING (1<<31)
+# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1(i) (0x0061c008+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED 0x00000600
+# define NV50_PDISPLAY_SOR_REGS_CLK_CTRL1_CONNECTED__SHIFT 9
+# define NV50_PDISPLAY_SOR_REGS_UNK_00C(i) (0x0061c00c+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_UNK_010(i) (0x0061c010+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_UNK_014(i) (0x0061c014+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_UNK_018(i) (0x0061c018+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE(i) (0x0061c030+(i)*0x800)
+# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE 0x00030000
+# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_ACTIVE__SHIFT 16
+# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_BLANKED (1<<19)
+# define NV50_PDISPLAY_SOR_REGS_DPMS_STATE_WAIT (1<<28)
+
+
+#define NV50_UNK640000 0x00640000
+#define NV50_UNK640000__LEN 0x6
+#define NV50_UNK640000__ESIZE 0x1000
+# define NV50_UNK640000_UNK_000(i) (0x00640000+(i)*0x1000)
+
+#define NV50_HW_CURSOR 0x00647000
+#define NV50_HW_CURSOR__LEN 0x2
+#define NV50_HW_CURSOR__ESIZE 0x1000
+# define NV50_HW_CURSOR_POS_CTRL(i) (0x00647080+(i)*0x1000)
+# define NV50_HW_CURSOR_POS(i) (0x00647084+(i)*0x1000)
diff --git a/shared-core/nouveau_state.c b/shared-core/nouveau_state.c
index d9c6efe7..3baae6ad 100644
--- a/shared-core/nouveau_state.c
+++ b/shared-core/nouveau_state.c
@@ -27,6 +27,8 @@
#include "drm_sarea.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
+#include "nv50_kms_wrapper.h"
+#include "nv50_fbcon.h"
static int nouveau_init_card_mappings(struct drm_device *dev)
{
@@ -362,6 +364,14 @@ nouveau_card_init(struct drm_device *dev)
if (ret) return ret;
dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ if (dev_priv->card_type >= NV_50) {
+ nv50_kms_init(dev);
+ //nv50_kms_connector_detect_all(dev);
+ nv50_fbcon_init(dev);
+ }
+
return 0;
}
@@ -410,8 +420,7 @@ void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
nouveau_mem_release(file_priv,dev_priv->pci_heap);
}
-/* first module load, setup the mmio/fb mapping */
-int nouveau_firstopen(struct drm_device *dev)
+int nouveau_setup_mappings(struct drm_device *dev)
{
#if defined(__powerpc__)
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -457,6 +466,16 @@ int nouveau_firstopen(struct drm_device *dev)
return 0;
}
+/* first module load, setup the mmio/fb mapping */
+/* KMS: we need mmio at load time, not when the first drm client opens. */
+int nouveau_firstopen(struct drm_device *dev)
+{
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+
+ return nouveau_setup_mappings(dev);
+}
+
#define NV40_CHIPSET_MASK 0x00000baf
#define NV44_CHIPSET_MASK 0x00005450
@@ -540,6 +559,10 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
return -EINVAL;
}
+ /* For those who think they want to be funny. */
+ if (dev_priv->card_type < NV_50)
+ dev->driver->driver_features &= ~DRIVER_MODESET;
+
/* Special flags */
if (dev->pci_device == 0x01a0) {
dev_priv->flags |= NV_NFORCE;
@@ -549,10 +572,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = (void *)dev_priv;
+ /* init card now, otherwise bad things happen */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ int rval = 0;
+
+ rval = nouveau_setup_mappings(dev);
+ if (rval != 0)
+ return rval;
+
+ rval = nouveau_card_init(dev);
+ if (rval != 0)
+ return rval;
+ }
+
return 0;
}
-void nouveau_lastclose(struct drm_device *dev)
+void nouveau_close(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -568,8 +604,23 @@ void nouveau_lastclose(struct drm_device *dev)
}
}
+/* KMS: we need mmio at load time, not when the first drm client opens. */
+void nouveau_lastclose(struct drm_device *dev)
+{
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ nouveau_close(dev);
+}
+
int nouveau_unload(struct drm_device *dev)
{
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ nv50_kms_destroy(dev);
+ nv50_fbcon_destroy(dev);
+ nouveau_close(dev);
+ }
+
drm_free(dev->dev_private, sizeof(*dev->dev_private), DRM_MEM_DRIVER);
dev->dev_private = NULL;
return 0;
diff --git a/shared-core/nv50_fifo.c b/shared-core/nv50_fifo.c
index edf4edbf..d6810666 100644
--- a/shared-core/nv50_fifo.c
+++ b/shared-core/nv50_fifo.c
@@ -289,6 +289,7 @@ void
nv50_fifo_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
DRM_DEBUG("ch%d\n", chan->id);
@@ -298,6 +299,9 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan)
if (chan->id == 0)
nv50_fifo_channel_disable(dev, 127, 0);
+ if ((NV_READ(NV03_PFIFO_CACHE1_PUSH1) & 0xffff) == chan->id)
+ NV_WRITE(NV03_PFIFO_CACHE1_PUSH1, 127);
+
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
}
diff --git a/tests/mode/modetest.c b/tests/mode/modetest.c
deleted file mode 100644
index bb91e832..00000000
--- a/tests/mode/modetest.c
+++ /dev/null
@@ -1,409 +0,0 @@
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <string.h>
-
-#include "xf86drm.h"
-#include "xf86drmMode.h"
-
-int dpms_prop_id = 0;
-const char* getConnectionText(drmModeConnection conn)
-{
- switch (conn) {
- case DRM_MODE_CONNECTED:
- return "connected";
- case DRM_MODE_DISCONNECTED:
- return "disconnected";
- default:
- return "unknown";
- }
-
-}
-
-int printMode(struct drm_mode_modeinfo *mode)
-{
-#if 1
- printf("Mode: %s\n", mode->name);
- printf("\tclock : %i\n", mode->clock);
- printf("\thdisplay : %i\n", mode->hdisplay);
- printf("\thsync_start : %i\n", mode->hsync_start);
- printf("\thsync_end : %i\n", mode->hsync_end);
- printf("\thtotal : %i\n", mode->htotal);
- printf("\thskew : %i\n", mode->hskew);
- printf("\tvdisplay : %i\n", mode->vdisplay);
- printf("\tvsync_start : %i\n", mode->vsync_start);
- printf("\tvsync_end : %i\n", mode->vsync_end);
- printf("\tvtotal : %i\n", mode->vtotal);
- printf("\tvscan : %i\n", mode->vscan);
- printf("\tvrefresh : %i\n", mode->vrefresh);
- printf("\tflags : %i\n", mode->flags);
-#else
- printf("Mode: \"%s\" %ix%i %.0f\n", mode->name,
- mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000.0);
-#endif
- return 0;
-}
-
-int printConnector(int fd, drmModeResPtr res, drmModeConnectorPtr connector, uint32_t id)
-{
- int i = 0, j;
- struct drm_mode_modeinfo *mode = NULL;
- drmModePropertyPtr props;
- drmModeEncoderPtr enc;
- unsigned char *name = NULL;
-
- printf("Connector: %d-%d\n", connector->connector_type, connector->connector_type_id);
- printf("\tid : %i\n", id);
- printf("\tencoder id : %i\n", connector->encoder);
- printf("\tconn : %s\n", getConnectionText(connector->connection));
- printf("\tsize : %ix%i (mm)\n", connector->mmWidth, connector->mmHeight);
- printf("\tcount_modes : %i\n", connector->count_modes);
- printf("\tcount_props : %i\n", connector->count_props);
- printf("\tcount_encs : %i\n", connector->count_encoders);
-
- for (i = 0; i < connector->count_encoders; i++) {
- enc = drmModeGetEncoder(fd, connector->encoders[i]);
- if (enc) {
- printf("Encoder: %d %d %d %d %d\n", enc->crtc, enc->encoder_id, enc->encoder_type, enc->crtcs, enc->clones);
- }
- }
-
- for (i = 0; i < connector->count_props; i++) {
- props = drmModeGetProperty(fd, connector->props[i]);
- name = NULL;
- if (props) {
- printf("Property: %s\n", props->name);
- printf("\tid: %i\n", props->prop_id);
- printf("\tflags: %i\n", props->flags);
- printf("\tvalues %d: ", props->count_values);
- for (j = 0; j < props->count_values; j++)
- printf("%lld ", props->values[j]);
-
- printf("\n\tenums %d: \n", props->count_enums);
-
- if (props->flags & DRM_MODE_PROP_BLOB) {
- drmModePropertyBlobPtr blob;
-
- blob = drmModeGetPropertyBlob(fd, connector->prop_values[i]);
- if (blob) {
- printf("blob is %d length, %08X\n", blob->length, *(uint32_t *)blob->data);
- drmModeFreePropertyBlob(blob);
- }
-
- } else {
- if (!strncmp(props->name, "DPMS", 4))
- dpms_prop_id = props->prop_id;
-
- for (j = 0; j < props->count_enums; j++) {
- printf("\t\t%lld = %s\n", props->enums[j].value, props->enums[j].name);
- if (connector->prop_values[i] == props->enums[j].value)
- name = props->enums[j].name;
-
- }
-
- if (props->count_enums && name) {
- printf("\tconnector property name %s %s\n", props->name, name);
- } else {
- printf("\tconnector property id %s %lli\n", props->name, connector->prop_values[i]);
- }
- }
-
- drmModeFreeProperty(props);
- }
- }
-
- for (i = 0; i < connector->count_modes; i++) {
- mode = &connector->modes[i];
- if (mode)
- printMode(mode);
- else
- printf("\t\tmode: Invalid mode %p\n", &connector->modes[i]);
- }
-
- return 0;
-}
-
-int printEncoder(int fd, drmModeResPtr res, drmModeEncoderPtr encoder, uint32_t id)
-{
- printf("Encoder\n");
- printf("\tid :%i\n", id);
- printf("\ttype :%d\n", encoder->encoder_type);
- printf("\tcrtcs :%d\n", encoder->crtcs);
- printf("\tclones :%d\n", encoder->clones);
- return 0;
-}
-
-int printCrtc(int fd, drmModeResPtr res, drmModeCrtcPtr crtc, uint32_t id)
-{
- printf("Crtc\n");
- printf("\tid : %i\n", id);
- printf("\tx : %i\n", crtc->x);
- printf("\ty : %i\n", crtc->y);
- printf("\twidth : %i\n", crtc->width);
- printf("\theight : %i\n", crtc->height);
- printf("\tmode : %p\n", &crtc->mode);
- printf("\tgamma size : %d\n", crtc->gamma_size);
- printf("\tnum connectors : %i\n", crtc->count_connectors);
- printf("\tconnectors : %i\n", crtc->connectors);
- printf("\tnum possible : %i\n", crtc->count_possibles);
- printf("\tpossibles : %i\n", crtc->possibles);
-
- return 0;
-}
-
-int printFrameBuffer(int fd, drmModeResPtr res, drmModeFBPtr fb)
-{
- printf("Framebuffer\n");
- printf("\thandle : %i\n", fb->handle);
- printf("\twidth : %i\n", fb->width);
- printf("\theight : %i\n", fb->height);
- printf("\tpitch : %i\n", fb->pitch);;
- printf("\tbpp : %i\n", fb->bpp);
- printf("\tdepth : %i\n", fb->depth);
- printf("\tbuffer_id : %i\n", fb->buffer_id);
-
- return 0;
-}
-
-int printRes(int fd, drmModeResPtr res)
-{
- int i;
- drmModeConnectorPtr connector;
- drmModeCrtcPtr crtc;
- drmModeFBPtr fb;
- drmModeEncoderPtr encoder;
-
- for (i = 0; i < res->count_connectors; i++) {
- connector = drmModeGetConnector(fd, res->connectors[i]);
-
- if (!connector)
- printf("Could not get connector %i\n", i);
- else {
- printConnector(fd, res, connector, res->connectors[i]);
- drmModeFreeConnector(connector);
- }
- }
-
- for (i = 0; i < res->count_encoders; i++) {
- encoder = drmModeGetEncoder(fd, res->encoders[i]);
-
- if (!encoder)
- printf("Could not get encoder %i\n", i);
- else {
- printEncoder(fd, res, encoder, res->encoders[i]);
- drmModeFreeEncoder(encoder);
- }
- }
-
-
- for (i = 0; i < res->count_crtcs; i++) {
- crtc = drmModeGetCrtc(fd, res->crtcs[i]);
-
- if (!crtc)
- printf("Could not get crtc %i\n", i);
- else {
- printCrtc(fd, res, crtc, res->crtcs[i]);
- drmModeFreeCrtc(crtc);
- }
- }
-
- for (i = 0; i < res->count_fbs; i++) {
- fb = drmModeGetFB(fd, res->fbs[i]);
-
- if (!fb)
- printf("Could not get fb %i\n", res->fbs[i]);
- else {
- printFrameBuffer(fd, res, fb);
- drmModeFreeFB(fb);
- }
- }
-
- return 0;
-}
-
-static struct drm_mode_modeinfo mode = {
- .name = "Test mode",
- .clock = 25200,
- .hdisplay = 640,
- .hsync_start = 656,
- .hsync_end = 752,
- .htotal = 800,
- .hskew = 0,
- .vdisplay = 480,
- .vsync_start = 490,
- .vsync_end = 492,
- .vtotal = 525,
- .vscan = 0,
- .vrefresh = 60000, /* vertical refresh * 1000 */
- .flags = 10,
-};
-
-int testMode(int fd, drmModeResPtr res)
-{
- uint32_t connector = res->connectors[0];
- uint32_t newMode = 0;
- int ret = 0;
- int error = 0;
-
- printf("Test: adding mode to connector %i\n", connector);
-
- /* printMode(&mode); */
-
- printf("\tAttaching mode %i to connector %i\n", newMode, connector);
-
- ret = drmModeAttachMode(fd, connector, &mode);
-
- if (ret)
- goto err_mode;
-
- printf("\tDetaching mode %i from connector %i\n", newMode, connector);
- ret = drmModeDetachMode(fd, connector, &mode);
-
- if (ret)
- goto err_mode;
- return 0;
-
-err_mode:
-
- printf("\tFailed\n");
-
- if (error)
- printf("\tFailed to delete mode %i\n", newMode);
- return 1;
-}
-
-/*
-int testFrameBufferGet(int fd, uint32_t fb)
-{
- drmModeFBPtr frame;
-
- printf("Test: get framebuffer %i\n", fb);
-
- frame = drmModeGetFB(fd, fb);
-
- if (!frame) {
- printf("\tFailed\n");
- } else {
- printFrameBuffer(fd, frame);
- drmModeFreeFB(frame);
- }
-
- return 0;
-}
-*/
-
-int testFrameBufferAdd(int fd, drmModeResPtr res)
-{
- uint32_t fb = 0;
- int ret = 0;
- drmModeFBPtr frame = 0;
- drmBO bo;
-
- printf("Test: adding framebuffer\n");
-
- printf("\tCreating BO\n");
-
- /* TODO */
- ret = drmBOCreate(fd, 800 * 600 * 4, 0, 0,
- DRM_BO_FLAG_READ |
- DRM_BO_FLAG_WRITE |
- DRM_BO_FLAG_MEM_TT |
- DRM_BO_FLAG_MEM_VRAM |
- DRM_BO_FLAG_NO_EVICT,
- DRM_BO_HINT_DONT_FENCE, &bo);
-
- printf("\tgot %i\n", ret);
- if (ret)
- goto err;
-
- printf("\tAdding FB\n");
- ret = drmModeAddFB(fd, 800, 600, 32, 8, 0, bo.handle, &fb);
- if (ret)
- goto err_bo;
-
- frame = drmModeGetFB(fd, fb);
-
- if (!frame) {
- printf("Couldn't retrive created framebuffer\n");
- } else {
- printFrameBuffer(fd, res, frame);
- drmModeFreeFB(frame);
- }
-
- printf("\tRemoveing FB\n");
-
- ret = drmModeRmFB(fd, fb);
-
- if (ret) {
- printf("\tFailed this shouldn't happen!\n");
- goto err_bo;
- }
-
- printf("\tRemoveing BO\n");
-
- ret = drmBOUnreference(fb, &bo);
-
- return 0;
-
-err_bo:
- drmBOUnreference(fd, &bo);
-
-err:
- printf("\tFailed\n");
-
- return 1;
-}
-
-int testDPMS(int fd, drmModeResPtr res)
-{
- int connector_id;
- int i;
-
- for (i = 0; i < res->count_connectors; i++) {
- connector_id = res->connectors[i];
- /* turn connector off */
- drmModeConnectorSetProperty(fd, connector_id, dpms_prop_id, 3);
- sleep(2);
- drmModeConnectorSetProperty(fd, connector_id, dpms_prop_id, 0);
- }
- return 1;
-
-}
-
-int main(int argc, char **argv)
-{
- int fd;
- drmModeResPtr res;
-
- printf("Starting test\n");
-
- fd = drmOpen("i915", NULL);
-
- if (fd < 0) {
- printf("Failed to open the card fb (%d)\n",fd);
- return 1;
- }
-
- res = drmModeGetResources(fd);
- if (res == 0) {
- printf("Failed to get resources from card\n");
- drmClose(fd);
- return 1;
- }
-
- printRes(fd, res);
-
- testMode(fd, res);
-
- testFrameBufferAdd(fd, res);
-
- /* try dpms test */
- testDPMS(fd, res);
- drmModeFreeResources(res);
- printf("Ok\n");
-
- return 0;
-}
diff --git a/tests/mode/test b/tests/mode/test
deleted file mode 100755
index f98e3708..00000000
--- a/tests/mode/test
+++ /dev/null
@@ -1 +0,0 @@
-LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app
diff --git a/tests/mode/Makefile b/tests/modeprint/Makefile
index 7a9c3c24..7a9c3c24 100644
--- a/tests/mode/Makefile
+++ b/tests/modeprint/Makefile
diff --git a/tests/modeprint/modetest.c b/tests/modeprint/modetest.c
new file mode 100644
index 00000000..cefa5262
--- /dev/null
+++ b/tests/modeprint/modetest.c
@@ -0,0 +1,371 @@
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+
+int connectors;
+int full_props;
+int edid;
+int modes;
+int full_modes;
+int encoders;
+int crtcs;
+int fbs;
+char *module_name;
+
+const char* getConnectionText(drmModeConnection conn)
+{
+ switch (conn) {
+ case DRM_MODE_CONNECTED:
+ return "connected";
+ case DRM_MODE_DISCONNECTED:
+ return "disconnected";
+ default:
+ return "unknown";
+ }
+
+}
+
+int printMode(struct drm_mode_modeinfo *mode)
+{
+ if (full_modes) {
+ printf("Mode: %s\n", mode->name);
+ printf("\tclock : %i\n", mode->clock);
+ printf("\thdisplay : %i\n", mode->hdisplay);
+ printf("\thsync_start : %i\n", mode->hsync_start);
+ printf("\thsync_end : %i\n", mode->hsync_end);
+ printf("\thtotal : %i\n", mode->htotal);
+ printf("\thskew : %i\n", mode->hskew);
+ printf("\tvdisplay : %i\n", mode->vdisplay);
+ printf("\tvsync_start : %i\n", mode->vsync_start);
+ printf("\tvsync_end : %i\n", mode->vsync_end);
+ printf("\tvtotal : %i\n", mode->vtotal);
+ printf("\tvscan : %i\n", mode->vscan);
+ printf("\tvrefresh : %i\n", mode->vrefresh);
+ printf("\tflags : %i\n", mode->flags);
+ } else {
+ printf("Mode: \"%s\" %ix%i %.0f\n", mode->name,
+ mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000.0);
+ }
+ return 0;
+}
+
+int printProperty(int fd, drmModeResPtr res, drmModePropertyPtr props, uint64_t value)
+{
+ const unsigned char *name = NULL;
+ int j;
+
+ printf("Property: %s\n", props->name);
+ printf("\tid : %i\n", props->prop_id);
+ printf("\tflags : %i\n", props->flags);
+ printf("\tcount_values : %d\n", props->count_values);
+
+
+ if (props->count_values) {
+ printf("\tvalues :");
+ for (j = 0; j < props->count_values; j++)
+ printf(" %lld", props->values[j]);
+ printf("\n");
+ }
+
+
+ printf("\tcount_enums : %d\n", props->count_enums);
+
+ if (props->flags & DRM_MODE_PROP_BLOB) {
+ drmModePropertyBlobPtr blob;
+
+ blob = drmModeGetPropertyBlob(fd, value);
+ if (blob) {
+ printf("blob is %d length, %08X\n", blob->length, *(uint32_t *)blob->data);
+ drmModeFreePropertyBlob(blob);
+ } else {
+ printf("error getting blob %lld\n", value);
+ }
+
+ } else {
+ if (!strncmp(props->name, "DPMS", 4))
+ ;
+
+ for (j = 0; j < props->count_enums; j++) {
+ printf("\t\t%lld = %s\n", props->enums[j].value, props->enums[j].name);
+ if (props->enums[j].value == value)
+ name = props->enums[j].name;
+ }
+
+ if (props->count_enums && name) {
+ printf("\tcon_value : %s\n", name);
+ } else {
+ printf("\tcon_value : %lld\n", value);
+ }
+ }
+
+ return 0;
+}
+
+int printConnector(int fd, drmModeResPtr res, drmModeConnectorPtr connector, uint32_t id)
+{
+ int i = 0;
+ struct drm_mode_modeinfo *mode = NULL;
+ drmModePropertyPtr props;
+
+ printf("Connector: %d-%d\n", connector->connector_type, connector->connector_type_id);
+ printf("\tid : %i\n", id);
+ printf("\tencoder id : %i\n", connector->encoder_id);
+ printf("\tconn : %s\n", getConnectionText(connector->connection));
+ printf("\tsize : %ix%i (mm)\n", connector->mmWidth, connector->mmHeight);
+ printf("\tcount_modes : %i\n", connector->count_modes);
+ printf("\tcount_props : %i\n", connector->count_props);
+ if (connector->count_props) {
+ printf("\tprops :");
+ for (i = 0; i < connector->count_props; i++)
+ printf(" %i", connector->props[i]);
+ printf("\n");
+ }
+
+ printf("\tcount_encoders : %i\n", connector->count_encoders);
+ if (connector->count_encoders) {
+ printf("\tencoders :");
+ for (i = 0; i < connector->count_encoders; i++)
+ printf(" %i", connector->encoders[i]);
+ printf("\n");
+ }
+
+ if (modes) {
+ for (i = 0; i < connector->count_modes; i++) {
+ mode = &connector->modes[i];
+ printMode(mode);
+ }
+ }
+
+ if (full_props) {
+ for (i = 0; i < connector->count_props; i++) {
+ props = drmModeGetProperty(fd, connector->props[i]);
+ if (props) {
+ printProperty(fd, res, props, connector->prop_values[i]);
+ drmModeFreeProperty(props);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int printEncoder(int fd, drmModeResPtr res, drmModeEncoderPtr encoder, uint32_t id)
+{
+ printf("Encoder\n");
+ printf("\tid :%i\n", id);
+ printf("\tcrtc_id :%d\n", encoder->crtc_id);
+ printf("\ttype :%d\n", encoder->encoder_type);
+ printf("\tpossible_crtcs :%d\n", encoder->possible_crtcs);
+ printf("\tpossible_clones :%d\n", encoder->possible_clones);
+ return 0;
+}
+
+int printCrtc(int fd, drmModeResPtr res, drmModeCrtcPtr crtc, uint32_t id)
+{
+ printf("Crtc\n");
+ printf("\tid : %i\n", id);
+ printf("\tx : %i\n", crtc->x);
+ printf("\ty : %i\n", crtc->y);
+ printf("\twidth : %i\n", crtc->width);
+ printf("\theight : %i\n", crtc->height);
+ printf("\tmode : %p\n", &crtc->mode);
+ printf("\tgamma size : %d\n", crtc->gamma_size);
+
+ return 0;
+}
+
+int printFrameBuffer(int fd, drmModeResPtr res, drmModeFBPtr fb)
+{
+ printf("Framebuffer\n");
+ printf("\thandle : %i\n", fb->handle);
+ printf("\twidth : %i\n", fb->width);
+ printf("\theight : %i\n", fb->height);
+ printf("\tpitch : %i\n", fb->pitch);;
+ printf("\tbpp : %i\n", fb->bpp);
+ printf("\tdepth : %i\n", fb->depth);
+ printf("\tbuffer_id : %i\n", fb->buffer_id);
+
+ return 0;
+}
+
+int printRes(int fd, drmModeResPtr res)
+{
+ int i;
+ drmModeFBPtr fb;
+ drmModeCrtcPtr crtc;
+ drmModeEncoderPtr encoder;
+ drmModeConnectorPtr connector;
+
+ printf("Resources\n\n");
+
+ printf("count_connectors : %i\n", res->count_connectors);
+ printf("count_encoders : %i\n", res->count_encoders);
+ printf("count_crtcs : %i\n", res->count_crtcs);
+ printf("count_fbs : %i\n", res->count_fbs);
+
+ printf("\n");
+
+ if (connectors) {
+ for (i = 0; i < res->count_connectors; i++) {
+ connector = drmModeGetConnector(fd, res->connectors[i]);
+
+ if (!connector)
+ printf("Could not get connector %i\n", res->connectors[i]);
+ else {
+ printConnector(fd, res, connector, res->connectors[i]);
+ drmModeFreeConnector(connector);
+ }
+ }
+ printf("\n");
+ }
+
+
+ if (encoders) {
+ for (i = 0; i < res->count_encoders; i++) {
+ encoder = drmModeGetEncoder(fd, res->encoders[i]);
+
+ if (!encoder)
+ printf("Could not get encoder %i\n", res->encoders[i]);
+ else {
+ printEncoder(fd, res, encoder, res->encoders[i]);
+ drmModeFreeEncoder(encoder);
+ }
+ }
+ printf("\n");
+ }
+
+ if (crtcs) {
+ for (i = 0; i < res->count_crtcs; i++) {
+ crtc = drmModeGetCrtc(fd, res->crtcs[i]);
+
+ if (!crtc)
+ printf("Could not get crtc %i\n", res->crtcs[i]);
+ else {
+ printCrtc(fd, res, crtc, res->crtcs[i]);
+ drmModeFreeCrtc(crtc);
+ }
+ }
+ printf("\n");
+ }
+
+ if (fbs) {
+ for (i = 0; i < res->count_fbs; i++) {
+ fb = drmModeGetFB(fd, res->fbs[i]);
+
+ if (!fb)
+ printf("Could not get fb %i\n", res->fbs[i]);
+ else {
+ printFrameBuffer(fd, res, fb);
+ drmModeFreeFB(fb);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void args(int argc, char **argv)
+{
+ int i;
+
+ fbs = 0;
+ edid = 0;
+ crtcs = 0;
+ modes = 0;
+ encoders = 0;
+ full_modes = 0;
+ full_props = 0;
+ connectors = 0;
+
+ module_name = argv[1];
+
+ for (i = 2; i < argc; i++) {
+ if (strcmp(argv[i], "-fb") == 0) {
+ fbs = 1;
+ } else if (strcmp(argv[i], "-crtcs") == 0) {
+ crtcs = 1;
+ } else if (strcmp(argv[i], "-cons") == 0) {
+ connectors = 1;
+ modes = 1;
+ } else if (strcmp(argv[i], "-modes") == 0) {
+ connectors = 1;
+ modes = 1;
+ } else if (strcmp(argv[i], "-full") == 0) {
+ connectors = 1;
+ modes = 1;
+ full_modes = 1;
+ } else if (strcmp(argv[i], "-props") == 0) {
+ connectors = 1;
+ full_props = 1;
+ } else if (strcmp(argv[i], "-edids") == 0) {
+ connectors = 1;
+ edid = 1;
+ } else if (strcmp(argv[i], "-encoders") == 0) {
+ encoders = 1;
+ } else if (strcmp(argv[i], "-v") == 0) {
+ fbs = 1;
+ edid = 1;
+ crtcs = 1;
+ modes = 1;
+ encoders = 1;
+ full_modes = 1;
+ full_props = 1;
+ connectors = 1;
+ }
+ }
+
+ if (argc == 2) {
+ fbs = 1;
+ edid = 1;
+ crtcs = 1;
+ modes = 1;
+ encoders = 1;
+ full_modes = 0;
+ full_props = 0;
+ connectors = 1;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+ drmModeResPtr res;
+
+ if (argc == 1) {
+ printf("Please add modulename as first argument\n");
+ return 1;
+ }
+
+ args(argc, argv);
+
+ printf("Starting test\n");
+
+ fd = drmOpen(module_name, NULL);
+
+ if (fd < 0) {
+ printf("Failed to open the card fd (%d)\n",fd);
+ return 1;
+ }
+
+ res = drmModeGetResources(fd);
+ if (res == 0) {
+ printf("Failed to get resources from card\n");
+ drmClose(fd);
+ return 1;
+ }
+
+ printRes(fd, res);
+
+ drmModeFreeResources(res);
+
+ printf("Ok\n");
+
+ return 0;
+}
diff --git a/tests/modeprint/test b/tests/modeprint/test
new file mode 100755
index 00000000..bd1952cc
--- /dev/null
+++ b/tests/modeprint/test
@@ -0,0 +1 @@
+LD_PRELOAD=../../libdrm/.libs/libdrm.so ./app $@