From 8fe8793a0fdf4e6082a0f0b0fc4650f171737356 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 11:20:23 +1000 Subject: borrow edid.h from fb directory --- linux-core/edid.h | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 linux-core/edid.h (limited to 'linux-core') diff --git a/linux-core/edid.h b/linux-core/edid.h new file mode 100644 index 00000000..bd89fb3b --- /dev/null +++ b/linux-core/edid.h @@ -0,0 +1,138 @@ +/* + * drivers/video/edid.h - EDID/DDC Header + * + * Based on: + * 1. XFree86 4.3.0, edid.h + * Copyright 1998 by Egbert Eich + * + * 2. John Fremlin and + * Ani Joshi + * + * DDC is a Trademark of VESA (Video Electronics Standard Association). + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. +*/ + +#ifndef __EDID_H__ +#define __EDID_H__ + +#define EDID_LENGTH 0x80 +#define EDID_HEADER 0x00 +#define EDID_HEADER_END 0x07 + +#define ID_MANUFACTURER_NAME 0x08 +#define ID_MANUFACTURER_NAME_END 0x09 +#define ID_MODEL 0x0a + +#define ID_SERIAL_NUMBER 0x0c + +#define MANUFACTURE_WEEK 0x10 +#define MANUFACTURE_YEAR 0x11 + +#define EDID_STRUCT_VERSION 0x12 +#define EDID_STRUCT_REVISION 0x13 + +#define EDID_STRUCT_DISPLAY 0x14 + +#define DPMS_FLAGS 0x18 +#define ESTABLISHED_TIMING_1 0x23 +#define ESTABLISHED_TIMING_2 0x24 +#define MANUFACTURERS_TIMINGS 0x25 + +/* standard timings supported */ +#define STD_TIMING 8 +#define STD_TIMING_DESCRIPTION_SIZE 2 +#define STD_TIMING_DESCRIPTIONS_START 0x26 + +#define DETAILED_TIMING_DESCRIPTIONS_START 0x36 +#define DETAILED_TIMING_DESCRIPTION_SIZE 18 +#define NO_DETAILED_TIMING_DESCRIPTIONS 4 + +#define DETAILED_TIMING_DESCRIPTION_1 0x36 +#define DETAILED_TIMING_DESCRIPTION_2 0x48 +#define DETAILED_TIMING_DESCRIPTION_3 0x5a +#define DETAILED_TIMING_DESCRIPTION_4 0x6c + +#define DESCRIPTOR_DATA 5 + +#define UPPER_NIBBLE( x ) \ + (((128|64|32|16) & (x)) >> 4) + +#define LOWER_NIBBLE( x ) \ + ((1|2|4|8) & (x)) + +#define COMBINE_HI_8LO( hi, lo ) \ + ( (((unsigned)hi) << 8) | (unsigned)lo ) + +#define COMBINE_HI_4LO( hi, lo ) \ + ( (((unsigned)hi) << 4) | (unsigned)lo ) + +#define PIXEL_CLOCK_LO (unsigned)block[ 0 ] +#define PIXEL_CLOCK_HI (unsigned)block[ 1 ] +#define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*10000) +#define H_ACTIVE_LO (unsigned)block[ 2 ] +#define H_BLANKING_LO (unsigned)block[ 3 ] +#define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 4 ] ) +#define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO ) +#define H_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 4 ] ) +#define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO ) + +#define V_ACTIVE_LO (unsigned)block[ 5 ] +#define V_BLANKING_LO (unsigned)block[ 6 ] +#define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 7 ] ) +#define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO ) +#define V_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 7 ] ) +#define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO ) + +#define H_SYNC_OFFSET_LO (unsigned)block[ 8 ] +#define H_SYNC_WIDTH_LO (unsigned)block[ 9 ] + +#define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)block[ 10 ] ) +#define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)block[ 10 ] ) + +#define V_SYNC_WIDTH_HI ((unsigned)block[ 11 ] & (1|2)) +#define V_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (4|8)) >> 2) + +#define H_SYNC_WIDTH_HI (((unsigned)block[ 11 ] & (16|32)) >> 4) +#define H_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (64|128)) >> 6) + +#define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO ) +#define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO ) + +#define H_SYNC_WIDTH COMBINE_HI_4LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO ) +#define H_SYNC_OFFSET COMBINE_HI_4LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO ) + +#define H_SIZE_LO (unsigned)block[ 12 ] +#define V_SIZE_LO (unsigned)block[ 13 ] + +#define H_SIZE_HI UPPER_NIBBLE( (unsigned)block[ 14 ] ) +#define V_SIZE_HI LOWER_NIBBLE( (unsigned)block[ 14 ] ) + +#define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO ) +#define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO ) + +#define H_BORDER (unsigned)block[ 15 ] +#define V_BORDER (unsigned)block[ 16 ] + +#define FLAGS (unsigned)block[ 17 ] + +#define INTERLACED (FLAGS&128) +#define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */ +#define SYNC_SEPARATE (3<<3) +#define HSYNC_POSITIVE (FLAGS & 4) +#define VSYNC_POSITIVE (FLAGS & 2) + +#define V_MIN_RATE block[ 5 ] +#define V_MAX_RATE block[ 6 ] +#define H_MIN_RATE block[ 7 ] +#define H_MAX_RATE block[ 8 ] +#define MAX_PIXEL_CLOCK (((int)block[ 9 ]) * 10) +#define GTF_SUPPORT block[10] + +#define DPMS_ACTIVE_OFF (1 << 5) +#define DPMS_SUSPEND (1 << 6) +#define DPMS_STANDBY (1 << 7) + +#endif /* __EDID_H__ */ -- cgit v1.2.3 From 52f9028c84baea81230dc673b756552e8e90aecd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 11:21:06 +1000 Subject: Initial import of modesetting for intel driver in DRM --- linux-core/Makefile.kernel | 6 +- linux-core/drmP.h | 5 + linux-core/drm_crtc.c | 540 +++++++++++++++++++++ linux-core/drm_crtc.h | 428 +++++++++++++++++ linux-core/drm_edid.c | 515 ++++++++++++++++++++ linux-core/drm_modes.c | 304 ++++++++++++ linux-core/i915_drv.c | 1 + linux-core/intel_crt.c | 226 +++++++++ linux-core/intel_display.c | 1087 ++++++++++++++++++++++++++++++++++++++++++ linux-core/intel_drv.h | 69 +++ linux-core/intel_i2c.c | 164 +++++++ linux-core/intel_lvds.c | 108 +++++ linux-core/intel_modes.c | 49 ++ linux-core/intel_sdvo.c | 999 ++++++++++++++++++++++++++++++++++++++ linux-core/intel_sdvo_regs.h | 302 ++++++++++++ 15 files changed, 4801 insertions(+), 2 deletions(-) create mode 100644 linux-core/drm_crtc.c create mode 100644 linux-core/drm_crtc.h create mode 100644 linux-core/drm_edid.c create mode 100644 linux-core/drm_modes.c create mode 100644 linux-core/intel_crt.c create mode 100644 linux-core/intel_display.c create mode 100644 linux-core/intel_drv.h create mode 100644 linux-core/intel_i2c.c create mode 100644 linux-core/intel_lvds.c create mode 100644 linux-core/intel_modes.c create mode 100644 linux-core/intel_sdvo.c create mode 100644 linux-core/intel_sdvo_regs.h (limited to 'linux-core') diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 6f5b021b..ac403f64 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -13,13 +13,15 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_sysfs.o drm_pci.o drm_agpsupport.o drm_scatter.o \ drm_memory_debug.o ati_pcigart.o drm_sman.o \ drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \ - drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o + drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \ + drm_edid.o drm_modes.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o i810-objs := i810_drv.o i810_dma.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \ - i915_buffer.o + i915_buffer.o intel_display.o intel_crt.o intel_lvds.o \ + intel_sdvo.o intel_modes.o intel_i2c.o nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nouveau_object.o nouveau_irq.o \ nv04_timer.o \ diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 648e29bc..db62ab83 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -164,6 +164,8 @@ #include "drm_compat.h" +#include "drm_crtc.h" + /***********************************************************************/ /** \name Macros to make printk easier */ /*@{*/ @@ -827,6 +829,9 @@ typedef struct drm_device { unsigned int drw_info_length; drm_drawable_info_t **drw_info; /*@} */ + + /* DRM mode setting */ + struct drm_crtc_config crtc_config; } drm_device_t; #if __OS_HAS_AGP 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 +#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); + diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h new file mode 100644 index 00000000..1be115d1 --- /dev/null +++ b/linux-core/drm_crtc.h @@ -0,0 +1,428 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007 Intel Corporation + * Jesse Barnes + */ +#ifndef __DRM_CRTC_H__ +#define __DRM_CRTC_H__ + +#include +#include +#include +#include "drmP.h" +#include "drm.h" + +struct drm_device; + +/* + * Note on terminology: here, for brevity and convenience, we refer to output + * control chips as 'CRTCs'. They can control any type of output, VGA, LVDS, + * DVI, etc. And 'screen' refers to the whole of the visible display, which + * may span multiple monitors (and therefore multiple CRTC and output + * structures). + */ + +enum drm_mode_status { + MODE_OK = 0, /* Mode OK */ + MODE_HSYNC, /* hsync out of range */ + MODE_VSYNC, /* vsync out of range */ + MODE_H_ILLEGAL, /* mode has illegal horizontal timings */ + MODE_V_ILLEGAL, /* mode has illegal horizontal timings */ + MODE_BAD_WIDTH, /* requires an unsupported linepitch */ + MODE_NOMODE, /* no mode with a maching name */ + MODE_NO_INTERLACE, /* interlaced mode not supported */ + MODE_NO_DBLESCAN, /* doublescan mode not supported */ + MODE_NO_VSCAN, /* multiscan mode not supported */ + MODE_MEM, /* insufficient video memory */ + MODE_VIRTUAL_X, /* mode width too large for specified virtual size */ + MODE_VIRTUAL_Y, /* mode height too large for specified virtual size */ + MODE_MEM_VIRT, /* insufficient video memory given virtual size */ + MODE_NOCLOCK, /* no fixed clock available */ + MODE_CLOCK_HIGH, /* clock required is too high */ + MODE_CLOCK_LOW, /* clock required is too low */ + MODE_CLOCK_RANGE, /* clock/mode isn't in a ClockRange */ + MODE_BAD_HVALUE, /* horizontal timing was out of range */ + MODE_BAD_VVALUE, /* vertical timing was out of range */ + MODE_BAD_VSCAN, /* VScan value out of range */ + MODE_HSYNC_NARROW, /* horizontal sync too narrow */ + MODE_HSYNC_WIDE, /* horizontal sync too wide */ + MODE_HBLANK_NARROW, /* horizontal blanking too narrow */ + MODE_HBLANK_WIDE, /* horizontal blanking too wide */ + MODE_VSYNC_NARROW, /* vertical sync too narrow */ + MODE_VSYNC_WIDE, /* vertical sync too wide */ + MODE_VBLANK_NARROW, /* vertical blanking too narrow */ + MODE_VBLANK_WIDE, /* vertical blanking too wide */ + MODE_PANEL, /* exceeds panel dimensions */ + MODE_INTERLACE_WIDTH, /* width too large for interlaced mode */ + MODE_ONE_WIDTH, /* only one width is supported */ + MODE_ONE_HEIGHT, /* only one height is supported */ + MODE_ONE_SIZE, /* only one resolution is supported */ + MODE_NO_REDUCED, /* monitor doesn't accept reduced blanking */ + MODE_BAD = -2, /* unspecified reason */ + MODE_ERROR = -1 /* error condition */ +}; + +#define DRM_MODE_TYPE_BUILTIN (1<<0) +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +#define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \ + DRM_MODE_TYPE_CRTC_C) + +#define DRM_DISPLAY_MODE_LEN 32 + +#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ + .name = nm, .status = 0, .type = (t), .clock = (c), \ + .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ + .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ + .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ + .vscan = (vs), .flags = (f) + +struct drm_display_mode { + /* Header */ + struct list_head head; + char name[DRM_DISPLAY_MODE_LEN]; + enum drm_mode_status status; + int type; + + /* Proposed mode values */ + int clock; + int hdisplay; + int hsync_start; + int hsync_end; + int htotal; + int hskew; + int vdisplay; + int vsync_start; + int vsync_end; + int vtotal; + int vscan; + unsigned int flags; + + /* Actual mode we give to hw */ + int clock_index; + int synth_clock; + int crtc_hdisplay; + int crtc_hblank_start; + int crtc_hblank_end; + int crtc_hsync_start; + int crtc_hsync_end; + int crtc_htotal; + int crtc_hskew; + int crtc_vdisplay; + int crtc_vblank_start; + int crtc_vblank_end; + int crtc_vsync_start; + int crtc_vsync_end; + int crtc_vtotal; + int crtc_hadjusted; + int crtc_vadjusted; + + /* Driver private mode info */ + int private_size; + int *private; + int private_flags; + + int vrefresh; + float hsync;//, vrefresh; +}; + +/* 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 + +enum drm_output_status { + output_status_connected, + output_status_disconnected, + output_status_unknown, +}; + +enum subpixel_order { + SubPixelUnknown = 0, + SubPixelHorizontalRGB, + SubPixelHorizontalBGR, + SubPixelVerticalRGB, + SubPixelVerticalBGR, + SubPixelNone, +}; + +struct drm_framebuffer { + struct drm_device *dev; + struct list_head head; + unsigned int pitch; + unsigned long offset; + unsigned int width; + unsigned int height; + /* depth can be 15 or 16 */ + unsigned int depth; + int bits_per_pixel; + int flags; +}; +struct drm_crtc; +struct drm_output; + +/** + * drm_crtc_funcs - control CRTCs for a given device + * @dpms: control display power levels + * @save: save CRTC state + * @resore: restore CRTC state + * @lock: lock the CRTC + * @unlock: unlock the CRTC + * @shadow_allocate: allocate shadow pixmap + * @shadow_create: create shadow pixmap for rotation support + * @shadow_destroy: free shadow pixmap + * @mode_fixup: fixup proposed mode + * @mode_set: set the desired mode on the CRTC + * @gamma_set: specify color ramp for CRTC + * @cleanup: cleanup driver private state prior to close + * + * The drm_crtc_funcs structure is the central CRTC management structure + * in the DRM. Each CRTC controls one or more outputs (note that the name + * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc. + * outputs, not just CRTs). + * + * Each driver is responsible for filling out this structure at startup time, + * in addition to providing other modesetting features, like i2c and DDC + * bus accessors. + */ +struct drm_crtc_funcs { + /* + * Control power levels on the CRTC. If the mode passed in is + * unsupported, the provider must use the next lowest power level. + */ + void (*dpms)(struct drm_crtc *crtc, int mode); + + /* JJJ: Are these needed? */ + /* Save CRTC state */ + void (*save)(struct drm_crtc *crtc); /* suspend? */ + /* Restore CRTC state */ + void (*restore)(struct drm_crtc *crtc); /* resume? */ + bool (*lock)(struct drm_crtc *crtc); + void (*unlock)(struct drm_crtc *crtc); + + void (*prepare)(struct drm_crtc *crtc); + void (*commit)(struct drm_crtc *crtc); + + /* Provider can fixup or change mode timings before modeset occurs */ + bool (*mode_fixup)(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + /* Actually set the mode */ + void (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y); + /* Set gamma on the CRTC */ + void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, + int size); + /* Driver cleanup routine */ + void (*cleanup)(struct drm_crtc *crtc); +}; + +/** + * drm_crtc - central CRTC control structure + * @enabled: is this CRTC enabled? + * @x: x position on screen + * @y: y position on screen + * @desired_mode: new desired mode + * @desired_x: desired x for desired_mode + * @desired_y: desired y for desired_mode + * @funcs: CRTC control functions + * @driver_private: arbitrary driver data + * + * Each CRTC may have one or more outputs associated with it. This structure + * allows the CRTC to be controlled. + */ +struct drm_crtc { + struct drm_device *dev; + struct list_head head; + + /* framebuffer the CRTC is currently bound to */ + struct drm_framebuffer *fb; + + bool enabled; + + /* JJJ: are these needed? */ + bool cursor_in_range; + bool cursor_shown; + + struct drm_display_mode mode; + + int x, y; + struct drm_display_mode desired_mode; + int desired_x, desired_y; + const struct drm_crtc_funcs *funcs; + void *driver_private; + + /* RRCrtcPtr randr_crtc? */ +}; + +extern struct drm_crtc *drm_crtc_create(struct drm_device *dev, + const struct drm_crtc_funcs *funcs); + +/** + * drm_output_funcs - control outputs on a given device + * @init: setup this output + * @dpms: set power state (see drm_crtc_funcs above) + * @save: save output state + * @restore: restore output state + * @mode_valid: is this mode valid on the given output? + * @mode_fixup: try to fixup proposed mode for this output + * @mode_set: set this mode + * @detect: is this output active? + * @get_modes: get mode list for this output + * @set_property: property for this output may need update + * @cleanup: output is going away, cleanup + * + * Each CRTC may have one or more outputs attached to it. The functions + * below allow the core DRM code to control outputs, enumerate available modes, + * etc. + */ +struct drm_output_funcs { + void (*init)(struct drm_output *output); + void (*dpms)(struct drm_output *output, int mode); + void (*save)(struct drm_output *output); + void (*restore)(struct drm_output *output); + int (*mode_valid)(struct drm_output *output, + struct drm_display_mode *mode); + bool (*mode_fixup)(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + void (*prepare)(struct drm_output *output); + void (*commit)(struct drm_output *output); + void (*mode_set)(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + enum drm_output_status (*detect)(struct drm_output *output); + int (*get_modes)(struct drm_output *output); + /* JJJ: type checking for properties via property value type */ + bool (*set_property)(struct drm_output *output, int prop, void *val); + void (*cleanup)(struct drm_output *output); +}; + +#define DRM_OUTPUT_LEN 32 +/** + * drm_output - central DRM output control structure + * @crtc: CRTC this output is currently connected to, NULL if none + * @possible_crtcs: bitmap of CRTCS this output could be attached to + * @possible_clones: bitmap of possible outputs this output could clone + * @interlace_allowed: can this output handle interlaced modes? + * @doublescan_allowed: can this output handle doublescan? + * @available_modes: modes available on this output (from get_modes() + user) + * @initial_x: initial x position for this output + * @initial_y: initial y position for this output + * @status: output connected? + * @subpixel_order: for this output + * @mm_width: displayable width of output in mm + * @mm_height: displayable height of output in mm + * @name: name of output (should be one of a few standard names) + * @funcs: output control functions + * @driver_private: private driver data + * + * Each output may be connected to one or more CRTCs, or may be clonable by + * another output if they can share a CRTC. Each output also has a specific + * position in the broader display (referred to as a 'screen' though it could + * span multiple monitors). + */ +struct drm_output { + struct drm_device *dev; + struct list_head head; + struct drm_crtc *crtc; + unsigned long possible_crtcs; + unsigned long possible_clones; + bool interlace_allowed; + bool doublescan_allowed; + spinlock_t modes_lock; + struct list_head modes; /* list of modes on this output */ + /* + OptionInfoPtr options; + XF86ConfMonitorPtr conf_monitor; + */ + int initial_x, initial_y; + enum drm_output_status status; + + /* these are modes added by probing with DDC or the BIOS */ + struct list_head probed_modes; + + /* xf86MonPtr MonInfo; */ + enum subpixel_order subpixel_order; + int mm_width, mm_height; + char name[DRM_OUTPUT_LEN]; + const struct drm_output_funcs *funcs; + void *driver_private; + /* RROutputPtr randr_output? */ +}; + +/** + * struct drm_crtc_config_funcs - configure CRTCs for a given screen layout + * @resize: adjust CRTCs as necessary for the proposed layout + * + * Currently only a resize hook is available. DRM will call back into the + * driver with a new screen width and height. If the driver can't support + * the proposed size, it can return false. Otherwise it should adjust + * the CRTC<->output mappings as needed and update its view of the screen. + */ +struct drm_crtc_config_funcs { + bool (*resize)(struct drm_device *dev, int width, int height); +}; + +/** + * drm_crtc_config - CRTC configuration control structure + * + */ +struct drm_crtc_config { + spinlock_t config_lock; + /* this is limited to one for now */ + int num_fb; + struct list_head fb_list; + int num_output; + struct list_head output_list; + + /* int compat_output? */ + int num_crtc; + struct list_head crtc_list; + + int min_width, min_height; + int max_width, max_height; + /* DamagePtr rotationDamage? */ + /* DGA stuff? */ + struct drm_crtc_config_funcs *funcs; +}; + +struct drm_output *drm_output_create(struct drm_device *dev, + const struct drm_output_funcs *funcs, + const char *name); +void drm_output_destroy(struct drm_output *output); +bool drm_output_rename(struct drm_output *output, const char *name); + +int drm_add_edid_modes(struct drm_output *output, + struct i2c_adapter *adapter); +void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode); +void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode); +extern struct drm_display_mode *drm_mode_duplicate(struct drm_display_mode *mode); +extern void drm_mode_debug_printmodeline(struct drm_device *dev, + struct drm_display_mode *mode); +extern void drm_crtc_config_init(struct drm_device *dev); +extern void drm_crtc_config_cleanup(struct drm_device *dev); +extern void drm_disable_unused_functions(struct drm_device *dev); +#endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c new file mode 100644 index 00000000..3c123751 --- /dev/null +++ b/linux-core/drm_edid.c @@ -0,0 +1,515 @@ +#include +#include +#include "drmP.h" +#include "intel_drv.h" + +/* + * DDC/EDID probing rippped off from FB layer + */ + +#include "edid.h" +#define DDC_ADDR 0x50 + +#ifdef BIG_ENDIAN +#error "EDID structure is little endian, need big endian versions" +#endif + +struct est_timings { + u8 t1; + u8 t2; + u8 mfg_rsvd; +} __attribute__((packed)); + +struct std_timing { + u8 hsize; /* need to multiply by 8 then add 248 */ + u8 vfreq:6; /* need to add 60 */ + u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ +} __attribute__((packed)); + +/* If detailed data is pixel timing */ +struct detailed_pixel_timing { + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_offset_lo; + u8 hsync_pulse_width_lo; + u8 vsync_pulse_width_lo:4; + u8 vsync_offset_lo:4; + u8 hsync_pulse_width_hi:2; + u8 hsync_offset_hi:2; + u8 vsync_pulse_width_hi:2; + u8 vsync_offset_hi:2; + u8 width_mm_lo; + u8 height_mm_lo; + u8 height_mm_hi:4; + u8 width_mm_hi:4; + u8 hborder; + u8 vborder; + u8 unknown0:1; + u8 vsync_positive:1; + u8 hsync_positive:1; + u8 separate_sync:2; + u8 stereo:1; + u8 unknown6:1; + u8 interlaced:1; +} __attribute__((packed)); + +/* If it's not pixel timing, it'll be one of the below */ +struct detailed_data_string { + u8 str[13]; +} __attribute__((packed)); + +struct detailed_data_monitor_range { + u8 min_vfreq; + u8 max_vfreq; + u8 min_hfreq_khz; + u8 max_hfreq_khz; + u8 pixel_clock_mhz; /* need to multiply by 10 */ + u16 sec_gtf_toggle; /* A000=use above, 20=use below */ + u8 hfreq_start_khz; /* need to multiply by 2 */ + u8 c; /* need to divide by 2 */ + u16 m; + u8 k; + u8 j; /* need to divide by 2 */ +} __attribute__((packed)); + +struct detailed_data_wpindex { + u8 white_y_lo:2; + u8 white_x_lo:2; + u8 pad:4; + u8 white_x_hi; + u8 white_y_hi; + u8 gamma; /* need to divide by 100 then add 1 */ +} __attribute__((packed)); + +struct detailed_data_color_point { + u8 windex1; + u8 wpindex1[3]; + u8 windex2; + u8 wpindex2[3]; +} __attribute__((packed)); + +struct detailed_non_pixel { + u8 pad1; + u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name + fb=color point data, fa=standard timing data, + f9=undefined, f8=mfg. reserved */ + u8 pad2; + union { + struct detailed_data_string str; + struct detailed_data_monitor_range range; + struct detailed_data_wpindex color; + struct std_timing timings[5]; + } data; +} __attribute__((packed)); + +#define EDID_DETAIL_STD_MODES 0xfa +#define EDID_DETAIL_CPDATA 0xfb +#define EDID_DETAIL_NAME 0xfc +#define EDID_DETAIL_RANGE 0xfd +#define EDID_DETAIL_STRING 0xfe +#define EDID_DETAIL_SERIAL 0xff + +struct detailed_timing { + u16 pixel_clock; /* need to multiply by 10 KHz */ + union { + struct detailed_pixel_timing pixel_data; + struct detailed_non_pixel other_data; + } data; +} __attribute__((packed)); + +struct edid { + u8 header[8]; + /* Vendor & product info */ + u16 mfg_id; + u16 prod_code; + u32 serial; + u8 mfg_week; + u8 mfg_year; + /* EDID version */ + u8 version; + u8 revision; + /* Display info: */ + /* input definition */ + u8 serration_vsync:1; + u8 sync_on_green:1; + u8 composite_sync:1; + u8 separate_syncs:1; + u8 blank_to_black:1; + u8 video_level:2; + u8 digital:1; /* bits below must be zero if set */ + u8 width_cm; + u8 height_cm; + u8 gamma; + /* feature support */ + u8 default_gtf:1; + u8 preferred_timing:1; + u8 standard_color:1; + u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ + u8 pm_active_off:1; + u8 pm_suspend:1; + u8 pm_standby:1; + /* Color characteristics */ + u8 red_green_lo; + u8 black_white_lo; + u8 red_x; + u8 red_y; + u8 green_x; + u8 green_y; + u8 blue_x; + u8 blue_y; + u8 white_x; + u8 white_y; + /* Est. timings and mfg rsvd timings*/ + struct est_timings established_timings; + /* Standard timings 1-8*/ + struct std_timing standard_timings[8]; + /* Detailing timings 1-4 */ + struct detailed_timing detailed_timings[4]; + /* Number of 128 byte ext. blocks */ + u8 extensions; + /* Checksum */ + u8 checksum; +} __attribute__((packed)); + +static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +static bool edid_valid(struct edid *edid) +{ + int i; + u8 csum = 0; + u8 *raw_edid = (u8 *)edid; + + if (memcmp(edid->header, edid_header, sizeof(edid_header))) + goto bad; + if (edid->version != 1) + goto bad; + if (edid->revision <= 0 || edid->revision > 3) + goto bad; + + for (i = 0; i < EDID_LENGTH; i++) + csum += raw_edid[i]; + if (csum) + goto bad; + + return 1; + +bad: + return 0; +} + +/** + * drm_mode_std - convert standard mode info (width, height, refresh) into mode + * @t: standard timing params + * + * Take the standard timing params (in this case width, aspect, and refresh) + * and convert them into a real mode using CVT. + * + * Punts for now. + */ +struct drm_display_mode *drm_mode_std(struct std_timing *t) +{ +// struct fb_videomode mode; + +// fb_find_mode_cvt(&mode, 0, 0); + /* JJJ: convert to drm_display_mode */ + struct drm_display_mode *mode; + int hsize = t->hsize * 8 + 248, vsize; + + mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!mode) + return NULL; + + if (t->aspect_ratio == 0) + vsize = (hsize * 10) / 16; + else if (t->aspect_ratio == 1) + vsize = (hsize * 3) / 4; + else if (t->aspect_ratio == 2) + vsize = (hsize * 4) / 5; + else + vsize = (hsize * 9) / 16; + + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", hsize, vsize); + + return mode; +} + +struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing, + bool preferred) +{ + struct drm_display_mode *mode; + struct detailed_pixel_timing *pt = &timing->data.pixel_data; + + if (pt->stereo) { + printk(KERN_ERR "stereo mode not supported\n"); + return NULL; + } + if (!pt->separate_sync) { + printk(KERN_ERR "integrated sync not supported\n"); + return NULL; + } + + mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->type = DRM_MODE_TYPE_DRIVER; + mode->type |= preferred ? DRM_MODE_TYPE_PREFERRED : 0; + mode->clock = timing->pixel_clock / 100; + + mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; + mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | + pt->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + + ((pt->hsync_pulse_width_hi << 8) | + pt->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo); + + mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo; + mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) | + pt->vsync_offset_lo); + mode->vsync_end = mode->vsync_start + + ((pt->vsync_pulse_width_hi << 8) | + pt->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo); + + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, + mode->vdisplay); + + if (pt->interlaced) + mode->flags |= V_INTERLACE; + + mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC; + mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC; + + return mode; +} + +static struct drm_display_mode established_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("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, + 896, 1024, 0, 600, 601, 603, 625, 0, + V_PHSYNC | V_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("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, + 704, 832, 0, 480, 489, 491, 520, 0, + V_NHSYNC | V_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("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + V_NHSYNC | V_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("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, + 846, 900, 0, 400, 412, 414, 449, 0, + V_NHSYNC | V_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("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, + 1136, 1312, 0, 768, 769, 772, 800, 0, + V_PHSYNC | V_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("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + V_NHSYNC | V_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("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, + 928, 1152, 0, 624, 625, 628, 667, 0, + V_NHSYNC | V_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("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, + 976, 1040, 0, 600, 637, 643, 666, 0, + V_PHSYNC | V_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 */ +}; + +#define EDID_EST_TIMINGS 16 +#define EDID_STD_TIMINGS 8 +#define EDID_DETAILED_TIMINGS 4 + +/** + * add_established_modes - get est. modes from EDID and add them + * @edid: EDID block to scan + * + * Each EDID block contains a bitmap of the supported "established modes" list + * (defined above). Tease them out and add them to the global modes list. + */ +static int add_established_modes(struct drm_output *output, struct edid *edid) +{ + unsigned long est_bits = edid->established_timings.t1 | + (edid->established_timings.t2 << 8) | + ((edid->established_timings.mfg_rsvd & 0x80) << 9); + int i, modes = 0; + + for (i = 0; i <= EDID_EST_TIMINGS; i++) + if (est_bits & (1<standard_timings[i]; + + /* If std timings bytes are 1, 1 it's empty */ + if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) + continue; + + drm_mode_probed_add(output, + drm_mode_std(&edid->standard_timings[i])); + modes++; + } + + return modes; +} + +/** + * add_detailed_modes - get detailed mode info from EDID data + * @edid: EDID block to scan + * + * Some of the detailed timing sections may contain mode information. Grab + * it and add it to the list. + */ +static int add_detailed_info(struct drm_output *output, struct edid *edid) +{ + int i, j, modes = 0; + bool preferred = 0; + + for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { + struct detailed_timing *timing = &edid->detailed_timings[i]; + struct detailed_non_pixel *data = &timing->data.other_data; + + /* EDID up to and including 1.2 may put monitor info here */ + if (edid->version == 1 && edid->revision < 3) + continue; + + /* Detailed mode timing */ + if (timing->pixel_clock) { + if (i == 0 && edid->preferred_timing) + preferred = 1; + drm_mode_probed_add(output, + drm_mode_detailed(timing, preferred)); + modes++; + continue; + } + + /* Other timing or info */ + switch (data->type) { + case EDID_DETAIL_SERIAL: + break; + case EDID_DETAIL_STRING: + break; + case EDID_DETAIL_RANGE: + break; + case EDID_DETAIL_NAME: + break; + case EDID_DETAIL_CPDATA: + break; + case EDID_DETAIL_STD_MODES: + /* Five modes per detailed section */ + for (j = 0; j < 5; i++) { + struct std_timing *std; + + std = &data->data.timings[j]; + drm_mode_probed_add(output, drm_mode_std(std)); + modes++; + } + break; + default: + break; + } + } + + return modes; +} + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @output: output we're probing + * @adapter: i2c adapter to use for DDC + * + * Poke the given output's i2c channel to grab EDID data if possible. If we + * get any, add the specified modes to the output's mode list. + * + * Return number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter) +{ + struct edid *edid; + u8 *raw_edid; + int i, est_modes, std_modes, det_modes; + + edid = (struct edid *)fb_ddc_read(adapter); + + if (!edid) { + dev_warn(&output->dev->pdev->dev, "no EDID data\n"); + goto out_err; + } + + if (!edid_valid(edid)) { + dev_warn(&output->dev->pdev->dev, "EDID invalid, ignoring.\n"); + goto out_err; + } + + est_modes = add_established_modes(output, edid); + std_modes = add_standard_modes(output, edid); + det_modes = add_detailed_info(output, edid); + printk(KERN_ERR "est modes: %d, std_modes: %d, det_modes: %d\n", + est_modes, std_modes, det_modes); + + raw_edid = (u8 *)edid; + printk(KERN_ERR "EDID:\n" KERN_ERR); + for (i = 0; i < EDID_LENGTH; i++) { + if (i != 0 && ((i % 16) == 0)) + printk("\n" KERN_ERR); + printk("%02x", raw_edid[i] & 0xff); + } + printk("\n"); + + printk(KERN_ERR "EDID info:\n"); + printk(KERN_ERR " mfg_id: 0x%04x\n", edid->mfg_id); + printk(KERN_ERR " digital? %s\n", edid->digital ? "Yes" : "No"); + printk(KERN_ERR " extensions: %d\n", edid->extensions); + + return est_modes + std_modes + det_modes; + +out_err: + kfree(edid); + return 0; +} +EXPORT_SYMBOL(drm_add_edid_modes); diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c new file mode 100644 index 00000000..2347a669 --- /dev/null +++ b/linux-core/drm_modes.c @@ -0,0 +1,304 @@ +/* + * Copyright © 2007 Dave Airlie + * + * Based on code from X.org - Copyright (c) 1997-2003 by The XFree86 Project, Inc. + */ + +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" + +void drm_mode_debug_printmodeline(struct drm_device *dev, + struct drm_display_mode *mode) +{ + DRM_DEBUG("Modeline \"%s\" %d %d %d %d %d %d %d %d %d %d\n", + mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal); +} +EXPORT_SYMBOL(drm_mode_debug_printmodeline); + +void drm_mode_list_concat(struct list_head *head, struct list_head *new) +{ + + struct list_head *entry, *tmp; + + list_for_each_safe(entry, tmp, head) { + list_move_tail(entry, new); + } +} + +int drm_mode_width(struct drm_display_mode *mode) +{ + return mode->hdisplay; + +} +EXPORT_SYMBOL(drm_mode_width); + +int drm_mode_height(struct drm_display_mode *mode) +{ + return mode->vdisplay; +} +EXPORT_SYMBOL(drm_mode_height); + +int drm_mode_vrefresh(struct drm_display_mode *mode) +{ + int refresh = 0; + + if (mode->vrefresh > 0) + refresh = mode->vrefresh; + else if (mode->htotal > 0 && mode->vtotal > 0) { + refresh = ((mode->clock * 1000) * 1000) / mode->htotal / mode->vtotal; + if (mode->flags & V_INTERLACE) + refresh *= 2; + if (mode->flags & V_DBLSCAN) + refresh /= 2; + if (mode->vscan > 1) + refresh /= mode->vscan; + } + return refresh; +} +EXPORT_SYMBOL(drm_mode_vrefresh); + + +void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) +{ + if ((p == NULL) || ((p->type & DRM_MODE_TYPE_CRTC_C) == DRM_MODE_TYPE_BUILTIN)) + return; + + p->crtc_hdisplay = p->hdisplay; + p->crtc_hsync_start = p->hsync_start; + p->crtc_hsync_end = p->hsync_end; + p->crtc_htotal = p->htotal; + p->crtc_hskew = p->hskew; + p->crtc_vdisplay = p->vdisplay; + p->crtc_vsync_start = p->vsync_start; + p->crtc_vsync_end = p->vsync_end; + p->crtc_vtotal = p->vtotal; + + if (p->flags & V_INTERLACE) { + if (adjust_flags & CRTC_INTERLACE_HALVE_V) { + p->crtc_vdisplay /= 2; + p->crtc_vsync_start /= 2; + p->crtc_vsync_end /= 2; + p->crtc_vtotal /= 2; + } + + p->crtc_vtotal |= 1; + } + + if (p->flags & V_DBLSCAN) { + p->crtc_vdisplay *= 2; + p->crtc_vsync_start *= 2; + p->crtc_vsync_end *= 2; + p->crtc_vtotal *= 2; + } + + if (p->vscan > 1) { + p->crtc_vdisplay *= p->vscan; + p->crtc_vsync_start *= p->vscan; + p->crtc_vsync_end *= p->vscan; + p->crtc_vtotal *= p->vscan; + } + + p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); + p->crtc_vblank_end = min(p->crtc_vsync_end, p->crtc_vtotal); + p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); + p->crtc_hblank_end = min(p->crtc_hsync_end, p->crtc_htotal); + + p->crtc_hadjusted = false; + p->crtc_vadjusted = false; +} +EXPORT_SYMBOL(drm_mode_set_crtcinfo); + + +struct drm_display_mode *drm_mode_duplicate(struct drm_display_mode *mode) +{ + struct drm_display_mode *nmode; + + nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!nmode) + return NULL; + + *nmode = *mode; + INIT_LIST_HEAD(&nmode->head); + return nmode; +} +EXPORT_SYMBOL(drm_mode_duplicate); + +bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) +{ + if (mode1->clock == mode2->clock && + mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1->vsync_start == mode2->vsync_start && + mode1->vsync_end == mode2->vsync_end && + mode1->vtotal == mode2->vtotal && + mode1->vscan == mode2->vscan && + mode1->flags == mode2->flags) + return true; + + return false; +} +EXPORT_SYMBOL(drm_mode_equal); + +void drm_mode_validate_size(struct drm_device *dev, + struct list_head *mode_list, + int maxX, int maxY, int maxPitch) +{ + struct drm_display_mode *mode; + + list_for_each_entry(mode, mode_list, head) { + if (maxPitch > 0 && mode->hdisplay > maxPitch) + mode->status = MODE_BAD_WIDTH; + + if (maxX > 0 && mode->hdisplay > maxX) + mode->status = MODE_VIRTUAL_X; + + if (maxY > 0 && mode->vdisplay > maxY) + mode->status = MODE_VIRTUAL_Y; + } +} +EXPORT_SYMBOL(drm_mode_validate_size); + +void drm_mode_validate_clocks(struct drm_device *dev, + struct list_head *mode_list, + int *min, int *max, int n_ranges) +{ + struct drm_display_mode *mode; + int i; + + list_for_each_entry(mode, mode_list, head) { + bool good = false; + for (i = 0; i < n_ranges; i++) { + if (mode->clock >= min[i] && mode->clock <= max[i]) { + good = true; + break; + } + } + if (!good) + mode->status = MODE_CLOCK_RANGE; + } +} +EXPORT_SYMBOL(drm_mode_validate_clocks); + +void drm_mode_prune_invalid(struct drm_device *dev, + struct list_head *mode_list, bool verbose) +{ + struct drm_display_mode *mode, *t; + + list_for_each_entry_safe(mode, t, mode_list, head) { + if (mode->status != MODE_OK) { + list_del(&mode->head); + if (verbose) + DRM_DEBUG("Not using %s mode %d\n", mode->name, mode->status); + kfree(mode); + } + } +} + +static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b) +{ + struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head); + struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head); + int diff; + + diff = ((b->type & DRM_MODE_TYPE_PREFERRED) != 0) - + ((a->type & DRM_MODE_TYPE_PREFERRED) != 0); + if (diff) + return diff; + diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; + if (diff) + return diff; + diff = b->clock - a->clock; + return diff; +} + +/* list sort from Mark J Roberts (mjr@znex.org) */ +void list_sort(struct list_head *head, int (*cmp)(struct list_head *a, struct list_head *b)) +{ + struct list_head *p, *q, *e, *list, *tail, *oldhead; + int insize, nmerges, psize, qsize, i; + + list = head->next; + list_del(head); + insize = 1; + for (;;) { + p = oldhead = list; + list = tail = NULL; + nmerges = 0; + + while (p) { + nmerges++; + q = p; + psize = 0; + for (i = 0; i < insize; i++) { + psize++; + q = q->next == oldhead ? NULL : q->next; + if (!q) + break; + } + + qsize = insize; + while (psize > 0 || (qsize > 0 && q)) { + if (!psize) { + e = q; + q = q->next; + qsize--; + if (q == oldhead) + q = NULL; + } else if (!qsize || !q) { + e = p; + p = p->next; + psize--; + if (p == oldhead) + p = NULL; + } else if (cmp(p, q) <= 0) { + e = p; + p = p->next; + psize--; + if (p == oldhead) + p = NULL; + } else { + e = q; + q = q->next; + qsize--; + if (q == oldhead) + q = NULL; + } + if (tail) + tail->next = e; + else + list = e; + e->prev = tail; + tail = e; + } + p = q; + } + + tail->next = list; + list->prev = tail; + + if (nmerges <= 1) + break; + + insize *= 2; + } + + head->next = list; + head->prev = list->prev; + list->prev->next = head; + list->prev = head; +} + +void drm_mode_sort(struct list_head *mode_list) +{ + list_sort(mode_list, drm_mode_compare); +} diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 7fdb0839..b9e624f9 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -79,6 +79,7 @@ static struct drm_driver driver = { DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | DRIVER_IRQ_VBL2, .load = i915_driver_load, + .unload = i915_driver_unload, .firstopen = i915_driver_firstopen, .lastclose = i915_driver_lastclose, .preclose = i915_driver_preclose, diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c new file mode 100644 index 00000000..5ff9f791 --- /dev/null +++ b/linux-core/intel_crt.c @@ -0,0 +1,226 @@ +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +static void intel_crt_dpms(struct drm_output *output, int mode) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + u32 temp; + + temp = I915_READ(ADPA); + temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); + temp &= ~ADPA_DAC_ENABLE; + + switch(mode) { + case DPMSModeOn: + temp |= ADPA_DAC_ENABLE; + break; + case DPMSModeStandby: + temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; + break; + case DPMSModeSuspend: + temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + case DPMSModeOff: + temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; + break; + } + + I915_WRITE(ADPA, temp); +} + +static void intel_crt_save(struct drm_output *output) +{ + +} + +static void intel_crt_restore(struct drm_output *output) +{ + +} + +static int intel_crt_mode_valid(struct drm_output *output, + struct drm_display_mode *mode) +{ + if (mode->flags & V_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (mode->clock > 400000 || mode->clock < 25000) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +static bool intel_crt_mode_fixup(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void intel_crt_mode_set(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + drm_device_t *dev = output->dev; + struct drm_crtc *crtc = output->crtc; + struct intel_crtc *intel_crtc = crtc->driver_private; + drm_i915_private_t *dev_priv = dev->dev_private; + int dpll_md_reg; + u32 adpa, dpll_md; + + if (intel_crtc->pipe == 0) + dpll_md_reg = DPLL_A_MD; + else + dpll_md_reg = DPLL_B_MD; + + /* + * Disable separate mode multiplier used when cloning SDVO to CRT + * XXX this needs to be adjusted when we really are cloning + */ + if (IS_I965G(dev)) + { + dpll_md = I915_READ(dpll_md_reg); + I915_WRITE(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); + } + + adpa = 0; + if (adjusted_mode->flags & V_PHSYNC) + adpa |= ADPA_HSYNC_ACTIVE_HIGH; + if (adjusted_mode->flags & V_PVSYNC) + adpa |= ADPA_VSYNC_ACTIVE_HIGH; + + if (intel_crtc->pipe == 0) + adpa |= ADPA_PIPE_A_SELECT; + else + adpa |= ADPA_PIPE_B_SELECT; + + I915_WRITE(ADPA, adpa); +} + +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. + * + * Only for I945G/GM. + * + * \return TRUE if CRT is connected. + * \return FALSE if CRT is disconnected. + */ +static bool intel_crt_detect_hotplug(struct drm_output *output) +{ + drm_device_t *dev = output->dev; +// struct intel_output *intel_output = output->driver_private; + drm_i915_private_t *dev_priv = dev->dev_private; + u32 temp; + const int timeout_ms = 1000; + int starttime, curtime; + + temp = I915_READ(PORT_HOTPLUG_EN); + + I915_WRITE(PORT_HOTPLUG_EN, temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5)); +#if 0 + for (curtime = starttime = GetTimeInMillis(); + (curtime - starttime) < timeout_ms; curtime = GetTimeInMillis()) + { + if ((I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0) + break; + } +#endif + if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) == + CRT_HOTPLUG_MONITOR_COLOR) + { + return true; + } else { + return false; + } +} + +static bool intel_crt_detect_ddc(struct drm_output *output) +{ + struct intel_output *intel_output = output->driver_private; + + /* CRT should always be at 0, but check anyway */ + if (intel_output->type != INTEL_OUTPUT_ANALOG) + return false; + + return intel_ddc_probe(output); +} + +static enum drm_output_status intel_crt_detect(struct drm_output *output) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + if (IS_I945G(dev)| IS_I945GM(dev) || IS_I965G(dev)) { + if (intel_crt_detect_hotplug(output)) + return output_status_connected; + else + return output_status_disconnected; + } + + if (intel_crt_detect_ddc(output)) + return output_status_connected; + + /* TODO use load detect */ + return output_status_unknown; +} + +static void intel_crt_destroy(struct drm_output *output) +{ + struct intel_output *intel_output = output->driver_private; + + intel_i2c_destroy(intel_output->ddc_bus); + + if (output->driver_private) + kfree(output->driver_private); +} + +/* + * Routines for controlling stuff on the analog port + */ +static const struct drm_output_funcs intel_crt_output_funcs = { + .dpms = intel_crt_dpms, + .save = intel_crt_save, + .restore = intel_crt_restore, + .mode_valid = intel_crt_mode_valid, + .mode_fixup = intel_crt_mode_fixup, + .prepare = intel_output_prepare, + .mode_set = intel_crt_mode_set, + .commit = intel_output_commit, + .detect = intel_crt_detect, + .get_modes = intel_ddc_get_modes, + .cleanup = intel_crt_destroy, +}; + +void intel_crt_init(drm_device_t *dev) +{ + struct drm_output *output; + struct intel_output *intel_output; + int modes; + + output = drm_output_create (dev, &intel_crt_output_funcs, "VGA"); + + intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); + if (!intel_output) { + drm_output_destroy(output); + return; + } + + intel_output->type = INTEL_OUTPUT_ANALOG; + output->driver_private = intel_output; + output->interlace_allowed = 0; + output->doublescan_allowed = 0; + + /* Set up the DDC bus. */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return; + } +} diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c new file mode 100644 index 00000000..495f4704 --- /dev/null +++ b/linux-core/intel_display.c @@ -0,0 +1,1087 @@ +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +bool intel_pipe_has_type (struct drm_crtc *crtc, int type); + +typedef struct { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +} intel_clock_t; + +typedef struct { + int min, max; +} intel_range_t; + +typedef struct { + int dot_limit; + int p2_slow, p2_fast; +} intel_p2_t; + +#define INTEL_P2_NUM 2 + +typedef struct { + intel_range_t dot, vco, n, m, m1, m2, p, p1; + intel_p2_t p2; +} intel_limit_t; + +#define I8XX_DOT_MIN 25000 +#define I8XX_DOT_MAX 350000 +#define I8XX_VCO_MIN 930000 +#define I8XX_VCO_MAX 1400000 +#define I8XX_N_MIN 3 +#define I8XX_N_MAX 16 +#define I8XX_M_MIN 96 +#define I8XX_M_MAX 140 +#define I8XX_M1_MIN 18 +#define I8XX_M1_MAX 26 +#define I8XX_M2_MIN 6 +#define I8XX_M2_MAX 16 +#define I8XX_P_MIN 4 +#define I8XX_P_MAX 128 +#define I8XX_P1_MIN 2 +#define I8XX_P1_MAX 33 +#define I8XX_P1_LVDS_MIN 1 +#define I8XX_P1_LVDS_MAX 6 +#define I8XX_P2_SLOW 4 +#define I8XX_P2_FAST 2 +#define I8XX_P2_LVDS_SLOW 14 +#define I8XX_P2_LVDS_FAST 14 /* No fast option */ +#define I8XX_P2_SLOW_LIMIT 165000 + +#define I9XX_DOT_MIN 20000 +#define I9XX_DOT_MAX 400000 +#define I9XX_VCO_MIN 1400000 +#define I9XX_VCO_MAX 2800000 +#define I9XX_N_MIN 3 +#define I9XX_N_MAX 8 +#define I9XX_M_MIN 70 +#define I9XX_M_MAX 120 +#define I9XX_M1_MIN 10 +#define I9XX_M1_MAX 20 +#define I9XX_M2_MIN 5 +#define I9XX_M2_MAX 9 +#define I9XX_P_SDVO_DAC_MIN 5 +#define I9XX_P_SDVO_DAC_MAX 80 +#define I9XX_P_LVDS_MIN 7 +#define I9XX_P_LVDS_MAX 98 +#define I9XX_P1_MIN 1 +#define I9XX_P1_MAX 8 +#define I9XX_P2_SDVO_DAC_SLOW 10 +#define I9XX_P2_SDVO_DAC_FAST 5 +#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000 +#define I9XX_P2_LVDS_SLOW 14 +#define I9XX_P2_LVDS_FAST 7 +#define I9XX_P2_LVDS_SLOW_LIMIT 112000 + +#define INTEL_LIMIT_I8XX_DVO_DAC 0 +#define INTEL_LIMIT_I8XX_LVDS 1 +#define INTEL_LIMIT_I9XX_SDVO_DAC 2 +#define INTEL_LIMIT_I9XX_LVDS 3 + +static const intel_limit_t intel_limits[] = { + { /* INTEL_LIMIT_I8XX_DVO_DAC */ + .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, + .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, + .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, + .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, + .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, + .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, + .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, + .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, + .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, + }, + { /* INTEL_LIMIT_I8XX_LVDS */ + .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, + .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, + .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, + .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, + .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, + .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, + .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, + .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, + .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, + }, + { /* INTEL_LIMIT_I9XX_SDVO_DAC */ + .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, + .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, + .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, + .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX }, + .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX }, + .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX }, + .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX }, + .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, + .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, + .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, + }, + { /* INTEL_LIMIT_I9XX_LVDS */ + .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, + .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, + .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, + .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX }, + .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX }, + .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX }, + .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX }, + .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, + /* The single-channel range is 25-112Mhz, and dual-channel + * is 80-224Mhz. Prefer single channel as much as possible. + */ + .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, + .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, + }, +}; + +static const intel_limit_t *intel_limit (struct drm_crtc *crtc) +{ + drm_device_t *dev = crtc->dev; + const intel_limit_t *limit; + + if (IS_I9XX(dev)) { + if (intel_pipe_has_type (crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS]; + else + limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; + } else { + if (intel_pipe_has_type (crtc, INTEL_OUTPUT_LVDS)) + limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS]; + else + limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC]; + } + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ + +static void i8xx_clock(int refclk, intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ + +static void i9xx_clock(int refclk, intel_clock_t *clock) +{ + clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->p = clock->p1 * clock->p2; + clock->vco = refclk * clock->m / (clock->n + 2); + clock->dot = clock->vco / clock->p; +} + +static void intel_clock(struct drm_device *dev, int refclk, + intel_clock_t *clock) +{ + if (IS_I9XX(dev)) + return i9xx_clock (refclk, clock); + else + return i8xx_clock (refclk, clock); +} + +/** + * Returns whether any output on the specified pipe is of the specified type + */ +bool intel_pipe_has_type (struct drm_crtc *crtc, int type) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc_config *crtc_config = &dev->crtc_config; + struct drm_output *l_entry; + + list_for_each_entry(l_entry, &crtc_config->output_list, head) { + if (l_entry->crtc == crtc) { + struct intel_output *intel_output = l_entry->driver_private; + if (intel_output->type == type) + return true; + } + } + return false; +} + +#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } +/** + * Returns whether the given set of divisors are valid for a given refclk with + * the given outputs. + */ + +static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock) +{ + const intel_limit_t *limit = intel_limit (crtc); + + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + INTELPllInvalid ("p1 out of range\n"); + if (clock->p < limit->p.min || limit->p.max < clock->p) + INTELPllInvalid ("p out of range\n"); + if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) + INTELPllInvalid ("m2 out of range\n"); + if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) + INTELPllInvalid ("m1 out of range\n"); + if (clock->m1 <= clock->m2) + INTELPllInvalid ("m1 <= m2\n"); + if (clock->m < limit->m.min || limit->m.max < clock->m) + INTELPllInvalid ("m out of range\n"); + if (clock->n < limit->n.min || limit->n.max < clock->n) + INTELPllInvalid ("n out of range\n"); + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) + INTELPllInvalid ("vco out of range\n"); + /* XXX: We may need to be checking "Dot clock" depending on the multiplier, + * output, etc., rather than just a single range. + */ + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) + INTELPllInvalid ("dot out of range\n"); + + return true; +} + +/** + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ +static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, + int refclk, intel_clock_t *best_clock) +{ + drm_device_t *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + intel_clock_t clock; + const intel_limit_t *limit = intel_limit (crtc); + int err = target; + + if (IS_I9XX(dev)& intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + (I915_READ(LVDS) & LVDS_PORT_EN) != 0) + { + /* For LVDS, if the panel is on, just rely on its current settings for + * dual-channel. We haven't figured out how to reliably set up + * different single/dual channel state, if we even can. + */ + if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + clock.p2 = limit->p2.p2_fast; + else + clock.p2 = limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + clock.p2 = limit->p2.p2_slow; + else + clock.p2 = limit->p2.p2_fast; + } + + memset (best_clock, 0, sizeof (*best_clock)); + + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) + { + for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; clock.m2++) + { + for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) + { + for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; clock.p1++) + { + int this_err; + + intel_clock (dev, refclk, &clock); + + if (!intel_PLL_is_valid(crtc, &clock)) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + return (err != target); +} + +void +intel_wait_for_vblank(drm_device_t *dev) +{ + /* Wait for 20ms, i.e. one cycle at 50hz. */ + udelay(20000); +} + +void +intel_pipe_set_base(struct drm_crtc *crtc, int x, int y) +{ + drm_device_t *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + unsigned long Start, Offset; + int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); + int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); + + Start = crtc->fb->offset; + Offset = ((y * crtc->fb->width + x) * (crtc->fb->bits_per_pixel / 8)); + + if (IS_I965G(dev)) { + I915_WRITE(dspbase, Offset); + I915_READ(dspbase); + I915_WRITE(dspsurf, Start); + I915_READ(dspsurf); + } else { + I915_WRITE(dspbase, Start + Offset); + I915_READ(dspbase); + } + + +#if 0 + drmI830Sarea *sPriv = (drmI830Sarea *) DRIGetSAREAPrivate(pScrn->pScreen); + + if (!sPriv) + return; + + switch (pipe) { + case 0: + sPriv->pipeA_x = x; + sPriv->pipeA_y = y; + break; + case 1: + sPriv->pipeB_x = x; + sPriv->pipeB_y = y; + break; + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Can't update pipe %d in SAREA\n", pipe); + break; + } +#endif +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + drm_device_t *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 temp; + + /* 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. + */ + switch (mode) { + case DPMSModeOn: + case DPMSModeStandby: + case DPMSModeSuspend: + /* Enable the DPLL */ + temp = I915_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) == 0) + { + I915_WRITE(dpll_reg, temp); + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + } + + /* Enable the pipe */ + temp = I915_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) + I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); + + /* Enable the plane */ + temp = I915_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) + { + I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + } + + intel_crtc_load_lut(crtc); + + /* Give the overlay scaler a chance to enable if it's on this pipe */ + //intel_crtc_dpms_video(crtc, TRUE); TODO + break; + case DPMSModeOff: + /* Give the overlay scaler a chance to disable if it's on this pipe */ + //intel_crtc_dpms_video(crtc, FALSE); TODO + + /* Disable the VGA plane that we never use */ + I915_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable display plane */ + temp = I915_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) + { + I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); + I915_READ(dspbase_reg); + } + + if (!IS_I9XX(dev)) { + /* Wait for vblank for the disable to take effect */ + intel_wait_for_vblank(dev); + } + + /* Next, disable display pipes */ + temp = I915_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); + I915_READ(pipeconf_reg); + } + + /* Wait for vblank for the disable to take effect. */ + intel_wait_for_vblank(dev); + + temp = I915_READ(dpll_reg); + if ((temp & DPLL_VCO_ENABLE) != 0) { + I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } + +#if 0 //TODO + if (pI830->directRenderingEnabled) { + drmI830Sarea *sPriv = (drmI830Sarea *) DRIGetSAREAPrivate(pScrn->pScreen); + Bool enabled = crtc->enabled && mode != DPMSModeOff; + + if (!sPriv) + return; + + switch (pipe) { + case 0: + sPriv->pipeA_w = enabled ? crtc->mode.HDisplay : 0; + sPriv->pipeA_h = enabled ? crtc->mode.VDisplay : 0; + break; + case 1: + sPriv->pipeB_w = enabled ? crtc->mode.HDisplay : 0; + sPriv->pipeB_h = enabled ? crtc->mode.VDisplay : 0; + break; + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Can't update pipe %d in SAREA\n", pipe); + break; + } + } +#endif +} + +static bool intel_crtc_lock(struct drm_crtc *crtc) +{ + /* Sync the engine before mode switch */ +// i830WaitSync(crtc->scrn); + +#if 0 // TODO def XF86DRI + return I830DRILock(crtc->scrn); +#else + return FALSE; +#endif +} + +static void intel_crtc_unlock (struct drm_crtc *crtc) +{ +#if 0 // TODO def XF86DRI + I830DRIUnlock (crtc->scrn); +#endif +} + +static void intel_crtc_prepare (struct drm_crtc *crtc) +{ + crtc->funcs->dpms (crtc, DPMSModeOff); +} + +static void intel_crtc_commit (struct drm_crtc *crtc) +{ + crtc->funcs->dpms (crtc, DPMSModeOn); +// if (crtc->scrn->pScreen != NULL) +// xf86_reload_cursors (crtc->scrn->pScreen); +} + +void intel_output_prepare (struct drm_output *output) +{ + output->funcs->dpms (output, DPMSModeOff); +} + +void intel_output_commit (struct drm_output *output) +{ + output->funcs->dpms (output, DPMSModeOn); +} + +static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + + +/** Returns the core display clock speed for i830 - i945 */ +static int intel_get_core_clock_speed(drm_device_t *dev) +{ + + /* Core clock values taken from the published datasheets. + * The 830 may go up to 166 Mhz, which we should check. + */ + if (IS_I945G(dev)) + return 400000; + else if (IS_I915G(dev)) + return 333000; + else if (IS_I945GM(dev) || IS_845G(dev)) + return 200000; + else if (IS_I915GM(dev)) { +#if 0 + u16 gcfgc = pciReadWord(dev->PciTag, I915_GCFGC); + + if (gcfgc & I915_LOW_FREQUENCY_ENABLE) + return 133000; + else { + switch (gcfgc & I915_DISPLAY_CLOCK_MASK) { + case I915_DISPLAY_CLOCK_333_MHZ: + return 333000; + default: + case I915_DISPLAY_CLOCK_190_200_MHZ: + return 190000; + } + } +#endif + } else if (IS_I865G(dev)) + return 266000; + else if (IS_I855(dev)) { +#if 0 + PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */ + u16 hpllcc = pciReadWord(bridge, I855_HPLLCC); + +#endif + u16 hpllcc = 0; + /* Assume that the hardware is in the high speed state. This + * should be the default. + */ + switch (hpllcc & I855_CLOCK_CONTROL_MASK) { + case I855_CLOCK_133_200: + case I855_CLOCK_100_200: + return 200000; + case I855_CLOCK_166_250: + return 250000; + case I855_CLOCK_100_133: + return 133000; + } + } else /* 852, 830 */ + return 133000; + + return 0; /* Silence gcc warning */ +} + + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int intel_panel_fitter_pipe (drm_device_t *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 pfit_control; + + /* i830 doesn't have a panel fitter */ + if (IS_I830(dev)) + return -1; + + pfit_control = I915_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + + /* 965 can place panel fitter on either pipe */ + if (IS_I965G(dev)) + return (pfit_control >> 29) & 0x3; + + /* older chips can only use pipe 1 */ + return 1; +} + +static void intel_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y) +{ + drm_device_t *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + int fp_reg = (pipe == 0) ? FPA0 : FPB0; + int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; + int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; + int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; + int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; + int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; + int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; + int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; + int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; + int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; + int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; + int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int refclk; + intel_clock_t clock; + u32 dpll = 0, fp = 0, dspcntr, pipeconf; + bool ok, is_sdvo = false, is_dvo = false; + bool is_crt = false, is_lvds = false, is_tv = false; + struct drm_crtc_config *crtc_config = &dev->crtc_config; + struct drm_output *output; + + list_for_each_entry(output, &crtc_config->output_list, head) { + struct intel_output *intel_output = output->driver_private; + + if (output->crtc != crtc) + continue; + + switch (intel_output->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = TRUE; + break; + case INTEL_OUTPUT_SDVO: + is_sdvo = TRUE; + break; + case INTEL_OUTPUT_DVO: + is_dvo = TRUE; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = TRUE; + break; + case INTEL_OUTPUT_ANALOG: + is_crt = TRUE; + break; + } + } + + if (IS_I9XX(dev)) { + refclk = 96000; + } else { + refclk = 48000; + } + + ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); + if (!ok) { + DRM_ERROR("Couldn't find PLL settings for mode!\n"); + return; + } + + fp = clock.n << 16 | clock.m1 << 8 | clock.m2; + + dpll = DPLL_VGA_MODE_DIS; + if (IS_I9XX(dev)) { + if (is_lvds) + dpll |= DPLLB_MODE_LVDS; + else + dpll |= DPLLB_MODE_DAC_SERIAL; + if (is_sdvo) + { + dpll |= DPLL_DVO_HIGH_SPEED; + if (IS_I945G(dev) || IS_I945GM(dev)) + { + int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; + } + } + + /* compute bitmask from p1 value */ + dpll |= (1 << (clock.p1 - 1)) << 16; + switch (clock.p2) { + case 5: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; + break; + case 7: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; + break; + case 10: + dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; + break; + case 14: + dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; + break; + } + if (IS_I965G(dev)) + dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); + } else { + if (is_lvds) { + dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + } else { + if (clock.p1 == 2) + dpll |= PLL_P1_DIVIDE_BY_TWO; + else + dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT; + if (clock.p2 == 4) + dpll |= PLL_P2_DIVIDE_BY_4; + } + } + + if (is_tv) + { + /* XXX: just matching BIOS for now */ +/* dpll |= PLL_REF_INPUT_TVCLKINBC; */ + dpll |= 3; + } +#if 0 + else if (is_lvds) + dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; +#endif + else + dpll |= PLL_REF_INPUT_DREFCLK; + + /* Set up the display plane register */ + dspcntr = DISPPLANE_GAMMA_ENABLE; + + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + DRM_ERROR("Unknown color depth\n"); + return; + } + + + if (pipe == 0) + dspcntr |= DISPPLANE_SEL_PIPE_A; + else + dspcntr |= DISPPLANE_SEL_PIPE_B; + + pipeconf = I915_READ(pipeconf_reg); + if (pipe == 0 && !IS_I965G(dev)) + { + /* Enable pixel doubling when the dot clock is > 90% of the (display) + * core speed. + * + * XXX: No double-wide on 915GM pipe B. Is that the only reason for the + * pipe == 0 check? + */ + if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10) + pipeconf |= PIPEACONF_DOUBLE_WIDE; + else + pipeconf &= ~PIPEACONF_DOUBLE_WIDE; + } + + dspcntr |= DISPLAY_PLANE_ENABLE; + pipeconf |= PIPEACONF_ENABLE; + dpll |= DPLL_VCO_ENABLE; + + + /* Disable the panel fitter if it was on our pipe */ + if (intel_panel_fitter_pipe(dev) == pipe) + I915_WRITE(PFIT_CONTROL, 0); + +#if 0 + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + xf86PrintModeline(pScrn->scrnIndex, mode); + if (!xf86ModesEqual(mode, adjusted_mode)) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + xf86PrintModeline(pScrn->scrnIndex, mode); + } + i830PrintPll("chosen", &clock); +#endif + + if (dpll & DPLL_VCO_ENABLE) + { + I915_WRITE(fp_reg, fp); + I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + udelay(150); + } + + /* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ + if (is_lvds) + { + u32 lvds = I915_READ(LVDS); + + lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; + /* Set the B0-B3 data pairs corresponding to whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (clock.p2 == 7) + lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more thoroughly into how + * panels behave in the two modes. + */ + + I915_WRITE(LVDS, lvds); + I915_READ(LVDS); + } + + I915_WRITE(fp_reg, fp); + I915_WRITE(dpll_reg, dpll); + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + if (IS_I965G(dev)) { + int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | + ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + } else { + /* write it again -- the BIOS does, after all */ + I915_WRITE(dpll_reg, dpll); + } + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + I915_WRITE(dspstride_reg, crtc->fb->pitch * (crtc->fb->bits_per_pixel / 8)); + /* pipesrc and dspsize control the size that is scaled from, which should + * always be the user's requested size. + */ + I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + I915_WRITE(dsppos_reg, 0); + I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + I915_WRITE(pipeconf_reg, pipeconf); + I915_READ(pipeconf_reg); + + intel_wait_for_vblank(dev); + + I915_WRITE(dspcntr_reg, dspcntr); + + /* Flush the plane changes */ + intel_pipe_set_base(crtc, x, y); + +#ifdef XF86DRI // TODO +// I830DRISetVBlankInterrupt (pScrn, TRUE); +#endif + + intel_wait_for_vblank(dev); + + +} + +/** Loads the palette/gamma unit for the CRTC with the prepared values */ +void intel_crtc_load_lut(struct drm_crtc *crtc) +{ + drm_device_t *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = crtc->driver_private; + int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B; + int i; + + /* The clocks have to be on to load the palette. */ + if (!crtc->enabled) + return; + + for (i = 0; i < 256; i++) { + I915_WRITE(palreg + 4 * i, + (intel_crtc->lut_r[i] << 16) | + (intel_crtc->lut_g[i] << 8) | + intel_crtc->lut_b[i]); + } +} + +/** Sets the color ramps on behalf of RandR */ +static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int size) +{ + struct intel_crtc *intel_crtc = crtc->driver_private; + int i; + + for (i = 0; i < 256; i++) { + intel_crtc->lut_r[i] = red[i] >> 8; + intel_crtc->lut_g[i] = green[i] >> 8; + intel_crtc->lut_b[i] = blue[i] >> 8; + } + + intel_crtc_load_lut(crtc); +} + +struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev, + struct drm_crtc *crtc) +{ + return NULL; +} + +static const struct drm_crtc_funcs intel_crtc_funcs = { + .dpms = intel_crtc_dpms, + .lock = intel_crtc_lock, + .unlock = intel_crtc_unlock, + .mode_fixup = intel_crtc_mode_fixup, + .mode_set = intel_crtc_mode_set, + .gamma_set = intel_crtc_gamma_set, + .prepare = intel_crtc_prepare, + .commit = intel_crtc_commit, +}; + + +void intel_crtc_init(drm_device_t *dev, int pipe) +{ + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + int i; + + crtc = drm_crtc_create(dev, &intel_crtc_funcs); + if (crtc == NULL) + return; + + intel_crtc = kmalloc(sizeof(struct intel_crtc), GFP_KERNEL); + if (intel_crtc == NULL) { + kfree(crtc); + return; + } + + intel_crtc->pipe = pipe; + for (i = 0; i < 256; i++) { + intel_crtc->lut_r[i] = i; + intel_crtc->lut_g[i] = i; + intel_crtc->lut_b[i] = i; + } + + crtc->driver_private = intel_crtc; +} + +int intel_output_clones (drm_device_t *dev, int type_mask) +{ + int index_mask = 0; + struct drm_output *output; + int entry = 0; + + list_for_each_entry(output, &dev->crtc_config.output_list, head) { + struct intel_output *intel_output = output->driver_private; + if (type_mask & (1 << intel_output->type)) + index_mask |= (1 << entry); + entry++; + } + return index_mask; +} + + +static void intel_setup_outputs(drm_device_t *dev) +{ + struct drm_output *output; + + intel_crt_init(dev); + + /* Set up integrated LVDS */ + if (IS_MOBILE(dev) && !IS_I830(dev)) + intel_lvds_init(dev); + + if (IS_I9XX(dev)) { + intel_sdvo_init(dev, SDVOB); + intel_sdvo_init(dev, SDVOC); + } + + list_for_each_entry(output, &dev->crtc_config.output_list, head) { + struct intel_output *intel_output = output->driver_private; + int crtc_mask = 0, clone_mask = 0; + + /* valid crtcs */ + switch(intel_output->type) { + case INTEL_OUTPUT_DVO: + case INTEL_OUTPUT_SDVO: + crtc_mask = ((1 << 0)| + (1 << 1)); + clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | + (1 << INTEL_OUTPUT_DVO) | + (1 << INTEL_OUTPUT_SDVO)); + break; + case INTEL_OUTPUT_ANALOG: + crtc_mask = ((1 << 0)); + clone_mask = ((1 << INTEL_OUTPUT_ANALOG) | + (1 << INTEL_OUTPUT_DVO) | + (1 << INTEL_OUTPUT_SDVO)); + break; + case INTEL_OUTPUT_LVDS: + crtc_mask = (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_LVDS); + break; + case INTEL_OUTPUT_TVOUT: + crtc_mask = ((1 << 0) | + (1 << 1)); + clone_mask = (1 << INTEL_OUTPUT_TVOUT); + break; + } + output->possible_crtcs = crtc_mask; + output->possible_clones = intel_output_clones(dev, clone_mask); + } +} + +void intel_modeset_init(drm_device_t *dev) +{ + int num_pipe; + int i; + + drm_crtc_config_init(dev); + + + if (IS_MOBILE(dev) || IS_I9XX(dev)) + num_pipe = 2; + else + num_pipe = 1; + DRM_DEBUG("%d display pipe%s available.\n", + num_pipe, num_pipe > 1 ? "s" : ""); + + for (i = 0; i < num_pipe; i++) { + intel_crtc_init(dev, i); + } + + intel_setup_outputs(dev); + + drm_initial_config(dev, false); +} + +void intel_modeset_cleanup(drm_device_t *dev) +{ + drm_crtc_config_cleanup(dev); +} diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h new file mode 100644 index 00000000..5b8bef69 --- /dev/null +++ b/linux-core/intel_drv.h @@ -0,0 +1,69 @@ +#ifndef __INTEL_DRV_H__ +#define __INTEL_DRV_H__ + +#include +#include +#include +#include "drm_crtc.h" + +/* + * Display related stuff + */ + +/* store information about an Ixxx DVO */ +/* The i830->i865 use multiple DVOs with multiple i2cs */ +/* the i915, i945 have a single sDVO i2c bus - which is different */ +#define MAX_OUTPUTS 6 + +#define INTEL_I2C_BUS_DVO 1 +#define INTEL_I2C_BUS_SDVO 2 + +/* these are outputs from the chip - integrated only + external chips are via DVO or SDVO output */ +#define INTEL_OUTPUT_UNUSED 0 +#define INTEL_OUTPUT_ANALOG 1 +#define INTEL_OUTPUT_DVO 2 +#define INTEL_OUTPUT_SDVO 3 +#define INTEL_OUTPUT_LVDS 4 +#define INTEL_OUTPUT_TVOUT 5 + +#define INTEL_DVO_CHIP_NONE 0 +#define INTEL_DVO_CHIP_LVDS 1 +#define INTEL_DVO_CHIP_TMDS 2 +#define INTEL_DVO_CHIP_TVOUT 4 + +struct intel_i2c_chan { + drm_device_t *drm_dev; /* for getting at dev. private (mmio etc.) */ + u32 reg; /* GPIO reg */ + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; + u8 slave_addr; +}; + +struct intel_output { + int type; + struct intel_i2c_chan *i2c_bus; /* for control functions */ + struct intel_i2c_chan *ddc_bus; /* for DDC only stuff */ + bool load_detect_tmp; + void *dev_priv; +}; + +struct intel_crtc { + int pipe; + u8 lut_r[256], lut_g[256], lut_b[256]; +}; + +struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg, + const char *name); +void intel_i2c_destroy(struct intel_i2c_chan *chan); +int intel_ddc_get_modes(struct drm_output *output); +extern bool intel_ddc_probe(struct drm_output *output); + +extern void intel_crt_init(drm_device_t *dev); +extern void intel_sdvo_init(drm_device_t *dev, int output_device); +extern void intel_lvds_init(drm_device_t *dev); + +extern void intel_crtc_load_lut(struct drm_crtc *crtc); +extern void intel_output_prepare (struct drm_output *output); +extern void intel_output_commit (struct drm_output *output); +#endif /* __INTEL_DRV_H__ */ diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c new file mode 100644 index 00000000..acae28a0 --- /dev/null +++ b/linux-core/intel_i2c.c @@ -0,0 +1,164 @@ +/* + * Copyright 2006 Dave Airlie + * Copyright © 2006 Intel Corporation + * Eric Anholt + * Jesse Barnes + */ + +#include +#include +#include +#include "drmP.h" +#include "drm.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/* + * Intel GPIO access functions + */ + +#define I2C_RISEFALL_TIME 20 + +static int get_clock(void *data) +{ + struct intel_i2c_chan *chan = data; + drm_i915_private_t *dev_priv = chan->drm_dev->dev_private; + u32 val; + + val = I915_READ(chan->reg); + return ((val & GPIO_CLOCK_VAL_IN) != 0); +} + +static int get_data(void *data) +{ + struct intel_i2c_chan *chan = data; + drm_i915_private_t *dev_priv = chan->drm_dev->dev_private; + u32 val; + + val = I915_READ(chan->reg); + return ((val & GPIO_DATA_VAL_IN) != 0); +} + +static void set_clock(void *data, int state_high) +{ + struct intel_i2c_chan *chan = data; + drm_device_t *dev = chan->drm_dev; + drm_i915_private_t *dev_priv = chan->drm_dev->dev_private; + u32 reserved = 0, clock_bits; + + /* On most chips, these bits must be preserved in software. */ + if (!IS_I830(dev) && !IS_845G(dev)) + reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); + + if (state_high) + clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; + else + clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | + GPIO_CLOCK_VAL_MASK; + I915_WRITE(chan->reg, reserved | clock_bits); + udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ +} + +static void set_data(void *data, int state_high) +{ + struct intel_i2c_chan *chan = data; + drm_device_t *dev = chan->drm_dev; + drm_i915_private_t *dev_priv = chan->drm_dev->dev_private; + u32 reserved = 0, data_bits; + + /* On most chips, these bits must be preserved in software. */ + if (!IS_I830(dev) && !IS_845G(dev)) + reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); + + if (state_high) + data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; + else + data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | + GPIO_DATA_VAL_MASK; + + I915_WRITE(chan->reg, reserved | data_bits); + udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ +} + +/** + * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg + * @dev: DRM device + * @output: driver specific output device + * @reg: GPIO reg to use + * @name: name for this bus + * + * Creates and registers a new i2c bus with the Linux i2c layer, for use + * in output probing and control (e.g. DDC or SDVO control functions). + * + * Possible values for @reg include: + * %GPIOA + * %GPIOB + * %GPIOC + * %GPIOD + * %GPIOE + * %GPIOF + * %GPIOG + * %GPIOH + * see PRM for details on how these different busses are used. + */ +struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg, + const char *name) +{ + struct intel_i2c_chan *chan; + + chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL); + if (!chan) + goto out_free; + + chan->drm_dev = dev; + chan->reg = reg; + snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); + chan->adapter.owner = THIS_MODULE; + chan->adapter.id = I2C_HW_B_INTELFB; + chan->adapter.algo_data = &chan->algo; + chan->adapter.dev.parent = &dev->pdev->dev; + chan->algo.setsda = set_data; + chan->algo.setscl = set_clock; + chan->algo.getsda = get_data; + chan->algo.getscl = get_clock; + chan->algo.udelay = 20; + chan->algo.timeout = usecs_to_jiffies(2200); + chan->algo.data = chan; + + i2c_set_adapdata(&chan->adapter, chan); + + if(i2c_bit_add_bus(&chan->adapter)) + goto out_free; + + /* JJJ: raise SCL and SDA? */ + set_data(chan, 1); + set_clock(chan, 1); + udelay(20); + + return chan; + +out_free: + kfree(chan); + return NULL; +} + +/** + * intel_i2c_destroy - unregister and free i2c bus resources + * @output: channel to free + * + * Unregister the adapter from the i2c layer, then free the structure. + */ +void intel_i2c_destroy(struct intel_i2c_chan *chan) +{ + if (!chan) + return; + + i2c_del_adapter(&chan->adapter); + kfree(chan); +} + + + diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c new file mode 100644 index 00000000..c2ac5679 --- /dev/null +++ b/linux-core/intel_lvds.c @@ -0,0 +1,108 @@ +/* + * Copyright 2006 Dave Airlie + * Copyright © 2006 Intel Corporation + * Eric Anholt + * Jesse Barnes + */ + +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/** + * Sets the backlight level. + * + * \param level backlight level, from 0 to i830_lvds_get_max_backlight(). + */ +static void lvds_set_backlight(drm_device_t *dev, u32 level) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long blc_pwm_ctl; + + level &= BACKLIGHT_DUTY_CYCLE_MASK; + blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; + I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); +} + +/** + * Returns the maximum level of the backlight duty cycle field. + */ +static u32 lvds_get_max_backlight(drm_device_t *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + return ((I915_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >> + BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; +} + +int lvds_backlight(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + unsigned long dvoa_enabled, dvob_enabled, dvoc_enabled, lvds_enabled; + drm_i915_private_t *dev_priv = dev->dev_private; + + printk(KERN_ERR "max backlight value: %d\n", + lvds_get_max_backlight(dev)); + dvoa_enabled = I915_READ(DVOA); + dvob_enabled = I915_READ(DVOB); + dvoc_enabled = I915_READ(DVOC); + lvds_enabled = I915_READ(LVDS); + + printk(KERN_ERR "dvoa_enabled: 0x%08lx\n", dvoa_enabled); + printk(KERN_ERR "dvob_enabled: 0x%08lx\n", dvob_enabled); + printk(KERN_ERR "dvoc_enabled: 0x%08lx\n", dvoc_enabled); + printk(KERN_ERR "lvds_enabled: 0x%08lx\n", lvds_enabled); + printk(KERN_ERR "BLC_PWM_CTL: 0x%08x\n", I915_READ(BLC_PWM_CTL)); + + return 0; +} + +static const struct drm_output_funcs intel_lvds_output_funcs; + +/** + * intel_lvds_init - setup LVDS outputs on this device + * @dev: drm device + * + * Create the output, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void intel_lvds_init(drm_device_t *dev) +{ + struct drm_output *output; + struct intel_output *intel_output; + int modes; + + output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS"); + if (!output) + return; + + intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); + if (!intel_output) { + drm_output_destroy(output); + return; + } + + intel_output->type = INTEL_OUTPUT_LVDS; + output->driver_private = intel_output; + output->subpixel_order = SubPixelHorizontalRGB; + output->interlace_allowed = 0; + output->doublescan_allowed = 0; + + intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return; + } + + modes = intel_ddc_get_modes(output); + printk(KERN_ERR "LVDS: added %d modes from EDID.\n", modes); + intel_i2c_destroy(intel_output->ddc_bus); + drm_output_destroy(output); +} + diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c new file mode 100644 index 00000000..0e56147d --- /dev/null +++ b/linux-core/intel_modes.c @@ -0,0 +1,49 @@ +#include +#include +#include "drmP.h" +#include "intel_drv.h" + +/** + * intel_ddc_probe + * + */ +bool intel_ddc_probe(struct drm_output *output) +{ + struct intel_output *intel_output = output->driver_private; + u8 out_buf[] = { 0x0, 0x0}; + u8 buf[2]; + int ret; + struct i2c_msg msgs[] = { + { + .addr = 0x50, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 1, + .buf = buf, + } + }; + + ret = i2c_transfer(&intel_output->ddc_bus->adapter, msgs, 2); + if (ret == 2) + return true; + + return false; +} + +/** + * intel_ddc_get_modes - get modelist from monitor + * @output: DRM output device to use + * + * Fetch the EDID information from @output using the DDC bus. + */ +int intel_ddc_get_modes(struct drm_output *output) +{ + struct intel_output *intel_output = output->driver_private; + + return drm_add_edid_modes(output, &intel_output->ddc_bus->adapter); +} diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c new file mode 100644 index 00000000..4094b788 --- /dev/null +++ b/linux-core/intel_sdvo.c @@ -0,0 +1,999 @@ +/* + * Copyright 2006 Dave Airlie + * Copyright © 2006 Intel Corporation + * Eric Anholt + * Jesse Barnes + */ + +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" +#include "i915_drm.h" +#include "i915_drv.h" +#include "intel_sdvo_regs.h" + +struct intel_sdvo_priv { + struct intel_i2c_chan *i2c_bus; + int slaveaddr; + int output_device; + + u16 active_outputs; + + struct intel_sdvo_caps caps; + int pixel_clock_min, pixel_clock_max; + + int save_sdvo_mult; + u16 save_active_outputs; + struct intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2; + struct intel_sdvo_dtd save_output_dtd[16]; + u32 save_SDVOX; +}; + +/** + * Writes the SDVOB or SDVOC with the given value, but always writes both + * SDVOB and SDVOC to work around apparent hardware issues (according to + * comments in the BIOS). + */ +static void intel_sdvo_write_sdvox(struct drm_output *output, u32 val) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u32 bval = val, cval = val; + int i; + + if (sdvo_priv->output_device == SDVOB) + cval = I915_READ(SDVOC); + else + bval = I915_READ(SDVOB); + + /* + * Write the registers twice for luck. Sometimes, + * writing them only once doesn't appear to 'stick'. + * The BIOS does this too. Yay, magic + */ + for (i = 0; i < 2; i++) + { + I915_WRITE(SDVOB, bval); + I915_READ(SDVOB); + I915_WRITE(SDVOC, cval); + I915_READ(SDVOC); + } +} + +static bool intel_sdvo_read_byte(struct drm_output *output, u8 addr, + u8 *ch) +{ + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u8 out_buf[2]; + u8 buf[2]; + int ret; + + struct i2c_msg msgs[] = { + { + .addr = sdvo_priv->i2c_bus->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = sdvo_priv->i2c_bus->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if ((ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2)) == 2) + { +// DRM_DEBUG("got back from addr %02X = %02x\n", out_buf[0], buf[0]); + *ch = buf[0]; + return true; + } + + DRM_DEBUG("i2c transfer returned %d\n", ret); + return false; +} + + +static bool intel_sdvo_read_byte_quiet(struct drm_output *output, int addr, + u8 *ch) +{ + return true; + +} + +static bool intel_sdvo_write_byte(struct drm_output *output, int addr, + u8 ch) +{ + struct intel_output *intel_output = output->driver_private; + u8 out_buf[2]; + struct i2c_msg msgs[] = { + { + .addr = intel_output->i2c_bus->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(&intel_output->i2c_bus->adapter, msgs, 1) == 1) + { + return true; + } + return false; +} + +#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd} +/** Mapping of command numbers to names, for debug output */ +const static struct _sdvo_cmd_name { + u8 cmd; + char *name; +} sdvo_cmd_names[] = { + SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_RESOLUTION_SUPPORT), + SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH), +}; + +#define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC") +#define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv) + +static void intel_sdvo_write_cmd(struct drm_output *output, u8 cmd, + void *args, int args_len) +{ + struct intel_output *intel_output = output->driver_private; + int i; + + for (i = 0; i < args_len; i++) { + intel_sdvo_write_byte(output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]); + } + intel_sdvo_write_byte(output, SDVO_I2C_OPCODE, cmd); +} + +static const char *cmd_status_names[] = { + "Power on", + "Success", + "Not supported", + "Invalid arg", + "Pending", + "Target not specified", + "Scaling not supported" +}; + +static u8 intel_sdvo_read_response(struct drm_output *output, void *response, + int response_len) +{ + int i; + u8 status; + + /* Read the command response */ + for (i = 0; i < response_len; i++) { + intel_sdvo_read_byte(output, SDVO_I2C_RETURN_0 + i, + &((u8 *)response)[i]); + } + + /* read the return status */ + intel_sdvo_read_byte(output, SDVO_I2C_CMD_STATUS, &status); + + return status; + +} + +int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode) +{ + if (mode->clock >= 100000) + return 1; + else if (mode->clock >= 50000) + return 2; + else + return 4; +} + +static void intel_sdvo_set_control_bus_switch(struct drm_output *output, u8 target) +{ + intel_sdvo_write_cmd(output, SDVO_CMD_SET_CONTROL_BUS_SWITCH, &target, 1); +} + +static bool intel_sdvo_set_target_input(struct drm_output *output, bool target_0, bool target_1) +{ + struct intel_sdvo_set_target_input_args targets = {0}; + u8 status; + + if (target_0 && target_1) + return SDVO_CMD_STATUS_NOTSUPP; + + if (target_1) + targets.target_1 = 1; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_INPUT, &targets, + sizeof(targets)); + + status = intel_sdvo_read_response(output, NULL, 0); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +/** + * Return whether each input is trained. + * + * This function is making an assumption about the layout of the response, + * which should be checked against the docs. + */ +static bool intel_sdvo_get_trained_inputs(struct drm_output *output, bool *input_1, bool *input_2) +{ + struct intel_sdvo_get_trained_inputs_response response; + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_TRAINED_INPUTS, NULL, 0); + status = intel_sdvo_read_response(output, &response, sizeof(response)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return false; + + *input_1 = response.input0_trained; + *input_2 = response.input1_trained; + return TRUE; +} + +static bool intel_sdvo_get_active_outputs(struct drm_output *output, + u16 *outputs) +{ + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_ACTIVE_OUTPUTS, NULL, 0); + status = intel_sdvo_read_response(output, outputs, sizeof(*outputs)); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_active_outputs(struct drm_output *output, + u16 outputs) +{ + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_ACTIVE_OUTPUTS, &outputs, + sizeof(outputs)); + status = intel_sdvo_read_response(output, NULL, 0); + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_set_encoder_power_state(struct drm_output *output, + int mode) +{ + u8 status, state = SDVO_ENCODER_STATE_ON; + + switch (mode) { + case DPMSModeOn: + state = SDVO_ENCODER_STATE_ON; + break; + case DPMSModeStandby: + state = SDVO_ENCODER_STATE_STANDBY; + break; + case DPMSModeSuspend: + state = SDVO_ENCODER_STATE_SUSPEND; + break; + case DPMSModeOff: + state = SDVO_ENCODER_STATE_OFF; + break; + } + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_ENCODER_POWER_STATE, &state, + sizeof(state)); + status = intel_sdvo_read_response(output, NULL, 0); + + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_input_pixel_clock_range(struct drm_output *output, + int *clock_min, + int *clock_max) +{ + struct intel_sdvo_pixel_clock_range clocks; + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, + NULL, 0); + + status = intel_sdvo_read_response(output, &clocks, sizeof(clocks)); + + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + /* Convert the values from units of 10 kHz to kHz. */ + *clock_min = clocks.min * 10; + *clock_max = clocks.max * 10; + + return TRUE; +} + +static bool intel_sdvo_set_target_output(struct drm_output *output, + u16 outputs) +{ + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_TARGET_OUTPUT, &outputs, + sizeof(outputs)); + + status = intel_sdvo_read_response(output, NULL, 0); + return (status == SDVO_CMD_STATUS_SUCCESS); +} + +static bool intel_sdvo_get_timing(struct drm_output *output, u8 cmd, + struct intel_sdvo_dtd *dtd) +{ + u8 status; + + intel_sdvo_write_cmd(output, cmd, NULL, 0); + status = intel_sdvo_read_response(output, &dtd->part1, + sizeof(dtd->part1)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + intel_sdvo_write_cmd(output, cmd + 1, NULL, 0); + status = intel_sdvo_read_response(output, &dtd->part2, + sizeof(dtd->part2)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + return TRUE; +} + +static bool intel_sdvo_get_input_timing(struct drm_output *output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_get_timing(output, + SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_get_output_timing(struct drm_output *output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_get_timing(output, + SDVO_CMD_GET_OUTPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_timing(struct drm_output *output, u8 cmd, + struct intel_sdvo_dtd *dtd) +{ + u8 status; + + intel_sdvo_write_cmd(output, cmd, &dtd->part1, sizeof(dtd->part1)); + status = intel_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + intel_sdvo_write_cmd(output, cmd + 1, &dtd->part2, sizeof(dtd->part2)); + status = intel_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + return TRUE; +} + +static bool intel_sdvo_set_input_timing(struct drm_output *output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_set_timing(output, + SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); +} + +static bool intel_sdvo_set_output_timing(struct drm_output *output, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_set_timing(output, + SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); +} + +#if 0 +static bool intel_sdvo_get_preferred_input_timing(struct drm_output *output, + struct intel_sdvo_dtd *dtd) +{ + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1, + NULL, 0); + + status = intel_sdvo_read_response(output, &dtd->part1, + sizeof(dtd->part1)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, + NULL, 0); + status = intel_sdvo_read_response(output, &dtd->part2, + sizeof(dtd->part2)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + return TRUE; +} +#endif + +static int intel_sdvo_get_clock_rate_mult(struct drm_output *output) +{ + struct intel_output *intel_output = output->driver_private; + u8 response, status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_CLOCK_RATE_MULT, NULL, 0); + status = intel_sdvo_read_response(output, &response, 1); + + if (status != SDVO_CMD_STATUS_SUCCESS) { + DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n"); + return SDVO_CLOCK_RATE_MULT_1X; + } else { + DRM_DEBUG("Current clock rate multiplier: %d\n", response); + } + + return response; +} + +static bool intel_sdvo_set_clock_rate_mult(struct drm_output *output, u8 val) +{ + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1); + status = intel_sdvo_read_response(output, NULL, 0); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + return TRUE; +} + +static bool intel_sdvo_mode_fixup(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* Make the CRTC code factor in the SDVO pixel multiplier. The SDVO + * device will be told of the multiplier during mode_set. + */ + adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); + return TRUE; +} + +static void intel_sdvo_mode_set(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_crtc *crtc = output->crtc; + struct intel_crtc *intel_crtc = crtc->driver_private; + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u16 width, height; + u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len; + u16 h_sync_offset, v_sync_offset; + u32 sdvox; + struct intel_sdvo_dtd output_dtd; + int sdvo_pixel_multiply; + + if (!mode) + return; + + width = mode->crtc_hdisplay; + height = mode->crtc_vdisplay; + + /* do some mode translations */ + h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; + h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + + v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; + v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + + h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; + v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + + output_dtd.part1.clock = mode->clock / 10; + output_dtd.part1.h_active = width & 0xff; + output_dtd.part1.h_blank = h_blank_len & 0xff; + output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) | + ((h_blank_len >> 8) & 0xf); + output_dtd.part1.v_active = height & 0xff; + output_dtd.part1.v_blank = v_blank_len & 0xff; + output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) | + ((v_blank_len >> 8) & 0xf); + + output_dtd.part2.h_sync_off = h_sync_offset; + output_dtd.part2.h_sync_width = h_sync_len & 0xff; + output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 | + (v_sync_len & 0xf); + output_dtd.part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) | + ((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) | + ((v_sync_len & 0x30) >> 4); + + output_dtd.part2.dtd_flags = 0x18; + if (mode->flags & V_PHSYNC) + output_dtd.part2.dtd_flags |= 0x2; + if (mode->flags & V_PVSYNC) + output_dtd.part2.dtd_flags |= 0x4; + + output_dtd.part2.sdvo_flags = 0; + output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0; + output_dtd.part2.reserved = 0; + + /* Set the output timing to the screen */ + intel_sdvo_set_target_output(output, sdvo_priv->active_outputs); + intel_sdvo_set_output_timing(output, &output_dtd); + + /* Set the input timing to the screen. Assume always input 0. */ + intel_sdvo_set_target_input(output, TRUE, FALSE); + + /* We would like to use i830_sdvo_create_preferred_input_timing() to + * provide the device with a timing it can support, if it supports that + * feature. However, presumably we would need to adjust the CRTC to + * output the preferred timing, and we don't support that currently. + */ +#if 0 + success = intel_sdvo_create_preferred_input_timing(output, clock, + width, height); + if (success) { + struct intel_sdvo_dtd *input_dtd; + + intel_sdvo_get_preferred_input_timing(output, &input_dtd); + intel_sdvo_set_input_timing(output, &input_dtd); + } +#else + intel_sdvo_set_input_timing(output, &output_dtd); +#endif + + switch (intel_sdvo_get_pixel_multiplier(mode)) { + case 1: + intel_sdvo_set_clock_rate_mult(output, + SDVO_CLOCK_RATE_MULT_1X); + break; + case 2: + intel_sdvo_set_clock_rate_mult(output, + SDVO_CLOCK_RATE_MULT_2X); + break; + case 4: + intel_sdvo_set_clock_rate_mult(output, + SDVO_CLOCK_RATE_MULT_4X); + break; + } + + /* Set the SDVO control regs. */ + sdvox = I915_READ(sdvo_priv->output_device); + switch (sdvo_priv->output_device) { + case SDVOB: + sdvox &= SDVOB_PRESERVE_MASK; + break; + case SDVOC: + sdvox &= SDVOC_PRESERVE_MASK; + break; + } + sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; + if (intel_crtc->pipe == 1) + sdvox |= SDVO_PIPE_B_SELECT; + + sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode); + if (IS_I965G(dev)) { + /* done in crtc_mode_set as the dpll_md reg must be written + early */ + } else if (IS_I945G(dev) || IS_I945GM(dev)) { + /* done in crtc_mode_set as it lives inside the + dpll register */ + } else { + sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; + } + + intel_sdvo_write_sdvox(output, sdvox); +} + +static void intel_sdvo_dpms(struct drm_output *output, int mode) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + u32 temp; + + if (mode != DPMSModeOn) { + intel_sdvo_set_active_outputs(output, 0); + if (0) + intel_sdvo_set_encoder_power_state(output, mode); + + if (mode == DPMSModeOff) { + temp = I915_READ(sdvo_priv->output_device); + if ((temp & SDVO_ENABLE) != 0) { + intel_sdvo_write_sdvox(output, temp & ~SDVO_ENABLE); + } + } + } else { + bool input1, input2; + int i; + u8 status; + + temp = I915_READ(sdvo_priv->output_device); + if ((temp & SDVO_ENABLE) == 0) + intel_sdvo_write_sdvox(output, temp | SDVO_ENABLE); + for (i = 0; i < 2; i++) + intel_wait_for_vblank(dev); + + status = intel_sdvo_get_trained_inputs(output, &input1, + &input2); + + + /* Warn if the device reported failure to sync. */ + if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { + DRM_ERROR("First %s output reported failure to sync\n", + SDVO_NAME(sdvo_priv)); + } + + if (0) + intel_sdvo_set_encoder_power_state(output, mode); + intel_sdvo_set_active_outputs(output, sdvo_priv->active_outputs); + } + return; +} + +static void intel_sdvo_save(struct drm_output *output) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int o; + + sdvo_priv->save_sdvo_mult = intel_sdvo_get_clock_rate_mult(output); + intel_sdvo_get_active_outputs(output, &sdvo_priv->save_active_outputs); + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { + intel_sdvo_set_target_input(output, TRUE, FALSE); + intel_sdvo_get_input_timing(output, + &sdvo_priv->save_input_dtd_1); + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { + intel_sdvo_set_target_input(output, FALSE, TRUE); + intel_sdvo_get_input_timing(output, + &sdvo_priv->save_input_dtd_2); + } + + for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++) + { + u16 this_output = (1 << o); + if (sdvo_priv->caps.output_flags & this_output) + { + intel_sdvo_set_target_output(output, this_output); + intel_sdvo_get_output_timing(output, + &sdvo_priv->save_output_dtd[o]); + } + } + + sdvo_priv->save_SDVOX = I915_READ(sdvo_priv->output_device); +} + +static void intel_sdvo_restore(struct drm_output *output) +{ + drm_device_t *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + int o; + int i; + bool input1, input2; + u8 status; + + intel_sdvo_set_active_outputs(output, 0); + + for (o = SDVO_OUTPUT_FIRST; o <= SDVO_OUTPUT_LAST; o++) + { + u16 this_output = (1 << o); + if (sdvo_priv->caps.output_flags & this_output) { + intel_sdvo_set_target_output(output, this_output); + intel_sdvo_set_output_timing(output, &sdvo_priv->save_output_dtd[o]); + } + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { + intel_sdvo_set_target_input(output, TRUE, FALSE); + intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_1); + } + + if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { + intel_sdvo_set_target_input(output, FALSE, TRUE); + intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_2); + } + + intel_sdvo_set_clock_rate_mult(output, sdvo_priv->save_sdvo_mult); + + I915_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX); + + if (sdvo_priv->save_SDVOX & SDVO_ENABLE) + { + for (i = 0; i < 2; i++) + intel_wait_for_vblank(dev); + status = intel_sdvo_get_trained_inputs(output, &input1, &input2); + if (status == SDVO_CMD_STATUS_SUCCESS && !input1) + DRM_DEBUG("First %s output reported failure to sync\n", + SDVO_NAME(sdvo_priv)); + } + + intel_sdvo_set_active_outputs(output, sdvo_priv->save_active_outputs); +} + +static int intel_sdvo_mode_valid(struct drm_output *output, + struct drm_display_mode *mode) +{ + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + + if (mode->flags & V_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (sdvo_priv->pixel_clock_min > mode->clock) + return MODE_CLOCK_HIGH; + + if (sdvo_priv->pixel_clock_max < mode->clock) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static bool intel_sdvo_get_capabilities(struct drm_output *output, struct intel_sdvo_caps *caps) +{ + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0); + status = intel_sdvo_read_response(output, caps, sizeof(*caps)); + if (status != SDVO_CMD_STATUS_SUCCESS) + return FALSE; + + return TRUE; +} + + +static void intel_sdvo_dump_cmd(struct drm_output *output, int opcode) +{ + + +} + +static void intel_sdvo_dump_device(struct drm_output *output) +{ + +} + +void intel_sdvo_dump(void) +{ + +} + + +static enum drm_output_status intel_sdvo_detect(struct drm_output *output) +{ + u8 response[2]; + u8 status; + + intel_sdvo_write_cmd(output, SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0); + status = intel_sdvo_read_response(output, &response, 2); + + if (status != SDVO_CMD_STATUS_SUCCESS) + return output_status_unknown; + + DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]); + if ((response[0] != 0) || (response[1] != 0)) + return output_status_connected; + else + return output_status_disconnected; +} + +static int intel_sdvo_get_modes(struct drm_output *output) +{ + struct drm_display_mode *modes; + + /* set the bus switch and get the modes */ + intel_sdvo_set_control_bus_switch(output, SDVO_CONTROL_BUS_DDC2); + intel_ddc_get_modes(output); + + if (list_empty(&output->probed_modes)) + return 0; + return 1; +#if 0 + /* Mac mini hack. On this device, I get DDC through the analog, which + * load-detects as disconnected. I fail to DDC through the SDVO DDC, + * but it does load-detect as connected. So, just steal the DDC bits + * from analog when we fail at finding it the right way. + */ + /* TODO */ + return NULL; + + return NULL; +#endif +} + +static void intel_sdvo_destroy(struct drm_output *output) +{ + struct intel_output *intel_output = output->driver_private; + + if (intel_output->i2c_bus) + intel_i2c_destroy(intel_output->i2c_bus); + + if (intel_output) { + kfree(intel_output); + output->driver_private = NULL; + } +} + +static const struct drm_output_funcs intel_sdvo_output_funcs = { + .dpms = intel_sdvo_dpms, + .save = intel_sdvo_save, + .restore = intel_sdvo_restore, + .mode_valid = intel_sdvo_mode_valid, + .mode_fixup = intel_sdvo_mode_fixup, + .prepare = intel_output_prepare, + .mode_set = intel_sdvo_mode_set, + .commit = intel_output_commit, + .detect = intel_sdvo_detect, + .get_modes = intel_sdvo_get_modes, + .cleanup = intel_sdvo_destroy +}; + +void intel_sdvo_init(drm_device_t *dev, int output_device) +{ + struct drm_output *output; + struct intel_output *intel_output; + struct intel_sdvo_priv *sdvo_priv; + struct intel_i2c_chan *i2cbus = NULL; + u8 ch[0x40]; + int i; + char name[DRM_OUTPUT_LEN]; + char *name_prefix; + char *name_suffix; + + + output = drm_output_create(dev, &intel_sdvo_output_funcs, NULL); + if (!output) + return; + + intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL); + if (!intel_output) { + drm_output_destroy(output); + return; + } + + sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1); + intel_output->type = INTEL_OUTPUT_SDVO; + output->driver_private = intel_output; + output->interlace_allowed = 0; + output->doublescan_allowed = 0; + + /* setup the DDC bus. */ + if (output_device == SDVOB) + i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB"); + else + i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC"); + + if (i2cbus == NULL) { + drm_output_destroy(output); + return; + } + + sdvo_priv->i2c_bus = i2cbus; + + if (output_device == SDVOB) { + name_suffix = "-1"; + sdvo_priv->i2c_bus->slave_addr = 0x38; + } else { + name_suffix = "-2"; + sdvo_priv->i2c_bus->slave_addr = 0x39; + } + + sdvo_priv->output_device = output_device; + intel_output->i2c_bus = i2cbus; + intel_output->dev_priv = sdvo_priv; + + + /* Read the regs to test if we can talk to the device */ + for (i = 0; i < 0x40; i++) { + if (!intel_sdvo_read_byte(output, i, &ch[i])) { + DRM_DEBUG("No SDVO device found on SDVO%c\n", + output_device == SDVOB ? 'B' : 'C'); + drm_output_destroy(output); + return; + } + } + + intel_sdvo_get_capabilities(output, &sdvo_priv->caps); + + memset(&sdvo_priv->active_outputs, 0, sizeof(sdvo_priv->active_outputs)); + + if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="TMDS"; + } + else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) + { + sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1; + output->subpixel_order = SubPixelHorizontalRGB; + name_prefix="TMDS"; + } + else + { + unsigned char bytes[2]; + + memcpy (bytes, &sdvo_priv->caps.output_flags, 2); + DRM_DEBUG("%s: No active TMDS outputs (0x%02x%02x)\n", + SDVO_NAME(sdvo_priv), + bytes[0], bytes[1]); + } + strcpy (name, name_prefix); + strcat (name, name_suffix); + if (!drm_output_rename(output, name)) + { + drm_output_destroy(output); + return; + } + + + /* Set the input timing to the screen. Assume always input 0. */ + intel_sdvo_set_target_input(output, TRUE, FALSE); + + intel_sdvo_get_input_pixel_clock_range(output, + &sdvo_priv->pixel_clock_min, + &sdvo_priv->pixel_clock_max); + + + DRM_DEBUG("%s device VID/DID: %02X:%02X.%02X, " + "clock range %dMHz - %dMHz, " + "input 1: %c, input 2: %c, " + "output 1: %c, output 2: %c\n", + SDVO_NAME(sdvo_priv), + sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id, + sdvo_priv->caps.device_rev_id, + sdvo_priv->pixel_clock_min / 1000, + sdvo_priv->pixel_clock_max / 1000, + (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N', + (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N', + sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0 ? 'Y' : 'N', + sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1 ? 'Y' : 'N'); + + intel_output->ddc_bus = i2cbus; +} diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h new file mode 100644 index 00000000..b8cb1dc6 --- /dev/null +++ b/linux-core/intel_sdvo_regs.h @@ -0,0 +1,302 @@ +/* + * Copyright © 2006 Intel Corporation + */ + +/** + * @file SDVO command definitions and structures. + */ + +#define SDVO_OUTPUT_FIRST (0) +#define SDVO_OUTPUT_TMDS0 (1 << 0) +#define SDVO_OUTPUT_RGB0 (1 << 1) +#define SDVO_OUTPUT_CVBS0 (1 << 2) +#define SDVO_OUTPUT_SVID0 (1 << 3) +#define SDVO_OUTPUT_YPRPB0 (1 << 4) +#define SDVO_OUTPUT_SCART0 (1 << 5) +#define SDVO_OUTPUT_LVDS0 (1 << 6) +#define SDVO_OUTPUT_TMDS1 (1 << 8) +#define SDVO_OUTPUT_RGB1 (1 << 13) +#define SDVO_OUTPUT_LVDS1 (1 << 14) +#define SDVO_OUTPUT_LAST (14) + +struct intel_sdvo_caps { + u8 vendor_id; + u8 device_id; + u8 device_rev_id; + u8 sdvo_version_major; + u8 sdvo_version_minor; + unsigned int sdvo_inputs_mask:2; + unsigned int smooth_scaling:1; + unsigned int sharp_scaling:1; + unsigned int up_scaling:1; + unsigned int down_scaling:1; + unsigned int stall_support:1; + unsigned int pad:1; + u16 output_flags; +} __attribute__((packed)); + +/** This matches the EDID DTD structure, more or less */ +struct intel_sdvo_dtd { + struct { + u16 clock; /**< pixel clock, in 10kHz units */ + u8 h_active; /**< lower 8 bits (pixels) */ + u8 h_blank; /**< lower 8 bits (pixels) */ + u8 h_high; /**< upper 4 bits each h_active, h_blank */ + u8 v_active; /**< lower 8 bits (lines) */ + u8 v_blank; /**< lower 8 bits (lines) */ + u8 v_high; /**< upper 4 bits each v_active, v_blank */ + } part1; + + struct { + u8 h_sync_off; /**< lower 8 bits, from hblank start */ + u8 h_sync_width; /**< lower 8 bits (pixels) */ + /** lower 4 bits each vsync offset, vsync width */ + u8 v_sync_off_width; + /** + * 2 high bits of hsync offset, 2 high bits of hsync width, + * bits 4-5 of vsync offset, and 2 high bits of vsync width. + */ + u8 sync_off_width_high; + u8 dtd_flags; + u8 sdvo_flags; + /** bits 6-7 of vsync offset at bits 6-7 */ + u8 v_sync_off_high; + u8 reserved; + } part2; +} __attribute__((packed)); + +struct intel_sdvo_pixel_clock_range { + u16 min; /**< pixel clock, in 10kHz units */ + u16 max; /**< pixel clock, in 10kHz units */ +} __attribute__((packed)); + +struct intel_sdvo_preferred_input_timing_args { + u16 clock; + u16 width; + u16 height; +} __attribute__((packed)); + +/* I2C registers for SDVO */ +#define SDVO_I2C_ARG_0 0x07 +#define SDVO_I2C_ARG_1 0x06 +#define SDVO_I2C_ARG_2 0x05 +#define SDVO_I2C_ARG_3 0x04 +#define SDVO_I2C_ARG_4 0x03 +#define SDVO_I2C_ARG_5 0x02 +#define SDVO_I2C_ARG_6 0x01 +#define SDVO_I2C_ARG_7 0x00 +#define SDVO_I2C_OPCODE 0x08 +#define SDVO_I2C_CMD_STATUS 0x09 +#define SDVO_I2C_RETURN_0 0x0a +#define SDVO_I2C_RETURN_1 0x0b +#define SDVO_I2C_RETURN_2 0x0c +#define SDVO_I2C_RETURN_3 0x0d +#define SDVO_I2C_RETURN_4 0x0e +#define SDVO_I2C_RETURN_5 0x0f +#define SDVO_I2C_RETURN_6 0x10 +#define SDVO_I2C_RETURN_7 0x11 +#define SDVO_I2C_VENDOR_BEGIN 0x20 + +/* Status results */ +#define SDVO_CMD_STATUS_POWER_ON 0x0 +#define SDVO_CMD_STATUS_SUCCESS 0x1 +#define SDVO_CMD_STATUS_NOTSUPP 0x2 +#define SDVO_CMD_STATUS_INVALID_ARG 0x3 +#define SDVO_CMD_STATUS_PENDING 0x4 +#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5 +#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6 + +/* SDVO commands, argument/result registers */ + +#define SDVO_CMD_RESET 0x01 + +/** Returns a struct intel_sdvo_caps */ +#define SDVO_CMD_GET_DEVICE_CAPS 0x02 + +#define SDVO_CMD_GET_FIRMWARE_REV 0x86 +# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0 +# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1 +# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2 + +/** + * Reports which inputs are trained (managed to sync). + * + * Devices must have trained within 2 vsyncs of a mode change. + */ +#define SDVO_CMD_GET_TRAINED_INPUTS 0x03 +struct intel_sdvo_get_trained_inputs_response { + unsigned int input0_trained:1; + unsigned int input1_trained:1; + unsigned int pad:6; +} __attribute__((packed)); + +/** Returns a struct intel_sdvo_output_flags of active outputs. */ +#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04 + +/** + * Sets the current set of active outputs. + * + * Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP + * on multi-output devices. + */ +#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05 + +/** + * Returns the current mapping of SDVO inputs to outputs on the device. + * + * Returns two struct intel_sdvo_output_flags structures. + */ +#define SDVO_CMD_GET_IN_OUT_MAP 0x06 + +/** + * Sets the current mapping of SDVO inputs to outputs on the device. + * + * Takes two struct i380_sdvo_output_flags structures. + */ +#define SDVO_CMD_SET_IN_OUT_MAP 0x07 + +/** + * Returns a struct intel_sdvo_output_flags of attached displays. + */ +#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b + +/** + * Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging. + */ +#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c + +/** + * Takes a struct intel_sdvo_output_flags. + */ +#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d + +/** + * Returns a struct intel_sdvo_output_flags of displays with hot plug + * interrupts enabled. + */ +#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e + +#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f +struct intel_sdvo_get_interrupt_event_source_response { + u16 interrupt_status; + unsigned int ambient_light_interrupt:1; + unsigned int pad:7; +} __attribute__((packed)); + +/** + * Selects which input is affected by future input commands. + * + * Commands affected include SET_INPUT_TIMINGS_PART[12], + * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12], + * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS. + */ +#define SDVO_CMD_SET_TARGET_INPUT 0x10 +struct intel_sdvo_set_target_input_args { + unsigned int target_1:1; + unsigned int pad:7; +} __attribute__((packed)); + +/** + * Takes a struct intel_sdvo_output_flags of which outputs are targetted by + * future output commands. + * + * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12], + * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE. + */ +#define SDVO_CMD_SET_TARGET_OUTPUT 0x11 + +#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12 +#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14 +#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16 +#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18 +#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19 +/* Part 1 */ +# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2 +# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3 +# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4 +# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5 +# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6 +# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7 +/* Part 2 */ +# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0 +# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1 +# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2 +# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4 +# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7) +# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5) +# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3) +# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1) +# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5 +# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7) +# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6) +# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6) +# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4) +# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4) +# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6 + +/** + * Generates a DTD based on the given width, height, and flags. + * + * This will be supported by any device supporting scaling or interlaced + * modes. + */ +#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0 +# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2 +# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4 +# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6 +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0) +# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1) + +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b +#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c + +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d +/** Returns a struct intel_sdvo_pixel_clock_range */ +#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e + +/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */ +#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f + +/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20 +/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */ +#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21 +# define SDVO_CLOCK_RATE_MULT_1X (1 << 0) +# define SDVO_CLOCK_RATE_MULT_2X (1 << 1) +# define SDVO_CLOCK_RATE_MULT_4X (1 << 3) + +#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27 + +#define SDVO_CMD_GET_TV_FORMAT 0x28 + +#define SDVO_CMD_SET_TV_FORMAT 0x29 + +#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a +#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b +#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c +# define SDVO_ENCODER_STATE_ON (1 << 0) +# define SDVO_ENCODER_STATE_STANDBY (1 << 1) +# define SDVO_ENCODER_STATE_SUSPEND (1 << 2) +# define SDVO_ENCODER_STATE_OFF (1 << 3) + +#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT 0x93 + +#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a +# define SDVO_CONTROL_BUS_PROM 0x0 +# define SDVO_CONTROL_BUS_DDC1 0x1 +# define SDVO_CONTROL_BUS_DDC2 0x2 +# define SDVO_CONTROL_BUS_DDC3 0x3 + -- cgit v1.2.3 From 5bffbd6e275efffbb649c20c528a11412ccf99cd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 13:34:50 +1000 Subject: initial userspace interface to get modes --- linux-core/drm_crtc.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++- linux-core/drm_crtc.h | 15 +++++- linux-core/drm_drv.c | 1 + linux-core/drm_edid.c | 21 +++++---- linux-core/drm_modes.c | 7 ++- 5 files changed, 155 insertions(+), 13 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index a52d82bc..1311fa63 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -154,7 +154,7 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, bool ret = false; struct drm_output *output; - adjusted_mode = drm_mode_duplicate(mode); + adjusted_mode = drm_mode_duplicate(dev, mode); crtc->enabled = drm_crtc_in_use(crtc); @@ -230,6 +230,7 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, } /* XXX free adjustedmode */ + drm_crtc_mode_destroy(dev, adjusted_mode); ret = TRUE; /* TODO */ // if (scrn->pScreen) @@ -401,12 +402,48 @@ bool drm_output_rename(struct drm_output *output, const char *name) } EXPORT_SYMBOL(drm_output_rename); +struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev) +{ + int ret; + struct drm_display_mode *nmode; + + nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!nmode) + return NULL; + +again: + if (idr_pre_get(&dev->crtc_config.mode_idr, GFP_KERNEL) == 0) { + DRM_ERROR("Ran out memory getting a mode number\n"); + kfree(nmode); + return NULL; + } + + spin_lock(&dev->crtc_config.config_lock); + + ret = idr_get_new(&dev->crtc_config.mode_idr, nmode, &nmode->mode_id); + if (ret == -EAGAIN) { + udelay(1); + spin_unlock(&dev->crtc_config.config_lock); + goto again; + } + spin_unlock(&dev->crtc_config.config_lock); + return nmode; +} + +void drm_crtc_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) +{ + idr_remove(&dev->crtc_config.mode_idr, mode->mode_id); + + kfree(mode); +} + 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); + idr_init(&dev->crtc_config.mode_idr); } EXPORT_SYMBOL(drm_crtc_config_init); @@ -441,7 +478,7 @@ void drm_framebuffer_set_object(drm_device_t *dev, unsigned long handle) ret = 0; out_err: mutex_unlock(&dev->struct_mutex); - return ret; + return; } EXPORT_SYMBOL(drm_framebuffer_set_object); @@ -538,3 +575,86 @@ void drm_crtc_config_cleanup(drm_device_t *dev) } EXPORT_SYMBOL(drm_crtc_config_cleanup); +void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in) +{ + + out->id = in->mode_id; + out->clock = in->clock; + out->hdisplay = in->hdisplay; + out->hsync_start = in->hsync_start; + out->hsync_end = in->hsync_end; + out->htotal = in->htotal; + out->hskew = in->hskew; + out->vdisplay = in->vdisplay; + out->vsync_start = in->vsync_start; + out->vsync_end = in->vsync_end; + out->vtotal = in->vtotal; + out->vscan = in->vscan; + + out->flags = in->flags; +} + + +/* IOCTL code from userspace */ +int drm_mode_getresources(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_card_res __user *argp = (void __user *)arg; + struct drm_mode_card_res card_res; + struct list_head *lh; + struct drm_output *output; + struct drm_mode_modeinfo u_mode; + struct drm_display_mode *mode; + int retcode = 0; + int mode_count= 0; + + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + list_for_each_entry(output, &dev->crtc_config.output_list, + head) + { + list_for_each(lh, &output->modes) + mode_count++; + } + + if (copy_from_user(&card_res, argp, sizeof(card_res))) + return -EFAULT; + + if (card_res.count_modes >= mode_count) { + int copied = 0; + list_for_each_entry(output, &dev->crtc_config.output_list, + head) { + list_for_each_entry(mode, &output->modes, head) { + drm_crtc_convert_to_umode(&u_mode, mode); + if (copy_to_user(&card_res.modes[copied++], &u_mode, sizeof(struct drm_mode_modeinfo))) { + retcode = -EFAULT; + goto done; + } + } + } + } + + else { + list_for_each(lh, &dev->crtc_config.crtc_list) + card_res.count_crtcs++; + + list_for_each_entry(output, &dev->crtc_config.output_list, + head) + { + list_for_each(lh, &output->modes) + card_res.count_modes++; + card_res.count_outputs++; + } + } + +done: + DRM_DEBUG("Counted %d %d %d\n", card_res.count_crtcs, + card_res.count_outputs, + card_res.count_modes); + + if (copy_to_user(argp, &card_res, sizeof(card_res))) + return -EFAULT; + + return retcode; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 1be115d1..003946bc 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "drmP.h" #include "drm.h" @@ -86,6 +87,7 @@ struct drm_display_mode { /* Header */ struct list_head head; char name[DRM_DISPLAY_MODE_LEN]; + int mode_id; enum drm_mode_status status; int type; @@ -392,6 +394,7 @@ struct drm_crtc_config_funcs { */ struct drm_crtc_config { spinlock_t config_lock; + struct idr mode_idr; /* this is limited to one for now */ int num_fb; struct list_head fb_list; @@ -419,10 +422,20 @@ int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter); void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode); void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode); -extern struct drm_display_mode *drm_mode_duplicate(struct drm_display_mode *mode); +extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, + struct drm_display_mode *mode); extern void drm_mode_debug_printmodeline(struct drm_device *dev, struct drm_display_mode *mode); extern void drm_crtc_config_init(struct drm_device *dev); extern void drm_crtc_config_cleanup(struct drm_device *dev); extern void drm_disable_unused_functions(struct drm_device *dev); + +extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); +extern void drm_crtc_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); + +/* IOCTLs */ +extern int drm_mode_getresources(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + #endif /* __DRM_CRTC_H__ */ + diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index b95f796f..9877db13 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -123,6 +123,7 @@ static drm_ioctl_desc_t drm_ioctls[] = { DRM_AUTH }, [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW)] = {drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETRESOURCES)] = {drm_mode_getresources, DRM_ROOT_ONLY}, }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 3c123751..bf1ea94c 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -212,7 +212,8 @@ bad: * * Punts for now. */ -struct drm_display_mode *drm_mode_std(struct std_timing *t) +struct drm_display_mode *drm_mode_std(struct drm_device *dev, + struct std_timing *t) { // struct fb_videomode mode; @@ -221,7 +222,7 @@ struct drm_display_mode *drm_mode_std(struct std_timing *t) struct drm_display_mode *mode; int hsize = t->hsize * 8 + 248, vsize; - mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + mode = drm_crtc_mode_create(dev); if (!mode) return NULL; @@ -239,7 +240,8 @@ struct drm_display_mode *drm_mode_std(struct std_timing *t) return mode; } -struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing, +struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, + struct detailed_timing *timing, bool preferred) { struct drm_display_mode *mode; @@ -254,7 +256,7 @@ struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing, return NULL; } - mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + mode = drm_crtc_mode_create(dev); if (!mode) return NULL; @@ -357,6 +359,7 @@ static struct drm_display_mode established_modes[] = { */ static int add_established_modes(struct drm_output *output, struct edid *edid) { + struct drm_device *dev = output->dev; unsigned long est_bits = edid->established_timings.t1 | (edid->established_timings.t2 << 8) | ((edid->established_timings.mfg_rsvd & 0x80) << 9); @@ -365,7 +368,7 @@ static int add_established_modes(struct drm_output *output, struct edid *edid) for (i = 0; i <= EDID_EST_TIMINGS; i++) if (est_bits & (1<dev; for (i = 0; i < EDID_STD_TIMINGS; i++) { struct std_timing *t = &edid->standard_timings[i]; @@ -391,7 +395,7 @@ static int add_standard_modes(struct drm_output *output, struct edid *edid) continue; drm_mode_probed_add(output, - drm_mode_std(&edid->standard_timings[i])); + drm_mode_std(dev, &edid->standard_timings[i])); modes++; } @@ -409,6 +413,7 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) { int i, j, modes = 0; bool preferred = 0; + struct drm_device *dev = output->dev; for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { struct detailed_timing *timing = &edid->detailed_timings[i]; @@ -423,7 +428,7 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) if (i == 0 && edid->preferred_timing) preferred = 1; drm_mode_probed_add(output, - drm_mode_detailed(timing, preferred)); + drm_mode_detailed(dev, timing, preferred)); modes++; continue; } @@ -446,7 +451,7 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) struct std_timing *std; std = &data->data.timings[j]; - drm_mode_probed_add(output, drm_mode_std(std)); + drm_mode_probed_add(output, drm_mode_std(dev, std)); modes++; } break; diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index 2347a669..940fc981 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -115,15 +115,18 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) EXPORT_SYMBOL(drm_mode_set_crtcinfo); -struct drm_display_mode *drm_mode_duplicate(struct drm_display_mode *mode) +struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *mode) { struct drm_display_mode *nmode; + int new_id; - nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + nmode = drm_crtc_mode_create(dev); if (!nmode) return NULL; + new_id = nmode->mode_id; *nmode = *mode; + nmode->mode_id = new_id; INIT_LIST_HEAD(&nmode->head); return nmode; } -- cgit v1.2.3 From 7bb112fecadc6fe42e5828b861600691071ccd91 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 17:06:42 +1000 Subject: checkpoint commit: added getresources, crtc and output This adds the user interfaces from Jakob and hooks them up for 3 ioctls GetResources, GetCrtc and GetOutput. I've made the ids for everything fbs, crtcs, outputs and modes go via idr as per krh's suggestion on irc as it make the code nice and consistent. --- linux-core/drm_crtc.c | 218 +++++++++++++++++++++++++++++++++++++++++--------- linux-core/drm_crtc.h | 12 ++- linux-core/drm_drv.c | 4 +- 3 files changed, 192 insertions(+), 42 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 1311fa63..2dbe6de1 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -3,6 +3,33 @@ #include "drm.h" #include "drm_crtc.h" +int drm_mode_idr_get(struct drm_device *dev, void *ptr) +{ + int new_id = 0; + int ret; +again: + if (idr_pre_get(&dev->crtc_config.crtc_idr, GFP_KERNEL) == 0) { + DRM_ERROR("Ran out memory getting a mode number\n"); + return 0; + } + + spin_lock(&dev->crtc_config.config_lock); + + ret = idr_get_new_above(&dev->crtc_config.crtc_idr, ptr, 1, &new_id); + if (ret == -EAGAIN) { + spin_unlock(&dev->crtc_config.config_lock); + goto again; + } + + spin_unlock(&dev->crtc_config.config_lock); + return new_id; +} + +void drm_mode_idr_put(struct drm_device *dev, int id) +{ + idr_remove(&dev->crtc_config.crtc_idr, id); +} + struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev) { struct drm_framebuffer *fb; @@ -20,7 +47,8 @@ struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev) return NULL; } - + + fb->id = drm_mode_idr_get(dev, fb); fb->dev = dev; spin_lock(&dev->crtc_config.config_lock); dev->crtc_config.num_fb++; @@ -35,6 +63,7 @@ void drm_framebuffer_destroy(struct drm_framebuffer *fb) drm_device_t *dev = fb->dev; spin_lock(&dev->crtc_config.config_lock); + drm_mode_idr_put(dev, fb->id); list_del(&fb->head); dev->crtc_config.num_fb--; spin_unlock(&dev->crtc_config.config_lock); @@ -45,19 +74,21 @@ void drm_framebuffer_destroy(struct drm_framebuffer *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); + struct drm_crtc *crtc; + + crtc = kzalloc(sizeof(struct drm_crtc), GFP_KERNEL); if (!crtc) return NULL; crtc->dev = dev; crtc->funcs = funcs; - spin_lock(&dev->crtc_config.config_lock); + crtc->id = drm_mode_idr_get(dev, crtc); + DRM_DEBUG("crtc %p got id %d\n", crtc, crtc->id); + 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; @@ -71,7 +102,9 @@ void drm_crtc_destroy(struct drm_crtc *crtc) if (crtc->funcs->cleanup) (*crtc->funcs->cleanup)(crtc); + spin_lock(&dev->crtc_config.config_lock); + drm_mode_idr_put(dev, crtc->id); list_del(&crtc->head); dev->crtc_config.num_crtc--; spin_unlock(&dev->crtc_config.config_lock); @@ -257,6 +290,7 @@ bool drm_set_desired_modes(struct drm_device *dev) list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) { output = NULL; + DRM_DEBUG("crtc is %d\n", crtc->id); list_for_each_entry(list_output, &dev->crtc_config.output_list, head) { if (list_output->crtc == crtc) { output = list_output; @@ -345,6 +379,7 @@ struct drm_output *drm_output_create(drm_device_t *dev, output->dev = dev; output->funcs = funcs; + output->id = drm_mode_idr_get(dev, output); if (name) strncpy(output->name, name, DRM_OUTPUT_LEN); output->name[DRM_OUTPUT_LEN - 1] = 0; @@ -382,6 +417,7 @@ void drm_output_destroy(struct drm_output *output) drm_mode_remove(output, mode); spin_lock(&dev->crtc_config.config_lock); + drm_mode_idr_put(dev, output->id); list_del(&output->head); spin_unlock(&dev->crtc_config.config_lock); kfree(output); @@ -411,28 +447,13 @@ struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev) if (!nmode) return NULL; -again: - if (idr_pre_get(&dev->crtc_config.mode_idr, GFP_KERNEL) == 0) { - DRM_ERROR("Ran out memory getting a mode number\n"); - kfree(nmode); - return NULL; - } - - spin_lock(&dev->crtc_config.config_lock); - - ret = idr_get_new(&dev->crtc_config.mode_idr, nmode, &nmode->mode_id); - if (ret == -EAGAIN) { - udelay(1); - spin_unlock(&dev->crtc_config.config_lock); - goto again; - } - spin_unlock(&dev->crtc_config.config_lock); + nmode->mode_id = drm_mode_idr_get(dev, nmode); return nmode; } void drm_crtc_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) { - idr_remove(&dev->crtc_config.mode_idr, mode->mode_id); + drm_mode_idr_put(dev, mode->mode_id); kfree(mode); } @@ -443,7 +464,7 @@ void drm_crtc_config_init(drm_device_t *dev) INIT_LIST_HEAD(&dev->crtc_config.fb_list); INIT_LIST_HEAD(&dev->crtc_config.crtc_list); INIT_LIST_HEAD(&dev->crtc_config.output_list); - idr_init(&dev->crtc_config.mode_idr); + idr_init(&dev->crtc_config.crtc_idr); } EXPORT_SYMBOL(drm_crtc_config_init); @@ -502,6 +523,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) /* bind both CRTCs to this fb */ /* only initialise one crtc to enabled state */ list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) { + DRM_DEBUG("crtc is %d\n", crtc->id); crtc->fb = fb; if (!vga_crtc) { vga_crtc = crtc; @@ -568,7 +590,6 @@ void drm_crtc_config_cleanup(drm_device_t *dev) drm_output_destroy(output); } - list_for_each_entry_safe(crtc, ct, &dev->crtc_config.crtc_list, head) { drm_crtc_destroy(crtc); } @@ -592,6 +613,8 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display out->vscan = in->vscan; out->flags = in->flags; + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); + out->name[DRM_DISPLAY_MODE_LEN-1] = 0; } @@ -605,15 +628,24 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, struct drm_mode_card_res card_res; struct list_head *lh; struct drm_output *output; + struct drm_crtc *crtc; struct drm_mode_modeinfo u_mode; struct drm_display_mode *mode; int retcode = 0; int mode_count= 0; + int output_count = 0; + int crtc_count = 0; + int copied = 0; memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + + list_for_each(lh, &dev->crtc_config.crtc_list) + crtc_count++; + list_for_each_entry(output, &dev->crtc_config.output_list, head) { + output_count++; list_for_each(lh, &output->modes) mode_count++; } @@ -621,8 +653,38 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, if (copy_from_user(&card_res, argp, sizeof(card_res))) return -EFAULT; + /* handle this in 3 parts */ + /* CRTCs */ + if (card_res.count_crtcs >= crtc_count) { + copied = 0; + list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head){ + DRM_DEBUG("CRTC ID is %d\n", crtc->id); + if (put_user(crtc->id, &card_res.crtc_id[copied++])) { + retcode = -EFAULT; + goto done; + } + } + } + card_res.count_crtcs = crtc_count; + + + /* Outputs */ + if (card_res.count_outputs >= output_count) { + copied = 0; + list_for_each_entry(output, &dev->crtc_config.output_list, + head) { + DRM_DEBUG("OUTPUT ID is %d\n", output->id); + if (put_user(output->id, &card_res.output_id[copied++])) { + retcode = -EFAULT; + goto done; + } + } + } + card_res.count_outputs = output_count; + + /* Modes */ if (card_res.count_modes >= mode_count) { - int copied = 0; + copied = 0; list_for_each_entry(output, &dev->crtc_config.output_list, head) { list_for_each_entry(mode, &output->modes, head) { @@ -634,19 +696,7 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, } } } - - else { - list_for_each(lh, &dev->crtc_config.crtc_list) - card_res.count_crtcs++; - - list_for_each_entry(output, &dev->crtc_config.output_list, - head) - { - list_for_each(lh, &output->modes) - card_res.count_modes++; - card_res.count_outputs++; - } - } + card_res.count_modes = mode_count; done: DRM_DEBUG("Counted %d %d %d\n", card_res.count_crtcs, @@ -658,3 +708,95 @@ done: return retcode; } + +int drm_mode_getcrtc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_crtc __user *argp = (void __user *)arg; + struct drm_mode_crtc crtc_resp; + struct drm_crtc *crtc; + struct drm_output *output; + int ocount; + int retcode = 0; + + if (copy_from_user(&crtc_resp, argp, sizeof(crtc_resp))) + return -EFAULT; + + crtc = idr_find(&dev->crtc_config.crtc_idr, crtc_resp.crtc_id); + if (!crtc || (crtc->id != crtc_resp.crtc_id)) + return -EINVAL; + crtc_resp.x = crtc->x; + crtc_resp.y = crtc->y; + crtc_resp.fb_id = 1; + + crtc_resp.outputs = 0; + if (crtc->enabled) { + + crtc_resp.mode = crtc->mode.mode_id; + ocount = 0; + list_for_each_entry(output, &dev->crtc_config.output_list, head) { + if (output->crtc == crtc) + crtc_resp.outputs |= 1 << (ocount++); + } + } else { + crtc_resp.mode = 0; + } + + if (copy_to_user(argp, &crtc_resp, sizeof(crtc_resp))) + return -EFAULT; + + return retcode; +} + +int drm_mode_getoutput(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_get_output __user *argp = (void __user *)arg; + struct drm_mode_get_output out_resp; + struct drm_crtc *crtc; + struct drm_output *output; + struct drm_display_mode *mode; + int mode_count = 0; + int retcode = 0; + int copied = 0; + + if (copy_from_user(&out_resp, argp, sizeof(out_resp))) + return -EFAULT; + + output= idr_find(&dev->crtc_config.crtc_idr, out_resp.output); + if (!output || (output->id != out_resp.output)) + return -EINVAL; + + list_for_each_entry(mode, &output->modes, head) + mode_count++; + + out_resp.mm_width = output->mm_width; + out_resp.mm_height = output->mm_height; + out_resp.subpixel = output->subpixel_order; + out_resp.connection = output->status; + if (output->crtc) + out_resp.crtc = output->crtc->id; + else + out_resp.crtc = 0; + + if (out_resp.count_modes >= mode_count) { + copied = 0; + list_for_each_entry(mode, &output->modes, head) { + if (put_user(mode->mode_id, &out_resp.modes[copied++])) { + retcode = -EFAULT; + goto done; + } + } + } + out_resp.count_modes = mode_count; + +done: + if (copy_to_user(argp, &out_resp, sizeof(out_resp))) + return -EFAULT; + + return retcode; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 003946bc..e608b462 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -74,8 +74,6 @@ enum drm_mode_status { #define DRM_MODE_TYPE_CLOCK_CRTC_C (DRM_MODE_TYPE_CLOCK_C | \ DRM_MODE_TYPE_CRTC_C) -#define DRM_DISPLAY_MODE_LEN 32 - #define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ .name = nm, .status = 0, .type = (t), .clock = (c), \ .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ @@ -173,6 +171,7 @@ enum subpixel_order { struct drm_framebuffer { struct drm_device *dev; struct list_head head; + int id; /* idr assigned */ unsigned int pitch; unsigned long offset; unsigned int width; @@ -259,6 +258,8 @@ struct drm_crtc { struct drm_device *dev; struct list_head head; + int id; /* idr assigned */ + /* framebuffer the CRTC is currently bound to */ struct drm_framebuffer *fb; @@ -350,6 +351,7 @@ struct drm_output { struct drm_device *dev; struct list_head head; struct drm_crtc *crtc; + int id; /* idr assigned */ unsigned long possible_crtcs; unsigned long possible_clones; bool interlace_allowed; @@ -394,7 +396,7 @@ struct drm_crtc_config_funcs { */ struct drm_crtc_config { spinlock_t config_lock; - struct idr mode_idr; + struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, output, modes - just makes life easier */ /* this is limited to one for now */ int num_fb; struct list_head fb_list; @@ -437,5 +439,9 @@ extern void drm_crtc_mode_destroy(struct drm_device *dev, struct drm_display_mod extern int drm_mode_getresources(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +extern int drm_mode_getcrtc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_getoutput(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 9877db13..7d436f8a 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -123,7 +123,9 @@ static drm_ioctl_desc_t drm_ioctls[] = { DRM_AUTH }, [DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW)] = {drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, - [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETRESOURCES)] = {drm_mode_getresources, DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETRESOURCES)] = {drm_mode_getresources, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETCRTC)] = {drm_mode_getcrtc, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETOUTPUT)] = {drm_mode_getoutput, DRM_MASTER|DRM_ROOT_ONLY}, }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) -- cgit v1.2.3 From b4094864f188a1346cc3b51bcb457beeacefbf82 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 18:01:02 +1000 Subject: checkpoint commit: implement SetCrtc so modes can in theory be set from user This hooks up the userspace mode set it "seems" to work. --- linux-core/drm_crtc.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++-- linux-core/drm_crtc.h | 4 +- linux-core/drm_drv.c | 1 + 3 files changed, 127 insertions(+), 6 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 2dbe6de1..8e03dd5f 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -302,10 +302,10 @@ bool drm_set_desired_modes(struct drm_device *dev) continue; memset(&crtc->mode, 0, sizeof(crtc->mode)); - if (!crtc->desired_mode.crtc_hdisplay) { + if (!crtc->desired_mode->crtc_hdisplay) { } - if (!drm_crtc_set_mode(crtc, &crtc->desired_mode, + if (!drm_crtc_set_mode(crtc, crtc->desired_mode, crtc->desired_x, crtc->desired_y)) return false; } @@ -556,7 +556,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) 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->crtc->desired_mode = des_mode; output->initial_x = 0; output->initial_y = 0; use_output = output; @@ -568,7 +568,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) 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->crtc->desired_mode = des_mode; #endif output->initial_x = 0; output->initial_y = 0; @@ -596,6 +596,68 @@ void drm_crtc_config_cleanup(drm_device_t *dev) } EXPORT_SYMBOL(drm_crtc_config_cleanup); +int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set) +{ + drm_device_t *dev = crtc->dev; + struct drm_crtc **save_crtcs, *new_crtc; + bool save_enabled = crtc->enabled; + bool changed; + struct drm_output *output; + int count = 0, ro; + + save_crtcs = kzalloc(dev->crtc_config.num_crtc * sizeof(struct drm_crtc *), GFP_KERNEL); + if (!save_crtcs) + return -ENOMEM; + + if (crtc_info->x != crtc->x || crtc_info->y != crtc->y) + changed = true; + + if (crtc->mode.mode_id != new_mode->mode_id) + changed = true; + + list_for_each_entry(output, &dev->crtc_config.output_list, head) { + save_crtcs[count++] = output->crtc; + + if (output->crtc == crtc) + new_crtc = NULL; + else + new_crtc = output->crtc; + + for (ro = 0; ro < crtc_info->count_outputs; ro++) + { + if (output_set[ro] == output) + new_crtc = crtc; + } + if (new_crtc != output->crtc) { + changed = true; + output->crtc = new_crtc; + } + } + + if (changed) { + crtc->enabled = new_mode != NULL; + if (new_mode) { + DRM_DEBUG("attempting to set mode from userspace\n"); + drm_mode_debug_printmodeline(dev, new_mode); + if (!drm_crtc_set_mode(crtc, new_mode, crtc_info->x, + crtc_info->y)) { + crtc->enabled = save_enabled; + count = 0; + list_for_each_entry(output, &dev->crtc_config.output_list, head) + output->crtc = save_crtcs[count++]; + kfree(save_crtcs); + return -EINVAL; + } + crtc->desired_x = crtc_info->x; + crtc->desired_y = crtc_info->y; + crtc->desired_mode = new_mode; + } + drm_disable_unused_functions(dev); + } + kfree(save_crtcs); + return 0; +} + void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, struct drm_display_mode *in) { @@ -757,7 +819,6 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, drm_device_t *dev = priv->head->dev; struct drm_mode_get_output __user *argp = (void __user *)arg; struct drm_mode_get_output out_resp; - struct drm_crtc *crtc; struct drm_output *output; struct drm_display_mode *mode; int mode_count = 0; @@ -800,3 +861,60 @@ done: return retcode; } + + +int drm_mode_setcrtc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_crtc __user *argp = (void __user *)arg; + struct drm_mode_crtc crtc_req; + struct drm_crtc *crtc; + struct drm_output **output_set = NULL, *output; + struct drm_display_mode *mode; + int retcode = 0; + int i; + + if (copy_from_user(&crtc_req, argp, sizeof(crtc_req))) + return -EFAULT; + + crtc = idr_find(&dev->crtc_config.crtc_idr, crtc_req.crtc_id); + if (!crtc || (crtc->id != crtc_req.crtc_id)) + return -EINVAL; + + if (crtc_req.mode) { + mode = idr_find(&dev->crtc_config.crtc_idr, crtc_req.mode); + if (!mode || (mode->mode_id != crtc_req.mode)) + return -EINVAL; + } else + mode = NULL; + + if (crtc_req.count_outputs == 0 && mode) + return -EINVAL; + + if (crtc_req.count_outputs > 0 && !mode) + return -EINVAL; + + if (crtc_req.count_outputs > 0) { + u32 out_id; + output_set = kmalloc(crtc_req.count_outputs * sizeof(struct drm_output *), GFP_KERNEL); + if (!output_set) + return -ENOMEM; + + for (i = 0; i < crtc_req.count_outputs; i++) + { + if (get_user(out_id, &crtc_req.set_outputs[i])) + return -EFAULT; + + output = idr_find(&dev->crtc_config.crtc_idr, out_id); + if (!output || (out_id != output->id)) + return -EINVAL; + + output_set[i] = output; + } + } + + retcode = drm_crtc_set_config(crtc, &crtc_req, mode, output_set); + return retcode; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index e608b462..a2c552e6 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -272,7 +272,7 @@ struct drm_crtc { struct drm_display_mode mode; int x, y; - struct drm_display_mode desired_mode; + struct drm_display_mode *desired_mode; int desired_x, desired_y; const struct drm_crtc_funcs *funcs; void *driver_private; @@ -443,5 +443,7 @@ extern int drm_mode_getcrtc(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_mode_getoutput(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +extern int drm_mode_setcrtc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 7d436f8a..c8ee054f 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -126,6 +126,7 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETRESOURCES)] = {drm_mode_getresources, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETCRTC)] = {drm_mode_getcrtc, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETOUTPUT)] = {drm_mode_getoutput, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_SETCRTC)] = {drm_mode_setcrtc, DRM_MASTER|DRM_ROOT_ONLY}, }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) -- cgit v1.2.3 From 652bbb77f6c9efb7e0a67cc868dfda42b00fc5fb Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 20:20:33 +1000 Subject: add back compat for bool --- linux-core/drm_compat.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'linux-core') diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h index bc5fadc5..bada1fdf 100644 --- a/linux-core/drm_compat.h +++ b/linux-core/drm_compat.h @@ -60,6 +60,13 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) #undef DRM_IRQ_ARGS #define DRM_IRQ_ARGS int irq, void *arg, struct pt_regs *regs + +typedef _Bool bool; +enum { + false = 0, + true = 1 +}; + #endif #ifndef list_for_each_safe -- cgit v1.2.3 From 6f3534a13abb0c8afb157511d0871dbc35bc403d Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 5 Apr 2007 09:21:31 -0700 Subject: Add copyrights before I forget --- linux-core/drm_edid.c | 13 +++++++++---- linux-core/intel_crt.c | 5 +++++ linux-core/intel_display.c | 4 ++++ linux-core/intel_drv.h | 5 +++++ linux-core/intel_i2c.c | 4 ++-- linux-core/intel_lvds.c | 4 ++-- linux-core/intel_modes.c | 6 ++++++ 7 files changed, 33 insertions(+), 8 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 3c123751..7e254eee 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes + */ + #include #include #include "drmP.h" @@ -73,7 +78,7 @@ struct detailed_data_monitor_range { u16 sec_gtf_toggle; /* A000=use above, 20=use below */ u8 hfreq_start_khz; /* need to multiply by 2 */ u8 c; /* need to divide by 2 */ - u16 m; + u16 m; /* FIXME: byte order */ u8 k; u8 j; /* need to divide by 2 */ } __attribute__((packed)); @@ -126,9 +131,9 @@ struct detailed_timing { struct edid { u8 header[8]; /* Vendor & product info */ - u16 mfg_id; - u16 prod_code; - u32 serial; + u16 mfg_id; /* FIXME: byte order */ + u16 prod_code; /* FIXME: byte order */ + u32 serial; /* FIXME: byte order */ u8 mfg_week; u8 mfg_year; /* EDID version */ diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c index 5ff9f791..a251d986 100644 --- a/linux-core/intel_crt.c +++ b/linux-core/intel_crt.c @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2006-2007 Intel Corporation + * Eric Anholt + */ + #include #include "drmP.h" #include "drm.h" diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 495f4704..67d7c7d5 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2006-2007 Intel Corporation + * Eric Anholt + */ #include #include "drmP.h" #include "drm.h" diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h index 5b8bef69..7b02d35f 100644 --- a/linux-core/intel_drv.h +++ b/linux-core/intel_drv.h @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2006 Dave Airlie + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes + */ #ifndef __INTEL_DRV_H__ #define __INTEL_DRV_H__ diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c index acae28a0..702e1376 100644 --- a/linux-core/intel_i2c.c +++ b/linux-core/intel_i2c.c @@ -1,6 +1,6 @@ /* - * Copyright 2006 Dave Airlie - * Copyright © 2006 Intel Corporation + * Copyright (c) 2006 Dave Airlie + * Copyright (c) 2006-2007 Intel Corporation * Eric Anholt * Jesse Barnes */ diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index c2ac5679..a2ac13a0 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -1,6 +1,6 @@ /* - * Copyright 2006 Dave Airlie - * Copyright © 2006 Intel Corporation + * Copyright (c) 2006 Dave Airlie + * Copyright (c) 2006-2007 Intel Corporation * Eric Anholt * Jesse Barnes */ diff --git a/linux-core/intel_modes.c b/linux-core/intel_modes.c index 0e56147d..601770e1 100644 --- a/linux-core/intel_modes.c +++ b/linux-core/intel_modes.c @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2007 Dave Airlie + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes + */ + #include #include #include "drmP.h" -- cgit v1.2.3 From 1c9ba24c2f37ca78965f8aa57ece02ef5bdb9b06 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 5 Apr 2007 11:34:11 -0700 Subject: Add required permission notices for code copied from X.Org source. --- linux-core/drm_modes.c | 28 ++++++++++++++++++++++++++-- linux-core/intel_crt.c | 25 +++++++++++++++++++++++-- linux-core/intel_display.c | 26 ++++++++++++++++++++++++-- linux-core/intel_i2c.c | 27 +++++++++++++++++++++++++-- linux-core/intel_lvds.c | 27 +++++++++++++++++++++++++-- linux-core/intel_sdvo.c | 27 +++++++++++++++++++++++++-- linux-core/intel_sdvo_regs.h | 24 +++++++++++++++++++++++- 7 files changed, 171 insertions(+), 13 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index 940fc981..eea2e754 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -1,7 +1,31 @@ +/* + * Copyright © 1997-2003 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the copyright holder(s) + * and author(s) shall not be used in advertising or otherwise to promote + * the sale, use or other dealings in this Software without prior written + * authorization from the copyright holder(s) and author(s). + */ /* * Copyright © 2007 Dave Airlie - * - * Based on code from X.org - Copyright (c) 1997-2003 by The XFree86 Project, Inc. */ #include diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c index a251d986..55d987a6 100644 --- a/linux-core/intel_crt.c +++ b/linux-core/intel_crt.c @@ -1,6 +1,27 @@ /* - * Copyright (c) 2006-2007 Intel Corporation - * Eric Anholt + * Copyright © 2006-2007 Intel Corporation + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt */ #include diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 67d7c7d5..58d66987 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -1,7 +1,29 @@ /* - * Copyright (c) 2006-2007 Intel Corporation - * Eric Anholt + * Copyright © 2006-2007 Intel Corporation + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt */ + #include #include "drmP.h" #include "drm.h" diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c index 702e1376..d4cf7eef 100644 --- a/linux-core/intel_i2c.c +++ b/linux-core/intel_i2c.c @@ -1,7 +1,30 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt + */ /* * Copyright (c) 2006 Dave Airlie - * Copyright (c) 2006-2007 Intel Corporation - * Eric Anholt * Jesse Barnes */ diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index a2ac13a0..a83f7d7a 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -1,7 +1,30 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt + */ /* * Copyright (c) 2006 Dave Airlie - * Copyright (c) 2006-2007 Intel Corporation - * Eric Anholt * Jesse Barnes */ diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c index 4094b788..0e870d15 100644 --- a/linux-core/intel_sdvo.c +++ b/linux-core/intel_sdvo.c @@ -1,7 +1,30 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt + */ /* * Copyright 2006 Dave Airlie - * Copyright © 2006 Intel Corporation - * Eric Anholt * Jesse Barnes */ diff --git a/linux-core/intel_sdvo_regs.h b/linux-core/intel_sdvo_regs.h index b8cb1dc6..c8ab950b 100644 --- a/linux-core/intel_sdvo_regs.h +++ b/linux-core/intel_sdvo_regs.h @@ -1,5 +1,27 @@ /* - * Copyright © 2006 Intel Corporation + * Copyright © 2006-2007 Intel Corporation + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Eric Anholt */ /** -- cgit v1.2.3 From a35ba455b27b72f1ee3e1136ca6659f672ada4fa Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sat, 7 Apr 2007 19:22:39 -0700 Subject: make drmP.h include drm_crtc.h for CRTC related stuff. fixup drm_crtc.c so it matches VGA and other outputs properly. make drm_crtc.c less verbose. add function declarations in drm_crtc.h for other files. --- linux-core/drmP.h | 1 + linux-core/drm_crtc.c | 37 +++++++++++++++++++++---------------- linux-core/drm_crtc.h | 29 ++++++++++++++++++++--------- 3 files changed, 42 insertions(+), 25 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drmP.h b/linux-core/drmP.h index db62ab83..74f52854 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -75,6 +75,7 @@ #include #include #include "drm.h" +#include "drm_crtc.h" #include #define __OS_HAS_AGP (defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE))) diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 8e03dd5f..fe27e386 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -340,8 +340,6 @@ void drm_disable_unused_functions(struct drm_device *dev) */ 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); @@ -440,7 +438,6 @@ EXPORT_SYMBOL(drm_output_rename); struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev) { - int ret; struct drm_display_mode *nmode; nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); @@ -506,7 +503,8 @@ 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_crtc *crtc, *vga_crtc = NULL, *dvi_crtc = NULL, + *lvds_crtc = NULL;; struct drm_framebuffer *fb; struct drm_output *output, *use_output = NULL; @@ -523,14 +521,18 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) /* bind both CRTCs to this fb */ /* only initialise one crtc to enabled state */ list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) { - DRM_DEBUG("crtc is %d\n", crtc->id); crtc->fb = fb; if (!vga_crtc) { vga_crtc = crtc; crtc->enabled = 1; crtc->desired_x = 0; crtc->desired_y = 0; - } + } else if (!lvds_crtc) { + lvds_crtc = crtc; + crtc->enabled = 1; + crtc->desired_x = 0; + crtc->desired_y = 0; + } #if 0 else if (!dvi_crtc) { dvi_crtc = crtc; @@ -549,29 +551,32 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) 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) + /* Get the first preferred moded */ + list_for_each_entry(des_mode, &output->modes, head) { + if (des_mode->flags & DRM_MODE_TYPE_PREFERRED) break; - DRM_DEBUG("Setting desired mode for output %s\n", output->name); + } + if (!strncmp(output->name, "VGA", 3)) { + output->crtc = vga_crtc; 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)) { + } 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 if (!strncmp(output->name, "LVDS", 3)) { + output->crtc = lvds_crtc; + drm_mode_debug_printmodeline(dev, des_mode); + output->crtc->desired_mode = des_mode; + output->initial_x = 0; + output->initial_y = 0; } else output->crtc = NULL; diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index a2c552e6..21908f0c 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -10,8 +10,6 @@ #include #include #include -#include "drmP.h" -#include "drm.h" struct drm_device; @@ -79,7 +77,7 @@ enum drm_mode_status { .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ - .vscan = (vs), .flags = (f) + .vscan = (vs), .flags = (f), .vrefresh = 0 struct drm_display_mode { /* Header */ @@ -128,7 +126,7 @@ struct drm_display_mode { int private_flags; int vrefresh; - float hsync;//, vrefresh; + float hsync; }; /* Video mode flags */ @@ -417,13 +415,13 @@ struct drm_crtc_config { struct drm_output *drm_output_create(struct drm_device *dev, const struct drm_output_funcs *funcs, const char *name); -void drm_output_destroy(struct drm_output *output); -bool drm_output_rename(struct drm_output *output, const char *name); +extern void drm_output_destroy(struct drm_output *output); +extern bool drm_output_rename(struct drm_output *output, const char *name); -int drm_add_edid_modes(struct drm_output *output, +extern int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter); -void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode); -void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode); +extern void drm_mode_probed_add(struct drm_output *output, struct drm_display_mode *mode); +extern void drm_mode_remove(struct drm_output *output, struct drm_display_mode *mode); extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *mode); extern void drm_mode_debug_printmodeline(struct drm_device *dev, @@ -445,5 +443,18 @@ extern int drm_mode_getoutput(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_mode_setcrtc(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +extern void drm_mode_list_concat(struct list_head *head, + struct list_head *new); +extern void drm_mode_validate_size(struct drm_device *dev, + struct list_head *mode_list, + int maxX, int maxY, int maxPitch); +extern void drm_mode_prune_invalid(struct drm_device *dev, + struct list_head *mode_list, bool verbose); +extern void drm_mode_sort(struct list_head *mode_list); +extern int drm_mode_vrefresh(struct drm_display_mode *mode); +extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, + int adjust_flags); +extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); + #endif /* __DRM_CRTC_H__ */ -- cgit v1.2.3 From 13d4ea90c09fa834eb6eecaa082780aace78dac7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sat, 7 Apr 2007 19:24:09 -0700 Subject: various cleanups to EDID code: - pull in FB DDC code (we'll have to rewrite it anyway it appears) - add comments - note a few FIXMEs - make it less quiet, and more informative when it actually does print --- linux-core/drm_edid.c | 240 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 175 insertions(+), 65 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 33018da6..fcd97d67 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -1,18 +1,15 @@ /* * Copyright (c) 2007 Intel Corporation * Jesse Barnes + * + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from + * FB layer. */ - #include -#include +#include #include "drmP.h" -#include "intel_drv.h" - -/* - * DDC/EDID probing rippped off from FB layer - */ -#include "edid.h" +#define EDID_LENGTH 128 #define DDC_ADDR 0x50 #ifdef BIG_ENDIAN @@ -75,7 +72,7 @@ struct detailed_data_monitor_range { u8 min_hfreq_khz; u8 max_hfreq_khz; u8 pixel_clock_mhz; /* need to multiply by 10 */ - u16 sec_gtf_toggle; /* A000=use above, 20=use below */ + u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ u8 hfreq_start_khz; /* need to multiply by 2 */ u8 c; /* need to divide by 2 */ u16 m; /* FIXME: byte order */ @@ -114,14 +111,14 @@ struct detailed_non_pixel { } __attribute__((packed)); #define EDID_DETAIL_STD_MODES 0xfa -#define EDID_DETAIL_CPDATA 0xfb -#define EDID_DETAIL_NAME 0xfc -#define EDID_DETAIL_RANGE 0xfd -#define EDID_DETAIL_STRING 0xfe -#define EDID_DETAIL_SERIAL 0xff +#define EDID_DETAIL_MONITOR_CPDATA 0xfb +#define EDID_DETAIL_MONITOR_NAME 0xfc +#define EDID_DETAIL_MONITOR_RANGE 0xfd +#define EDID_DETAIL_MONITOR_STRING 0xfe +#define EDID_DETAIL_MONITOR_SERIAL 0xff struct detailed_timing { - u16 pixel_clock; /* need to multiply by 10 KHz */ + u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ union { struct detailed_pixel_timing pixel_data; struct detailed_non_pixel other_data; @@ -184,6 +181,14 @@ struct edid { static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +/** + * edid_valid - sanity check EDID data + * @edid: EDID data + * + * Sanity check the EDID block by looking at the header, the version number + * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's + * valid. + */ static bool edid_valid(struct edid *edid) { int i; @@ -215,7 +220,8 @@ bad: * Take the standard timing params (in this case width, aspect, and refresh) * and convert them into a real mode using CVT. * - * Punts for now. + * Punts for now, but should eventually use the FB layer's CVT based mode + * generation code. */ struct drm_display_mode *drm_mode_std(struct drm_device *dev, struct std_timing *t) @@ -245,19 +251,28 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, return mode; } +/** + * drm_mode_detailed - create a new mode from an EDID detailed timing section + * @timing: EDID detailed timing info + * @preferred: is this a preferred mode? + * + * An EDID detailed timing block contains enough info for us to create and + * return a new struct drm_display_mode. The @preferred flag will be set + * if this is the display's preferred timing, and we'll use it to indicate + * to the other layers that this mode is desired. + */ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, - struct detailed_timing *timing, - bool preferred) + struct detailed_timing *timing) { struct drm_display_mode *mode; struct detailed_pixel_timing *pt = &timing->data.pixel_data; if (pt->stereo) { - printk(KERN_ERR "stereo mode not supported\n"); + printk(KERN_WARNING "stereo mode not supported\n"); return NULL; } if (!pt->separate_sync) { - printk(KERN_ERR "integrated sync not supported\n"); + printk(KERN_WARNING "integrated sync not supported\n"); return NULL; } @@ -266,7 +281,6 @@ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, return NULL; mode->type = DRM_MODE_TYPE_DRIVER; - mode->type |= preferred ? DRM_MODE_TYPE_PREFERRED : 0; mode->clock = timing->pixel_clock / 100; mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; @@ -297,7 +311,10 @@ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, return mode; } -static struct drm_display_mode established_modes[] = { +/* + * Detailed mode info for the EDID "established modes" data to use. + */ +static struct drm_display_mode edid_est_modes[] = { { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628, 0, V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */ @@ -372,8 +389,9 @@ static int add_established_modes(struct drm_output *output, struct edid *edid) for (i = 0; i <= EDID_EST_TIMINGS; i++) if (est_bits & (1<dev; + int i, modes = 0; for (i = 0; i < EDID_STD_TIMINGS; i++) { struct std_timing *t = &edid->standard_timings[i]; + struct drm_display_mode *newmode; /* If std timings bytes are 1, 1 it's empty */ if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) continue; - drm_mode_probed_add(output, - drm_mode_std(dev, &edid->standard_timings[i])); + newmode = drm_mode_std(dev, &edid->standard_timings[i]); + drm_mode_probed_add(output, newmode); modes++; } @@ -416,13 +435,13 @@ static int add_standard_modes(struct drm_output *output, struct edid *edid) */ static int add_detailed_info(struct drm_output *output, struct edid *edid) { - int i, j, modes = 0; - bool preferred = 0; struct drm_device *dev = output->dev; + int i, j, modes = 0; for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { struct detailed_timing *timing = &edid->detailed_timings[i]; struct detailed_non_pixel *data = &timing->data.other_data; + struct drm_display_mode *newmode; /* EDID up to and including 1.2 may put monitor info here */ if (edid->version == 1 && edid->revision < 3) @@ -430,33 +449,38 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) /* Detailed mode timing */ if (timing->pixel_clock) { + newmode = drm_mode_detailed(dev, timing); + /* First detailed mode is preferred */ if (i == 0 && edid->preferred_timing) - preferred = 1; - drm_mode_probed_add(output, - drm_mode_detailed(dev, timing, preferred)); + newmode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(output, newmode); + modes++; continue; } /* Other timing or info */ switch (data->type) { - case EDID_DETAIL_SERIAL: + case EDID_DETAIL_MONITOR_SERIAL: break; - case EDID_DETAIL_STRING: + case EDID_DETAIL_MONITOR_STRING: break; - case EDID_DETAIL_RANGE: + case EDID_DETAIL_MONITOR_RANGE: + /* Get monitor range data */ break; - case EDID_DETAIL_NAME: + case EDID_DETAIL_MONITOR_NAME: break; - case EDID_DETAIL_CPDATA: + case EDID_DETAIL_MONITOR_CPDATA: break; case EDID_DETAIL_STD_MODES: /* Five modes per detailed section */ for (j = 0; j < 5; i++) { struct std_timing *std; + struct drm_display_mode *newmode; std = &data->data.timings[j]; - drm_mode_probed_add(output, drm_mode_std(dev, std)); + newmode = drm_mode_std(dev, std); + drm_mode_probed_add(output, newmode); modes++; } break; @@ -468,6 +492,108 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) return modes; } +#define DDC_ADDR 0x50 + +static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) +{ + unsigned char start = 0x0; + unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &start, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = EDID_LENGTH, + .buf = buf, + } + }; + + if (!buf) { + dev_warn(&adapter->dev, "unable to allocate memory for EDID " + "block.\n"); + return NULL; + } + + if (i2c_transfer(adapter, msgs, 2) == 2) + return buf; + + dev_info(&adapter->dev, "unable to read EDID block.\n"); + kfree(buf); + return NULL; +} + +static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) +{ + struct i2c_algo_bit_data *algo_data = adapter->algo_data; + 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); + msleep(13); + + algo_data->setscl(algo_data->data, 1); + for (j = 0; j < 5; j++) { + msleep(10); + if (algo_data->getscl(algo_data->data)) + break; + } + if (j == 5) + continue; + + algo_data->setsda(algo_data->data, 0); + msleep(15); + algo_data->setscl(algo_data->data, 0); + msleep(15); + algo_data->setsda(algo_data->data, 1); + msleep(15); + + /* Do the real work */ + edid = drm_do_probe_ddc_edid(adapter); + algo_data->setsda(algo_data->data, 0); + algo_data->setscl(algo_data->data, 0); + msleep(15); + + algo_data->setscl(algo_data->data, 1); + for (j = 0; j < 10; j++) { + msleep(10); + if (algo_data->getscl(algo_data->data)) + break; + } + + algo_data->setsda(algo_data->data, 1); + msleep(15); + algo_data->setscl(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->setscl(algo_data->data, 1); + + return edid; +} + /** * drm_add_edid_modes - add modes from EDID data, if available * @output: output we're probing @@ -481,42 +607,26 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter) { struct edid *edid; - u8 *raw_edid; - int i, est_modes, std_modes, det_modes; - - edid = (struct edid *)fb_ddc_read(adapter); + int num_modes = 0; + edid = (struct edid *)drm_ddc_read(adapter); if (!edid) { - dev_warn(&output->dev->pdev->dev, "no EDID data\n"); + dev_warn(&output->dev->pdev->dev, "%s: no EDID data\n", + output->name); goto out_err; } if (!edid_valid(edid)) { - dev_warn(&output->dev->pdev->dev, "EDID invalid, ignoring.\n"); + dev_warn(&output->dev->pdev->dev, "%s: EDID invalid.\n", + output->name); goto out_err; } - est_modes = add_established_modes(output, edid); - std_modes = add_standard_modes(output, edid); - det_modes = add_detailed_info(output, edid); - printk(KERN_ERR "est modes: %d, std_modes: %d, det_modes: %d\n", - est_modes, std_modes, det_modes); - - raw_edid = (u8 *)edid; - printk(KERN_ERR "EDID:\n" KERN_ERR); - for (i = 0; i < EDID_LENGTH; i++) { - if (i != 0 && ((i % 16) == 0)) - printk("\n" KERN_ERR); - printk("%02x", raw_edid[i] & 0xff); - } - printk("\n"); - - printk(KERN_ERR "EDID info:\n"); - printk(KERN_ERR " mfg_id: 0x%04x\n", edid->mfg_id); - printk(KERN_ERR " digital? %s\n", edid->digital ? "Yes" : "No"); - printk(KERN_ERR " extensions: %d\n", edid->extensions); + num_modes += add_established_modes(output, edid); + num_modes += add_standard_modes(output, edid); + num_modes += add_detailed_info(output, edid); - return est_modes + std_modes + det_modes; + return num_modes; out_err: kfree(edid); -- cgit v1.2.3 From 491ed9e4c27da6b1b5a6a6921039a7bf3a98c290 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sat, 7 Apr 2007 19:24:53 -0700 Subject: document drm_mode_duplicate and fix vrefresh calculation (off by 1000 error) --- linux-core/drm_modes.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index eea2e754..1ae43447 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -75,7 +75,7 @@ int drm_mode_vrefresh(struct drm_display_mode *mode) if (mode->vrefresh > 0) refresh = mode->vrefresh; else if (mode->htotal > 0 && mode->vtotal > 0) { - refresh = ((mode->clock * 1000) * 1000) / mode->htotal / mode->vtotal; + refresh = (mode->clock * 1000) / mode->htotal / mode->vtotal; if (mode->flags & V_INTERLACE) refresh *= 2; if (mode->flags & V_DBLSCAN) @@ -139,6 +139,13 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) EXPORT_SYMBOL(drm_mode_set_crtcinfo); +/** + * drm_mode_duplicate - allocate and duplicate an existing mode + * @m: mode to duplicate + * + * Just allocate a new mode, copy the existing mode into it, and return + * a pointer to it. Used to create new instances of established modes. + */ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *mode) { struct drm_display_mode *nmode; -- cgit v1.2.3 From 2430d0c3157ef20a3319a4f93dc44b28d0189868 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sat, 7 Apr 2007 19:25:25 -0700 Subject: just codingstyle cleanups --- linux-core/intel_crt.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c index 55d987a6..e62aa8d3 100644 --- a/linux-core/intel_crt.c +++ b/linux-core/intel_crt.c @@ -109,10 +109,10 @@ static void intel_crt_mode_set(struct drm_output *output, * Disable separate mode multiplier used when cloning SDVO to CRT * XXX this needs to be adjusted when we really are cloning */ - if (IS_I965G(dev)) - { + if (IS_I965G(dev)) { dpll_md = I915_READ(dpll_md_reg); - I915_WRITE(dpll_md_reg, dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); + I915_WRITE(dpll_md_reg, + dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); } adpa = 0; @@ -143,8 +143,8 @@ static bool intel_crt_detect_hotplug(struct drm_output *output) // struct intel_output *intel_output = output->driver_private; drm_i915_private_t *dev_priv = dev->dev_private; u32 temp; - const int timeout_ms = 1000; - int starttime, curtime; +// const int timeout_ms = 1000; +// int starttime, curtime; temp = I915_READ(PORT_HOTPLUG_EN); @@ -180,7 +180,6 @@ static bool intel_crt_detect_ddc(struct drm_output *output) static enum drm_output_status intel_crt_detect(struct drm_output *output) { drm_device_t *dev = output->dev; - drm_i915_private_t *dev_priv = dev->dev_private; if (IS_I945G(dev)| IS_I945GM(dev) || IS_I965G(dev)) { if (intel_crt_detect_hotplug(output)) @@ -227,9 +226,8 @@ void intel_crt_init(drm_device_t *dev) { struct drm_output *output; struct intel_output *intel_output; - int modes; - output = drm_output_create (dev, &intel_crt_output_funcs, "VGA"); + output = drm_output_create(dev, &intel_crt_output_funcs, "VGA"); intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); if (!intel_output) { -- cgit v1.2.3 From ab7ee9c1af3bd844653a83b5160773db671bbcad Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sat, 7 Apr 2007 19:26:55 -0700 Subject: remove a printk to make things less verbose --- linux-core/intel_lvds.c | 1 - 1 file changed, 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index a83f7d7a..a444f145 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -124,7 +124,6 @@ void intel_lvds_init(drm_device_t *dev) } modes = intel_ddc_get_modes(output); - printk(KERN_ERR "LVDS: added %d modes from EDID.\n", modes); intel_i2c_destroy(intel_output->ddc_bus); drm_output_destroy(output); } -- cgit v1.2.3 From 7e2b1a6cf55579c6f8b1fd56a97e9f41e34b88fc Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 9 Apr 2007 08:52:53 -0700 Subject: Fix refresh calculation (mistakenly removed 1000 factor needed for integer calulations, fixed mode printout debugging routine instead). --- linux-core/drm_modes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index 1ae43447..bedef163 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -37,7 +37,7 @@ void drm_mode_debug_printmodeline(struct drm_device *dev, struct drm_display_mode *mode) { DRM_DEBUG("Modeline \"%s\" %d %d %d %d %d %d %d %d %d %d\n", - mode->name, mode->vrefresh, mode->clock, + mode->name, mode->vrefresh / 1000, mode->clock, mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, mode->vdisplay, mode->vsync_start, @@ -75,7 +75,7 @@ int drm_mode_vrefresh(struct drm_display_mode *mode) if (mode->vrefresh > 0) refresh = mode->vrefresh; else if (mode->htotal > 0 && mode->vtotal > 0) { - refresh = (mode->clock * 1000) / mode->htotal / mode->vtotal; + refresh = ((mode->clock * 1000) * 1000) / mode->htotal / mode->vtotal; if (mode->flags & V_INTERLACE) refresh *= 2; if (mode->flags & V_DBLSCAN) -- cgit v1.2.3 From b50bda002b824efb24e18e8d514ff0ca763c15b9 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Tue, 10 Apr 2007 18:44:47 +1000 Subject: add addfb/rmfb ioctls Originally from Jakob, cleaned up by airlied. --- linux-core/drm_crtc.c | 95 ++++++++++++++++++++++++++++++++++++++++++++------- linux-core/drm_crtc.h | 27 ++++++++------- linux-core/drm_drv.c | 2 ++ 3 files changed, 99 insertions(+), 25 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index fe27e386..a02124f3 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -465,18 +465,19 @@ void drm_crtc_config_init(drm_device_t *dev) } EXPORT_SYMBOL(drm_crtc_config_init); -void drm_framebuffer_set_object(drm_device_t *dev, unsigned long handle) +static int drm_get_buffer_object(drm_device_t *dev, struct drm_buffer_object **bo, unsigned long handle) { - struct drm_framebuffer *fb; drm_user_object_t *uo; drm_hash_item_t *hash; - drm_buffer_object_t *bo; int ret; + *bo = NULL; + mutex_lock(&dev->struct_mutex); ret = drm_ht_find_item(&dev->object_hash, handle, &hash); if (ret) { DRM_ERROR("Couldn't find handle.\n"); + ret = -EINVAL; goto out_err; } @@ -485,20 +486,13 @@ void drm_framebuffer_set_object(drm_device_t *dev, unsigned long handle) 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; - } + + *bo = drm_user_object_entry(uo, drm_buffer_object_t, base); ret = 0; out_err: mutex_unlock(&dev->struct_mutex); - return; + return ret; } -EXPORT_SYMBOL(drm_framebuffer_set_object); bool drm_initial_config(drm_device_t *dev, bool can_grow) { @@ -923,3 +917,78 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, retcode = drm_crtc_set_config(crtc, &crtc_req, mode, output_set); return retcode; } + +/* Add framebuffer ioctl */ +int drm_mode_addfb(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct drm_file *priv = filp->private_data; + struct drm_device *dev = priv->head->dev; + struct drm_mode_fb_cmd __user *argp = (void __user *)arg; + struct drm_mode_fb_cmd r; + struct drm_crtc_config *config = &dev->crtc_config; + struct drm_framebuffer *fb; + struct drm_buffer_object *bo; + int ret; + + if (!copy_from_user(&r, argp, sizeof(r))) + return -EFAULT; + + if (config->min_width > r.width || r.width > config->max_width) { + DRM_ERROR("mode new framebuffer width not within limits"); + return -EINVAL; + } + if (config->min_height > r.height || r.height > config->min_height) { + DRM_ERROR("mode new framebuffer height not within limits"); + return -EINVAL; + } + + /* TODO check limits are okay */ + ret = drm_get_buffer_object(dev, &bo, r.handle); + if (ret || !bo) + return -EINVAL; + + /* TODO check buffer is sufficently large */ + /* TODO setup destructor callback */ + + fb = drm_framebuffer_create(dev); + if(!fb) + return -EINVAL;; + + fb->width = r.width; + fb->height = r.height; + fb->pitch = r.pitch; + fb->bits_per_pixel = r.bpp; + fb->offset = bo->offset; + fb->bo = bo; + + r.buffer_id = fb->id; + if (!copy_to_user(argp, &r, sizeof(r))) + return -EFAULT; + + return 0; +} + +int drm_mode_rmfb(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_framebuffer *fb = 0; + uint32_t id = arg; + + fb = idr_find(&dev->crtc_config.crtc_idr, id); + /* TODO check that we realy get a framebuffer back. */ + if (!fb || (id != fb->id)) { + DRM_ERROR("mode invalid framebuffer id\n"); + return -EINVAL; + } + + /* TODO check if we own the buffer */ + /* TODO release all crtc connected to the framebuffer */ + /* TODO unhock the destructor from the buffer object */ + + drm_framebuffer_destroy(fb); + + return 0; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 21908f0c..36d5f316 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -178,6 +178,7 @@ struct drm_framebuffer { unsigned int depth; int bits_per_pixel; int flags; + struct drm_buffer_object *bo; }; struct drm_crtc; struct drm_output; @@ -432,17 +433,6 @@ extern void drm_disable_unused_functions(struct drm_device *dev); extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); extern void drm_crtc_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); - -/* IOCTLs */ -extern int drm_mode_getresources(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); - -extern int drm_mode_getcrtc(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); -extern int drm_mode_getoutput(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); -extern int drm_mode_setcrtc(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); extern void drm_mode_list_concat(struct list_head *head, struct list_head *new); extern void drm_mode_validate_size(struct drm_device *dev, @@ -454,7 +444,20 @@ extern void drm_mode_sort(struct list_head *mode_list); extern int drm_mode_vrefresh(struct drm_display_mode *mode); extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); -extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); +/* IOCTLs */ +extern int drm_mode_getresources(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int drm_mode_getcrtc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_getoutput(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_setcrtc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_addfb(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mode_rmfb(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index c8ee054f..80007170 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -127,6 +127,8 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETCRTC)] = {drm_mode_getcrtc, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETOUTPUT)] = {drm_mode_getoutput, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_SETCRTC)] = {drm_mode_setcrtc, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB)] = {drm_mode_addfb, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_RMFB)] = {drm_mode_rmfb, DRM_MASTER|DRM_ROOT_ONLY}, }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) -- cgit v1.2.3 From ed0ebd9d3da2e5c4e8053b6e7a7d2898b184f857 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 10 Apr 2007 18:56:02 +1000 Subject: make crtc_config be named mode_config X.org calls this crtc_config but this is a bad name and will confuse ppl later (and me now :-) --- linux-core/drmP.h | 2 +- linux-core/drm_crtc.c | 136 ++++++++++++++++++++++----------------------- linux-core/drm_crtc.h | 14 ++--- linux-core/intel_display.c | 16 +++--- 4 files changed, 84 insertions(+), 84 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 74f52854..25fc8733 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -832,7 +832,7 @@ typedef struct drm_device { /*@} */ /* DRM mode setting */ - struct drm_crtc_config crtc_config; + struct drm_mode_config mode_config; } drm_device_t; #if __OS_HAS_AGP diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index a02124f3..4f8af001 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -8,39 +8,39 @@ int drm_mode_idr_get(struct drm_device *dev, void *ptr) int new_id = 0; int ret; again: - if (idr_pre_get(&dev->crtc_config.crtc_idr, GFP_KERNEL) == 0) { + if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { DRM_ERROR("Ran out memory getting a mode number\n"); return 0; } - spin_lock(&dev->crtc_config.config_lock); + spin_lock(&dev->mode_config.config_lock); - ret = idr_get_new_above(&dev->crtc_config.crtc_idr, ptr, 1, &new_id); + ret = idr_get_new_above(&dev->mode_config.crtc_idr, ptr, 1, &new_id); if (ret == -EAGAIN) { - spin_unlock(&dev->crtc_config.config_lock); + spin_unlock(&dev->mode_config.config_lock); goto again; } - spin_unlock(&dev->crtc_config.config_lock); + spin_unlock(&dev->mode_config.config_lock); return new_id; } void drm_mode_idr_put(struct drm_device *dev, int id) { - idr_remove(&dev->crtc_config.crtc_idr, id); + idr_remove(&dev->mode_config.crtc_idr, id); } struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev) { struct drm_framebuffer *fb; - spin_lock(&dev->crtc_config.config_lock); + spin_lock(&dev->mode_config.config_lock); /* Limit to single framebuffer for now */ - if (dev->crtc_config.num_fb > 1) { + if (dev->mode_config.num_fb > 1) { DRM_ERROR("Attempt to add multiple framebuffers failed\n"); return NULL; } - spin_unlock(&dev->crtc_config.config_lock); + spin_unlock(&dev->mode_config.config_lock); fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL); if (!fb) { @@ -50,10 +50,10 @@ struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev) fb->id = drm_mode_idr_get(dev, fb); 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); + spin_lock(&dev->mode_config.config_lock); + dev->mode_config.num_fb++; + list_add(&fb->head, &dev->mode_config.fb_list); + spin_unlock(&dev->mode_config.config_lock); return fb; } @@ -62,11 +62,11 @@ void drm_framebuffer_destroy(struct drm_framebuffer *fb) { drm_device_t *dev = fb->dev; - spin_lock(&dev->crtc_config.config_lock); + spin_lock(&dev->mode_config.config_lock); drm_mode_idr_put(dev, fb->id); list_del(&fb->head); - dev->crtc_config.num_fb--; - spin_unlock(&dev->crtc_config.config_lock); + dev->mode_config.num_fb--; + spin_unlock(&dev->mode_config.config_lock); kfree(fb); } @@ -86,10 +86,10 @@ struct drm_crtc *drm_crtc_create(drm_device_t *dev, crtc->id = drm_mode_idr_get(dev, crtc); DRM_DEBUG("crtc %p got id %d\n", crtc, crtc->id); - 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); + spin_lock(&dev->mode_config.config_lock); + list_add_tail(&crtc->head, &dev->mode_config.crtc_list); + dev->mode_config.num_crtc++; + spin_unlock(&dev->mode_config.config_lock); return crtc; } @@ -103,11 +103,11 @@ void drm_crtc_destroy(struct drm_crtc *crtc) (*crtc->funcs->cleanup)(crtc); - spin_lock(&dev->crtc_config.config_lock); + spin_lock(&dev->mode_config.config_lock); drm_mode_idr_put(dev, crtc->id); list_del(&crtc->head); - dev->crtc_config.num_crtc--; - spin_unlock(&dev->crtc_config.config_lock); + dev->mode_config.num_crtc--; + spin_unlock(&dev->mode_config.config_lock); kfree(crtc); } EXPORT_SYMBOL(drm_crtc_destroy); @@ -116,7 +116,7 @@ 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) + list_for_each_entry(output, &dev->mode_config.output_list, head) if (output->crtc == crtc) return true; return false; @@ -131,7 +131,7 @@ void drm_crtc_probe_output_modes(struct drm_device *dev, int maxX, int maxY) //if (maxX == 0 || maxY == 0) // TODO - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { list_for_each_entry_safe(mode, t, &output->modes, head) drm_mode_remove(output, mode); @@ -214,7 +214,7 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, * 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) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { if (output->crtc != crtc) continue; @@ -229,7 +229,7 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, } /* Prepare the outputs and CRTCs before setting the mode. */ - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { if (output->crtc != crtc) continue; @@ -244,14 +244,14 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, * on the DPLL. */ crtc->funcs->mode_set(crtc, mode, adjusted_mode, x, y); - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_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) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { if (output->crtc == crtc) { output->funcs->commit(output); @@ -287,11 +287,11 @@ 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) { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { output = NULL; DRM_DEBUG("crtc is %d\n", crtc->id); - list_for_each_entry(list_output, &dev->crtc_config.output_list, head) { + list_for_each_entry(list_output, &dev->mode_config.output_list, head) { if (list_output->crtc == crtc) { output = list_output; break; @@ -320,12 +320,12 @@ 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) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { if (!output->crtc) (*output->funcs->dpms)(output, DPMSModeOff); } - list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (!crtc->enabled) crtc->funcs->dpms(crtc, DPMSModeOff); } @@ -389,11 +389,11 @@ struct drm_output *drm_output_create(drm_device_t *dev, /* 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_lock(&dev->mode_config.config_lock); + list_add_tail(&output->head, &dev->mode_config.output_list); + dev->mode_config.num_output++; - spin_unlock(&dev->crtc_config.config_lock); + spin_unlock(&dev->mode_config.config_lock); return output; @@ -414,10 +414,10 @@ void drm_output_destroy(struct drm_output *output) list_for_each_entry_safe(mode, t, &output->modes, head) drm_mode_remove(output, mode); - spin_lock(&dev->crtc_config.config_lock); + spin_lock(&dev->mode_config.config_lock); drm_mode_idr_put(dev, output->id); list_del(&output->head); - spin_unlock(&dev->crtc_config.config_lock); + spin_unlock(&dev->mode_config.config_lock); kfree(output); } EXPORT_SYMBOL(drm_output_destroy); @@ -455,15 +455,15 @@ void drm_crtc_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode kfree(mode); } -void drm_crtc_config_init(drm_device_t *dev) +void drm_mode_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); - idr_init(&dev->crtc_config.crtc_idr); + spin_lock_init(&dev->mode_config.config_lock); + INIT_LIST_HEAD(&dev->mode_config.fb_list); + INIT_LIST_HEAD(&dev->mode_config.crtc_list); + INIT_LIST_HEAD(&dev->mode_config.output_list); + idr_init(&dev->mode_config.crtc_idr); } -EXPORT_SYMBOL(drm_crtc_config_init); +EXPORT_SYMBOL(drm_mode_config_init); static int drm_get_buffer_object(drm_device_t *dev, struct drm_buffer_object **bo, unsigned long handle) { @@ -514,7 +514,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) /* bind both CRTCs to this fb */ /* only initialise one crtc to enabled state */ - list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head) { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { crtc->fb = fb; if (!vga_crtc) { vga_crtc = crtc; @@ -542,7 +542,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) /* hard bind the CRTCS */ /* bind analog output to one crtc */ - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { struct drm_display_mode *des_mode; /* Get the first preferred moded */ @@ -580,20 +580,20 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) } EXPORT_SYMBOL(drm_initial_config); -void drm_crtc_config_cleanup(drm_device_t *dev) +void drm_mode_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) { + list_for_each_entry_safe(output, ot, &dev->mode_config.output_list, head) { drm_output_destroy(output); } - list_for_each_entry_safe(crtc, ct, &dev->crtc_config.crtc_list, head) { + list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { drm_crtc_destroy(crtc); } } -EXPORT_SYMBOL(drm_crtc_config_cleanup); +EXPORT_SYMBOL(drm_mode_config_cleanup); int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set) { @@ -604,7 +604,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_output *output; int count = 0, ro; - save_crtcs = kzalloc(dev->crtc_config.num_crtc * sizeof(struct drm_crtc *), GFP_KERNEL); + save_crtcs = kzalloc(dev->mode_config.num_crtc * sizeof(struct drm_crtc *), GFP_KERNEL); if (!save_crtcs) return -ENOMEM; @@ -614,7 +614,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, if (crtc->mode.mode_id != new_mode->mode_id) changed = true; - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { save_crtcs[count++] = output->crtc; if (output->crtc == crtc) @@ -642,7 +642,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, crtc_info->y)) { crtc->enabled = save_enabled; count = 0; - list_for_each_entry(output, &dev->crtc_config.output_list, head) + list_for_each_entry(output, &dev->mode_config.output_list, head) output->crtc = save_crtcs[count++]; kfree(save_crtcs); return -EINVAL; @@ -700,10 +700,10 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); - list_for_each(lh, &dev->crtc_config.crtc_list) + list_for_each(lh, &dev->mode_config.crtc_list) crtc_count++; - list_for_each_entry(output, &dev->crtc_config.output_list, + list_for_each_entry(output, &dev->mode_config.output_list, head) { output_count++; @@ -718,7 +718,7 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, /* CRTCs */ if (card_res.count_crtcs >= crtc_count) { copied = 0; - list_for_each_entry(crtc, &dev->crtc_config.crtc_list, head){ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head){ DRM_DEBUG("CRTC ID is %d\n", crtc->id); if (put_user(crtc->id, &card_res.crtc_id[copied++])) { retcode = -EFAULT; @@ -732,7 +732,7 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, /* Outputs */ if (card_res.count_outputs >= output_count) { copied = 0; - list_for_each_entry(output, &dev->crtc_config.output_list, + list_for_each_entry(output, &dev->mode_config.output_list, head) { DRM_DEBUG("OUTPUT ID is %d\n", output->id); if (put_user(output->id, &card_res.output_id[copied++])) { @@ -746,7 +746,7 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, /* Modes */ if (card_res.count_modes >= mode_count) { copied = 0; - list_for_each_entry(output, &dev->crtc_config.output_list, + list_for_each_entry(output, &dev->mode_config.output_list, head) { list_for_each_entry(mode, &output->modes, head) { drm_crtc_convert_to_umode(&u_mode, mode); @@ -785,7 +785,7 @@ int drm_mode_getcrtc(struct inode *inode, struct file *filp, if (copy_from_user(&crtc_resp, argp, sizeof(crtc_resp))) return -EFAULT; - crtc = idr_find(&dev->crtc_config.crtc_idr, crtc_resp.crtc_id); + crtc = idr_find(&dev->mode_config.crtc_idr, crtc_resp.crtc_id); if (!crtc || (crtc->id != crtc_resp.crtc_id)) return -EINVAL; crtc_resp.x = crtc->x; @@ -797,7 +797,7 @@ int drm_mode_getcrtc(struct inode *inode, struct file *filp, crtc_resp.mode = crtc->mode.mode_id; ocount = 0; - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { if (output->crtc == crtc) crtc_resp.outputs |= 1 << (ocount++); } @@ -827,7 +827,7 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, if (copy_from_user(&out_resp, argp, sizeof(out_resp))) return -EFAULT; - output= idr_find(&dev->crtc_config.crtc_idr, out_resp.output); + output= idr_find(&dev->mode_config.crtc_idr, out_resp.output); if (!output || (output->id != out_resp.output)) return -EINVAL; @@ -878,12 +878,12 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, if (copy_from_user(&crtc_req, argp, sizeof(crtc_req))) return -EFAULT; - crtc = idr_find(&dev->crtc_config.crtc_idr, crtc_req.crtc_id); + crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req.crtc_id); if (!crtc || (crtc->id != crtc_req.crtc_id)) return -EINVAL; if (crtc_req.mode) { - mode = idr_find(&dev->crtc_config.crtc_idr, crtc_req.mode); + mode = idr_find(&dev->mode_config.crtc_idr, crtc_req.mode); if (!mode || (mode->mode_id != crtc_req.mode)) return -EINVAL; } else @@ -906,7 +906,7 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, if (get_user(out_id, &crtc_req.set_outputs[i])) return -EFAULT; - output = idr_find(&dev->crtc_config.crtc_idr, out_id); + output = idr_find(&dev->mode_config.crtc_idr, out_id); if (!output || (out_id != output->id)) return -EINVAL; @@ -926,7 +926,7 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, struct drm_device *dev = priv->head->dev; struct drm_mode_fb_cmd __user *argp = (void __user *)arg; struct drm_mode_fb_cmd r; - struct drm_crtc_config *config = &dev->crtc_config; + struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; struct drm_buffer_object *bo; int ret; @@ -977,7 +977,7 @@ int drm_mode_rmfb(struct inode *inode, struct file *filp, struct drm_framebuffer *fb = 0; uint32_t id = arg; - fb = idr_find(&dev->crtc_config.crtc_idr, id); + fb = idr_find(&dev->mode_config.crtc_idr, id); /* TODO check that we realy get a framebuffer back. */ if (!fb || (id != fb->id)) { DRM_ERROR("mode invalid framebuffer id\n"); diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 36d5f316..19d7cf51 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -377,7 +377,7 @@ struct drm_output { }; /** - * struct drm_crtc_config_funcs - configure CRTCs for a given screen layout + * struct drm_mode_config_funcs - configure CRTCs for a given screen layout * @resize: adjust CRTCs as necessary for the proposed layout * * Currently only a resize hook is available. DRM will call back into the @@ -385,15 +385,15 @@ struct drm_output { * the proposed size, it can return false. Otherwise it should adjust * the CRTC<->output mappings as needed and update its view of the screen. */ -struct drm_crtc_config_funcs { +struct drm_mode_config_funcs { bool (*resize)(struct drm_device *dev, int width, int height); }; /** - * drm_crtc_config - CRTC configuration control structure + * drm_mode_config - Mode configuration control structure * */ -struct drm_crtc_config { +struct drm_mode_config { spinlock_t config_lock; struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, output, modes - just makes life easier */ /* this is limited to one for now */ @@ -410,7 +410,7 @@ struct drm_crtc_config { int max_width, max_height; /* DamagePtr rotationDamage? */ /* DGA stuff? */ - struct drm_crtc_config_funcs *funcs; + struct drm_mode_config_funcs *funcs; }; struct drm_output *drm_output_create(struct drm_device *dev, @@ -427,8 +427,8 @@ extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, struct drm_display_mode *mode); extern void drm_mode_debug_printmodeline(struct drm_device *dev, struct drm_display_mode *mode); -extern void drm_crtc_config_init(struct drm_device *dev); -extern void drm_crtc_config_cleanup(struct drm_device *dev); +extern void drm_mode_config_init(struct drm_device *dev); +extern void drm_mode_config_cleanup(struct drm_device *dev); extern void drm_disable_unused_functions(struct drm_device *dev); extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 58d66987..e8d15ce5 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -224,10 +224,10 @@ static void intel_clock(struct drm_device *dev, int refclk, bool intel_pipe_has_type (struct drm_crtc *crtc, int type) { struct drm_device *dev = crtc->dev; - struct drm_crtc_config *crtc_config = &dev->crtc_config; + struct drm_mode_config *mode_config = &dev->mode_config; struct drm_output *l_entry; - list_for_each_entry(l_entry, &crtc_config->output_list, head) { + list_for_each_entry(l_entry, &mode_config->output_list, head) { if (l_entry->crtc == crtc) { struct intel_output *intel_output = l_entry->driver_private; if (intel_output->type == type) @@ -681,10 +681,10 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, u32 dpll = 0, fp = 0, dspcntr, pipeconf; bool ok, is_sdvo = false, is_dvo = false; bool is_crt = false, is_lvds = false, is_tv = false; - struct drm_crtc_config *crtc_config = &dev->crtc_config; + struct drm_mode_config *mode_config = &dev->mode_config; struct drm_output *output; - list_for_each_entry(output, &crtc_config->output_list, head) { + list_for_each_entry(output, &mode_config->output_list, head) { struct intel_output *intel_output = output->driver_private; if (output->crtc != crtc) @@ -1023,7 +1023,7 @@ int intel_output_clones (drm_device_t *dev, int type_mask) struct drm_output *output; int entry = 0; - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { struct intel_output *intel_output = output->driver_private; if (type_mask & (1 << intel_output->type)) index_mask |= (1 << entry); @@ -1048,7 +1048,7 @@ static void intel_setup_outputs(drm_device_t *dev) intel_sdvo_init(dev, SDVOC); } - list_for_each_entry(output, &dev->crtc_config.output_list, head) { + list_for_each_entry(output, &dev->mode_config.output_list, head) { struct intel_output *intel_output = output->driver_private; int crtc_mask = 0, clone_mask = 0; @@ -1088,7 +1088,7 @@ void intel_modeset_init(drm_device_t *dev) int num_pipe; int i; - drm_crtc_config_init(dev); + drm_mode_config_init(dev); if (IS_MOBILE(dev) || IS_I9XX(dev)) @@ -1109,5 +1109,5 @@ void intel_modeset_init(drm_device_t *dev) void intel_modeset_cleanup(drm_device_t *dev) { - drm_crtc_config_cleanup(dev); + drm_mode_config_cleanup(dev); } -- cgit v1.2.3 From eb9bdc27879d1aa307b234bbdb0f81494dcf7095 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 11:51:31 +1000 Subject: mode: fixup problems with framebuffer add function --- linux-core/drm_crtc.c | 14 ++++++++------ linux-core/intel_display.c | 7 ++++++- 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 4f8af001..ce2073d1 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -502,6 +502,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) struct drm_framebuffer *fb; struct drm_output *output, *use_output = NULL; +#if 0 fb = drm_framebuffer_create(dev); if (!fb) return false; @@ -512,6 +513,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) fb->depth = 24; fb->bits_per_pixel = 32; +#endif /* bind both CRTCs to this fb */ /* only initialise one crtc to enabled state */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -931,15 +933,15 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, struct drm_buffer_object *bo; int ret; - if (!copy_from_user(&r, argp, sizeof(r))) + if (copy_from_user(&r, argp, sizeof(r))) return -EFAULT; - if (config->min_width > r.width || r.width > config->max_width) { - DRM_ERROR("mode new framebuffer width not within limits"); + if ((config->min_width > r.width) || (r.width > config->max_width)) { + DRM_ERROR("mode new framebuffer width not within limits\n"); return -EINVAL; } - if (config->min_height > r.height || r.height > config->min_height) { - DRM_ERROR("mode new framebuffer height not within limits"); + if ((config->min_height > r.height) || (r.height > config->max_height)) { + DRM_ERROR("mode new framebuffer height not within limits\n"); return -EINVAL; } @@ -963,7 +965,7 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, fb->bo = bo; r.buffer_id = fb->id; - if (!copy_to_user(argp, &r, sizeof(r))) + if (copy_to_user(argp, &r, sizeof(r))) return -EFAULT; return 0; diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index e8d15ce5..fce0fafd 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -1090,6 +1090,11 @@ void intel_modeset_init(drm_device_t *dev) drm_mode_config_init(dev); + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; if (IS_MOBILE(dev) || IS_I9XX(dev)) num_pipe = 2; @@ -1104,7 +1109,7 @@ void intel_modeset_init(drm_device_t *dev) intel_setup_outputs(dev); - drm_initial_config(dev, false); + //drm_initial_config(dev, false); } void intel_modeset_cleanup(drm_device_t *dev) -- cgit v1.2.3 From c446bf50e3ae730f272c6842f4ad04d523bd40c3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 9 Apr 2007 20:46:38 -0700 Subject: Slam in most of X.Org's i830_lvds (not quite done yet so removed from Makefile.kernel too). --- linux-core/Makefile.kernel | 2 +- linux-core/intel_lvds.c | 423 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 384 insertions(+), 41 deletions(-) (limited to 'linux-core') diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index ac403f64..1b767b6a 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -20,7 +20,7 @@ r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o i810-objs := i810_drv.o i810_dma.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \ - i915_buffer.o intel_display.o intel_crt.o intel_lvds.o \ + i915_buffer.o intel_display.o intel_crt.o \ intel_sdvo.o intel_modes.o intel_i2c.o nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nouveau_object.o nouveau_irq.o \ diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index a444f145..7527dfce 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -1,5 +1,6 @@ /* * Copyright © 2006-2007 Intel Corporation + * Copyright (c) 2006 Dave Airlie * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -22,10 +23,8 @@ * * Authors: * Eric Anholt - */ -/* - * Copyright (c) 2006 Dave Airlie - * Jesse Barnes + * Dave Airlie + * Jesse Barnes */ #include @@ -39,23 +38,22 @@ /** * Sets the backlight level. * - * \param level backlight level, from 0 to i830_lvds_get_max_backlight(). + * \param level backlight level, from 0 to intel_lvds_get_max_backlight(). */ -static void lvds_set_backlight(drm_device_t *dev, u32 level) +static void intel_lvds_set_backlight(struct drm_device *dev, int level) { drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long blc_pwm_ctl; + u32 blc_pwm_ctl; - level &= BACKLIGHT_DUTY_CYCLE_MASK; blc_pwm_ctl = I915_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; - I915_WRITE(BLC_PWM_CTL, blc_pwm_ctl | - (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); + I915_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); } /** * Returns the maximum level of the backlight duty cycle field. */ -static u32 lvds_get_max_backlight(drm_device_t *dev) +static u32 intel_lvds_get_max_backlight(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -63,30 +61,386 @@ static u32 lvds_get_max_backlight(drm_device_t *dev) BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; } -int lvds_backlight(DRM_IOCTL_ARGS) +/** + * Sets the power state for the panel. + */ +static void intel_lvds_set_power(struct drm_device *dev, bool on) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 pp_status; + + if (on) { + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | + POWER_TARGET_ON); + do { + pp_status = I915_READ(PP_STATUS); + } while ((pp_status & PP_ON) == 0); + + intel_lvds_set_backlight(dev, dev_priv->backlight_duty_cycle); + } else { + intel_lvds_set_backlight(dev, 0); + + I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & + ~POWER_TARGET_ON); + do { + pp_status = I915_READ(PP_STATUS); + } while (pp_status & PP_ON); + } +} + +static void intel_lvds_dpms(struct drm_output *output, int mode) +{ + struct drm_device *dev = output->dev; + + if (mode == DPMSModeOn) + intel_lvds_set_power(dev, true); + else + intel_lvds_set_power(dev, false); + + /* XXX: We never power down the LVDS pairs. */ +} + +static void intel_lvds_save(struct drm_output *output) +{ + struct drm_device *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + dev_priv->savePP_ON = I915_READ(LVDSPP_ON); + dev_priv->savePP_OFF = I915_READ(LVDSPP_OFF); + dev_priv->savePP_CONTROL = I915_READ(PP_CONTROL); + dev_priv->savePP_CYCLE = I915_READ(PP_CYCLE); + dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_CTL); + dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & + BACKLIGHT_DUTY_CYCLE_MASK); + + /* + * If the light is off at server startup, just make it full brightness + */ + if (dev_priv->backlight_duty_cycle == 0) + dev_priv->backlight_duty_cycle = + intel_lvds_get_max_backlight(dev); +} + +static void intel_lvds_restore(struct drm_output *output) +{ + struct drm_device *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL); + I915_WRITE(LVDSPP_ON, dev_priv->savePP_ON); + I915_WRITE(LVDSPP_OFF, dev_priv->savePP_OFF); + I915_WRITE(PP_CYCLE, dev_priv->savePP_CYCLE); + I915_WRITE(PP_CONTROL, dev_priv->savePP_CONTROL); + if (dev_priv->savePP_CONTROL & POWER_TARGET_ON) + intel_lvds_set_power(dev, true); + else + intel_lvds_set_power(dev, false); +} + +static int intel_lvds_mode_valid(struct drm_output *output, + struct drm_display_mode *mode) +{ + struct drm_device *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_display_mode *fixed_mode = dev_priv->panel_fixed_mode; + + if (fixed_mode) { + if (mode->hdisplay > fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > fixed_mode->vdisplay) + return MODE_PANEL; + } + + return MODE_OK; +} + +static bool intel_lvds_mode_fixup(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = output->crtc->driver_private; + struct drm_output *tmp_output; + + spin_lock(&dev->crtc_config.config_lock); + list_for_each_entry(tmp_output, &dev->crtc_config.output_list, head) { + if (tmp_output != output && tmp_output->crtc == output->crtc) { + printk(KERN_ERR "Can't enable LVDS and another " + "output on the same pipe\n"); + return false; + } + } + spin_lock(&dev->crtc_config.config_lock); + + if (intel_crtc->pipe == 0) { + printk(KERN_ERR "Can't support LVDS on pipe A\n"); + return false; + } + + /* + * If we have timings from the BIOS for the panel, put them in + * to the adjusted mode. The CRTC will be set up for this mode, + * with the panel scaling set up to source from the H/VDisplay + * of the original mode. + */ + if (dev_priv->panel_fixed_mode != NULL) { + adjusted_mode->hdisplay = dev_priv->panel_fixed_mode->hdisplay; + adjusted_mode->hsync_start = + dev_priv->panel_fixed_mode->hsync_start; + adjusted_mode->hsync_end = + dev_priv->panel_fixed_mode->hsync_end; + adjusted_mode->htotal = dev_priv->panel_fixed_mode->htotal; + adjusted_mode->vdisplay = dev_priv->panel_fixed_mode->vdisplay; + adjusted_mode->vsync_start = + dev_priv->panel_fixed_mode->vsync_start; + adjusted_mode->vsync_end = + dev_priv->panel_fixed_mode->vsync_end; + adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal; + adjusted_mode->clock = dev_priv->panel_fixed_mode->clock; +// xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); + } + + /* + * XXX: It would be nice to support lower refresh rates on the + * panels to reduce power consumption, and perhaps match the + * user's requested refresh rate. + */ + + return true; +} + +static void intel_lvds_mode_set(struct drm_output *output, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = output->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = output->crtc->driver_private; + u32 pfit_control; + + /* + * The LVDS pin pair will already have been turned on in the + * intel_crtc_mode_set since it has a large impact on the DPLL + * settings. + */ + + /* + * Enable automatic panel scaling so that non-native modes fill the + * screen. Should be enabled before the pipe is enabled, according to + * register description and PRM. + */ + pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | + VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR); + + if (!IS_I965G(dev)) { + if (dev_priv->panel_wants_dither) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + } + else + pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT; + + I915_WRITE(PFIT_CONTROL, pfit_control); +} + +/** + * Detect the LVDS connection. + * + * This always returns OUTPUT_STATUS_CONNECTED. This output should only have + * been set up if the LVDS was actually connected anyway. + */ +static enum drm_output_status intel_lvds_detect(struct drm_output *output) +{ + return output_status_connected; +} + +/** + * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. + */ +static int intel_lvds_get_modes(struct drm_output *output) { - DRM_DEVICE; - unsigned long dvoa_enabled, dvob_enabled, dvoc_enabled, lvds_enabled; + struct intel_output *intel_output = output->driver_private; + struct drm_device *dev = output->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_monitor_info *edid_mon; + int ret = 0; + + intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return 0; + } + intel_i2c_destroy(intel_output->ddc_bus); + + ret = intel_ddc_get_modes(output); + if (ret) + return ret; +#if 0 + if (!output->monitor_info) { + edid_mon = kzalloc(sizeof(*output->monitor_info), GFP_KERNEL); + if (edid_mon) { + /* Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + edid_mon->det_mon[0].type = DS_RANGES; + edid_mon->det_mon[0].section.ranges.min_v = 0; + edid_mon->det_mon[0].section.ranges.max_v = 200; + edid_mon->det_mon[0].section.ranges.min_h = 0; + edid_mon->det_mon[0].section.ranges.max_h = 200; + + output->monitor_info = edid_mon; + } + } +#endif + if (dev_priv->panel_fixed_mode != NULL) { + struct drm_display_mode *mode = + drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); + drm_mode_probed_add(output, mode); + return 1; + } - printk(KERN_ERR "max backlight value: %d\n", - lvds_get_max_backlight(dev)); - dvoa_enabled = I915_READ(DVOA); - dvob_enabled = I915_READ(DVOB); - dvoc_enabled = I915_READ(DVOC); - lvds_enabled = I915_READ(LVDS); - - printk(KERN_ERR "dvoa_enabled: 0x%08lx\n", dvoa_enabled); - printk(KERN_ERR "dvob_enabled: 0x%08lx\n", dvob_enabled); - printk(KERN_ERR "dvoc_enabled: 0x%08lx\n", dvoc_enabled); - printk(KERN_ERR "lvds_enabled: 0x%08lx\n", lvds_enabled); - printk(KERN_ERR "BLC_PWM_CTL: 0x%08x\n", I915_READ(BLC_PWM_CTL)); - return 0; } -static const struct drm_output_funcs intel_lvds_output_funcs; +static void intel_lvds_destroy(struct drm_output *output) +{ + drm_output_destroy(output); +} + +static const struct drm_output_funcs intel_lvds_output_funcs = { + .dpms = intel_lvds_dpms, + .save = intel_lvds_save, + .restore = intel_lvds_restore, + .mode_valid = intel_lvds_mode_valid, + .mode_fixup = intel_lvds_mode_fixup, + .prepare = intel_output_prepare, + .mode_set = intel_lvds_mode_set, + .commit = intel_output_commit, + .detect = intel_lvds_detect, + .get_modes = intel_lvds_get_modes, + .cleanup = intel_lvds_destroy +}; + +void +intel_lvds_init(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_output *output; + struct intel_output *intel_output; + struct drm_display_mode *modes, scan, bios_mode; + + output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS"); + if (!output) + return; + + intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); + if (!intel_output) { + drm_output_destroy(output); + return; + } + + intel_output->type = INTEL_OUTPUT_LVDS; + output->driver_private = intel_output; + output->subpixel_order = SubPixelHorizontalRGB; + output->interlace_allowed = FALSE; + output->doublescan_allowed = FALSE; + + /* + * Attempt to get the fixed panel mode from DDC. Assume that the + * preferred mode is the right one. + */ + intel_ddc_get_modes(output); + list_for_each_entry(scan, &output->probed_modes, head) { + if (scan->type & DRM_MODE_TYPE_PREFERRED) + break; + } + + if (scan != NULL) + dev_priv->panel_fixed_mode = scan; + +#if 0 + /* + * If we didn't get EDID, try checking if the panel is already turned + * on. If so, assume that whatever is currently programmed is the + * correct mode. + */ + if (dev_priv->panel_fixed_mode == NULL) { + u32 lvds = I915_READ(LVDS); + int pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; + struct drm_crtc_config *crtc_config = dev->crtc_config; + struct drm_crtc *crtc = crtc_config->crtc[pipe]; + + if (lvds & LVDS_PORT_EN) { + dev_priv->panel_fixed_mode = intel_crtc_mode_get(pScrn, crtc); + if (dev_priv->panel_fixed_mode != NULL) + dev_priv->panel_fixed_mode->type |= M_T_PREFERRED; + } + } + + /* Get the LVDS fixed mode out of the BIOS. We should support LVDS with + * the BIOS being unavailable or broken, but lack the configuration options + * for now. + */ + bios_mode = intel_bios_get_panel_mode(pScrn); + if (bios_mode != NULL) { + if (dev_priv->panel_fixed_mode != NULL) { + if (dev_priv->debug_modes && + !xf86ModesEqual(dev_priv->panel_fixed_mode, bios_mode)) + { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "BIOS panel mode data doesn't match probed data, " + "continuing with probed.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS mode:\n"); + xf86PrintModeline(pScrn->scrnIndex, bios_mode); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "probed mode:\n"); + xf86PrintModeline(pScrn->scrnIndex, dev_priv->panel_fixed_mode); + xfree(bios_mode->name); + xfree(bios_mode); + } + } else { + dev_priv->panel_fixed_mode = bios_mode; + } + } else { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Couldn't detect panel mode. Disabling panel\n"); + goto disable_exit; + } + + /* Blacklist machines with BIOSes that list an LVDS panel without actually + * having one. + */ + if (dev_priv->PciInfo->chipType == PCI_CHIP_I945_GM) { + if (dev_priv->PciInfo->subsysVendor == 0xa0a0) /* aopen mini pc */ + goto disable_exit; + + if ((dev_priv->PciInfo->subsysVendor == 0x8086) && + (dev_priv->PciInfo->subsysCard == 0x7270)) { + /* It's a Mac Mini or Macbook Pro. + * + * Apple hardware is out to get us. The macbook pro has a real + * LVDS panel, but the mac mini does not, and they have the same + * device IDs. We'll distinguish by panel size, on the assumption + * that Apple isn't about to make any machines with an 800x600 + * display. + */ + if (dev_priv->panel_fixed_mode != NULL && + dev_priv->panel_fixed_mode->HDisplay == 800 && + dev_priv->panel_fixed_mode->VDisplay == 600) + { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Suspected Mac Mini, ignoring the LVDS\n"); + goto disable_exit; + } + } + } + +#endif + return; +} + +#if 0 /** * intel_lvds_init - setup LVDS outputs on this device * @dev: drm device @@ -115,16 +469,5 @@ void intel_lvds_init(drm_device_t *dev) output->subpixel_order = SubPixelHorizontalRGB; output->interlace_allowed = 0; output->doublescan_allowed = 0; - - intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); - if (!intel_output->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " - "failed.\n"); - return; - } - - modes = intel_ddc_get_modes(output); - intel_i2c_destroy(intel_output->ddc_bus); - drm_output_destroy(output); } - +#endif -- cgit v1.2.3 From 65f465ed5ad3caf773658bb2832785c963b987f6 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 14:49:49 +1000 Subject: fixup numerous issues with adding framebuffer support This still isn't perfect but it fixes a few oopses and cleans up some of the tabs and bugs in the original fb limit code --- linux-core/drm_crtc.c | 101 ++++++++++++++++++++++++++++++++++--------------- linux-core/drm_crtc.h | 6 +-- linux-core/drm_modes.c | 4 +- 3 files changed, 76 insertions(+), 35 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index ce2073d1..46b7f7ae 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -371,7 +371,7 @@ struct drm_output *drm_output_create(drm_device_t *dev, { struct drm_output *output = NULL; - output = kmalloc(sizeof(struct drm_output), GFP_KERNEL); + output = kzalloc(sizeof(struct drm_output), GFP_KERNEL); if (!output) return NULL; @@ -471,13 +471,13 @@ static int drm_get_buffer_object(drm_device_t *dev, struct drm_buffer_object **b drm_hash_item_t *hash; int ret; - *bo = NULL; + *bo = NULL; mutex_lock(&dev->struct_mutex); ret = drm_ht_find_item(&dev->object_hash, handle, &hash); if (ret) { DRM_ERROR("Couldn't find handle.\n"); - ret = -EINVAL; + ret = -EINVAL; goto out_err; } @@ -486,7 +486,7 @@ static int drm_get_buffer_object(drm_device_t *dev, struct drm_buffer_object **b ret = -EINVAL; goto out_err; } - + *bo = drm_user_object_entry(uo, drm_buffer_object_t, base); ret = 0; out_err: @@ -517,7 +517,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) /* bind both CRTCs to this fb */ /* only initialise one crtc to enabled state */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - crtc->fb = fb; + // crtc->fb = fb; if (!vga_crtc) { vga_crtc = crtc; crtc->enabled = 1; @@ -699,15 +699,14 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, int output_count = 0; int crtc_count = 0; int copied = 0; - + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); list_for_each(lh, &dev->mode_config.crtc_list) crtc_count++; list_for_each_entry(output, &dev->mode_config.output_list, - head) - { + head) { output_count++; list_for_each(lh, &output->modes) mode_count++; @@ -716,6 +715,16 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, if (copy_from_user(&card_res, argp, sizeof(card_res))) return -EFAULT; + if (card_res.count_modes == 0) { + DRM_DEBUG("probing modes %dx%d\n", dev->mode_config.max_width, dev->mode_config.max_height); + drm_crtc_probe_output_modes(dev, dev->mode_config.max_width, dev->mode_config.max_height); + mode_count = 0; + list_for_each_entry(output, &dev->mode_config.output_list, head) { + list_for_each(lh, &output->modes) + mode_count++; + } + } + /* handle this in 3 parts */ /* CRTCs */ if (card_res.count_crtcs >= crtc_count) { @@ -829,13 +838,16 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, if (copy_from_user(&out_resp, argp, sizeof(out_resp))) return -EFAULT; + DRM_DEBUG("output id %d\n", out_resp.output); output= idr_find(&dev->mode_config.crtc_idr, out_resp.output); if (!output || (output->id != out_resp.output)) return -EINVAL; + DRM_DEBUG("about to count modes\n"); list_for_each_entry(mode, &output->modes, head) mode_count++; + DRM_DEBUG("about to count modes %d %d %p\n", mode_count, out_resp.count_modes, output->crtc); out_resp.mm_width = output->mm_width; out_resp.mm_height = output->mm_height; out_resp.subpixel = output->subpixel_order; @@ -845,7 +857,7 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, else out_resp.crtc = 0; - if (out_resp.count_modes >= mode_count) { + if ((out_resp.count_modes >= mode_count) && mode_count) { copied = 0; list_for_each_entry(mode, &output->modes, head) { if (put_user(mode->mode_id, &out_resp.modes[copied++])) { @@ -882,20 +894,40 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req.crtc_id); if (!crtc || (crtc->id != crtc_req.crtc_id)) + { + DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req.crtc_id); return -EINVAL; + } if (crtc_req.mode) { mode = idr_find(&dev->mode_config.crtc_idr, crtc_req.mode); if (!mode || (mode->mode_id != crtc_req.mode)) + { + { + struct drm_output *output; + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + list_for_each_entry(mode, &output->modes, head) { + drm_mode_debug_printmodeline(dev, mode); + } + } + } + + DRM_DEBUG("Unknown mode id %d, %p\n", crtc_req.mode, mode); return -EINVAL; + } } else mode = NULL; - if (crtc_req.count_outputs == 0 && mode) + if (crtc_req.count_outputs == 0 && mode) { + DRM_DEBUG("Count outputs is 0 but mode set\n"); return -EINVAL; + } - if (crtc_req.count_outputs > 0 && !mode) + if (crtc_req.count_outputs > 0 && !mode) { + DRM_DEBUG("Count outputs is %d but no mode set\n", crtc_req.count_outputs); return -EINVAL; + } if (crtc_req.count_outputs > 0) { u32 out_id; @@ -903,14 +935,15 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, if (!output_set) return -ENOMEM; - for (i = 0; i < crtc_req.count_outputs; i++) - { + for (i = 0; i < crtc_req.count_outputs; i++) { if (get_user(out_id, &crtc_req.set_outputs[i])) return -EFAULT; output = idr_find(&dev->mode_config.crtc_idr, out_id); - if (!output || (out_id != output->id)) + if (!output || (out_id != output->id)) { + DRM_DEBUG("Output id %d unknown\n", out_id); return -EINVAL; + } output_set[i] = output; } @@ -922,16 +955,16 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, /* Add framebuffer ioctl */ int drm_mode_addfb(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct drm_file *priv = filp->private_data; struct drm_device *dev = priv->head->dev; struct drm_mode_fb_cmd __user *argp = (void __user *)arg; struct drm_mode_fb_cmd r; - struct drm_mode_config *config = &dev->mode_config; + struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; struct drm_buffer_object *bo; - int ret; + int ret; if (copy_from_user(&r, argp, sizeof(r))) return -EFAULT; @@ -939,16 +972,16 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, if ((config->min_width > r.width) || (r.width > config->max_width)) { DRM_ERROR("mode new framebuffer width not within limits\n"); return -EINVAL; - } + } if ((config->min_height > r.height) || (r.height > config->max_height)) { DRM_ERROR("mode new framebuffer height not within limits\n"); return -EINVAL; } /* TODO check limits are okay */ - ret = drm_get_buffer_object(dev, &bo, r.handle); - if (ret || !bo) - return -EINVAL; + ret = drm_get_buffer_object(dev, &bo, r.handle); + if (ret || !bo) + return -EINVAL; /* TODO check buffer is sufficently large */ /* TODO setup destructor callback */ @@ -957,22 +990,30 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, if(!fb) return -EINVAL;; - fb->width = r.width; - fb->height = r.height; - fb->pitch = r.pitch; + fb->width = r.width; + fb->height = r.height; + fb->pitch = r.pitch; fb->bits_per_pixel = r.bpp; - fb->offset = bo->offset; - fb->bo = bo; + fb->offset = bo->offset; + fb->bo = bo; - r.buffer_id = fb->id; - if (copy_to_user(argp, &r, sizeof(r))) - return -EFAULT; + r.buffer_id = fb->id; + + /* bind the fb to the crtc for now */ + { + struct drm_crtc *crtc; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + crtc->fb = fb; + } + } + if (copy_to_user(argp, &r, sizeof(r))) + return -EFAULT; return 0; } int drm_mode_rmfb(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->head->dev; diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 19d7cf51..142e0ecb 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -178,7 +178,7 @@ struct drm_framebuffer { unsigned int depth; int bits_per_pixel; int flags; - struct drm_buffer_object *bo; + struct drm_buffer_object *bo; }; struct drm_crtc; struct drm_output; @@ -456,8 +456,8 @@ extern int drm_mode_getoutput(struct inode *inode, struct file *filp, extern int drm_mode_setcrtc(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_mode_addfb(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); extern int drm_mode_rmfb(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index bedef163..0f2a612a 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -36,8 +36,8 @@ void drm_mode_debug_printmodeline(struct drm_device *dev, struct drm_display_mode *mode) { - DRM_DEBUG("Modeline \"%s\" %d %d %d %d %d %d %d %d %d %d\n", - mode->name, mode->vrefresh / 1000, mode->clock, + DRM_DEBUG("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d\n", + mode->mode_id, mode->name, mode->vrefresh / 1000, mode->clock, mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, mode->vdisplay, mode->vsync_start, -- cgit v1.2.3 From 40bd6dcd86d554ca426deccd4fbada693c4be8a6 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 15:20:50 +1000 Subject: set the base address of the CRTC correctly --- linux-core/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index fce0fafd..92b39406 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -351,9 +351,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y) int dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - Start = crtc->fb->offset; + Start = crtc->fb->offset + dev_priv->baseaddr; Offset = ((y * crtc->fb->width + x) * (crtc->fb->bits_per_pixel / 8)); + DRM_DEBUG("Writing base %08X %08X %d %d\n", Start, Offset, x, y); if (IS_I965G(dev)) { I915_WRITE(dspbase, Offset); I915_READ(dspbase); -- cgit v1.2.3 From 1e39dc43230ba1827eedc29ab422464281ec3e1b Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 16:25:31 +1000 Subject: export output name to userspace --- linux-core/drm_crtc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 46b7f7ae..cc082d91 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -838,16 +838,19 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, if (copy_from_user(&out_resp, argp, sizeof(out_resp))) return -EFAULT; - DRM_DEBUG("output id %d\n", out_resp.output); + DRM_DEBUG("output id %d:\n", out_resp.output); output= idr_find(&dev->mode_config.crtc_idr, out_resp.output); if (!output || (output->id != out_resp.output)) return -EINVAL; - DRM_DEBUG("about to count modes\n"); + DRM_DEBUG("about to count modes: %s\n", output->name); list_for_each_entry(mode, &output->modes, head) mode_count++; DRM_DEBUG("about to count modes %d %d %p\n", mode_count, out_resp.count_modes, output->crtc); + strncpy(out_resp.name, output->name, DRM_OUTPUT_NAME_LEN); + out_resp.name[DRM_OUTPUT_NAME_LEN-1] = 0; + out_resp.mm_width = output->mm_width; out_resp.mm_height = output->mm_height; out_resp.subpixel = output->subpixel_order; -- cgit v1.2.3 From f1476e4e5cefd4aa8c487b4e651a26056110e2f0 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 16:25:52 +1000 Subject: re-tab and fixup the i915GM get core clock function to actually work --- linux-core/intel_display.c | 186 ++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 95 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 92b39406..75d7359f 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -583,8 +583,9 @@ static int intel_get_core_clock_speed(drm_device_t *dev) else if (IS_I945GM(dev) || IS_845G(dev)) return 200000; else if (IS_I915GM(dev)) { -#if 0 - u16 gcfgc = pciReadWord(dev->PciTag, I915_GCFGC); + u16 gcfgc = 0; + + pci_read_config_word(dev->pdev, I915_GCFGC, &gcfgc); if (gcfgc & I915_LOW_FREQUENCY_ENABLE) return 133000; @@ -597,7 +598,6 @@ static int intel_get_core_clock_speed(drm_device_t *dev) return 190000; } } -#endif } else if (IS_I865G(dev)) return 266000; else if (IS_I855(dev)) { @@ -835,106 +835,102 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, if (intel_panel_fitter_pipe(dev) == pipe) I915_WRITE(PFIT_CONTROL, 0); + DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + drm_mode_debug_printmodeline(dev, mode); + #if 0 - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); - xf86PrintModeline(pScrn->scrnIndex, mode); - if (!xf86ModesEqual(mode, adjusted_mode)) { - xf86DrvMsg(pScrn->scrnIndex, X_INFO, - "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); - xf86PrintModeline(pScrn->scrnIndex, mode); - } - i830PrintPll("chosen", &clock); + if (!xf86ModesEqual(mode, adjusted_mode)) { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + xf86PrintModeline(pScrn->scrnIndex, mode); + } + i830PrintPll("chosen", &clock); #endif - if (dpll & DPLL_VCO_ENABLE) - { + if (dpll & DPLL_VCO_ENABLE) { + I915_WRITE(fp_reg, fp); + I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); + I915_READ(dpll_reg); + udelay(150); + } + + /* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ + if (is_lvds) { + u32 lvds = I915_READ(LVDS); + + lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; + /* Set the B0-B3 data pairs corresponding to whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (clock.p2 == 7) + lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more thoroughly into how + * panels behave in the two modes. + */ + + I915_WRITE(LVDS, lvds); + I915_READ(LVDS); + } + I915_WRITE(fp_reg, fp); - I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); + I915_WRITE(dpll_reg, dpll); I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ udelay(150); - } - - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (is_lvds) - { - u32 lvds = I915_READ(LVDS); - - lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock.p2 == 7) - lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - - I915_WRITE(LVDS, lvds); - I915_READ(LVDS); - } - - I915_WRITE(fp_reg, fp); - I915_WRITE(dpll_reg, dpll); - I915_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - - if (IS_I965G(dev)) { - int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | - ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); - } else { - /* write it again -- the BIOS does, after all */ - I915_WRITE(dpll_reg, dpll); - } - I915_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - - I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | - ((adjusted_mode->crtc_htotal - 1) << 16)); - I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | - ((adjusted_mode->crtc_hblank_end - 1) << 16)); - I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | - ((adjusted_mode->crtc_hsync_end - 1) << 16)); - I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); - I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); - I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | - ((adjusted_mode->crtc_vsync_end - 1) << 16)); - I915_WRITE(dspstride_reg, crtc->fb->pitch * (crtc->fb->bits_per_pixel / 8)); - /* pipesrc and dspsize control the size that is scaled from, which should - * always be the user's requested size. - */ - I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - I915_WRITE(dsppos_reg, 0); - I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - I915_WRITE(pipeconf_reg, pipeconf); - I915_READ(pipeconf_reg); - - intel_wait_for_vblank(dev); - - I915_WRITE(dspcntr_reg, dspcntr); - - /* Flush the plane changes */ - intel_pipe_set_base(crtc, x, y); - + + if (IS_I965G(dev)) { + int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; + I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | + ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + } else { + /* write it again -- the BIOS does, after all */ + I915_WRITE(dpll_reg, dpll); + } + I915_READ(dpll_reg); + /* Wait for the clocks to stabilize. */ + udelay(150); + + I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + I915_WRITE(dspstride_reg, crtc->fb->pitch * (crtc->fb->bits_per_pixel / 8)); + /* pipesrc and dspsize control the size that is scaled from, which should + * always be the user's requested size. + */ + I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + I915_WRITE(dsppos_reg, 0); + I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); + I915_WRITE(pipeconf_reg, pipeconf); + I915_READ(pipeconf_reg); + + intel_wait_for_vblank(dev); + + I915_WRITE(dspcntr_reg, dspcntr); + + /* Flush the plane changes */ + intel_pipe_set_base(crtc, x, y); + #ifdef XF86DRI // TODO // I830DRISetVBlankInterrupt (pScrn, TRUE); #endif - - intel_wait_for_vblank(dev); - - + + intel_wait_for_vblank(dev); } /** Loads the palette/gamma unit for the CRTC with the prepared values */ @@ -1002,7 +998,7 @@ void intel_crtc_init(drm_device_t *dev, int pipe) if (crtc == NULL) return; - intel_crtc = kmalloc(sizeof(struct intel_crtc), GFP_KERNEL); + intel_crtc = kzalloc(sizeof(struct intel_crtc), GFP_KERNEL); if (intel_crtc == NULL) { kfree(crtc); return; -- cgit v1.2.3 From 23a66fd506e71fdfde906d1679fb07c0df8bec4c Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 16:26:07 +1000 Subject: fixup true/false in intel_sdvo.c --- linux-core/intel_sdvo.c | 72 ++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 34 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c index 0e870d15..4a954f9e 100644 --- a/linux-core/intel_sdvo.c +++ b/linux-core/intel_sdvo.c @@ -298,7 +298,7 @@ static bool intel_sdvo_get_trained_inputs(struct drm_output *output, bool *input *input_1 = response.input0_trained; *input_2 = response.input1_trained; - return TRUE; + return true; } static bool intel_sdvo_get_active_outputs(struct drm_output *output, @@ -363,13 +363,13 @@ static bool intel_sdvo_get_input_pixel_clock_range(struct drm_output *output, status = intel_sdvo_read_response(output, &clocks, sizeof(clocks)); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; /* Convert the values from units of 10 kHz to kHz. */ *clock_min = clocks.min * 10; *clock_max = clocks.max * 10; - return TRUE; + return true; } static bool intel_sdvo_set_target_output(struct drm_output *output, @@ -393,15 +393,15 @@ static bool intel_sdvo_get_timing(struct drm_output *output, u8 cmd, status = intel_sdvo_read_response(output, &dtd->part1, sizeof(dtd->part1)); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; intel_sdvo_write_cmd(output, cmd + 1, NULL, 0); status = intel_sdvo_read_response(output, &dtd->part2, sizeof(dtd->part2)); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; - return TRUE; + return true; } static bool intel_sdvo_get_input_timing(struct drm_output *output, @@ -426,14 +426,14 @@ static bool intel_sdvo_set_timing(struct drm_output *output, u8 cmd, intel_sdvo_write_cmd(output, cmd, &dtd->part1, sizeof(dtd->part1)); status = intel_sdvo_read_response(output, NULL, 0); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; intel_sdvo_write_cmd(output, cmd + 1, &dtd->part2, sizeof(dtd->part2)); status = intel_sdvo_read_response(output, NULL, 0); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; - return TRUE; + return true; } static bool intel_sdvo_set_input_timing(struct drm_output *output, @@ -464,16 +464,16 @@ static bool intel_sdvo_get_preferred_input_timing(struct drm_output *output, status = intel_sdvo_read_response(output, &dtd->part1, sizeof(dtd->part1)); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; intel_sdvo_write_cmd(output, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2, NULL, 0); status = intel_sdvo_read_response(output, &dtd->part2, sizeof(dtd->part2)); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; - return TRUE; + return true; } #endif @@ -502,9 +502,9 @@ static bool intel_sdvo_set_clock_rate_mult(struct drm_output *output, u8 val) intel_sdvo_write_cmd(output, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1); status = intel_sdvo_read_response(output, NULL, 0); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; - return TRUE; + return true; } static bool intel_sdvo_mode_fixup(struct drm_output *output, @@ -515,7 +515,7 @@ static bool intel_sdvo_mode_fixup(struct drm_output *output, * device will be told of the multiplier during mode_set. */ adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); - return TRUE; + return true; } static void intel_sdvo_mode_set(struct drm_output *output, @@ -584,7 +584,7 @@ static void intel_sdvo_mode_set(struct drm_output *output, intel_sdvo_set_output_timing(output, &output_dtd); /* Set the input timing to the screen. Assume always input 0. */ - intel_sdvo_set_target_input(output, TRUE, FALSE); + intel_sdvo_set_target_input(output, true, false); /* We would like to use i830_sdvo_create_preferred_input_timing() to * provide the device with a timing it can support, if it supports that @@ -620,16 +620,20 @@ static void intel_sdvo_mode_set(struct drm_output *output, } /* Set the SDVO control regs. */ - sdvox = I915_READ(sdvo_priv->output_device); - switch (sdvo_priv->output_device) { - case SDVOB: - sdvox &= SDVOB_PRESERVE_MASK; - break; - case SDVOC: - sdvox &= SDVOC_PRESERVE_MASK; - break; - } - sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; + if (0/*IS_I965GM(dev)*/) { + sdvox = SDVO_BORDER_ENABLE; + } else { + sdvox = I915_READ(sdvo_priv->output_device); + switch (sdvo_priv->output_device) { + case SDVOB: + sdvox &= SDVOB_PRESERVE_MASK; + break; + case SDVOC: + sdvox &= SDVOC_PRESERVE_MASK; + break; + } + sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; + } if (intel_crtc->pipe == 1) sdvox |= SDVO_PIPE_B_SELECT; @@ -706,13 +710,13 @@ static void intel_sdvo_save(struct drm_output *output) intel_sdvo_get_active_outputs(output, &sdvo_priv->save_active_outputs); if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { - intel_sdvo_set_target_input(output, TRUE, FALSE); + intel_sdvo_set_target_input(output, true, false); intel_sdvo_get_input_timing(output, &sdvo_priv->save_input_dtd_1); } if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { - intel_sdvo_set_target_input(output, FALSE, TRUE); + intel_sdvo_set_target_input(output, false, true); intel_sdvo_get_input_timing(output, &sdvo_priv->save_input_dtd_2); } @@ -754,12 +758,12 @@ static void intel_sdvo_restore(struct drm_output *output) } if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) { - intel_sdvo_set_target_input(output, TRUE, FALSE); + intel_sdvo_set_target_input(output, true, false); intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_1); } if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) { - intel_sdvo_set_target_input(output, FALSE, TRUE); + intel_sdvo_set_target_input(output, false, true); intel_sdvo_set_input_timing(output, &sdvo_priv->save_input_dtd_2); } @@ -805,9 +809,9 @@ static bool intel_sdvo_get_capabilities(struct drm_output *output, struct intel_ intel_sdvo_write_cmd(output, SDVO_CMD_GET_DEVICE_CAPS, NULL, 0); status = intel_sdvo_read_response(output, caps, sizeof(*caps)); if (status != SDVO_CMD_STATUS_SUCCESS) - return FALSE; + return false; - return TRUE; + return true; } @@ -980,7 +984,7 @@ void intel_sdvo_init(drm_device_t *dev, int output_device) } else { - unsigned char bytes[2]; + unsigned char bytes[2]; memcpy (bytes, &sdvo_priv->caps.output_flags, 2); DRM_DEBUG("%s: No active TMDS outputs (0x%02x%02x)\n", @@ -997,7 +1001,7 @@ void intel_sdvo_init(drm_device_t *dev, int output_device) /* Set the input timing to the screen. Assume always input 0. */ - intel_sdvo_set_target_input(output, TRUE, FALSE); + intel_sdvo_set_target_input(output, true, false); intel_sdvo_get_input_pixel_clock_range(output, &sdvo_priv->pixel_clock_min, -- cgit v1.2.3 From b9c7fa55e2c6685c4c533613ab14f305f033c353 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 16:32:17 +1000 Subject: fixup sarea writes for set pipe base and dpms --- linux-core/intel_display.c | 63 ++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 36 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 75d7359f..9fb497dd 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -366,27 +366,22 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y) } -#if 0 - drmI830Sarea *sPriv = (drmI830Sarea *) DRIGetSAREAPrivate(pScrn->pScreen); - - if (!sPriv) + if (!dev_priv->sarea_priv) return; switch (pipe) { - case 0: - sPriv->pipeA_x = x; - sPriv->pipeA_y = y; - break; + case 0: + dev_priv->sarea_priv->pipeA_x = x; + dev_priv->sarea_priv->pipeA_y = y; + break; case 1: - sPriv->pipeB_x = x; - sPriv->pipeB_y = y; + dev_priv->sarea_priv->pipeB_x = x; + dev_priv->sarea_priv->pipeB_y = y; break; default: - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Can't update pipe %d in SAREA\n", pipe); + DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); break; } -#endif } /** @@ -406,6 +401,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; + 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. @@ -494,30 +490,25 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) break; } -#if 0 //TODO - if (pI830->directRenderingEnabled) { - drmI830Sarea *sPriv = (drmI830Sarea *) DRIGetSAREAPrivate(pScrn->pScreen); - Bool enabled = crtc->enabled && mode != DPMSModeOff; - - if (!sPriv) - return; - - switch (pipe) { - case 0: - sPriv->pipeA_w = enabled ? crtc->mode.HDisplay : 0; - sPriv->pipeA_h = enabled ? crtc->mode.VDisplay : 0; - break; - case 1: - sPriv->pipeB_w = enabled ? crtc->mode.HDisplay : 0; - sPriv->pipeB_h = enabled ? crtc->mode.VDisplay : 0; - break; - default: - xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Can't update pipe %d in SAREA\n", pipe); - break; - } + + if (!dev_priv->sarea_priv) + return; + + enabled = crtc->enabled && mode != DPMSModeOff; + + switch (pipe) { + case 0: + dev_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0; + dev_priv->sarea_priv->pipeA_h = enabled ? crtc->mode.vdisplay : 0; + break; + case 1: + dev_priv->sarea_priv->pipeB_w = enabled ? crtc->mode.hdisplay : 0; + dev_priv->sarea_priv->pipeB_h = enabled ? crtc->mode.vdisplay : 0; + break; + default: + DRM_ERROR("Can't update pipe %d in SAREA\n", pipe); + break; } -#endif } static bool intel_crtc_lock(struct drm_crtc *crtc) -- cgit v1.2.3 From 50672adb3142abca743535a8e60c360ef47b2a08 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Tue, 10 Apr 2007 16:49:36 +1000 Subject: add sdvo debugging output --- linux-core/intel_sdvo.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'linux-core') diff --git a/linux-core/intel_sdvo.c b/linux-core/intel_sdvo.c index 4a954f9e..1b45afdb 100644 --- a/linux-core/intel_sdvo.c +++ b/linux-core/intel_sdvo.c @@ -209,11 +209,30 @@ static void intel_sdvo_write_cmd(struct drm_output *output, u8 cmd, void *args, int args_len) { struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; int i; + if (1) { + printk("%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd); + for (i = 0; i < args_len; i++) + printk("%02X ", ((u8 *)args)[i]); + for (; i < 8; i++) + printk(" "); + for (i = 0; i < sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]); i++) { + if (cmd == sdvo_cmd_names[i].cmd) { + printk("(%s)", sdvo_cmd_names[i].name); + break; + } + } + if (i == sizeof(sdvo_cmd_names)/ sizeof(sdvo_cmd_names[0])) + printk("(%02X)",cmd); + printk("\n"); + } + for (i = 0; i < args_len; i++) { intel_sdvo_write_byte(output, SDVO_I2C_ARG_0 - i, ((u8*)args)[i]); } + intel_sdvo_write_byte(output, SDVO_I2C_OPCODE, cmd); } @@ -230,6 +249,8 @@ static const char *cmd_status_names[] = { static u8 intel_sdvo_read_response(struct drm_output *output, void *response, int response_len) { + struct intel_output *intel_output = output->driver_private; + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; int i; u8 status; @@ -242,6 +263,18 @@ static u8 intel_sdvo_read_response(struct drm_output *output, void *response, /* read the return status */ intel_sdvo_read_byte(output, SDVO_I2C_CMD_STATUS, &status); + if (1) { + printk("%s: R: ", SDVO_NAME(sdvo_priv)); + for (i = 0; i < response_len; i++) + printk("%02X ", ((u8 *)response)[i]); + for (; i < 8; i++) + printk(" "); + if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) + printk("(%s)", cmd_status_names[status]); + else + printk("(??? %d)", status); + printk("\n"); + } return status; } -- cgit v1.2.3 From 183cbd92dd016f8935f9b58ef9345fde1391173e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 10 Apr 2007 09:47:37 -0700 Subject: Finish bringing in LVDS code, re-add to Makefile. Needed other changes too: - move EDID structures to drm_edid.h - add EDID info structure to drm_output - add a few routines to intel_display for getting current mode info - add some prototypes to intel_drv.h and drm_crtc.h --- linux-core/Makefile.kernel | 2 +- linux-core/drm_crtc.h | 5 ++ linux-core/drm_edid.c | 173 +------------------------------------------- linux-core/drm_edid.h | 176 +++++++++++++++++++++++++++++++++++++++++++++ linux-core/intel_display.c | 114 +++++++++++++++++++++++++++-- linux-core/intel_drv.h | 3 + linux-core/intel_lvds.c | 117 ++++++++++++++---------------- 7 files changed, 350 insertions(+), 240 deletions(-) create mode 100644 linux-core/drm_edid.h (limited to 'linux-core') diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 1b767b6a..ac403f64 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -20,7 +20,7 @@ r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o i810-objs := i810_drv.o i810_dma.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \ - i915_buffer.o intel_display.o intel_crt.o \ + i915_buffer.o intel_display.o intel_crt.o intel_lvds.o \ intel_sdvo.o intel_modes.o intel_i2c.o nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \ nouveau_object.o nouveau_irq.o \ diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 21908f0c..775d21e7 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -369,6 +369,7 @@ struct drm_output { /* xf86MonPtr MonInfo; */ enum subpixel_order subpixel_order; int mm_width, mm_height; + struct edid *monitor_info; char name[DRM_OUTPUT_LEN]; const struct drm_output_funcs *funcs; void *driver_private; @@ -455,6 +456,10 @@ extern int drm_mode_vrefresh(struct drm_display_mode *mode); extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); +extern bool drm_initial_config(struct drm_device *dev, bool cangrow); +extern void drm_framebuffer_set_object(struct drm_device *dev, + unsigned long handle); +extern bool drm_set_desired_modes(struct drm_device *dev); #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index fcd97d67..b79bc2db 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -4,181 +4,14 @@ * * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from * FB layer. + * Copyright (C) 2006 Dennis Munsie */ #include #include #include "drmP.h" +#include "drm_edid.h" -#define EDID_LENGTH 128 -#define DDC_ADDR 0x50 - -#ifdef BIG_ENDIAN -#error "EDID structure is little endian, need big endian versions" -#endif - -struct est_timings { - u8 t1; - u8 t2; - u8 mfg_rsvd; -} __attribute__((packed)); - -struct std_timing { - u8 hsize; /* need to multiply by 8 then add 248 */ - u8 vfreq:6; /* need to add 60 */ - u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ -} __attribute__((packed)); - -/* If detailed data is pixel timing */ -struct detailed_pixel_timing { - u8 hactive_lo; - u8 hblank_lo; - u8 hblank_hi:4; - u8 hactive_hi:4; - u8 vactive_lo; - u8 vblank_lo; - u8 vblank_hi:4; - u8 vactive_hi:4; - u8 hsync_offset_lo; - u8 hsync_pulse_width_lo; - u8 vsync_pulse_width_lo:4; - u8 vsync_offset_lo:4; - u8 hsync_pulse_width_hi:2; - u8 hsync_offset_hi:2; - u8 vsync_pulse_width_hi:2; - u8 vsync_offset_hi:2; - u8 width_mm_lo; - u8 height_mm_lo; - u8 height_mm_hi:4; - u8 width_mm_hi:4; - u8 hborder; - u8 vborder; - u8 unknown0:1; - u8 vsync_positive:1; - u8 hsync_positive:1; - u8 separate_sync:2; - u8 stereo:1; - u8 unknown6:1; - u8 interlaced:1; -} __attribute__((packed)); - -/* If it's not pixel timing, it'll be one of the below */ -struct detailed_data_string { - u8 str[13]; -} __attribute__((packed)); - -struct detailed_data_monitor_range { - u8 min_vfreq; - u8 max_vfreq; - u8 min_hfreq_khz; - u8 max_hfreq_khz; - u8 pixel_clock_mhz; /* need to multiply by 10 */ - u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ - u8 hfreq_start_khz; /* need to multiply by 2 */ - u8 c; /* need to divide by 2 */ - u16 m; /* FIXME: byte order */ - u8 k; - u8 j; /* need to divide by 2 */ -} __attribute__((packed)); - -struct detailed_data_wpindex { - u8 white_y_lo:2; - u8 white_x_lo:2; - u8 pad:4; - u8 white_x_hi; - u8 white_y_hi; - u8 gamma; /* need to divide by 100 then add 1 */ -} __attribute__((packed)); - -struct detailed_data_color_point { - u8 windex1; - u8 wpindex1[3]; - u8 windex2; - u8 wpindex2[3]; -} __attribute__((packed)); - -struct detailed_non_pixel { - u8 pad1; - u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name - fb=color point data, fa=standard timing data, - f9=undefined, f8=mfg. reserved */ - u8 pad2; - union { - struct detailed_data_string str; - struct detailed_data_monitor_range range; - struct detailed_data_wpindex color; - struct std_timing timings[5]; - } data; -} __attribute__((packed)); - -#define EDID_DETAIL_STD_MODES 0xfa -#define EDID_DETAIL_MONITOR_CPDATA 0xfb -#define EDID_DETAIL_MONITOR_NAME 0xfc -#define EDID_DETAIL_MONITOR_RANGE 0xfd -#define EDID_DETAIL_MONITOR_STRING 0xfe -#define EDID_DETAIL_MONITOR_SERIAL 0xff - -struct detailed_timing { - u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ - union { - struct detailed_pixel_timing pixel_data; - struct detailed_non_pixel other_data; - } data; -} __attribute__((packed)); - -struct edid { - u8 header[8]; - /* Vendor & product info */ - u16 mfg_id; /* FIXME: byte order */ - u16 prod_code; /* FIXME: byte order */ - u32 serial; /* FIXME: byte order */ - u8 mfg_week; - u8 mfg_year; - /* EDID version */ - u8 version; - u8 revision; - /* Display info: */ - /* input definition */ - u8 serration_vsync:1; - u8 sync_on_green:1; - u8 composite_sync:1; - u8 separate_syncs:1; - u8 blank_to_black:1; - u8 video_level:2; - u8 digital:1; /* bits below must be zero if set */ - u8 width_cm; - u8 height_cm; - u8 gamma; - /* feature support */ - u8 default_gtf:1; - u8 preferred_timing:1; - u8 standard_color:1; - u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ - u8 pm_active_off:1; - u8 pm_suspend:1; - u8 pm_standby:1; - /* Color characteristics */ - u8 red_green_lo; - u8 black_white_lo; - u8 red_x; - u8 red_y; - u8 green_x; - u8 green_y; - u8 blue_x; - u8 blue_y; - u8 white_x; - u8 white_y; - /* Est. timings and mfg rsvd timings*/ - struct est_timings established_timings; - /* Standard timings 1-8*/ - struct std_timing standard_timings[8]; - /* Detailing timings 1-4 */ - struct detailed_timing detailed_timings[4]; - /* Number of 128 byte ext. blocks */ - u8 extensions; - /* Checksum */ - u8 checksum; -} __attribute__((packed)); - +/* Valid EDID header has these bytes */ static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; /** diff --git a/linux-core/drm_edid.h b/linux-core/drm_edid.h new file mode 100644 index 00000000..0d2eeaa1 --- /dev/null +++ b/linux-core/drm_edid.h @@ -0,0 +1,176 @@ +#ifndef __DRM_EDID_H__ +#define __DRM_EDID_H__ + +#include + +#define EDID_LENGTH 128 +#define DDC_ADDR 0x50 + +#ifdef BIG_ENDIAN +#error "EDID structure is little endian, need big endian versions" +#endif + +struct est_timings { + u8 t1; + u8 t2; + u8 mfg_rsvd; +} __attribute__((packed)); + +struct std_timing { + u8 hsize; /* need to multiply by 8 then add 248 */ + u8 vfreq:6; /* need to add 60 */ + u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ +} __attribute__((packed)); + +/* If detailed data is pixel timing */ +struct detailed_pixel_timing { + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_offset_lo; + u8 hsync_pulse_width_lo; + u8 vsync_pulse_width_lo:4; + u8 vsync_offset_lo:4; + u8 hsync_pulse_width_hi:2; + u8 hsync_offset_hi:2; + u8 vsync_pulse_width_hi:2; + u8 vsync_offset_hi:2; + u8 width_mm_lo; + u8 height_mm_lo; + u8 height_mm_hi:4; + u8 width_mm_hi:4; + u8 hborder; + u8 vborder; + u8 unknown0:1; + u8 vsync_positive:1; + u8 hsync_positive:1; + u8 separate_sync:2; + u8 stereo:1; + u8 unknown6:1; + u8 interlaced:1; +} __attribute__((packed)); + +/* If it's not pixel timing, it'll be one of the below */ +struct detailed_data_string { + u8 str[13]; +} __attribute__((packed)); + +struct detailed_data_monitor_range { + u8 min_vfreq; + u8 max_vfreq; + u8 min_hfreq_khz; + u8 max_hfreq_khz; + u8 pixel_clock_mhz; /* need to multiply by 10 */ + u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ + u8 hfreq_start_khz; /* need to multiply by 2 */ + u8 c; /* need to divide by 2 */ + u16 m; /* FIXME: byte order */ + u8 k; + u8 j; /* need to divide by 2 */ +} __attribute__((packed)); + +struct detailed_data_wpindex { + u8 white_y_lo:2; + u8 white_x_lo:2; + u8 pad:4; + u8 white_x_hi; + u8 white_y_hi; + u8 gamma; /* need to divide by 100 then add 1 */ +} __attribute__((packed)); + +struct detailed_data_color_point { + u8 windex1; + u8 wpindex1[3]; + u8 windex2; + u8 wpindex2[3]; +} __attribute__((packed)); + +struct detailed_non_pixel { + u8 pad1; + u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name + fb=color point data, fa=standard timing data, + f9=undefined, f8=mfg. reserved */ + u8 pad2; + union { + struct detailed_data_string str; + struct detailed_data_monitor_range range; + struct detailed_data_wpindex color; + struct std_timing timings[5]; + } data; +} __attribute__((packed)); + +#define EDID_DETAIL_STD_MODES 0xfa +#define EDID_DETAIL_MONITOR_CPDATA 0xfb +#define EDID_DETAIL_MONITOR_NAME 0xfc +#define EDID_DETAIL_MONITOR_RANGE 0xfd +#define EDID_DETAIL_MONITOR_STRING 0xfe +#define EDID_DETAIL_MONITOR_SERIAL 0xff + +struct detailed_timing { + u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ + union { + struct detailed_pixel_timing pixel_data; + struct detailed_non_pixel other_data; + } data; +} __attribute__((packed)); + +struct edid { + u8 header[8]; + /* Vendor & product info */ + u16 mfg_id; /* FIXME: byte order */ + u16 prod_code; /* FIXME: byte order */ + u32 serial; /* FIXME: byte order */ + u8 mfg_week; + u8 mfg_year; + /* EDID version */ + u8 version; + u8 revision; + /* Display info: */ + /* input definition */ + u8 serration_vsync:1; + u8 sync_on_green:1; + u8 composite_sync:1; + u8 separate_syncs:1; + u8 blank_to_black:1; + u8 video_level:2; + u8 digital:1; /* bits below must be zero if set */ + u8 width_cm; + u8 height_cm; + u8 gamma; + /* feature support */ + u8 default_gtf:1; + u8 preferred_timing:1; + u8 standard_color:1; + u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ + u8 pm_active_off:1; + u8 pm_suspend:1; + u8 pm_standby:1; + /* Color characteristics */ + u8 red_green_lo; + u8 black_white_lo; + u8 red_x; + u8 red_y; + u8 green_x; + u8 green_y; + u8 blue_x; + u8 blue_y; + u8 white_x; + u8 white_y; + /* Est. timings and mfg rsvd timings*/ + struct est_timings established_timings; + /* Standard timings 1-8*/ + struct std_timing standard_timings[8]; + /* Detailing timings 1-4 */ + struct detailed_timing detailed_timings[4]; + /* Number of 128 byte ext. blocks */ + u8 extensions; + /* Checksum */ + u8 checksum; +} __attribute__((packed)); + +#endif /* __DRM_EDID_H__ */ diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 58d66987..90c894ee 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -26,8 +26,6 @@ #include #include "drmP.h" -#include "drm.h" -#include "drm_crtc.h" #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" @@ -973,10 +971,115 @@ static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, intel_crtc_load_lut(crtc); } +/* Returns the clock of the currently programmed mode of the given pipe. */ +static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + u32 dpll = I915_READ((pipe == 0) ? DPLL_A : DPLL_B); + u32 fp; + intel_clock_t clock; + + if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) + fp = I915_READ((pipe == 0) ? FPA0 : FPB0); + else + fp = I915_READ((pipe == 0) ? FPA1 : FPB1); + + clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; + clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; + clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; + if (IS_I9XX(dev)) { + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + + switch (dpll & DPLL_MODE_MASK) { + case DPLLB_MODE_DAC_SERIAL: + clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? + 5 : 10; + break; + case DPLLB_MODE_LVDS: + clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? + 7 : 14; + break; + default: + DRM_DEBUG("Unknown DPLL mode %08x in programmed " + "mode\n", (int)(dpll & DPLL_MODE_MASK)); + return 0; + } + + /* XXX: Handle the 100Mhz refclk */ + i9xx_clock(96000, &clock); + } else { + bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN); + + if (is_lvds) { + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + clock.p2 = 14; + + if ((dpll & PLL_REF_INPUT_MASK) == + PLLB_REF_INPUT_SPREADSPECTRUMIN) { + /* XXX: might not be 66MHz */ + i8xx_clock(66000, &clock); + } else + i8xx_clock(48000, &clock); + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) + clock.p1 = 2; + else { + clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 4; + else + clock.p2 = 2; + + i8xx_clock(48000, &clock); + } + } + + /* XXX: It would be nice to validate the clocks, but we can't reuse + * i830PllIsValid() because it relies on the xf86_config output + * configuration being accurate, which it isn't necessarily. + */ + + return clock.dot; +} + +/** Returns the currently programmed mode of the given pipe. */ struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev, struct drm_crtc *crtc) { - return NULL; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = crtc->driver_private; + int pipe = intel_crtc->pipe; + struct drm_display_mode *mode; + int htot = I915_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); + int hsync = I915_READ((pipe == 0) ? HSYNC_A : HSYNC_B); + int vtot = I915_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); + int vsync = I915_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->clock = intel_crtc_clock_get(dev, crtc); + mode->hdisplay = (htot & 0xffff) + 1; + mode->htotal = ((htot & 0xffff0000) >> 16) + 1; + mode->hsync_start = (hsync & 0xffff) + 1; + mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; + mode->vdisplay = (vtot & 0xffff) + 1; + mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; + mode->vsync_start = (vsync & 0xffff) + 1; + mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; + /* FIXME: pull name generation into a common routine */ + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, + mode->vdisplay); + drm_mode_set_crtcinfo(mode, 0); + + return mode; } static const struct drm_crtc_funcs intel_crtc_funcs = { @@ -1017,7 +1120,7 @@ void intel_crtc_init(drm_device_t *dev, int pipe) crtc->driver_private = intel_crtc; } -int intel_output_clones (drm_device_t *dev, int type_mask) +int intel_output_clones(drm_device_t *dev, int type_mask) { int index_mask = 0; struct drm_output *output; @@ -1039,9 +1142,11 @@ static void intel_setup_outputs(drm_device_t *dev) intel_crt_init(dev); +#if 0 /* Set up integrated LVDS */ if (IS_MOBILE(dev) && !IS_I830(dev)) intel_lvds_init(dev); +#endif if (IS_I9XX(dev)) { intel_sdvo_init(dev, SDVOB); @@ -1105,6 +1210,7 @@ void intel_modeset_init(drm_device_t *dev) intel_setup_outputs(dev); drm_initial_config(dev, false); + drm_set_desired_modes(dev); } void intel_modeset_cleanup(drm_device_t *dev) diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h index 7b02d35f..0675e069 100644 --- a/linux-core/intel_drv.h +++ b/linux-core/intel_drv.h @@ -71,4 +71,7 @@ extern void intel_lvds_init(drm_device_t *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_output_prepare (struct drm_output *output); extern void intel_output_commit (struct drm_output *output); +extern struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev, + struct drm_crtc *crtc); + #endif /* __INTEL_DRV_H__ */ diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index 7527dfce..4638fb30 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -31,6 +31,7 @@ #include "drmP.h" #include "drm.h" #include "drm_crtc.h" +#include "drm_edid.h" #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" @@ -262,7 +263,7 @@ static int intel_lvds_get_modes(struct drm_output *output) struct intel_output *intel_output = output->driver_private; struct drm_device *dev = output->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_monitor_info *edid_mon; + struct edid *edid_info; int ret = 0; intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); @@ -276,23 +277,29 @@ static int intel_lvds_get_modes(struct drm_output *output) ret = intel_ddc_get_modes(output); if (ret) return ret; -#if 0 + + /* Didn't get an EDID */ if (!output->monitor_info) { - edid_mon = kzalloc(sizeof(*output->monitor_info), GFP_KERNEL); - if (edid_mon) { - /* Set wide sync ranges so we get all modes - * handed to valid_mode for checking - */ - edid_mon->det_mon[0].type = DS_RANGES; - edid_mon->det_mon[0].section.ranges.min_v = 0; - edid_mon->det_mon[0].section.ranges.max_v = 200; - edid_mon->det_mon[0].section.ranges.min_h = 0; - edid_mon->det_mon[0].section.ranges.max_h = 200; - - output->monitor_info = edid_mon; - } + struct detailed_data_monitor_range *edid_range; + edid_info = kzalloc(sizeof(*output->monitor_info), GFP_KERNEL); + if (!edid_info) + goto out; + + edid_info->detailed_timings[0].data.other_data.type = + EDID_DETAIL_MONITOR_RANGE; + edid_range = &edid_info->detailed_timings[0].data.other_data.data.range; + + /* Set wide sync ranges so we get all modes + * handed to valid_mode for checking + */ + edid_range->min_vfreq = 0; + edid_range->max_vfreq = 200; + edid_range->min_hfreq_khz = 0; + edid_range->max_hfreq_khz = 200; + output->monitor_info = edid_info; } -#endif + +out: if (dev_priv->panel_fixed_mode != NULL) { struct drm_display_mode *mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); @@ -322,13 +329,19 @@ static const struct drm_output_funcs intel_lvds_output_funcs = { .cleanup = intel_lvds_destroy }; -void -intel_lvds_init(struct drm_device *dev) +/** + * intel_lvds_init - setup LVDS outputs on this device + * @dev: drm device + * + * Create the output, register the LVDS DDC bus, and try to figure out what + * modes we can display on the LVDS panel (if present). + */ +void intel_lvds_init(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_output *output; struct intel_output *intel_output; - struct drm_display_mode *modes, scan, bios_mode; + struct drm_display_mode *scan; /* *modes, *bios_mode; */ output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS"); if (!output) @@ -356,37 +369,43 @@ intel_lvds_init(struct drm_device *dev) break; } - if (scan != NULL) + if (scan) dev_priv->panel_fixed_mode = scan; -#if 0 /* * If we didn't get EDID, try checking if the panel is already turned * on. If so, assume that whatever is currently programmed is the * correct mode. */ - if (dev_priv->panel_fixed_mode == NULL) { + if (!dev_priv->panel_fixed_mode) { u32 lvds = I915_READ(LVDS); int pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; - struct drm_crtc_config *crtc_config = dev->crtc_config; - struct drm_crtc *crtc = crtc_config->crtc[pipe]; - - if (lvds & LVDS_PORT_EN) { - dev_priv->panel_fixed_mode = intel_crtc_mode_get(pScrn, crtc); - if (dev_priv->panel_fixed_mode != NULL) - dev_priv->panel_fixed_mode->type |= M_T_PREFERRED; + struct drm_crtc_config *crtc_config = &dev->crtc_config; + struct drm_crtc *crtc; + /* FIXME: need drm_crtc_from_pipe */ + //crtc = drm_crtc_from_pipe(crtc_config, pipe); + + if (lvds & LVDS_PORT_EN && 0) { + dev_priv->panel_fixed_mode = + intel_crtc_mode_get(dev, crtc); + if (dev_priv->panel_fixed_mode) + dev_priv->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; } } - /* Get the LVDS fixed mode out of the BIOS. We should support LVDS with - * the BIOS being unavailable or broken, but lack the configuration options - * for now. +/* No BIOS poking yet... */ +#if 0 + /* Get the LVDS fixed mode out of the BIOS. We should support LVDS + * with the BIOS being unavailable or broken, but lack the + * configuration options for now. */ bios_mode = intel_bios_get_panel_mode(pScrn); if (bios_mode != NULL) { if (dev_priv->panel_fixed_mode != NULL) { if (dev_priv->debug_modes && - !xf86ModesEqual(dev_priv->panel_fixed_mode, bios_mode)) + !xf86ModesEqual(dev_priv->panel_fixed_mode, + bios_mode)) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "BIOS panel mode data doesn't match probed data, " @@ -439,35 +458,3 @@ intel_lvds_init(struct drm_device *dev) #endif return; } - -#if 0 -/** - * intel_lvds_init - setup LVDS outputs on this device - * @dev: drm device - * - * Create the output, register the LVDS DDC bus, and try to figure out what - * modes we can display on the LVDS panel (if present). - */ -void intel_lvds_init(drm_device_t *dev) -{ - struct drm_output *output; - struct intel_output *intel_output; - int modes; - - output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS"); - if (!output) - return; - - intel_output = kmalloc(sizeof(struct intel_output), GFP_KERNEL); - if (!intel_output) { - drm_output_destroy(output); - return; - } - - intel_output->type = INTEL_OUTPUT_LVDS; - output->driver_private = intel_output; - output->subpixel_order = SubPixelHorizontalRGB; - output->interlace_allowed = 0; - output->doublescan_allowed = 0; -} -#endif -- cgit v1.2.3 From c0336989884e75bcd05284257e884754bb5f85b6 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 10 Apr 2007 09:48:20 -0700 Subject: Remove some delays from Intel i2c code, we'll need a more comprehensive fix in the Linux i2c layer to make DDC reliable on old monitors. --- linux-core/intel_i2c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c index d4cf7eef..e23283fb 100644 --- a/linux-core/intel_i2c.c +++ b/linux-core/intel_i2c.c @@ -80,8 +80,8 @@ static void set_clock(void *data, int state_high) else clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; + I915_WRITE(chan->reg, reserved | clock_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ } static void set_data(void *data, int state_high) @@ -103,7 +103,6 @@ static void set_data(void *data, int state_high) GPIO_DATA_VAL_MASK; I915_WRITE(chan->reg, reserved | data_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ } /** @@ -147,7 +146,7 @@ struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg, chan->algo.setscl = set_clock; chan->algo.getsda = get_data; chan->algo.getscl = get_clock; - chan->algo.udelay = 20; + chan->algo.udelay = 20; /* between calls to (set|get)_(clock|data) */ chan->algo.timeout = usecs_to_jiffies(2200); chan->algo.data = chan; @@ -159,7 +158,6 @@ struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg, /* JJJ: raise SCL and SDA? */ set_data(chan, 1); set_clock(chan, 1); - udelay(20); return chan; -- cgit v1.2.3 From 8785679f893ef9257c589a70113ac731edba0194 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 10 Apr 2007 09:49:02 -0700 Subject: Remove some debug #if 0 codes and add a reminder to check locking around output enumeration stuff. --- linux-core/drm_crtc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index fe27e386..6874265e 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -116,6 +116,7 @@ bool drm_crtc_in_use(struct drm_crtc *crtc) { struct drm_output *output; drm_device_t *dev = crtc->dev; + /* FIXME: Locking around list access? */ list_for_each_entry(output, &dev->crtc_config.output_list, head) if (output->crtc == crtc) return true; @@ -504,7 +505,7 @@ 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, - *lvds_crtc = NULL;; + *lvds_crtc = NULL; struct drm_framebuffer *fb; struct drm_output *output, *use_output = NULL; @@ -517,7 +518,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) 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) { @@ -533,14 +534,12 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) 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); @@ -565,10 +564,8 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) use_output = output; } else if (!strncmp(output->name, "TMDS", 4)) { output->crtc = vga_crtc; -#if 0 drm_mode_debug_printmodeline(dev, des_mode); output->crtc->desired_mode = des_mode; -#endif output->initial_x = 0; output->initial_y = 0; } else if (!strncmp(output->name, "LVDS", 3)) { -- cgit v1.2.3 From b59285d738b1a832b12d9258bd6f1db8f7e61f08 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 10 Apr 2007 10:31:10 -0700 Subject: Move i915 init code to new file, i915_init.c, and create a new high level init routine that runs at driver load time. --- linux-core/i915_init.c | 1 + 1 file changed, 1 insertion(+) create mode 120000 linux-core/i915_init.c (limited to 'linux-core') diff --git a/linux-core/i915_init.c b/linux-core/i915_init.c new file mode 120000 index 00000000..473ddf7b --- /dev/null +++ b/linux-core/i915_init.c @@ -0,0 +1 @@ +../shared-core/i915_init.c \ No newline at end of file -- cgit v1.2.3 From e114b981bc291049fa6996d487334a408acc1ce2 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 10 Apr 2007 10:31:58 -0700 Subject: Export drm_setup for use by new driver init code. --- linux-core/drm_fops.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index d400a4d5..6f0465fd 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -41,7 +41,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t * dev); -static int drm_setup(drm_device_t * dev) +int drm_setup(drm_device_t * dev) { drm_local_map_t *map; int i; @@ -121,6 +121,7 @@ static int drm_setup(drm_device_t * dev) return 0; } +EXPORT_SYMBOL(drm_setup); /** * Open file. -- cgit v1.2.3 From b62ffb8e91dafbe46b4daa5be13a867b149b0170 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 09:56:09 +1000 Subject: fixup calculation to make sdvo work --- linux-core/drm_modes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_modes.c b/linux-core/drm_modes.c index 0f2a612a..14c7acd5 100644 --- a/linux-core/drm_modes.c +++ b/linux-core/drm_modes.c @@ -129,9 +129,9 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags) } p->crtc_vblank_start = min(p->crtc_vsync_start, p->crtc_vdisplay); - p->crtc_vblank_end = min(p->crtc_vsync_end, p->crtc_vtotal); + p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal); p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay); - p->crtc_hblank_end = min(p->crtc_hsync_end, p->crtc_htotal); + p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal); p->crtc_hadjusted = false; p->crtc_vadjusted = false; -- cgit v1.2.3 From 3e994a56be1bfc633e49434c9e4a3e3262070248 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 13:18:49 +1000 Subject: use fb pitch and fix up some whitespace --- linux-core/intel_display.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 1eed71f0..04a6e086 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -350,7 +350,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y) int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); Start = crtc->fb->offset + dev_priv->baseaddr; - Offset = ((y * crtc->fb->width + x) * (crtc->fb->bits_per_pixel / 8)); + Offset = ((y * crtc->fb->pitch + x) * (crtc->fb->bits_per_pixel / 8)); DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y); if (IS_I965G(dev)) { @@ -530,24 +530,22 @@ static void intel_crtc_unlock (struct drm_crtc *crtc) static void intel_crtc_prepare (struct drm_crtc *crtc) { - crtc->funcs->dpms (crtc, DPMSModeOff); + crtc->funcs->dpms(crtc, DPMSModeOff); } static void intel_crtc_commit (struct drm_crtc *crtc) { - crtc->funcs->dpms (crtc, DPMSModeOn); -// if (crtc->scrn->pScreen != NULL) -// xf86_reload_cursors (crtc->scrn->pScreen); + crtc->funcs->dpms(crtc, DPMSModeOn); } void intel_output_prepare (struct drm_output *output) { - output->funcs->dpms (output, DPMSModeOff); + output->funcs->dpms(output, DPMSModeOff); } void intel_output_commit (struct drm_output *output) { - output->funcs->dpms (output, DPMSModeOn); + output->funcs->dpms(output, DPMSModeOn); } static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, -- cgit v1.2.3 From 44be9c9d5950d3b2ba4d5527189abec8dac0686f Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 13:19:30 +1000 Subject: add an fb count + id get to the get resources code path --- linux-core/drm_crtc.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 1899df2d..12705272 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -84,7 +84,6 @@ struct drm_crtc *drm_crtc_create(drm_device_t *dev, crtc->funcs = funcs; crtc->id = drm_mode_idr_get(dev, crtc); - DRM_DEBUG("crtc %p got id %d\n", crtc, crtc->id); spin_lock(&dev->mode_config.config_lock); list_add_tail(&crtc->head, &dev->mode_config.crtc_list); @@ -291,7 +290,6 @@ bool drm_set_desired_modes(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { output = NULL; - DRM_DEBUG("crtc is %d\n", crtc->id); list_for_each_entry(list_output, &dev->mode_config.output_list, head) { if (list_output->crtc == crtc) { output = list_output; @@ -610,7 +608,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, if (crtc_info->x != crtc->x || crtc_info->y != crtc->y) changed = true; - if (crtc->mode.mode_id != new_mode->mode_id) + if (new_mode && (crtc->mode.mode_id != new_mode->mode_id)) changed = true; list_for_each_entry(output, &dev->mode_config.output_list, head) { @@ -633,8 +631,8 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, } if (changed) { - crtc->enabled = new_mode != NULL; - if (new_mode) { + crtc->enabled = (new_mode != NULL); + if (new_mode != NULL) { DRM_DEBUG("attempting to set mode from userspace\n"); drm_mode_debug_printmodeline(dev, new_mode); if (!drm_crtc_set_mode(crtc, new_mode, crtc_info->x, @@ -648,7 +646,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, } crtc->desired_x = crtc_info->x; crtc->desired_y = crtc_info->y; - crtc->desired_mode = new_mode; + crtc->desired_mode = new_mode; } drm_disable_unused_functions(dev); } @@ -687,6 +685,7 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, struct drm_mode_card_res __user *argp = (void __user *)arg; struct drm_mode_card_res card_res; struct list_head *lh; + struct drm_framebuffer *fb; struct drm_output *output; struct drm_crtc *crtc; struct drm_mode_modeinfo u_mode; @@ -695,10 +694,14 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, int mode_count= 0; int output_count = 0; int crtc_count = 0; + int fb_count = 0; int copied = 0; memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); + list_for_each(lh, &dev->mode_config.fb_list) + fb_count++; + list_for_each(lh, &dev->mode_config.crtc_list) crtc_count++; @@ -722,7 +725,19 @@ int drm_mode_getresources(struct inode *inode, struct file *filp, } } - /* handle this in 3 parts */ + /* handle this in 4 parts */ + /* FBs */ + if (card_res.count_fbs >= fb_count) { + copied = 0; + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + if (put_user(fb->id, &card_res.fb_id[copied++])) { + retcode = -EFAULT; + goto done; + } + } + } + card_res.count_fbs = fb_count; + /* CRTCs */ if (card_res.count_crtcs >= crtc_count) { copied = 0; @@ -840,11 +855,9 @@ int drm_mode_getoutput(struct inode *inode, struct file *filp, if (!output || (output->id != out_resp.output)) return -EINVAL; - DRM_DEBUG("about to count modes: %s\n", output->name); list_for_each_entry(mode, &output->modes, head) mode_count++; - DRM_DEBUG("about to count modes %d %d %p\n", mode_count, out_resp.count_modes, output->crtc); strncpy(out_resp.name, output->name, DRM_OUTPUT_NAME_LEN); out_resp.name[DRM_OUTPUT_NAME_LEN-1] = 0; @@ -1029,9 +1042,18 @@ int drm_mode_rmfb(struct inode *inode, struct file *filp, /* TODO check if we own the buffer */ /* TODO release all crtc connected to the framebuffer */ + /* bind the fb to the crtc for now */ + { + struct drm_crtc *crtc; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) + crtc->fb = NULL; + } + } /* TODO unhock the destructor from the buffer object */ drm_framebuffer_destroy(fb); return 0; } + -- cgit v1.2.3 From 7e58276c76ff2297fdf9ba295d696338377d6e14 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Apr 2007 13:40:50 +1000 Subject: Revert "Remove some delays from Intel i2c code, we'll need a more comprehensive fix" This reverts commit c0336989884e75bcd05284257e884754bb5f85b6. this break SDVO --- linux-core/intel_i2c.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_i2c.c b/linux-core/intel_i2c.c index e23283fb..d4cf7eef 100644 --- a/linux-core/intel_i2c.c +++ b/linux-core/intel_i2c.c @@ -80,8 +80,8 @@ static void set_clock(void *data, int state_high) else clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; - I915_WRITE(chan->reg, reserved | clock_bits); + udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ } static void set_data(void *data, int state_high) @@ -103,6 +103,7 @@ static void set_data(void *data, int state_high) GPIO_DATA_VAL_MASK; I915_WRITE(chan->reg, reserved | data_bits); + udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ } /** @@ -146,7 +147,7 @@ struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg, chan->algo.setscl = set_clock; chan->algo.getsda = get_data; chan->algo.getscl = get_clock; - chan->algo.udelay = 20; /* between calls to (set|get)_(clock|data) */ + chan->algo.udelay = 20; chan->algo.timeout = usecs_to_jiffies(2200); chan->algo.data = chan; @@ -158,6 +159,7 @@ struct intel_i2c_chan *intel_i2c_create(drm_device_t *dev, const u32 reg, /* JJJ: raise SCL and SDA? */ set_data(chan, 1); set_clock(chan, 1); + udelay(20); return chan; -- cgit v1.2.3 From 9d12da5917ec57605a2c4cd81c1753145f7e229c Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 11 Apr 2007 14:34:22 +1000 Subject: only bo finish at driver unload --- linux-core/drm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 80007170..b43af328 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -156,8 +156,6 @@ int drm_lastclose(drm_device_t * dev) * We can't do much about this function failing. */ - drm_bo_driver_finish(dev); - if (dev->driver->lastclose) dev->driver->lastclose(dev); DRM_DEBUG("driver lastclose completed\n"); @@ -400,6 +398,8 @@ static void drm_cleanup(drm_device_t * dev) DRM_DEBUG("mtrr_del=%d\n", retval); } + drm_bo_driver_finish(dev); + if (drm_core_has_AGP(dev) && dev->agp) { drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); dev->agp = NULL; -- cgit v1.2.3 From 32f6a58db216f23a7c71ca9c7eda56aaa8293078 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 11 Apr 2007 16:33:03 +1000 Subject: add initial drm_fb framebuffer So far I can load fbcon, once I use my miniglx to add a framebuffer. fbcon doesn't show anything on screen but baby steps and all that. --- linux-core/Makefile.kernel | 2 +- linux-core/drm_crtc.c | 2 + linux-core/drm_crtc.h | 6 ++ linux-core/drm_fb.c | 160 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 linux-core/drm_fb.c (limited to 'linux-core') diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index c767116a..b9684d68 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -14,7 +14,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \ drm_memory_debug.o ati_pcigart.o drm_sman.o \ drm_hashtab.o drm_mm.o drm_object.o drm_compat.o \ drm_fence.o drm_ttm.o drm_bo.o drm_bo_move.o drm_crtc.o \ - drm_edid.o drm_modes.o + drm_edid.o drm_modes.o drm_fb.o tdfx-objs := tdfx_drv.o r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 12705272..83f8e167 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -1022,6 +1022,7 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, if (copy_to_user(argp, &r, sizeof(r))) return -EFAULT; + drmfb_probe(dev, fb); return 0; } @@ -1040,6 +1041,7 @@ int drm_mode_rmfb(struct inode *inode, struct file *filp, return -EINVAL; } + drmfb_remove(dev, fb); /* TODO check if we own the buffer */ /* TODO release all crtc connected to the framebuffer */ /* bind the fb to the crtc for now */ diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 57bfb10b..d5d256d4 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -11,6 +11,8 @@ #include #include +#include + struct drm_device; /* @@ -179,6 +181,9 @@ struct drm_framebuffer { int bits_per_pixel; int flags; struct drm_buffer_object *bo; + void *fbdev; + u32 pseudo_palette[17]; + void *virtual_base; }; struct drm_crtc; struct drm_output; @@ -412,6 +417,7 @@ struct drm_mode_config { /* DamagePtr rotationDamage? */ /* DGA stuff? */ struct drm_mode_config_funcs *funcs; + int fb_base; }; struct drm_output *drm_output_create(struct drm_device *dev, diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c new file mode 100644 index 00000000..9bf2187c --- /dev/null +++ b/linux-core/drm_fb.c @@ -0,0 +1,160 @@ + /* + * Modularization + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmP.h" +struct drmfb_par { + struct drm_device *dev; + struct drm_framebuffer *fb; +}; + +static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct drmfb_par *par = info->par; + struct drm_framebuffer *fb = par->fb; + if (regno > 17) + return 1; + + printk(KERN_INFO "Got set col reg %d %d %d %d\n", red, green, blue, regno); + + if (regno < 16) { + switch (fb->depth) { + case 15: + fb->pseudo_palette[regno] = ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | + ((blue & 0xf800) >> 11); + break; + case 16: + fb->pseudo_palette[regno] = (red & 0xf800) | + ((green & 0xfc00) >> 5) | + ((blue & 0xf800) >> 11); + break; + case 24: + fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | + (green & 0xff00) | + ((blue & 0xff00) >> 8); + break; + } + } + + return 0; +} + +static struct fb_ops drmfb_ops = { + .owner = THIS_MODULE, + // .fb_open = drmfb_open, + // .fb_read = drmfb_read, + // .fb_write = drmfb_write, + // .fb_release = drmfb_release, + // .fb_ioctl = drmfb_ioctl, + .fb_setcolreg = drmfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) +{ + struct fb_info *info; + struct drmfb_par *par; + struct device *device = &dev->pdev->dev; + struct fb_var_screeninfo *var_info; + unsigned long base, size; + + info = framebuffer_alloc(sizeof(struct drmfb_par), device); + if (!info){ + return -EINVAL; + } + + fb->fbdev = info; + + par = info->par; + + par->dev = dev; + par->fb = fb; + + info->fbops = &drmfb_ops; + + strcpy(info->fix.id, "drmfb"); + info->fix.smem_start = fb->offset + dev->mode_config.fb_base; + info->fix.smem_len = (8*1024*1024); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_DIRECTCOLOR; + info->fix.type_aux = 0; + info->fix.mmio_start = 0; + info->fix.mmio_len = 0; + info->fix.line_length = fb->pitch; + + info->flags = FBINFO_DEFAULT; + + base = fb->bo->offset + dev->mode_config.fb_base; + size = (fb->bo->mem.num_pages * PAGE_SIZE); + + DRM_DEBUG("remapping %08X %d\n", base, size); + fb->virtual_base = ioremap_nocache(base, size); + + info->screen_base = fb->virtual_base; + info->screen_size = size; + info->pseudo_palette = fb->pseudo_palette; + info->var.xres = fb->width; + info->var.xres_virtual = fb->pitch; + info->var.yres = fb->height; + info->var.yres_virtual = fb->height; + info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.xoffset = 0; + info->var.yoffset = 0; + + switch(fb->depth) { + case 8: + case 15: + case 16: + break; + case 24: + case 32: + info->var.red.offset = 16; + info->var.green.offset = 8; + info->var.blue.offset = 0; + info->var.red.length = info->var.green.length = + info->var.blue.length = 8; + if (fb->depth == 32) { + info->var.transp.offset = 24; + info->var.transp.length = 8; + } + break; + } + + if (register_framebuffer(info) < 0) + return -EINVAL; + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + return 0; +} +EXPORT_SYMBOL(drmfb_probe); + +int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +{ + struct fb_info *info = fb->fbdev; + + if (info) { + iounmap(fb->virtual_base); + unregister_framebuffer(info); + framebuffer_release(info); + } + return 0; +} +EXPORT_SYMBOL(drmfb_remove); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From c582eaac194411f52a2c0527ffa093b5a422d7b9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 11 Apr 2007 16:34:40 +1000 Subject: add copyright statement --- linux-core/drm_fb.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'linux-core') diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c index 9bf2187c..1fe3e54b 100644 --- a/linux-core/drm_fb.c +++ b/linux-core/drm_fb.c @@ -1,3 +1,28 @@ +/* + * Copyright © 2007 David Airlie + * + * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * David Airlie + */ /* * Modularization */ -- cgit v1.2.3 From a6cc6a778f8b2f86300a8ce87441d044fd67f930 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 17:13:45 +1000 Subject: add support for setting a framebuffer depth --- linux-core/drm_crtc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 83f8e167..1c1c3006 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -1003,12 +1003,13 @@ int drm_mode_addfb(struct inode *inode, struct file *filp, if(!fb) return -EINVAL;; - fb->width = r.width; - fb->height = r.height; - fb->pitch = r.pitch; + fb->width = r.width; + fb->height = r.height; + fb->pitch = r.pitch; fb->bits_per_pixel = r.bpp; - fb->offset = bo->offset; - fb->bo = bo; + fb->depth = r.depth; + fb->offset = bo->offset; + fb->bo = bo; r.buffer_id = fb->id; -- cgit v1.2.3 From 1147fefed8d1154482c9cc9a9785e6871cd6e6a1 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 17:13:57 +1000 Subject: fixup framebuffer depth --- linux-core/drm_fb.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c index 1fe3e54b..e404642f 100644 --- a/linux-core/drm_fb.c +++ b/linux-core/drm_fb.c @@ -53,8 +53,6 @@ static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, if (regno > 17) return 1; - printk(KERN_INFO "Got set col reg %d %d %d %d\n", red, green, blue, regno); - if (regno < 16) { switch (fb->depth) { case 15: @@ -118,6 +116,7 @@ int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) info->fix.smem_len = (8*1024*1024); info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.visual = FB_VISUAL_DIRECTCOLOR; + info->fix.accel = FB_ACCEL_NONE; info->fix.type_aux = 0; info->fix.mmio_start = 0; info->fix.mmio_len = 0; @@ -141,12 +140,18 @@ int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) info->var.bits_per_pixel = 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; + info->var.vmode = FB_VMODE_NONINTERLACED; + DRM_DEBUG("fb depth is %d\n", fb->depth); switch(fb->depth) { case 8: case 15: case 16: break; + default: case 24: case 32: info->var.red.offset = 16; -- cgit v1.2.3 From 0392badd84ec833ddd9e2b187844d246d860bbf7 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 17:25:37 +1000 Subject: oops for 32 pitch.. hey I can see stuff on fbcon now.. it looks like text.. just a bit garbled --- linux-core/drm_fb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux-core') diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c index e404642f..30b14188 100644 --- a/linux-core/drm_fb.c +++ b/linux-core/drm_fb.c @@ -66,6 +66,7 @@ static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, ((blue & 0xf800) >> 11); break; case 24: + case 32: fb->pseudo_palette[regno] = ((red & 0xff00) << 8) | (green & 0xff00) | ((blue & 0xff00) >> 8); -- cgit v1.2.3 From 7e48d47fb51cc0f1a38a99acfe591821a45d7081 Mon Sep 17 00:00:00 2001 From: David Airlie Date: Wed, 11 Apr 2007 17:35:00 +1000 Subject: line_length calculation was incorrect.. I now can get fbcon to run --- linux-core/drm_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c index 30b14188..1a0fb79c 100644 --- a/linux-core/drm_fb.c +++ b/linux-core/drm_fb.c @@ -121,7 +121,7 @@ int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb) info->fix.type_aux = 0; info->fix.mmio_start = 0; info->fix.mmio_len = 0; - info->fix.line_length = fb->pitch; + info->fix.line_length = fb->pitch * ((fb->bits_per_pixel + 1) / 8); info->flags = FBINFO_DEFAULT; -- cgit v1.2.3 From 78598fdaa8b23a199880a63b79f17cfd7f14cb0f Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 07:07:54 -0700 Subject: Various changes for in-kernel modesetting: - allow drm_buffer_object_create to be called w/o dev_mapping - fixup i915 init code to allocate memory, fb and set modes right - pass fb to drm_initial_config for setup - change some debug output to make it easier to spot - fixup lvds code to use DDC probing correctly --- linux-core/drm_bo.c | 10 +++++++--- linux-core/drm_crtc.c | 40 +++++++++++++++++++--------------------- linux-core/drm_crtc.h | 5 ++++- linux-core/intel_display.c | 2 -- linux-core/intel_lvds.c | 21 +++++++++++++++++---- 5 files changed, 47 insertions(+), 31 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 17d6fbc0..2107a3a8 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -1530,7 +1530,7 @@ static int drm_bo_handle_wait(drm_file_t * priv, uint32_t handle, return ret; } -int drm_buffer_object_create(drm_file_t * priv, +int drm_buffer_object_create(drm_device_t *dev, unsigned long size, drm_bo_type_t type, uint32_t mask, @@ -1539,7 +1539,6 @@ int drm_buffer_object_create(drm_file_t * priv, unsigned long buffer_start, drm_buffer_object_t ** buf_obj) { - drm_device_t *dev = priv->head->dev; drm_buffer_manager_t *bm = &dev->bm; drm_buffer_object_t *bo; int ret = 0; @@ -1615,6 +1614,7 @@ int drm_buffer_object_create(drm_file_t * priv, drm_bo_usage_deref_unlocked(bo); return ret; } +EXPORT_SYMBOL(drm_buffer_object_create); static int drm_bo_add_user_object(drm_file_t * priv, drm_buffer_object_t * bo, int shareable) @@ -1670,7 +1670,8 @@ int drm_bo_ioctl(DRM_IOCTL_ARGS) switch (req->op) { case drm_bo_create: rep.ret = - drm_buffer_object_create(priv, req->size, + drm_buffer_object_create(priv->head->dev, + req->size, req->type, req->mask, req->hint, @@ -2301,6 +2302,9 @@ void drm_bo_unmap_virtual(drm_buffer_object_t * bo) loff_t offset = ((loff_t) bo->map_list.hash.key) << PAGE_SHIFT; loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT; + if (!dev->dev_mapping) + return; + unmap_mapping_range(dev->dev_mapping, offset, holelen, 1); } diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 1c1c3006..b349527d 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -57,6 +57,7 @@ struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev) return fb; } +EXPORT_SYMBOL(drm_framebuffer_create); void drm_framebuffer_destroy(struct drm_framebuffer *fb) { @@ -493,30 +494,28 @@ out_err: return ret; } -bool drm_initial_config(drm_device_t *dev, bool can_grow) +/** + * drm_initial_config - setup a sane initial output configuration + * @dev: DRM device + * @fb: framebuffer backing for new setup + * @can_grow: this configuration is growable + * + * Scan the CRTCs and outputs and try to put together an initial setup. + * At the moment, this is a cloned configuration across all heads with + * @fb as the backing store. + */ +bool drm_initial_config(drm_device_t *dev, struct drm_framebuffer *fb, + bool can_grow) { /* do a hardcoded initial configuration here */ struct drm_crtc *crtc, *vga_crtc = NULL, *dvi_crtc = NULL, *lvds_crtc = NULL; - struct drm_framebuffer *fb; struct drm_output *output, *use_output = NULL; -#if 0 - 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; -#endif - /* bind both CRTCs to this fb */ /* only initialise one crtc to enabled state */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - // crtc->fb = fb; + crtc->fb = fb; if (!vga_crtc) { vga_crtc = crtc; crtc->enabled = 1; @@ -527,8 +526,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) crtc->enabled = 1; crtc->desired_x = 0; crtc->desired_y = 0; - } - else if (!dvi_crtc) { + } else if (!dvi_crtc) { dvi_crtc = crtc; crtc->enabled = 1; crtc->desired_x = 0; @@ -536,7 +534,7 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) } } - drm_crtc_probe_output_modes(dev, 1024, 768); + drm_crtc_probe_output_modes(dev, 2048, 2048); /* hard bind the CRTCS */ @@ -551,20 +549,20 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow) } if (!strncmp(output->name, "VGA", 3)) { output->crtc = vga_crtc; - drm_mode_debug_printmodeline(dev, des_mode); + DRM_DEBUG("VGA preferred mode: %s\n", des_mode->name); 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; - drm_mode_debug_printmodeline(dev, des_mode); + DRM_DEBUG("TMDS preferred mode: %s\n", des_mode->name); output->crtc->desired_mode = des_mode; output->initial_x = 0; output->initial_y = 0; } else if (!strncmp(output->name, "LVDS", 3)) { output->crtc = lvds_crtc; - drm_mode_debug_printmodeline(dev, des_mode); + DRM_DEBUG("LVDS preferred mode: %s\n", des_mode->name); output->crtc->desired_mode = des_mode; output->initial_x = 0; output->initial_y = 0; diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index d5d256d4..54c508c5 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -452,10 +452,13 @@ extern int drm_mode_vrefresh(struct drm_display_mode *mode); extern void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags); extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); -extern bool drm_initial_config(struct drm_device *dev, bool cangrow); +extern bool drm_initial_config(struct drm_device *dev, + struct drm_framebuffer *fb, bool cangrow); extern void drm_framebuffer_set_object(struct drm_device *dev, unsigned long handle); extern bool drm_set_desired_modes(struct drm_device *dev); +extern int drmfb_probe(struct drm_device *dev, struct drm_framebuffer *fb); +extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); /* IOCTLs */ extern int drm_mode_getresources(struct inode *inode, struct file *filp, diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 04a6e086..bb6f7f23 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -1128,11 +1128,9 @@ static void intel_setup_outputs(drm_device_t *dev) intel_crt_init(dev); -#if 0 /* Set up integrated LVDS */ if (IS_MOBILE(dev) && !IS_I830(dev)) intel_lvds_init(dev); -#endif if (IS_I9XX(dev)) { intel_sdvo_init(dev, SDVOB); diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index 90a26109..8ecb204c 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -164,6 +164,7 @@ static bool intel_lvds_mode_fixup(struct drm_output *output, struct intel_crtc *intel_crtc = output->crtc->driver_private; struct drm_output *tmp_output; +#if 0 spin_lock(&dev->mode_config.config_lock); list_for_each_entry(tmp_output, &dev->mode_config.output_list, head) { if (tmp_output != output && tmp_output->crtc == output->crtc) { @@ -173,6 +174,7 @@ static bool intel_lvds_mode_fixup(struct drm_output *output, } } spin_lock(&dev->mode_config.config_lock); +#endif if (intel_crtc->pipe == 0) { printk(KERN_ERR "Can't support LVDS on pipe A\n"); @@ -199,7 +201,7 @@ static bool intel_lvds_mode_fixup(struct drm_output *output, dev_priv->panel_fixed_mode->vsync_end; adjusted_mode->vtotal = dev_priv->panel_fixed_mode->vtotal; adjusted_mode->clock = dev_priv->panel_fixed_mode->clock; -// xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V); + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); } /* @@ -272,11 +274,11 @@ static int intel_lvds_get_modes(struct drm_output *output) "failed.\n"); return 0; } - intel_i2c_destroy(intel_output->ddc_bus); ret = intel_ddc_get_modes(output); if (ret) return ret; + intel_i2c_destroy(intel_output->ddc_bus); /* Didn't get an EDID */ if (!output->monitor_info) { @@ -359,19 +361,31 @@ void intel_lvds_init(struct drm_device *dev) output->interlace_allowed = FALSE; output->doublescan_allowed = FALSE; + /* Set up the DDC bus. */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return; + } + /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ intel_ddc_get_modes(output); + intel_i2c_destroy(intel_output->ddc_bus); list_for_each_entry(scan, &output->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) break; } - if (scan) + if (scan) { dev_priv->panel_fixed_mode = scan; + DRM_DEBUG("LVDS panel_fixed: %s\n", scan->name); + } +#if 0 /* * If we didn't get EDID, try checking if the panel is already turned * on. If so, assume that whatever is currently programmed is the @@ -395,7 +409,6 @@ void intel_lvds_init(struct drm_device *dev) } /* No BIOS poking yet... */ -#if 0 /* Get the LVDS fixed mode out of the BIOS. We should support LVDS * with the BIOS being unavailable or broken, but lack the * configuration options for now. -- cgit v1.2.3 From f35db6690625ccd01fb61dc766e6380a9c14c331 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 07:08:29 -0700 Subject: Fixup DDC probing. We only have one DDC bus so we have to use it only on demand, and unregister when we're done. --- linux-core/intel_crt.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_crt.c b/linux-core/intel_crt.c index e62aa8d3..ca7ae7b3 100644 --- a/linux-core/intel_crt.c +++ b/linux-core/intel_crt.c @@ -180,6 +180,7 @@ static bool intel_crt_detect_ddc(struct drm_output *output) static enum drm_output_status intel_crt_detect(struct drm_output *output) { drm_device_t *dev = output->dev; + struct intel_output *intel_output = output->driver_private; if (IS_I945G(dev)| IS_I945GM(dev) || IS_I965G(dev)) { if (intel_crt_detect_hotplug(output)) @@ -188,21 +189,47 @@ static enum drm_output_status intel_crt_detect(struct drm_output *output) return output_status_disconnected; } - if (intel_crt_detect_ddc(output)) + /* Set up the DDC bus. */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return 0; + } + + if (intel_crt_detect_ddc(output)) { + intel_i2c_destroy(intel_output->ddc_bus); return output_status_connected; + } + intel_i2c_destroy(intel_output->ddc_bus); /* TODO use load detect */ return output_status_unknown; } static void intel_crt_destroy(struct drm_output *output) { + if (output->driver_private) + kfree(output->driver_private); +} + +static int intel_crt_get_modes(struct drm_output *output) +{ + struct drm_device *dev = output->dev; struct intel_output *intel_output = output->driver_private; + int ret; - intel_i2c_destroy(intel_output->ddc_bus); + /* Set up the DDC bus. */ + intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); + if (!intel_output->ddc_bus) { + dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " + "failed.\n"); + return 0; + } - if (output->driver_private) - kfree(output->driver_private); + ret = intel_ddc_get_modes(output); + intel_i2c_destroy(intel_output->ddc_bus); + return ret; } /* @@ -218,7 +245,7 @@ static const struct drm_output_funcs intel_crt_output_funcs = { .mode_set = intel_crt_mode_set, .commit = intel_output_commit, .detect = intel_crt_detect, - .get_modes = intel_ddc_get_modes, + .get_modes = intel_crt_get_modes, .cleanup = intel_crt_destroy, }; @@ -239,12 +266,4 @@ void intel_crt_init(drm_device_t *dev) output->driver_private = intel_output; output->interlace_allowed = 0; output->doublescan_allowed = 0; - - /* Set up the DDC bus. */ - intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); - if (!intel_output->ddc_bus) { - dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " - "failed.\n"); - return; - } } -- cgit v1.2.3 From dd00aa5851ca7c5590ae0b0825dd84c027cfd420 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 07:08:48 -0700 Subject: export vblank routine for use by intel_display.c and intel_sdvo.c. --- linux-core/intel_drv.h | 1 + 1 file changed, 1 insertion(+) (limited to 'linux-core') diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h index 0675e069..f47e6233 100644 --- a/linux-core/intel_drv.h +++ b/linux-core/intel_drv.h @@ -73,5 +73,6 @@ extern void intel_output_prepare (struct drm_output *output); extern void intel_output_commit (struct drm_output *output); extern struct drm_display_mode *intel_crtc_mode_get(drm_device_t *dev, struct drm_crtc *crtc); +extern void intel_wait_for_vblank(drm_device_t *dev); #endif /* __INTEL_DRV_H__ */ -- cgit v1.2.3 From cc7faa4de80a68d5a7a484046b9b42de961cdbef Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 07:21:24 -0700 Subject: fix modeset cleanup for LVDS and reenable it in i915. --- linux-core/intel_lvds.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index 8ecb204c..ec693275 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -314,7 +314,8 @@ out: static void intel_lvds_destroy(struct drm_output *output) { - drm_output_destroy(output); + if (output->driver_private) + kfree(output->driver_private); } static const struct drm_output_funcs intel_lvds_output_funcs = { -- cgit v1.2.3 From c731b68091aa7284ee3a89c8a7ea3fdabac45a54 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 11:42:00 -0700 Subject: Fix EDID pixel clock calculation. --- linux-core/drm_edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index b79bc2db..9acdc8da 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -114,7 +114,7 @@ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, return NULL; mode->type = DRM_MODE_TYPE_DRIVER; - mode->clock = timing->pixel_clock / 100; + mode->clock = timing->pixel_clock * 10; mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | -- cgit v1.2.3 From 425da42e95606fec19cc87fad9329d48f93dfe6b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 11:44:54 -0700 Subject: Whitespace cleanups. --- linux-core/intel_display.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index bb6f7f23..396f4cfa 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -168,18 +168,18 @@ static const intel_limit_t intel_limits[] = { }, }; -static const intel_limit_t *intel_limit (struct drm_crtc *crtc) +static const intel_limit_t *intel_limit(struct drm_crtc *crtc) { drm_device_t *dev = crtc->dev; const intel_limit_t *limit; if (IS_I9XX(dev)) { - if (intel_pipe_has_type (crtc, INTEL_OUTPUT_LVDS)) + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS]; else limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; } else { - if (intel_pipe_has_type (crtc, INTEL_OUTPUT_LVDS)) + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS]; else limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC]; @@ -280,18 +280,20 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, { drm_device_t *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; - intel_clock_t clock; - const intel_limit_t *limit = intel_limit (crtc); + intel_clock_t clock; + const intel_limit_t *limit = intel_limit(crtc); int err = target; - if (IS_I9XX(dev)& intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (I915_READ(LVDS) & LVDS_PORT_EN) != 0) - { - /* For LVDS, if the panel is on, just rely on its current settings for - * dual-channel. We haven't figured out how to reliably set up - * different single/dual channel state, if we even can. - */ - if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + (I915_READ(LVDS) & LVDS_PORT_EN) != 0) { + /* + * For LVDS, if the panel is on, just rely on its current + * settings for dual-channel. We haven't figured out how to + * reliably set up different single/dual channel state, if we + * even can. + */ + if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) == + LVDS_CLKB_POWER_UP) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -304,17 +306,16 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, memset (best_clock, 0, sizeof (*best_clock)); - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) - { - for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; clock.m2++) - { - for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) - { - for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; clock.p1++) - { + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { + for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && + clock.m2 <= limit->m2.max; clock.m2++) { + for (clock.n = limit->n.min; clock.n <= limit->n.max; + clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; clock.p1++) { int this_err; - intel_clock (dev, refclk, &clock); + intel_clock(dev, refclk, &clock); if (!intel_PLL_is_valid(crtc, &clock)) continue; @@ -328,6 +329,7 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, } } } + return (err != target); } -- cgit v1.2.3 From 63d4d40463b04f1277470ccf5cc96dafd81e8687 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 11:46:37 -0700 Subject: Fix i2c unregistration, cleanup panel_fixed_mode assignment. --- linux-core/intel_lvds.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'linux-core') diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c index ec693275..e8670adc 100644 --- a/linux-core/intel_lvds.c +++ b/linux-core/intel_lvds.c @@ -164,7 +164,7 @@ static bool intel_lvds_mode_fixup(struct drm_output *output, struct intel_crtc *intel_crtc = output->crtc->driver_private; struct drm_output *tmp_output; -#if 0 +#if 0 /* FIXME: Check for other outputs on this pipe */ spin_lock(&dev->mode_config.config_lock); list_for_each_entry(tmp_output, &dev->mode_config.output_list, head) { if (tmp_output != output && tmp_output->crtc == output->crtc) { @@ -274,11 +274,11 @@ static int intel_lvds_get_modes(struct drm_output *output) "failed.\n"); return 0; } - ret = intel_ddc_get_modes(output); + intel_i2c_destroy(intel_output->ddc_bus); + if (ret) return ret; - intel_i2c_destroy(intel_output->ddc_bus); /* Didn't get an EDID */ if (!output->monitor_info) { @@ -377,13 +377,11 @@ void intel_lvds_init(struct drm_device *dev) intel_ddc_get_modes(output); intel_i2c_destroy(intel_output->ddc_bus); list_for_each_entry(scan, &output->probed_modes, head) { - if (scan->type & DRM_MODE_TYPE_PREFERRED) + if (scan->type & DRM_MODE_TYPE_PREFERRED) { + dev_priv->panel_fixed_mode = + drm_mode_duplicate(dev, scan); break; - } - - if (scan) { - dev_priv->panel_fixed_mode = scan; - DRM_DEBUG("LVDS panel_fixed: %s\n", scan->name); + } } #if 0 -- cgit v1.2.3 From 2e21779992bd5026d8ec4dea52466377dbe5a0ed Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 12:51:52 -0700 Subject: Add new buffer object type for kernel allocations that don't initially have a user mapping. --- linux-core/drm_bo.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_bo.c b/linux-core/drm_bo.c index 2107a3a8..a379c741 100644 --- a/linux-core/drm_bo.c +++ b/linux-core/drm_bo.c @@ -89,6 +89,9 @@ static int drm_bo_vm_pre_move(drm_buffer_object_t * bo, int old_is_pci) #ifdef DRM_ODD_MM_COMPAT int ret; + if (!bo->map_list.map) + return 0; + ret = drm_bo_lock_kmm(bo); if (ret) return ret; @@ -96,6 +99,9 @@ static int drm_bo_vm_pre_move(drm_buffer_object_t * bo, int old_is_pci) if (old_is_pci) drm_bo_finish_unmap(bo); #else + if (!bo->map_list.map) + return 0; + drm_bo_unmap_virtual(bo); #endif return 0; @@ -106,6 +112,9 @@ static void drm_bo_vm_post_move(drm_buffer_object_t * bo) #ifdef DRM_ODD_MM_COMPAT int ret; + if (!bo->map_list.map) + return; + ret = drm_bo_remap_bound(bo); if (ret) { DRM_ERROR("Failed to remap a bound buffer object.\n" @@ -131,6 +140,11 @@ static int drm_bo_add_ttm(drm_buffer_object_t * bo) if (!bo->ttm) ret = -ENOMEM; break; + case drm_bo_type_kernel: + bo->ttm = drm_ttm_init(dev, bo->mem.num_pages << PAGE_SHIFT); + if (!bo->ttm) + ret = -ENOMEM; + break; case drm_bo_type_user: case drm_bo_type_fake: break; @@ -2302,9 +2316,6 @@ void drm_bo_unmap_virtual(drm_buffer_object_t * bo) loff_t offset = ((loff_t) bo->map_list.hash.key) << PAGE_SHIFT; loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT; - if (!dev->dev_mapping) - return; - unmap_mapping_range(dev->dev_mapping, offset, holelen, 1); } -- cgit v1.2.3 From a81558d8b3ee17fbf46e32b10732e22fcd997858 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 12 Apr 2007 08:45:40 +1000 Subject: add getfb ioctl --- linux-core/drm_crtc.c | 30 ++++++++++++++++++++++++++++++ linux-core/drm_crtc.h | 2 ++ linux-core/drm_drv.c | 1 + 3 files changed, 33 insertions(+) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index b349527d..d1f3c077 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -1058,3 +1058,33 @@ int drm_mode_rmfb(struct inode *inode, struct file *filp, return 0; } +int drm_mode_getfb(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + struct drm_mode_fb_cmd __user *argp = (void __user *)arg; + struct drm_mode_fb_cmd r; + struct drm_framebuffer *fb; + + if (copy_from_user(&r, argp, sizeof(r))) + return -EFAULT; + + fb = idr_find(&dev->mode_config.crtc_idr, r.buffer_id); + if (!fb || (r.buffer_id != fb->id)) { + DRM_ERROR("invalid framebuffer id\n"); + return -EINVAL; + } + + r.height = fb->height; + r.width = fb->width; + r.depth = fb->depth; + r.bpp = fb->bits_per_pixel; + r.handle = fb->bo->base.hash.key; + r.pitch = fb->pitch; + + if (copy_to_user(argp, &r, sizeof(r))) + return -EFAULT; + + return 0; +} diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index 54c508c5..c02dcedc 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -474,5 +474,7 @@ extern int drm_mode_addfb(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); extern int drm_mode_rmfb(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +extern int drm_mode_getfb(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); #endif /* __DRM_CRTC_H__ */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index b43af328..b7a7aded 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -129,6 +129,7 @@ static drm_ioctl_desc_t drm_ioctls[] = { [DRM_IOCTL_NR(DRM_IOCTL_MODE_SETCRTC)] = {drm_mode_setcrtc, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB)] = {drm_mode_addfb, DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_MODE_RMFB)] = {drm_mode_rmfb, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODE_GETFB)] = {drm_mode_getfb, DRM_MASTER|DRM_ROOT_ONLY}, }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) -- cgit v1.2.3 From 981f8156de0c5ec6387f659fbcac031d663d943c Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 12 Apr 2007 08:54:31 +1000 Subject: allow framebuffer changes on the crtc setup --- linux-core/drm_crtc.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index d1f3c077..21d7012e 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -590,7 +590,7 @@ void drm_mode_config_cleanup(drm_device_t *dev) } EXPORT_SYMBOL(drm_mode_config_cleanup); -int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set) +int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, struct drm_display_mode *new_mode, struct drm_output **output_set, struct drm_framebuffer *fb) { drm_device_t *dev = crtc->dev; struct drm_crtc **save_crtcs, *new_crtc; @@ -603,6 +603,9 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, if (!save_crtcs) return -ENOMEM; + if (crtc->fb != fb) + changed = true; + if (crtc_info->x != crtc->x || crtc_info->y != crtc->y) changed = true; @@ -629,6 +632,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, } if (changed) { + crtc->fb = fb; crtc->enabled = (new_mode != NULL); if (new_mode != NULL) { DRM_DEBUG("attempting to set mode from userspace\n"); @@ -897,6 +901,7 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, struct drm_crtc *crtc; struct drm_output **output_set = NULL, *output; struct drm_display_mode *mode; + struct drm_framebuffer *fb = NULL; int retcode = 0; int i; @@ -911,6 +916,15 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, } if (crtc_req.mode) { + + /* if we have a mode we need a framebuffer */ + if (crtc_req.fb_id) { + fb = idr_find(&dev->mode_config.crtc_idr, crtc_req.fb_id); + if (!fb || (fb->id != crtc_req.fb_id)) { + DRM_DEBUG("Unknown FB ID%d\n", crtc_req.fb_id); + return -EINVAL; + } + } mode = idr_find(&dev->mode_config.crtc_idr, crtc_req.mode); if (!mode || (mode->mode_id != crtc_req.mode)) { @@ -935,8 +949,8 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, return -EINVAL; } - if (crtc_req.count_outputs > 0 && !mode) { - DRM_DEBUG("Count outputs is %d but no mode set\n", crtc_req.count_outputs); + if (crtc_req.count_outputs > 0 && !mode && !fb) { + DRM_DEBUG("Count outputs is %d but no mode or fb set\n", crtc_req.count_outputs); return -EINVAL; } @@ -960,7 +974,7 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, } } - retcode = drm_crtc_set_config(crtc, &crtc_req, mode, output_set); + retcode = drm_crtc_set_config(crtc, &crtc_req, mode, output_set, fb); return retcode; } -- cgit v1.2.3 From a5cf4cc369fcc2cf7b84bbaef1e458250ecb91ee Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 12 Apr 2007 11:28:55 +1000 Subject: fix unbalanced lock and make sure mode list has modes so lvds code doesn't crash --- linux-core/drm_crtc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 21d7012e..63ad829d 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -37,6 +37,7 @@ struct drm_framebuffer *drm_framebuffer_create(drm_device_t *dev) spin_lock(&dev->mode_config.config_lock); /* Limit to single framebuffer for now */ if (dev->mode_config.num_fb > 1) { + spin_unlock(&dev->mode_config.config_lock); DRM_ERROR("Attempt to add multiple framebuffers failed\n"); return NULL; } @@ -542,6 +543,9 @@ bool drm_initial_config(drm_device_t *dev, struct drm_framebuffer *fb, list_for_each_entry(output, &dev->mode_config.output_list, head) { struct drm_display_mode *des_mode; + if (list_empty(&output->modes)) + continue; + /* Get the first preferred moded */ list_for_each_entry(des_mode, &output->modes, head) { if (des_mode->flags & DRM_MODE_TYPE_PREFERRED) -- cgit v1.2.3 From b49b3ba4c1aad0d3f34f06013f2ffa67fc8d82c9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 12 Apr 2007 11:43:13 +1000 Subject: set bracing style like Linux --- linux-core/drm_crtc.c | 24 +++++++++--------------- linux-core/intel_display.c | 21 +++++++-------------- 2 files changed, 16 insertions(+), 29 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 63ad829d..259ea1b8 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -254,8 +254,7 @@ bool drm_crtc_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, /* Now, enable the clocks, plane, pipe, and outputs that we set up. */ crtc->funcs->commit(crtc); list_for_each_entry(output, &dev->mode_config.output_list, head) { - if (output->crtc == crtc) - { + if (output->crtc == crtc) { output->funcs->commit(output); #if 0 // TODO def RANDR_12_INTERFACE if (output->randr_output) @@ -624,8 +623,7 @@ int drm_crtc_set_config(struct drm_crtc *crtc, struct drm_mode_crtc *crtc_info, else new_crtc = output->crtc; - for (ro = 0; ro < crtc_info->count_outputs; ro++) - { + for (ro = 0; ro < crtc_info->count_outputs; ro++) { if (output_set[ro] == output) new_crtc = crtc; } @@ -913,8 +911,7 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, return -EFAULT; crtc = idr_find(&dev->mode_config.crtc_idr, crtc_req.crtc_id); - if (!crtc || (crtc->id != crtc_req.crtc_id)) - { + if (!crtc || (crtc->id != crtc_req.crtc_id)) { DRM_DEBUG("Unknown CRTC ID %d\n", crtc_req.crtc_id); return -EINVAL; } @@ -930,15 +927,12 @@ int drm_mode_setcrtc(struct inode *inode, struct file *filp, } } mode = idr_find(&dev->mode_config.crtc_idr, crtc_req.mode); - if (!mode || (mode->mode_id != crtc_req.mode)) - { - { - struct drm_output *output; - - list_for_each_entry(output, &dev->mode_config.output_list, head) { - list_for_each_entry(mode, &output->modes, head) { - drm_mode_debug_printmodeline(dev, mode); - } + if (!mode || (mode->mode_id != crtc_req.mode)) { + struct drm_output *output; + + list_for_each_entry(output, &dev->mode_config.output_list, head) { + list_for_each_entry(mode, &output->modes, head) { + drm_mode_debug_printmodeline(dev, mode); } } diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 396f4cfa..aed86231 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -412,8 +412,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) case DPMSModeSuspend: /* Enable the DPLL */ temp = I915_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) == 0) - { + if ((temp & DPLL_VCO_ENABLE) == 0) { I915_WRITE(dpll_reg, temp); I915_READ(dpll_reg); /* Wait for the clocks to stabilize. */ @@ -435,8 +434,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) /* Enable the plane */ temp = I915_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) - { + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); @@ -456,8 +454,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) /* Disable display plane */ temp = I915_READ(dspcntr_reg); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) - { + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE); /* Flush the plane changes */ I915_WRITE(dspbase_reg, I915_READ(dspbase_reg)); @@ -719,11 +716,9 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= DPLLB_MODE_LVDS; else dpll |= DPLLB_MODE_DAC_SERIAL; - if (is_sdvo) - { + if (is_sdvo) { dpll |= DPLL_DVO_HIGH_SPEED; - if (IS_I945G(dev) || IS_I945GM(dev)) - { + if (IS_I945G(dev) || IS_I945GM(dev)) { int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; } @@ -760,8 +755,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, } } - if (is_tv) - { + if (is_tv) { /* XXX: just matching BIOS for now */ /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; @@ -801,8 +795,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc, dspcntr |= DISPPLANE_SEL_PIPE_B; pipeconf = I915_READ(pipeconf_reg); - if (pipe == 0 && !IS_I965G(dev)) - { + if (pipe == 0 && !IS_I965G(dev)) { /* Enable pixel doubling when the dot clock is > 90% of the (display) * core speed. * -- cgit v1.2.3 From fb6c5aacb9955248300e0c62f68a5a65b40e15e1 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 12 Apr 2007 11:54:49 +1000 Subject: only initialise modes when fbcon or fbset asks for it --- linux-core/drm_fb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'linux-core') diff --git a/linux-core/drm_fb.c b/linux-core/drm_fb.c index 1a0fb79c..a70e4d52 100644 --- a/linux-core/drm_fb.c +++ b/linux-core/drm_fb.c @@ -77,6 +77,15 @@ static int drmfb_setcolreg(unsigned regno, unsigned red, unsigned green, return 0; } +/* this will let fbcon do the mode init */ +static int drmfb_set_par(struct fb_info *info) +{ + struct drmfb_par *par = info->par; + struct drm_device *dev = par->dev; + + drm_set_desired_modes(dev); +} + static struct fb_ops drmfb_ops = { .owner = THIS_MODULE, // .fb_open = drmfb_open, @@ -84,6 +93,7 @@ static struct fb_ops drmfb_ops = { // .fb_write = drmfb_write, // .fb_release = drmfb_release, // .fb_ioctl = drmfb_ioctl, + .fb_set_par = drmfb_set_par, .fb_setcolreg = drmfb_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, -- cgit v1.2.3 From 1bba3cb3b37ca9bc302d83377c1e9d5441653d0d Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 12 Apr 2007 11:55:10 +1000 Subject: cleanup framebuffers on drm unload --- linux-core/drm_crtc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'linux-core') diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index 259ea1b8..2f140dbd 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -582,7 +582,7 @@ void drm_mode_config_cleanup(drm_device_t *dev) { struct drm_output *output, *ot; struct drm_crtc *crtc, *ct; - + struct drm_crtc *fb, *fbt; list_for_each_entry_safe(output, ot, &dev->mode_config.output_list, head) { drm_output_destroy(output); } @@ -590,6 +590,11 @@ void drm_mode_config_cleanup(drm_device_t *dev) list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { drm_crtc_destroy(crtc); } + + list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { + drmfb_remove(dev, fb); + drm_framebuffer_destroy(fb); + } } EXPORT_SYMBOL(drm_mode_config_cleanup); -- cgit v1.2.3