diff options
Diffstat (limited to 'linux-core')
| -rw-r--r-- | linux-core/Makefile.kernel | 2 | ||||
| -rw-r--r-- | linux-core/drm_crtc.c | 9 | ||||
| -rw-r--r-- | linux-core/drm_crtc.h | 6 | ||||
| -rw-r--r-- | linux-core/drm_edid.c | 173 | ||||
| -rw-r--r-- | linux-core/drm_edid.h | 176 | ||||
| -rw-r--r-- | linux-core/drm_fops.c | 3 | ||||
| l--------- | linux-core/i915_init.c | 1 | ||||
| -rw-r--r-- | linux-core/intel_display.c | 116 | ||||
| -rw-r--r-- | linux-core/intel_drv.h | 3 | ||||
| -rw-r--r-- | linux-core/intel_i2c.c | 6 | ||||
| -rw-r--r-- | linux-core/intel_lvds.c | 412 | 
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; +}  | 
