aboutsummaryrefslogtreecommitdiff
path: root/linux-core/drm_crtc.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@linux.ie>2007-04-05 11:21:06 +1000
committerDave Airlie <airlied@linux.ie>2007-04-05 11:21:06 +1000
commit52f9028c84baea81230dc673b756552e8e90aecd (patch)
tree0a08cc634a6202c3e110feef802344d3a39b1e3c /linux-core/drm_crtc.c
parent8fe8793a0fdf4e6082a0f0b0fc4650f171737356 (diff)
Initial import of modesetting for intel driver in DRM
Diffstat (limited to 'linux-core/drm_crtc.c')
-rw-r--r--linux-core/drm_crtc.c540
1 files changed, 540 insertions, 0 deletions
diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c
new file mode 100644
index 00000000..a52d82bc
--- /dev/null
+++ b/linux-core/drm_crtc.c
@@ -0,0 +1,540 @@
+#include <linux/list.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+
+struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev)
+{
+ struct drm_framebuffer *fb;
+
+ spin_lock(&dev->crtc_config.config_lock);
+ /* Limit to single framebuffer for now */
+ if (dev->crtc_config.num_fb > 1) {
+ DRM_ERROR("Attempt to add multiple framebuffers failed\n");
+ return NULL;
+ }
+ spin_unlock(&dev->crtc_config.config_lock);
+
+ fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL);
+ if (!fb) {
+
+ return NULL;
+ }
+
+ fb->dev = dev;
+ spin_lock(&dev->crtc_config.config_lock);
+ dev->crtc_config.num_fb++;
+ list_add(&fb->head, &dev->crtc_config.fb_list);
+ spin_unlock(&dev->crtc_config.config_lock);
+
+ return fb;
+}
+
+void drm_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ drm_device_t *dev = fb->dev;
+
+ spin_lock(&dev->crtc_config.config_lock);
+ list_del(&fb->head);
+ dev->crtc_config.num_fb--;
+ spin_unlock(&dev->crtc_config.config_lock);
+
+ kfree(fb);
+}
+
+struct drm_crtc *drm_crtc_create(drm_device_t *dev,
+ const struct drm_crtc_funcs *funcs)
+{
+ struct drm_crtc *crtc = NULL;
+ crtc = kmalloc(sizeof(struct drm_crtc), GFP_KERNEL);
+ if (!crtc)
+ return NULL;
+
+ crtc->dev = dev;
+ crtc->funcs = funcs;
+
+ spin_lock(&dev->crtc_config.config_lock);
+
+ list_add_tail(&crtc->head, &dev->crtc_config.crtc_list);
+ dev->crtc_config.num_crtc++;
+
+ spin_unlock(&dev->crtc_config.config_lock);
+
+ return crtc;
+}
+EXPORT_SYMBOL(drm_crtc_create);
+
+void drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_device_t *dev = crtc->dev;
+
+ if (crtc->funcs->cleanup)
+ (*crtc->funcs->cleanup)(crtc);
+
+ spin_lock(&dev->crtc_config.config_lock);
+ list_del(&crtc->head);
+ dev->crtc_config.num_crtc--;
+ spin_unlock(&dev->crtc_config.config_lock);
+ kfree(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_destroy);
+
+bool drm_crtc_in_use(struct drm_crtc *crtc)
+{
+ struct drm_output *output;
+ drm_device_t *dev = crtc->dev;
+ list_for_each_entry(output, &dev->crtc_config.output_list, head)
+ if (output->crtc == crtc)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_crtc_in_use);
+
+void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY)
+{
+ struct drm_output *output;
+ struct drm_display_mode *mode, *t;
+ int ret;
+ //if (maxX == 0 || maxY == 0)
+ // TODO
+
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+
+ list_for_each_entry_safe(mode, t, &output->modes, head)
+ drm_mode_remove(output, mode);
+
+ output->status = (*output->funcs->detect)(output);
+
+ if (output->status == output_status_disconnected) {
+ /* TODO set EDID to NULL */
+ continue;
+ }
+
+ ret = (*output->funcs->get_modes)(output);
+
+ if (ret) {
+ /* move the modes over to the main mode list */
+ drm_mode_list_concat(&output->probed_modes,
+ &output->modes);
+ }
+
+ if (maxX && maxY)
+ drm_mode_validate_size(dev, &output->modes, maxX,
+ maxY, 0);
+ list_for_each_entry_safe(mode, t, &output->modes, head) {
+ if (mode->status == MODE_OK)
+ mode->status = (*output->funcs->mode_valid)(output,mode);
+ }
+
+
+ drm_mode_prune_invalid(dev, &output->modes, TRUE);
+
+ if (list_empty(&output->modes))
+ continue;
+
+ drm_mode_sort(&output->modes);
+
+ DRM_DEBUG("Probed modes for %s\n", output->name);
+ list_for_each_entry_safe(mode, t, &output->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_debug_printmodeline(dev, mode);
+ }
+ }
+}
+
+bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ int x, int y)
+{
+ drm_device_t *dev = crtc->dev;
+ struct drm_display_mode *adjusted_mode, saved_mode;
+ int saved_x, saved_y;
+ bool didLock = false;
+ bool ret = false;
+ struct drm_output *output;
+
+ adjusted_mode = drm_mode_duplicate(mode);
+
+ crtc->enabled = drm_crtc_in_use(crtc);
+
+ if (!crtc->enabled) {
+ return true;
+ }
+
+ didLock = crtc->funcs->lock(crtc);
+
+ saved_mode = crtc->mode;
+ saved_x = crtc->x;
+ saved_y = crtc->y;
+
+ /* Update crtc values up front so the driver can rely on them for mode
+ * setting.
+ */
+ crtc->mode = *mode;
+ crtc->x = x;
+ crtc->y = y;
+
+ /* XXX short-circuit changes to base location only */
+
+ /* Pass our mode to the outputs and the CRTC to give them a chance to
+ * adjust it according to limitations or output properties, and also
+ * a chance to reject the mode entirely.
+ */
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+
+ if (output->crtc != crtc)
+ continue;
+
+ if (!output->funcs->mode_fixup(output, mode, adjusted_mode)) {
+ goto done;
+ }
+ }
+
+ if (!crtc->funcs->mode_fixup(crtc, mode, adjusted_mode)) {
+ goto done;
+ }
+
+ /* Prepare the outputs and CRTCs before setting the mode. */
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+
+ if (output->crtc != crtc)
+ continue;
+
+ /* Disable the output as the first thing we do. */
+ output->funcs->prepare(output);
+ }
+
+ crtc->funcs->prepare(crtc);
+
+ /* Set up the DPLL and any output state that needs to adjust or depend
+ * on the DPLL.
+ */
+ crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y);
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+ if (output->crtc == crtc)
+ output->funcs->mode_set(output, mode, adjusted_mode);
+ }
+
+ /* Now, enable the clocks, plane, pipe, and outputs that we set up. */
+ crtc->funcs->commit(crtc);
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+ if (output->crtc == crtc)
+ {
+ output->funcs->commit(output);
+#if 0 // TODO def RANDR_12_INTERFACE
+ if (output->randr_output)
+ RRPostPendingProperties (output->randr_output);
+#endif
+ }
+ }
+
+ /* XXX free adjustedmode */
+ ret = TRUE;
+ /* TODO */
+// if (scrn->pScreen)
+// drm_crtc_set_screen_sub_pixel_order(dev);
+
+done:
+ if (!ret) {
+ crtc->x = saved_x;
+ crtc->y = saved_y;
+ crtc->mode = saved_mode;
+ }
+
+ if (didLock)
+ crtc->funcs->unlock (crtc);
+
+ return ret;
+}
+
+bool drm_set_desired_modes(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_output *output, *list_output;
+
+ list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) {
+ output = NULL;
+
+ list_for_each_entry(list_output, &dev->crtc_config.output_list, head) {
+ if (list_output->crtc == crtc) {
+ output = list_output;
+ break;
+ }
+ }
+ /* Skip disabled crtcs */
+ if (!output)
+ continue;
+
+ memset(&crtc->mode, 0, sizeof(crtc->mode));
+ if (!crtc->desired_mode.crtc_hdisplay) {
+
+ }
+ if (!drm_crtc_set_mode(crtc, &crtc->desired_mode,
+ crtc->desired_x, crtc->desired_y))
+ return false;
+ }
+
+ drm_disable_unused_functions(dev);
+ return true;
+}
+EXPORT_SYMBOL(drm_set_desired_modes);
+
+void drm_disable_unused_functions(struct drm_device *dev)
+{
+ struct drm_output *output;
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+ if (!output->crtc)
+ (*output->funcs->dpms)(output, DPMSModeOff);
+ }
+
+ list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) {
+ if (!crtc->enabled)
+ crtc->funcs->dpms(crtc, DPMSModeOff);
+ }
+}
+
+/**
+ * drm_mode_probed_add - add a mode to the specified output's probed mode list
+ * @output: output the new mode
+ * @mode: mode data
+ *
+ * Add @mode to @output's mode list for later use.
+ */
+void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode)
+{
+ printk(KERN_ERR "adding DDC mode %s to output %s\n", mode->name,
+ output->name);
+ spin_lock(&output->modes_lock);
+ list_add(&mode->head, &output->probed_modes);
+ spin_unlock(&output->modes_lock);
+}
+EXPORT_SYMBOL(drm_mode_probed_add);
+
+/**
+ * drm_mode_remove - remove and free a mode
+ * @output: output list to modify
+ * @mode: mode to remove
+ *
+ * Remove @mode from @output's mode list, then free it.
+ */
+void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode)
+{
+ spin_lock(&output->modes_lock);
+ list_del(&mode->head);
+ spin_unlock(&output->modes_lock);
+ kfree(mode);
+}
+EXPORT_SYMBOL(drm_mode_remove);
+
+/*
+ * Probably belongs in the DRM device structure
+ */
+struct drm_output *drm_output_create(drm_device_t *dev,
+ const struct drm_output_funcs *funcs,
+ const char *name)
+{
+ struct drm_output *output = NULL;
+
+ output = kmalloc(sizeof(struct drm_output), GFP_KERNEL);
+ if (!output)
+ return NULL;
+
+ output->dev = dev;
+ output->funcs = funcs;
+ if (name)
+ strncpy(output->name, name, DRM_OUTPUT_LEN);
+ output->name[DRM_OUTPUT_LEN - 1] = 0;
+ output->subpixel_order = SubPixelUnknown;
+ INIT_LIST_HEAD(&output->probed_modes);
+ INIT_LIST_HEAD(&output->modes);
+ spin_lock_init(&output->modes_lock);
+ /* randr_output? */
+ /* output_set_monitor(output)? */
+ /* check for output_ignored(output)? */
+
+ spin_lock(&dev->crtc_config.config_lock);
+ list_add_tail(&output->head, &dev->crtc_config.output_list);
+ dev->crtc_config.num_output++;
+
+ spin_unlock(&dev->crtc_config.config_lock);
+
+ return output;
+
+}
+EXPORT_SYMBOL(drm_output_create);
+
+void drm_output_destroy(struct drm_output *output)
+{
+ struct drm_device *dev = output->dev;
+ struct drm_display_mode *mode, *t;
+
+ if (*output->funcs->cleanup)
+ (*output->funcs->cleanup)(output);
+
+ list_for_each_entry_safe(mode, t, &output->probed_modes, head)
+ drm_mode_remove(output, mode);
+
+ list_for_each_entry_safe(mode, t, &output->modes, head)
+ drm_mode_remove(output, mode);
+
+ spin_lock(&dev->crtc_config.config_lock);
+ list_del(&output->head);
+ spin_unlock(&dev->crtc_config.config_lock);
+ kfree(output);
+}
+EXPORT_SYMBOL(drm_output_destroy);
+
+bool drm_output_rename(struct drm_output *output, const char *name)
+{
+ if (!name)
+ return false;
+
+ strncpy(output->name, name, DRM_OUTPUT_LEN);
+ output->name[DRM_OUTPUT_LEN - 1] = 0;
+// drm_output_set_monitor(output);
+// if (drm_output_ignored(output))
+// return FALSE;
+ return TRUE;
+}
+EXPORT_SYMBOL(drm_output_rename);
+
+void drm_crtc_config_init(drm_device_t *dev)
+{
+ spin_lock_init(&dev->crtc_config.config_lock);
+ INIT_LIST_HEAD(&dev->crtc_config.fb_list);
+ INIT_LIST_HEAD(&dev->crtc_config.crtc_list);
+ INIT_LIST_HEAD(&dev->crtc_config.output_list);
+}
+EXPORT_SYMBOL(drm_crtc_config_init);
+
+void drm_framebuffer_set_object(drm_device_t *dev, unsigned long handle)
+{
+ struct drm_framebuffer *fb;
+ drm_user_object_t *uo;
+ drm_hash_item_t *hash;
+ drm_buffer_object_t *bo;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_ht_find_item(&dev->object_hash, handle, &hash);
+ if (ret) {
+ DRM_ERROR("Couldn't find handle.\n");
+ goto out_err;
+ }
+
+ uo = drm_hash_entry(hash, drm_user_object_t, hash);
+ if (uo->type != drm_buffer_type) {
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ bo = drm_user_object_entry(uo, drm_buffer_object_t, base);
+
+ /* get the first fb */
+ list_for_each_entry(fb, &dev->crtc_config.fb_list, head) {
+ fb->offset = bo->offset;
+ break;
+ }
+ ret = 0;
+out_err:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(drm_framebuffer_set_object);
+
+bool drm_initial_config(drm_device_t *dev, bool can_grow)
+{
+ /* do a hardcoded initial configuration here */
+ struct drm_crtc *crtc, *vga_crtc = NULL, *dvi_crtc = NULL;
+ struct drm_framebuffer *fb;
+ struct drm_output *output, *use_output = NULL;
+
+ fb = drm_framebuffer_create(dev);
+ if (!fb)
+ return false;
+
+ fb->pitch = 1024;
+ fb->width = 1024;
+ fb->height = 768;
+ fb->depth = 24;
+ fb->bits_per_pixel = 32;
+
+ /* bind both CRTCs to this fb */
+ /* only initialise one crtc to enabled state */
+ list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) {
+ crtc->fb = fb;
+ if (!vga_crtc) {
+ vga_crtc = crtc;
+ crtc->enabled = 1;
+ crtc->desired_x = 0;
+ crtc->desired_y = 0;
+ }
+#if 0
+ else if (!dvi_crtc) {
+ dvi_crtc = crtc;
+ crtc->enabled = 1;
+ crtc->desired_x = 0;
+ crtc->desired_y = 0;
+ }
+#endif
+ }
+
+ drm_crtc_probe_output_modes(dev, 1024, 768);
+
+ /* hard bind the CRTCS */
+
+ /* bind analog output to one crtc */
+ list_for_each_entry(output, &dev->crtc_config.output_list, head) {
+ struct drm_display_mode *des_mode;
+
+ if (strncmp(output->name, "VGA", 3)) {
+ output->crtc = vga_crtc;
+ /* just pull the first mode out of that hat */
+ list_for_each_entry(des_mode, &output->modes, head)
+ break;
+ DRM_DEBUG("Setting desired mode for output %s\n", output->name);
+ drm_mode_debug_printmodeline(dev, des_mode);
+ output->crtc->desired_mode = *des_mode;
+ output->initial_x = 0;
+ output->initial_y = 0;
+ use_output = output;
+ } else if (strncmp(output->name, "TMDS", 4)) {
+ output->crtc = vga_crtc;
+#if 0
+ /* just pull the first mode out of that hat */
+ list_for_each_entry(des_mode, &output->modes, head)
+ break;
+ DRM_DEBUG("Setting desired mode for output %s\n", output->name);
+ drm_mode_debug_printmodeline(dev, des_mode);
+ output->crtc->desired_mode = *des_mode;
+#endif
+ output->initial_x = 0;
+ output->initial_y = 0;
+ } else
+ output->crtc = NULL;
+
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(drm_initial_config);
+
+void drm_crtc_config_cleanup(drm_device_t *dev)
+{
+ struct drm_output *output, *ot;
+ struct drm_crtc *crtc, *ct;
+
+ list_for_each_entry_safe(output, ot, &dev->crtc_config.output_list, head) {
+ drm_output_destroy(output);
+ }
+
+
+ list_for_each_entry_safe(crtc, ct, &dev->crtc_config.crtc_list, head) {
+ drm_crtc_destroy(crtc);
+ }
+}
+EXPORT_SYMBOL(drm_crtc_config_cleanup);
+