summaryrefslogtreecommitdiff
path: root/linux-core
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core')
-rw-r--r--linux-core/Makefile.kernel2
-rw-r--r--linux-core/drm_crtc.c9
-rw-r--r--linux-core/drm_crtc.h6
-rw-r--r--linux-core/drm_edid.c173
-rw-r--r--linux-core/drm_edid.h176
-rw-r--r--linux-core/drm_fops.c3
l---------linux-core/i915_init.c1
-rw-r--r--linux-core/intel_display.c116
-rw-r--r--linux-core/intel_drv.h3
-rw-r--r--linux-core/intel_i2c.c6
-rw-r--r--linux-core/intel_lvds.c412
11 files changed, 679 insertions, 228 deletions
diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel
index ac403f64..c767116a 100644
--- a/linux-core/Makefile.kernel
+++ b/linux-core/Makefile.kernel
@@ -21,7 +21,7 @@ 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 \
- intel_sdvo.o intel_modes.o intel_i2c.o
+ intel_sdvo.o intel_modes.o intel_i2c.o i915_init.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/drm_crtc.c b/linux-core/drm_crtc.c
index cc082d91..1899df2d 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->mode_config.output_list, head)
if (output->crtc == crtc)
return true;
@@ -498,7 +499,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;
@@ -512,8 +513,8 @@ bool drm_initial_config(drm_device_t *dev, bool can_grow)
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) {
@@ -529,14 +530,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);
@@ -561,10 +560,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)) {
diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h
index 142e0ecb..57bfb10b 100644
--- a/linux-core/drm_crtc.h
+++ b/linux-core/drm_crtc.h
@@ -370,6 +370,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;
@@ -444,6 +445,11 @@ 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);
+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);
/* IOCTLs */
extern int drm_mode_getresources(struct inode *inode, struct file *filp,
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 <dmunsie@cecropia.com>
*/
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#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 <linux/types.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));
+
+#endif /* __DRM_EDID_H__ */
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.
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
diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c
index 9fb497dd..1eed71f0 100644
--- a/linux-core/intel_display.c
+++ b/linux-core/intel_display.c
@@ -26,8 +26,6 @@
#include <linux/i2c.h>
#include "drmP.h"
-#include "drm.h"
-#include "drm_crtc.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
@@ -354,7 +352,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y)
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);
+ DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
if (IS_I965G(dev)) {
I915_WRITE(dspbase, Offset);
I915_READ(dspbase);
@@ -961,10 +959,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 = {
@@ -1005,7 +1108,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;
@@ -1027,9 +1130,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);
@@ -1098,6 +1203,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_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;
diff --git a/linux-core/intel_lvds.c b/linux-core/intel_lvds.c
index a444f145..90a26109 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 <airlied@linux.ie>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -22,16 +23,15 @@
*
* Authors:
* Eric Anholt <eric@anholt.net>
- */
-/*
- * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
- * Jesse Barnes <jesse.barnes@intel.com>
+ * Dave Airlie <airlied@linux.ie>
+ * Jesse Barnes <jesse.barnes@intel.com>
*/
#include <linux/i2c.h>
#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"
@@ -39,23 +39,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,29 +62,272 @@ 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->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) {
+ printk(KERN_ERR "Can't enable LVDS and another "
+ "output on the same pipe\n");
+ return false;
+ }
+ }
+ spin_lock(&dev->mode_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 edid *edid_info;
+ 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;
+
+ /* Didn't get an EDID */
+ if (!output->monitor_info) {
+ 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;
+ }
+
+out:
+ 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
+};
/**
* intel_lvds_init - setup LVDS outputs on this device
@@ -94,11 +336,12 @@ static const struct drm_output_funcs intel_lvds_output_funcs;
* 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)
+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;
- int modes;
+ struct drm_display_mode *scan; /* *modes, *bios_mode; */
output = drm_output_create(dev, &intel_lvds_output_funcs, "LVDS");
if (!output)
@@ -113,18 +356,105 @@ void intel_lvds_init(drm_device_t *dev)
intel_output->type = INTEL_OUTPUT_LVDS;
output->driver_private = intel_output;
output->subpixel_order = SubPixelHorizontalRGB;
- output->interlace_allowed = 0;
- output->doublescan_allowed = 0;
+ output->interlace_allowed = FALSE;
+ output->doublescan_allowed = FALSE;
- 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);
+ list_for_each_entry(scan, &output->probed_modes, head) {
+ if (scan->type & DRM_MODE_TYPE_PREFERRED)
+ break;
}
- modes = intel_ddc_get_modes(output);
- intel_i2c_destroy(intel_output->ddc_bus);
- drm_output_destroy(output);
-}
+ if (scan)
+ dev_priv->panel_fixed_mode = scan;
+
+ /*
+ * 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) {
+ u32 lvds = I915_READ(LVDS);
+ int pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct drm_crtc *crtc;
+ /* FIXME: need drm_crtc_from_pipe */
+ //crtc = drm_crtc_from_pipe(mode_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;
+ }
+ }
+
+/* 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))
+ {
+ 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;
+}