diff options
Diffstat (limited to 'linux-core')
| -rw-r--r-- | linux-core/Makefile.kernel | 3 | ||||
| -rw-r--r-- | linux-core/dvo.h | 159 | ||||
| -rw-r--r-- | linux-core/dvo_ch7017.c | 454 | ||||
| -rw-r--r-- | linux-core/dvo_ch7xxx.c | 368 | ||||
| -rw-r--r-- | linux-core/dvo_ivch.c | 459 | ||||
| -rw-r--r-- | linux-core/dvo_sil164.c | 302 | ||||
| -rw-r--r-- | linux-core/dvo_tfp410.c | 335 | ||||
| -rw-r--r-- | linux-core/intel_display.c | 3 | ||||
| -rw-r--r-- | linux-core/intel_drv.h | 1 | ||||
| -rw-r--r-- | linux-core/intel_dvo.c | 493 | 
10 files changed, 2575 insertions, 2 deletions
| diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 91decfc8..28f6ec06 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -22,7 +22,8 @@ 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 i915_init.o intel_fb.o \ -		intel_tv.o i915_compat.o +		intel_tv.o i915_compat.o intel_dvo.o dvo_ch7xxx.o \ +		dvo_ch7017.o dvo_ivch.o dvo_tfp410.o dvo_sil164.o  nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \  		nouveau_object.o nouveau_irq.o nouveau_notifier.o nouveau_swmthd.o \  		nouveau_sgdma.o nouveau_dma.o nouveau_buffer.o nouveau_fence.o \ diff --git a/linux-core/dvo.h b/linux-core/dvo.h new file mode 100644 index 00000000..c6c1dbdb --- /dev/null +++ b/linux-core/dvo.h @@ -0,0 +1,159 @@ +/* + * Copyright © 2006 Eric Anholt + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission.  The copyright holders make no representations + * about the suitability of this software for any purpose.  It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _INTEL_DVO_H  +#define _INTEL_DVO_H  + +#include <linux/i2c.h> +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "intel_drv.h" + +struct intel_dvo_device { +	char *name; +	int type; +	/* DVOA/B/C output register */ +	u32 dvo_reg; +	/* GPIO register used for i2c bus to control this device */ +	u32 gpio; +	int slave_addr; +	struct intel_i2c_chan *i2c_bus; + +	const struct intel_dvo_dev_ops *dev_ops; +	void *dev_priv; + +	struct drm_display_mode *panel_fixed_mode; +	bool panel_wants_dither; +}; + +struct intel_dvo_dev_ops { +	/* +	 * Initialize the device at startup time. +	 * Returns NULL if the device does not exist. +	 */ +	bool (*init)(struct intel_dvo_device *dvo, +		     struct intel_i2c_chan *i2cbus); + +	/* +	 * Called to allow the output a chance to create properties after the +	 * RandR objects have been created. +	 */ +	void (*create_resources)(struct intel_dvo_device *dvo); + +	/* +	 * Turn on/off output or set intermediate power levels if available. +	 * +	 * Unsupported intermediate modes drop to the lower power setting. +	 * If the  mode is DPMSModeOff, the output must be disabled, +	 * as the DPLL may be disabled afterwards. +	 */ +	void (*dpms)(struct intel_dvo_device *dvo, int mode); + +	/* +	 * Saves the output's state for restoration on VT switch. +	 */ +	void (*save)(struct intel_dvo_device *dvo); + +	/* +	 * Restore's the output's state at VT switch. +	 */ +	void (*restore)(struct intel_dvo_device *dvo); + +	/* +	 * Callback for testing a video mode for a given output. +	 * +	 * This function should only check for cases where a mode can't +	 * be supported on the output specifically, and not represent +	 * generic CRTC limitations. +	 * +	 * \return MODE_OK if the mode is valid, or another MODE_* otherwise. +	 */ +	int (*mode_valid)(struct intel_dvo_device *dvo, +			  struct drm_display_mode *mode); + +	/* +	 * Callback to adjust the mode to be set in the CRTC. +	 * +	 * This allows an output to adjust the clock or even the entire set of +	 * timings, which is used for panels with fixed timings or for +	 * buses with clock limitations. +	 */ +	bool (*mode_fixup)(struct intel_dvo_device *dvo, +			   struct drm_display_mode *mode, +			   struct drm_display_mode *adjusted_mode); + +	/* +	 * Callback for preparing mode changes on an output +	 */ +	void (*prepare)(struct intel_dvo_device *dvo); + +	/* +	 * Callback for committing mode changes on an output +	 */ +	void (*commit)(struct intel_dvo_device *dvo); + +	/* +	 * Callback for setting up a video mode after fixups have been made. +	 * +	 * This is only called while the output is disabled.  The dpms callback +	 * must be all that's necessary for the output, to turn the output on +	 * after this function is called. +	 */ +	void (*mode_set)(struct intel_dvo_device *dvo, +			 struct drm_display_mode *mode, +			 struct drm_display_mode *adjusted_mode); + +	/* +	 * Probe for a connected output, and return detect_status. +	 */ +	enum drm_output_status (*detect)(struct intel_dvo_device *dvo); + +	/** +	 * Query the device for the modes it provides. +	 * +	 * This function may also update MonInfo, mm_width, and mm_height. +	 * +	 * \return singly-linked list of modes or NULL if no modes found. +	 */ +	struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo); + +#ifdef RANDR_12_INTERFACE +	/** +	 * Callback when an output's property has changed. +	 */ +	bool (*set_property)(struct intel_dvo_device *dvo, +			     struct drm_property *property, uint64_t val); +#endif + +	/** +	 * Clean up driver-specific bits of the output +	 */ +	void (*destroy) (struct intel_dvo_device *dvo); + +	/** +	 * Debugging hook to dump device registers to log file +	 */ +	void (*dump_regs)(struct intel_dvo_device *dvo); +}; + +#endif /* _INTEL_DVO_H */ diff --git a/linux-core/dvo_ch7017.c b/linux-core/dvo_ch7017.c new file mode 100644 index 00000000..8349da1b --- /dev/null +++ b/linux-core/dvo_ch7017.c @@ -0,0 +1,454 @@ +/* + * Copyright © 2006 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 <eric@anholt.net> + * + */ + +#include "dvo.h" + +#define CH7017_TV_DISPLAY_MODE		0x00 +#define CH7017_FLICKER_FILTER		0x01 +#define CH7017_VIDEO_BANDWIDTH		0x02 +#define CH7017_TEXT_ENHANCEMENT		0x03 +#define CH7017_START_ACTIVE_VIDEO	0x04 +#define CH7017_HORIZONTAL_POSITION	0x05 +#define CH7017_VERTICAL_POSITION	0x06 +#define CH7017_BLACK_LEVEL		0x07 +#define CH7017_CONTRAST_ENHANCEMENT	0x08 +#define CH7017_TV_PLL			0x09 +#define CH7017_TV_PLL_M			0x0a +#define CH7017_TV_PLL_N			0x0b +#define CH7017_SUB_CARRIER_0		0x0c +#define CH7017_CIV_CONTROL		0x10 +#define CH7017_CIV_0			0x11 +#define CH7017_CHROMA_BOOST		0x14 +#define CH7017_CLOCK_MODE		0x1c +#define CH7017_INPUT_CLOCK		0x1d +#define CH7017_GPIO_CONTROL		0x1e +#define CH7017_INPUT_DATA_FORMAT	0x1f +#define CH7017_CONNECTION_DETECT	0x20 +#define CH7017_DAC_CONTROL		0x21 +#define CH7017_BUFFERED_CLOCK_OUTPUT	0x22 +#define CH7017_DEFEAT_VSYNC		0x47 +#define CH7017_TEST_PATTERN		0x48 + +#define CH7017_POWER_MANAGEMENT		0x49 +/** Enables the TV output path. */ +#define CH7017_TV_EN			(1 << 0) +#define CH7017_DAC0_POWER_DOWN		(1 << 1) +#define CH7017_DAC1_POWER_DOWN		(1 << 2) +#define CH7017_DAC2_POWER_DOWN		(1 << 3) +#define CH7017_DAC3_POWER_DOWN		(1 << 4) +/** Powers down the TV out block, and DAC0-3 */ +#define CH7017_TV_POWER_DOWN_EN		(1 << 5) + +#define CH7017_VERSION_ID		0x4a + +#define CH7017_DEVICE_ID		0x4b +#define CH7017_DEVICE_ID_VALUE		0x1b +#define CH7018_DEVICE_ID_VALUE		0x1a +#define CH7019_DEVICE_ID_VALUE		0x19 + +#define CH7017_XCLK_D2_ADJUST		0x53 +#define CH7017_UP_SCALER_COEFF_0	0x55 +#define CH7017_UP_SCALER_COEFF_1	0x56 +#define CH7017_UP_SCALER_COEFF_2	0x57 +#define CH7017_UP_SCALER_COEFF_3	0x58 +#define CH7017_UP_SCALER_COEFF_4	0x59 +#define CH7017_UP_SCALER_VERTICAL_INC_0	0x5a +#define CH7017_UP_SCALER_VERTICAL_INC_1	0x5b +#define CH7017_GPIO_INVERT		0x5c +#define CH7017_UP_SCALER_HORIZONTAL_INC_0	0x5d +#define CH7017_UP_SCALER_HORIZONTAL_INC_1	0x5e + +#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT	0x5f +/**< Low bits of horizontal active pixel input */ + +#define CH7017_ACTIVE_INPUT_LINE_OUTPUT	0x60 +/** High bits of horizontal active pixel input */ +#define CH7017_LVDS_HAP_INPUT_MASK	(0x7 << 0) +/** High bits of vertical active line output */ +#define CH7017_LVDS_VAL_HIGH_MASK	(0x7 << 3) + +#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT	0x61 +/**< Low bits of vertical active line output */ + +#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT	0x62 +/**< Low bits of horizontal active pixel output */ + +#define CH7017_LVDS_POWER_DOWN		0x63 +/** High bits of horizontal active pixel output */ +#define CH7017_LVDS_HAP_HIGH_MASK	(0x7 << 0) +/** Enables the LVDS power down state transition */ +#define CH7017_LVDS_POWER_DOWN_EN	(1 << 6) +/** Enables the LVDS upscaler */ +#define CH7017_LVDS_UPSCALER_EN		(1 << 7) +#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08 + +#define CH7017_LVDS_ENCODING		0x64 +#define CH7017_LVDS_DITHER_2D		(1 << 2) +#define CH7017_LVDS_DITHER_DIS		(1 << 3) +#define CH7017_LVDS_DUAL_CHANNEL_EN	(1 << 4) +#define CH7017_LVDS_24_BIT		(1 << 5) + +#define CH7017_LVDS_ENCODING_2		0x65 + +#define CH7017_LVDS_PLL_CONTROL		0x66 +/** Enables the LVDS panel output path */ +#define CH7017_LVDS_PANEN		(1 << 0) +/** Enables the LVDS panel backlight */ +#define CH7017_LVDS_BKLEN		(1 << 3) + +#define CH7017_POWER_SEQUENCING_T1	0x67 +#define CH7017_POWER_SEQUENCING_T2	0x68 +#define CH7017_POWER_SEQUENCING_T3	0x69 +#define CH7017_POWER_SEQUENCING_T4	0x6a +#define CH7017_POWER_SEQUENCING_T5	0x6b +#define CH7017_GPIO_DRIVER_TYPE		0x6c +#define CH7017_GPIO_DATA		0x6d +#define CH7017_GPIO_DIRECTION_CONTROL	0x6e + +#define CH7017_LVDS_PLL_FEEDBACK_DIV	0x71 +# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4 +# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0 +# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80 + +#define CH7017_LVDS_PLL_VCO_CONTROL	0x72 +# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80 +# define CH7017_LVDS_PLL_VCO_SHIFT	4 +# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0 + +#define CH7017_OUTPUTS_ENABLE		0x73 +# define CH7017_CHARGE_PUMP_LOW		0x0 +# define CH7017_CHARGE_PUMP_HIGH	0x3 +# define CH7017_LVDS_CHANNEL_A		(1 << 3) +# define CH7017_LVDS_CHANNEL_B		(1 << 4) +# define CH7017_TV_DAC_A		(1 << 5) +# define CH7017_TV_DAC_B		(1 << 6) +# define CH7017_DDC_SELECT_DC2		(1 << 7) + +#define CH7017_LVDS_OUTPUT_AMPLITUDE	0x74 +#define CH7017_LVDS_PLL_EMI_REDUCTION	0x75 +#define CH7017_LVDS_POWER_DOWN_FLICKER	0x76 + +#define CH7017_LVDS_CONTROL_2		0x78 +# define CH7017_LOOP_FILTER_SHIFT	5 +# define CH7017_PHASE_DETECTOR_SHIFT	0 + +#define CH7017_BANG_LIMIT_CONTROL	0x7f + +struct ch7017_priv { +	uint8_t save_hapi; +	uint8_t save_vali; +	uint8_t save_valo; +	uint8_t save_ailo; +	uint8_t save_lvds_pll_vco; +	uint8_t save_feedback_div; +	uint8_t save_lvds_control_2; +	uint8_t save_outputs_enable; +	uint8_t save_lvds_power_down; +	uint8_t save_power_management; +}; + +static void ch7017_dump_regs(struct intel_dvo_device *dvo); +static void ch7017_dpms(struct intel_dvo_device *dvo, int mode); + +static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val) +{ +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*val= in_buf[0]; +		return true; +	}; + +	return false; +} + +static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val) +{ +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = val; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	return false; +} + +/** Probes for a CH7017 on the given bus and slave address. */ +static bool ch7017_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	struct ch7017_priv *priv; +	uint8_t val; + +	priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL); +	if (priv == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = priv; + +	if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val)) +		goto fail; + +	if (val != CH7017_DEVICE_ID_VALUE && +	    val != CH7018_DEVICE_ID_VALUE && +	    val != CH7019_DEVICE_ID_VALUE) { +		DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n", +			  val, i2cbus->adapter.name,i2cbus->slave_addr);  +		goto fail; +	} + +	return true; +fail: +	kfree(priv); +	return false; +} + +static enum drm_output_status ch7017_detect(struct intel_dvo_device *dvo) +{ +	return output_status_unknown; +} + +static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	if (mode->clock > 160000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +static void ch7017_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +	uint8_t lvds_pll_feedback_div, lvds_pll_vco_control; +	uint8_t outputs_enable, lvds_control_2, lvds_power_down; +	uint8_t horizontal_active_pixel_input; +	uint8_t horizontal_active_pixel_output, vertical_active_line_output; +	uint8_t active_input_line_output; + +	DRM_DEBUG("Registers before mode setting\n"); +	ch7017_dump_regs(dvo); + +	/* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/ +	if (mode->clock < 100000) { +		outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW; +		lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | +			(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | +			(13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); +		lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | +			(2 << CH7017_LVDS_PLL_VCO_SHIFT) | +			(3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); +		lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) | +			(0 << CH7017_PHASE_DETECTOR_SHIFT); +	} else { +		outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH; +		lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED | +			(2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) | +			(3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT); +		lvds_pll_feedback_div = 35; +		lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) | +			(0 << CH7017_PHASE_DETECTOR_SHIFT); +		if (1) { /* XXX: dual channel panel detection.  Assume yes for now. */ +			outputs_enable |= CH7017_LVDS_CHANNEL_B; +			lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | +				(2 << CH7017_LVDS_PLL_VCO_SHIFT) | +				(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); +		} else { +			lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED | +				(1 << CH7017_LVDS_PLL_VCO_SHIFT) | +				(13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT); +		} +	} + +	horizontal_active_pixel_input = mode->hdisplay & 0x00ff; + +	vertical_active_line_output = mode->vdisplay & 0x00ff; +	horizontal_active_pixel_output = mode->hdisplay & 0x00ff; + +	active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) | +				   (((mode->vdisplay & 0x0700) >> 8) << 3); + +	lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | +			  (mode->hdisplay & 0x0700) >> 8; + +	ch7017_dpms(dvo, DPMSModeOff); +	ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, +			horizontal_active_pixel_input); +	ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, +			horizontal_active_pixel_output); +	ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, +			vertical_active_line_output); +	ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, +			active_input_line_output); +	ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control); +	ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div); +	ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2); +	ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable); + +	/* Turn the LVDS back on with new settings. */ +	ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down); + +	DRM_DEBUG("Registers after mode setting\n"); +	ch7017_dump_regs(dvo); +} + +/* set the CH7017 power state */ +static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) +{ +	uint8_t val; + +	ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val); + +	/* Turn off TV/VGA, and never turn it on since we don't support it. */ +	ch7017_write(dvo, CH7017_POWER_MANAGEMENT, +			CH7017_DAC0_POWER_DOWN | +			CH7017_DAC1_POWER_DOWN | +			CH7017_DAC2_POWER_DOWN | +			CH7017_DAC3_POWER_DOWN | +			CH7017_TV_POWER_DOWN_EN); + +	if (mode == DPMSModeOn) { +		/* Turn on the LVDS */ +		ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, +			     val & ~CH7017_LVDS_POWER_DOWN_EN); +	} else { +		/* Turn off the LVDS */ +		ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, +			     val | CH7017_LVDS_POWER_DOWN_EN); +	} + +	/* XXX: Should actually wait for update power status somehow */ +	udelay(20000); +} + +static void ch7017_dump_regs(struct intel_dvo_device *dvo) +{ +	uint8_t val; + +#define DUMP(reg)					\ +do {							\ +	ch7017_read(dvo, reg, &val);			\ +	DRM_DEBUG(#reg ": %02x\n", val);		\ +} while (0) + +	DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT); +	DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT); +	DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT); +	DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT); +	DUMP(CH7017_LVDS_PLL_VCO_CONTROL); +	DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV); +	DUMP(CH7017_LVDS_CONTROL_2); +	DUMP(CH7017_OUTPUTS_ENABLE); +	DUMP(CH7017_LVDS_POWER_DOWN); +} + +static void ch7017_save(struct intel_dvo_device *dvo) +{ +	struct ch7017_priv *priv = dvo->dev_priv; + +	ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi); +	ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo); +	ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo); +	ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco); +	ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div); +	ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2); +	ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable); +	ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down); +	ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management); +} + +static void ch7017_restore(struct intel_dvo_device *dvo) +{ +	struct ch7017_priv *priv = dvo->dev_priv; + +	/* Power down before changing mode */ +	ch7017_dpms(dvo, DPMSModeOff); + +	ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi); +	ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo); +	ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo); +	ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco); +	ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div); +	ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2); +	ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable); +	ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down); +	ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management); +} + +static void ch7017_destroy(struct intel_dvo_device *dvo) +{ +	struct ch7017_priv *priv = dvo->dev_priv; + +	if (priv) { +		kfree(priv); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops ch7017_ops = { +	.init = ch7017_init, +	.detect = ch7017_detect, +	.mode_valid = ch7017_mode_valid, +	.mode_set = ch7017_mode_set, +	.dpms = ch7017_dpms, +	.dump_regs = ch7017_dump_regs, +	.save = ch7017_save, +	.restore = ch7017_restore, +	.destroy = ch7017_destroy, +}; diff --git a/linux-core/dvo_ch7xxx.c b/linux-core/dvo_ch7xxx.c new file mode 100644 index 00000000..69827a7d --- /dev/null +++ b/linux-core/dvo_ch7xxx.c @@ -0,0 +1,368 @@ +/************************************************************************** + +Copyright © 2006 Dave Airlie + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, 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 NON-INFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +#include "dvo.h" + +#define CH7xxx_REG_VID		0x4a +#define CH7xxx_REG_DID		0x4b + +#define CH7011_VID		0x83 /* 7010 as well */ +#define CH7009A_VID		0x84 +#define CH7009B_VID		0x85 +#define CH7301_VID		0x95 + +#define CH7xxx_VID		0x84 +#define CH7xxx_DID		0x17 + +#define CH7xxx_NUM_REGS		0x4c + +#define CH7xxx_CM		0x1c +#define CH7xxx_CM_XCM		(1<<0) +#define CH7xxx_CM_MCP		(1<<2) +#define CH7xxx_INPUT_CLOCK	0x1d +#define CH7xxx_GPIO		0x1e +#define CH7xxx_GPIO_HPIR	(1<<3) +#define CH7xxx_IDF		0x1f + +#define CH7xxx_IDF_HSP		(1<<3) +#define CH7xxx_IDF_VSP		(1<<4) + +#define CH7xxx_CONNECTION_DETECT 0x20 +#define CH7xxx_CDET_DVI		(1<<5) + +#define CH7301_DAC_CNTL		0x21 +#define CH7301_HOTPLUG		0x23 +#define CH7xxx_TCTL		0x31 +#define CH7xxx_TVCO		0x32 +#define CH7xxx_TPCP		0x33 +#define CH7xxx_TPD		0x34 +#define CH7xxx_TPVT		0x35 +#define CH7xxx_TLPF		0x36 +#define CH7xxx_TCT		0x37 +#define CH7301_TEST_PATTERN	0x48 + +#define CH7xxx_PM		0x49 +#define CH7xxx_PM_FPD		(1<<0) +#define CH7301_PM_DACPD0	(1<<1) +#define CH7301_PM_DACPD1	(1<<2) +#define CH7301_PM_DACPD2	(1<<3) +#define CH7xxx_PM_DVIL		(1<<6) +#define CH7xxx_PM_DVIP		(1<<7) + +#define CH7301_SYNC_POLARITY	0x56 +#define CH7301_SYNC_RGB_YUV	(1<<0) +#define CH7301_SYNC_POL_DVI	(1<<5) + +/** @file + * driver for the Chrontel 7xxx DVI chip over DVO. + */ + +static struct ch7xxx_id_struct { +	uint8_t vid; +	char *name; +} ch7xxx_ids[] = {  +	{ CH7011_VID, "CH7011" }, +	{ CH7009A_VID, "CH7009A" }, +	{ CH7009B_VID, "CH7009B" }, +	{ CH7301_VID, "CH7301" }, +}; + +struct ch7xxx_reg_state { +    uint8_t regs[CH7xxx_NUM_REGS]; +}; + +struct ch7xxx_priv { +	bool quiet; + +	struct ch7xxx_reg_state save_reg; +	struct ch7xxx_reg_state mode_reg; +	uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT; +	uint8_t save_TLPF, save_TCT, save_PM, save_IDF; +}; + +static void ch7xxx_save(struct intel_dvo_device *dvo); + +static char *ch7xxx_get_id(uint8_t vid) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) { +		if (ch7xxx_ids[i].vid == vid) +			return ch7xxx_ids[i].name; +	} + +	return NULL; +} + +/** Reads an 8 bit register */ +static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ +	struct ch7xxx_priv *ch7xxx= dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*ch = in_buf[0]; +		return true; +	}; + +	if (!ch7xxx->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} + +/** Writes an 8 bit register */ +static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!ch7xxx->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +static bool ch7xxx_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	/* this will detect the CH7xxx chip on the specified i2c bus */ +	struct ch7xxx_priv *ch7xxx; +	uint8_t vendor, device; +	char *name; + +	ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); +	if (ch7xxx == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = ch7xxx; +	ch7xxx->quiet = true; + +	if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor)) +		goto out; + +	name = ch7xxx_get_id(vendor); +	if (!name) { +		DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n", +			  vendor, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + + +	if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) +		goto out; + +	if (device != CH7xxx_DID) { +		DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n", +			  vendor, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + +	ch7xxx->quiet = FALSE; +	DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n", +		  name, vendor, device); +	return true; +out: +	kfree(ch7xxx); +	return false; +} + +static enum drm_output_status ch7xxx_detect(struct intel_dvo_device *dvo) +{ +	uint8_t cdet, orig_pm, pm; + +	ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm); + +	pm = orig_pm; +	pm &= ~CH7xxx_PM_FPD; +	pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP; + +	ch7xxx_writeb(dvo, CH7xxx_PM, pm); + +	ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet); + +	ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm); + +	if (cdet & CH7xxx_CDET_DVI)  +		return output_status_connected; +	return output_status_disconnected; +} + +static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	if (mode->clock > 165000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +static void ch7xxx_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +	uint8_t tvco, tpcp, tpd, tlpf, idf; + +	if (mode->clock <= 65000) { +		tvco = 0x23; +		tpcp = 0x08; +		tpd = 0x16; +		tlpf = 0x60; +	} else { +		tvco = 0x2d; +		tpcp = 0x06; +		tpd = 0x26; +		tlpf = 0xa0; +	} + +	ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00); +	ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco); +	ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp); +	ch7xxx_writeb(dvo, CH7xxx_TPD, tpd); +	ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30); +	ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf); +	ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00); + +	ch7xxx_readb(dvo, CH7xxx_IDF, &idf); + +	idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP); +	if (mode->flags & V_PHSYNC) +		idf |= CH7xxx_IDF_HSP; + +	if (mode->flags & V_PVSYNC) +		idf |= CH7xxx_IDF_HSP; + +	ch7xxx_writeb(dvo, CH7xxx_IDF, idf); +} + +/* set the CH7xxx power state */ +static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode) +{ +	if (mode == DPMSModeOn) +		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); +	else +		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); +} + +static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; +	int i; + +	for (i = 0; i < CH7xxx_NUM_REGS; i++) { +		if ((i % 8) == 0 ) +			DRM_DEBUG("\n %02X: ", i); +		DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]); +	} +} + +static void ch7xxx_save(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx= dvo->dev_priv; + +	ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL); +	ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP); +	ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD); +	ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT); +	ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF); +	ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM); +	ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF); +} + +static void ch7xxx_restore(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + +	ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL); +	ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP); +	ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD); +	ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT); +	ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF); +	ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF); +	ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM); +} + +static void ch7xxx_destroy(struct intel_dvo_device *dvo) +{ +	struct ch7xxx_priv *ch7xxx = dvo->dev_priv; + +	if (ch7xxx) { +		kfree(ch7xxx); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops ch7xxx_ops = { +	.init = ch7xxx_init, +	.detect = ch7xxx_detect, +	.mode_valid = ch7xxx_mode_valid, +	.mode_set = ch7xxx_mode_set, +	.dpms = ch7xxx_dpms, +	.dump_regs = ch7xxx_dump_regs, +	.save = ch7xxx_save, +	.restore = ch7xxx_restore, +	.destroy = ch7xxx_destroy, +}; diff --git a/linux-core/dvo_ivch.c b/linux-core/dvo_ivch.c new file mode 100644 index 00000000..5fce2462 --- /dev/null +++ b/linux-core/dvo_ivch.c @@ -0,0 +1,459 @@ +/* + * Copyright © 2006 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 <eric@anholt.net> + * + */ + +#include "dvo.h" + +/*  + * register definitions for the i82807aa. + * + * Documentation on this chipset can be found in datasheet #29069001 at + * intel.com. + */ + +/*  + * VCH Revision & GMBus Base Addr + */ +#define VR00		0x00 +# define VR00_BASE_ADDRESS_MASK		0x007f + +/*  + * Functionality Enable + */ +#define VR01		0x01 + +/* + * Enable the panel fitter + */ +# define VR01_PANEL_FIT_ENABLE		(1 << 3) +/* + * Enables the LCD display. + * + * This must not be set while VR01_DVO_BYPASS_ENABLE is set. + */ +# define VR01_LCD_ENABLE		(1 << 2) +/** Enables the DVO repeater. */ +# define VR01_DVO_BYPASS_ENABLE		(1 << 1) +/** Enables the DVO clock */ +# define VR01_DVO_ENABLE		(1 << 0) + +/*  + * LCD Interface Format + */ +#define VR10		0x10 +/** Enables LVDS output instead of CMOS */ +# define VR10_LVDS_ENABLE		(1 << 4) +/** Enables 18-bit LVDS output. */ +# define VR10_INTERFACE_1X18		(0 << 2) +/** Enables 24-bit LVDS or CMOS output */ +# define VR10_INTERFACE_1X24		(1 << 2) +/** Enables 2x18-bit LVDS or CMOS output. */ +# define VR10_INTERFACE_2X18		(2 << 2) +/** Enables 2x24-bit LVDS output */ +# define VR10_INTERFACE_2X24		(3 << 2) + +/* + * VR20 LCD Horizontal Display Size + */ +#define VR20	0x20 + +/*  + * LCD Vertical Display Size + */ +#define VR21	0x20 + +/*  + * Panel power down status + */ +#define VR30		0x30 +/** Read only bit indicating that the panel is not in a safe poweroff state. */ +# define VR30_PANEL_ON			(1 << 15) + +#define VR40		0x40 +# define VR40_STALL_ENABLE		(1 << 13) +# define VR40_VERTICAL_INTERP_ENABLE	(1 << 12) +# define VR40_ENHANCED_PANEL_FITTING	(1 << 11) +# define VR40_HORIZONTAL_INTERP_ENABLE	(1 << 10) +# define VR40_AUTO_RATIO_ENABLE		(1 << 9) +# define VR40_CLOCK_GATING_ENABLE	(1 << 8) + +/* + * Panel Fitting Vertical Ratio + * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2 + */ +#define VR41		0x41 + +/*  + * Panel Fitting Horizontal Ratio + * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2 + */ +#define VR42		0x42 + +/* + * Horizontal Image Size + */ +#define VR43		0x43 + +/* VR80 GPIO 0 + */ +#define VR80	    0x80 +#define VR81	    0x81 +#define VR82	    0x82 +#define VR83	    0x83 +#define VR84	    0x84 +#define VR85	    0x85 +#define VR86	    0x86 +#define VR87	    0x87 +     +/* VR88 GPIO 8 + */ +#define VR88	    0x88 + +/* Graphics BIOS scratch 0 + */ +#define VR8E	    0x8E +# define VR8E_PANEL_TYPE_MASK		(0xf << 0) +# define VR8E_PANEL_INTERFACE_CMOS	(0 << 4) +# define VR8E_PANEL_INTERFACE_LVDS	(1 << 4) +# define VR8E_FORCE_DEFAULT_PANEL	(1 << 5) + +/* Graphics BIOS scratch 1 + */ +#define VR8F	    0x8F +# define VR8F_VCH_PRESENT		(1 << 0) +# define VR8F_DISPLAY_CONN		(1 << 1) +# define VR8F_POWER_MASK		(0x3c) +# define VR8F_POWER_POS			(2) + + +struct ivch_priv { +	bool quiet; + +	uint16_t width, height; + +	uint16_t save_VR01; +	uint16_t save_VR40; +}; + +#if 0 +struct vch_capabilities { +    struct aimdb_block	aimdb_block; +    uint8_t		panel_type; +    uint8_t		set_panel_type; +    uint8_t		slave_address; +    uint8_t		capabilities; +#define VCH_PANEL_FITTING_SUPPORT	(0x3 << 0) +#define VCH_PANEL_FITTING_TEXT		(1 << 2) +#define VCH_PANEL_FITTING_GRAPHICS	(1 << 3) +#define VCH_PANEL_FITTING_RATIO		(1 << 4) +#define VCH_DITHERING			(1 << 5) +    uint8_t		backlight_gpio; +    uint8_t		set_panel_type_us_gpios; +} __attribute__ ((packed)); +#endif + +static void ivch_dump_regs(struct intel_dvo_device *dvo); + +/** + * Reads a register on the ivch. + * + * Each of the 256 registers are 16 bits long. + */ +static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data) +{ +	struct ivch_priv *priv = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 2, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*data = (in_buf[1] << 8) | in_buf[0]; +		return true; +	}; + +	if (!priv->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} +  +/** Writes a 16-bit register on the ivch */ +static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data) +{ +	struct ivch_priv *priv = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[3]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 3, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = data & 0xff; +	out_buf[2] = data >> 8; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!priv->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +/** Probes the given bus and slave address for an ivch */ +static bool ivch_init(struct intel_dvo_device *dvo, +		      struct intel_i2c_chan *i2cbus) +{ +	struct ivch_priv *priv; +	uint16_t temp; + +	priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL); +	if (priv == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = priv; +	priv->quiet = TRUE; + +	if (!ivch_read(dvo, VR00, &temp)) +		goto out; +	priv->quiet = false; + +	/* Since the identification bits are probably zeroes, which doesn't seem +	 * very unique, check that the value in the base address field matches +	 * the address it's responding on. +	 */ +	if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) { +		DRM_DEBUG("ivch detect failed due to address mismatch " +			  "(%d vs %d)\n", +			  (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr); +		goto out; +	} + +#if 0 +	if (!xf86I2CDevInit(&priv->d)) { +		goto out; +	} +#endif +	ivch_read(dvo, VR20, &priv->width); +	ivch_read(dvo, VR21, &priv->height); + +	return true; + +out: +	kfree(priv); +	return false; +} + +static enum drm_output_status ivch_detect(struct intel_dvo_device *dvo) +{ +	return output_status_connected; +} + +static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, +					    struct drm_display_mode *mode) +{ +	if (mode->clock > 112000) +		return MODE_CLOCK_HIGH; + +	return MODE_OK; +} + +/** Sets the power state of the panel connected to the ivch */ +static void ivch_dpms(struct intel_dvo_device *dvo, int mode) +{ +	int i; +	uint16_t vr01, vr30, backlight; + +	/* Set the new power state of the panel. */ +	if (!ivch_read(dvo, VR01, &vr01)) +		return; + +	if (mode == DPMSModeOn) +		backlight = 1; +	else +		backlight = 0; +	ivch_write(dvo, VR80, backlight); + +	if (mode == DPMSModeOn) +		vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; +	else +		vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); + +	ivch_write(dvo, VR01, vr01); + +	/* Wait for the panel to make its state transition */ +	for (i = 0; i < 100; i++) { +		if (!ivch_read(dvo, VR30, &vr30)) +			break; + +		if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DPMSModeOn)) +			break; +		udelay(1000); +	} +	/* wait some more; vch may fail to resync sometimes without this */ +	udelay(16 * 1000); +} + +static void ivch_mode_set(struct intel_dvo_device *dvo, +			  struct drm_display_mode *mode, +			  struct drm_display_mode *adjusted_mode) +{ +	uint16_t vr40 = 0; +	uint16_t vr01; + +	vr01 = 0; +	vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE | +		VR40_HORIZONTAL_INTERP_ENABLE); + +	if (mode->hdisplay != adjusted_mode->hdisplay ||  +	    mode->vdisplay != adjusted_mode->vdisplay) { +		uint16_t x_ratio, y_ratio; + +		vr01 |= VR01_PANEL_FIT_ENABLE; +		vr40 |= VR40_CLOCK_GATING_ENABLE; +		x_ratio = (((mode->hdisplay - 1) << 16) / +			   (adjusted_mode->hdisplay - 1)) >> 2; +		y_ratio = (((mode->vdisplay - 1) << 16) / +			   (adjusted_mode->vdisplay - 1)) >> 2; +		ivch_write (dvo, VR42, x_ratio); +		ivch_write (dvo, VR41, y_ratio); +	} else { +		vr01 &= ~VR01_PANEL_FIT_ENABLE; +		vr40 &= ~VR40_CLOCK_GATING_ENABLE; +	} +	vr40 &= ~VR40_AUTO_RATIO_ENABLE; + +	ivch_write(dvo, VR01, vr01); +	ivch_write(dvo, VR40, vr40); + +	ivch_dump_regs(dvo); +} + +static void ivch_dump_regs(struct intel_dvo_device *dvo) +{ +	uint16_t val; + +	ivch_read(dvo, VR00, &val); +	DRM_DEBUG("VR00: 0x%04x\n", val); +	ivch_read(dvo, VR01, &val); +	DRM_DEBUG("VR01: 0x%04x\n", val); +	ivch_read(dvo, VR30, &val); +	DRM_DEBUG("VR30: 0x%04x\n", val); +	ivch_read(dvo, VR40, &val); +	DRM_DEBUG("VR40: 0x%04x\n", val); + +	/* GPIO registers */ +	ivch_read(dvo, VR80, &val); +	DRM_DEBUG("VR80: 0x%04x\n", val); +	ivch_read(dvo, VR81, &val); +	DRM_DEBUG("VR81: 0x%04x\n", val); +	ivch_read(dvo, VR82, &val); +	DRM_DEBUG("VR82: 0x%04x\n", val); +	ivch_read(dvo, VR83, &val); +	DRM_DEBUG("VR83: 0x%04x\n", val); +	ivch_read(dvo, VR84, &val); +	DRM_DEBUG("VR84: 0x%04x\n", val); +	ivch_read(dvo, VR85, &val); +	DRM_DEBUG("VR85: 0x%04x\n", val); +	ivch_read(dvo, VR86, &val); +	DRM_DEBUG("VR86: 0x%04x\n", val); +	ivch_read(dvo, VR87, &val); +	DRM_DEBUG("VR87: 0x%04x\n", val); +	ivch_read(dvo, VR88, &val); +	DRM_DEBUG("VR88: 0x%04x\n", val); + +	/* Scratch register 0 - AIM Panel type */ +	ivch_read(dvo, VR8E, &val); +	DRM_DEBUG("VR8E: 0x%04x\n", val); + +	/* Scratch register 1 - Status register */ +	ivch_read(dvo, VR8F, &val); +	DRM_DEBUG("VR8F: 0x%04x\n", val); +} + +static void ivch_save(struct intel_dvo_device *dvo) +{ +	struct ivch_priv *priv = dvo->dev_priv; + +	ivch_read(dvo, VR01, &priv->save_VR01); +	ivch_read(dvo, VR40, &priv->save_VR40); +} + +static void ivch_restore(struct intel_dvo_device *dvo) +{ +	struct ivch_priv *priv = dvo->dev_priv; + +	ivch_write(dvo, VR01, priv->save_VR01); +	ivch_write(dvo, VR40, priv->save_VR40); +} + +static void ivch_destroy(struct intel_dvo_device *dvo) +{ +	struct ivch_priv *priv = dvo->dev_priv; + +	if (priv) { +		kfree(priv); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops ivch_ops= { +	.init = ivch_init, +	.dpms = ivch_dpms, +	.save = ivch_save, +	.restore = ivch_restore, +	.mode_valid = ivch_mode_valid, +	.mode_set = ivch_mode_set, +	.detect = ivch_detect, +	.dump_regs = ivch_dump_regs, +	.destroy = ivch_destroy, +}; diff --git a/linux-core/dvo_sil164.c b/linux-core/dvo_sil164.c new file mode 100644 index 00000000..0cee59b1 --- /dev/null +++ b/linux-core/dvo_sil164.c @@ -0,0 +1,302 @@ +/************************************************************************** + +Copyright © 2006 Dave Airlie + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sub license, 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 NON-INFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +**************************************************************************/ + +#include "dvo.h" + +#define SIL164_VID 0x0001 +#define SIL164_DID 0x0006 + +#define SIL164_VID_LO 0x00 +#define SIL164_VID_HI 0x01 +#define SIL164_DID_LO 0x02 +#define SIL164_DID_HI 0x03 +#define SIL164_REV    0x04 +#define SIL164_RSVD   0x05 +#define SIL164_FREQ_LO 0x06 +#define SIL164_FREQ_HI 0x07 + +#define SIL164_REG8 0x08 +#define SIL164_8_VEN (1<<5) +#define SIL164_8_HEN (1<<4) +#define SIL164_8_DSEL (1<<3) +#define SIL164_8_BSEL (1<<2) +#define SIL164_8_EDGE (1<<1) +#define SIL164_8_PD   (1<<0) + +#define SIL164_REG9 0x09 +#define SIL164_9_VLOW (1<<7) +#define SIL164_9_MSEL_MASK (0x7<<4) +#define SIL164_9_TSEL (1<<3) +#define SIL164_9_RSEN (1<<2) +#define SIL164_9_HTPLG (1<<1) +#define SIL164_9_MDI (1<<0) + +#define SIL164_REGC 0x0c + +struct sil164_save_rec { +	uint8_t reg8; +	uint8_t reg9; +	uint8_t regc; +}; + +struct sil164_priv { +	//I2CDevRec d; +	bool quiet; +	struct sil164_save_rec save_regs; +	struct sil164_save_rec mode_regs; +}; + +#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr)) + +static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ +	struct sil164_priv *sil = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*ch = in_buf[0]; +		return true; +	}; + +	if (!sil->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} + +static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ +	struct sil164_priv *sil= dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!sil->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +/* Silicon Image 164 driver for chip on i2c bus */ +static bool sil164_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	/* this will detect the SIL164 chip on the specified i2c bus */ +	struct sil164_priv *sil; +	unsigned char ch; + +	sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL); +	if (sil == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = sil; +	sil->quiet = true; + +	if (!sil164_readb(dvo, SIL164_VID_LO, &ch)) +		goto out; + +	if (ch != (SIL164_VID & 0xff)) { +		DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n", +			  ch, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + +	if (!sil164_readb(dvo, SIL164_DID_LO, &ch)) +		goto out; + +	if (ch != (SIL164_DID & 0xff)) { +		DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n", +			  ch, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} +	sil->quiet = false; + +	DRM_DEBUG("init sil164 dvo controller successfully!\n"); +	return true; + +out: +	kfree(sil); +	return false; +} + +static enum drm_output_status sil164_detect(struct intel_dvo_device *dvo) +{ +	uint8_t reg9; + +	sil164_readb(dvo, SIL164_REG9, ®9); + +	if (reg9 & SIL164_9_HTPLG) +		return output_status_connected; +	else +		return output_status_disconnected; +} + +static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	return MODE_OK; +} + +static void sil164_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +	/* As long as the basics are set up, since we don't have clock +	 * dependencies in the mode setup, we can just leave the +	 * registers alone and everything will work fine. +	 */ +	/* recommended programming sequence from doc */ +	/*sil164_writeb(sil, 0x08, 0x30); +	  sil164_writeb(sil, 0x09, 0x00); +	  sil164_writeb(sil, 0x0a, 0x90); +	  sil164_writeb(sil, 0x0c, 0x89); +	  sil164_writeb(sil, 0x08, 0x31);*/ +	/* don't do much */ +	return; +} + +/* set the SIL164 power state */ +static void sil164_dpms(struct intel_dvo_device *dvo, int mode) +{ +	int ret; +	unsigned char ch; + +	ret = sil164_readb(dvo, SIL164_REG8, &ch); +	if (ret == false) +		return; + +	if (mode == DPMSModeOn) +		ch |= SIL164_8_PD; +	else +		ch &= ~SIL164_8_PD; + +	sil164_writeb(dvo, SIL164_REG8, ch); +	return; +} + +static void sil164_dump_regs(struct intel_dvo_device *dvo) +{ +	uint8_t val; + +	sil164_readb(dvo, SIL164_FREQ_LO, &val); +	DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_FREQ_HI, &val); +	DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_REG8, &val); +	DRM_DEBUG("SIL164_REG8: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_REG9, &val); +	DRM_DEBUG("SIL164_REG9: 0x%02x\n", val); +	sil164_readb(dvo, SIL164_REGC, &val); +	DRM_DEBUG("SIL164_REGC: 0x%02x\n", val); +} + +static void sil164_save(struct intel_dvo_device *dvo) +{ +	struct sil164_priv *sil= dvo->dev_priv; + +	if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8)) +		return; + +	if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9)) +		return; + +	if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc)) +		return; + +	return; +} + +static void sil164_restore(struct intel_dvo_device *dvo) +{ +	struct sil164_priv *sil = dvo->dev_priv; + +	/* Restore it powered down initially */ +	sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1); + +	sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9); +	sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc); +	sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8); +} + +static void sil164_destroy(struct intel_dvo_device *dvo) +{ +	struct sil164_priv *sil = dvo->dev_priv; + +	if (sil) { +		kfree(sil); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops sil164_ops = { +	.init = sil164_init, +	.detect = sil164_detect, +	.mode_valid = sil164_mode_valid, +	.mode_set = sil164_mode_set, +	.dpms = sil164_dpms, +	.dump_regs = sil164_dump_regs, +	.save = sil164_save, +	.restore = sil164_restore, +	.destroy = sil164_destroy, +}; diff --git a/linux-core/dvo_tfp410.c b/linux-core/dvo_tfp410.c new file mode 100644 index 00000000..64448509 --- /dev/null +++ b/linux-core/dvo_tfp410.c @@ -0,0 +1,335 @@ +/* + * Copyright © 2007 Dave Mueller + * + * 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: + *    Dave Mueller <dave.mueller@gmx.ch> + * + */ + +#include "dvo.h" + +/* register definitions according to the TFP410 data sheet */ +#define TFP410_VID		0x014C +#define TFP410_DID		0x0410 + +#define TFP410_VID_LO		0x00 +#define TFP410_VID_HI		0x01 +#define TFP410_DID_LO		0x02 +#define TFP410_DID_HI		0x03 +#define TFP410_REV		0x04 + +#define TFP410_CTL_1		0x08 +#define TFP410_CTL_1_TDIS	(1<<6) +#define TFP410_CTL_1_VEN	(1<<5) +#define TFP410_CTL_1_HEN	(1<<4) +#define TFP410_CTL_1_DSEL	(1<<3) +#define TFP410_CTL_1_BSEL	(1<<2) +#define TFP410_CTL_1_EDGE	(1<<1) +#define TFP410_CTL_1_PD		(1<<0) + +#define TFP410_CTL_2		0x09 +#define TFP410_CTL_2_VLOW	(1<<7) +#define TFP410_CTL_2_MSEL_MASK	(0x7<<4) +#define TFP410_CTL_2_MSEL	(1<<4) +#define TFP410_CTL_2_TSEL	(1<<3) +#define TFP410_CTL_2_RSEN	(1<<2) +#define TFP410_CTL_2_HTPLG	(1<<1) +#define TFP410_CTL_2_MDI	(1<<0) + +#define TFP410_CTL_3		0x0A +#define TFP410_CTL_3_DK_MASK 	(0x7<<5) +#define TFP410_CTL_3_DK		(1<<5) +#define TFP410_CTL_3_DKEN	(1<<4) +#define TFP410_CTL_3_CTL_MASK	(0x7<<1) +#define TFP410_CTL_3_CTL	(1<<1) + +#define TFP410_USERCFG		0x0B + +#define TFP410_DE_DLY		0x32 + +#define TFP410_DE_CTL		0x33 +#define TFP410_DE_CTL_DEGEN	(1<<6) +#define TFP410_DE_CTL_VSPOL	(1<<5) +#define TFP410_DE_CTL_HSPOL	(1<<4) +#define TFP410_DE_CTL_DEDLY8	(1<<0) + +#define TFP410_DE_TOP		0x34 + +#define TFP410_DE_CNT_LO	0x36 +#define TFP410_DE_CNT_HI	0x37 + +#define TFP410_DE_LIN_LO	0x38 +#define TFP410_DE_LIN_HI	0x39 + +#define TFP410_H_RES_LO		0x3A +#define TFP410_H_RES_HI		0x3B + +#define TFP410_V_RES_LO		0x3C +#define TFP410_V_RES_HI		0x3D + +struct tfp410_save_rec { +	uint8_t ctl1; +	uint8_t ctl2; +}; + +struct tfp410_priv { +	bool quiet; + +	struct tfp410_save_rec saved_reg; +	struct tfp410_save_rec mode_reg; +}; + +static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	u8 out_buf[2]; +	u8 in_buf[2]; + +	struct i2c_msg msgs[] = { +		{ +			.addr = i2cbus->slave_addr, +			.flags = 0, +			.len = 1, +			.buf = out_buf, +		}, +		{ +			.addr = i2cbus->slave_addr, +			.flags = I2C_M_RD, +			.len = 1, +			.buf = in_buf, +		} +	}; + +	out_buf[0] = addr; +	out_buf[1] = 0; + +	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) { +		*ch = in_buf[0]; +		return true; +	}; + +	if (!tfp->quiet) { +		DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} +	return false; +} + +static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; +	struct intel_i2c_chan *i2cbus = dvo->i2c_bus; +	uint8_t out_buf[2]; +	struct i2c_msg msg = { +		.addr = i2cbus->slave_addr, +		.flags = 0, +		.len = 2, +		.buf = out_buf, +	}; + +	out_buf[0] = addr; +	out_buf[1] = ch; + +	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1) +		return true; + +	if (!tfp->quiet) { +		DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n", +			  addr, i2cbus->adapter.name, i2cbus->slave_addr); +	} + +	return false; +} + +static int tfp410_getid(struct intel_dvo_device *dvo, int addr) +{ +	uint8_t ch1, ch2; + +	if (tfp410_readb(dvo, addr+0, &ch1) && +	    tfp410_readb(dvo, addr+1, &ch2)) +		return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF); + +	return -1; +} + +/* Ti TFP410 driver for chip on i2c bus */ +static bool tfp410_init(struct intel_dvo_device *dvo, +			struct intel_i2c_chan *i2cbus) +{ +	/* this will detect the tfp410 chip on the specified i2c bus */ +	struct tfp410_priv *tfp; +	int id; + +	tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL); +	if (tfp == NULL) +		return false; + +	dvo->i2c_bus = i2cbus; +	dvo->i2c_bus->slave_addr = dvo->slave_addr; +	dvo->dev_priv = tfp; +	tfp->quiet = TRUE; + +	if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) { +		DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n", +			  id, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} + +	if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) { +		DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n", +			  id, i2cbus->adapter.name, i2cbus->slave_addr); +		goto out; +	} +	tfp->quiet = FALSE; +	return true; +out: +	kfree(tfp); +	return false; +} + +static enum drm_output_status tfp410_detect(struct intel_dvo_device *dvo) +{ +	enum drm_output_status ret = output_status_disconnected; +	uint8_t ctl2; + +	if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) { +		if (ctl2 & TFP410_CTL_2_HTPLG) +			ret = output_status_connected; +		else +			ret = output_status_disconnected; +	} + +	return ret; +} + +static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo, +					      struct drm_display_mode *mode) +{ +	return MODE_OK; +} + +static void tfp410_mode_set(struct intel_dvo_device *dvo, +			    struct drm_display_mode *mode, +			    struct drm_display_mode *adjusted_mode) +{ +    /* As long as the basics are set up, since we don't have clock dependencies +     * in the mode setup, we can just leave the registers alone and everything +     * will work fine. +     */ +    /* don't do much */ +    return; +} + +/* set the tfp410 power state */ +static void tfp410_dpms(struct intel_dvo_device *dvo, int mode) +{ +	uint8_t ctl1; + +	if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) +		return; + +	if (mode == DPMSModeOn) +		ctl1 |= TFP410_CTL_1_PD; +	else +		ctl1 &= ~TFP410_CTL_1_PD; + +	tfp410_writeb(dvo, TFP410_CTL_1, ctl1); +} + +static void tfp410_dump_regs(struct intel_dvo_device *dvo) +{ +	uint8_t val, val2; + +	tfp410_readb(dvo, TFP410_REV, &val); +	DRM_DEBUG("TFP410_REV: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_CTL_1, &val); +	DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_CTL_2, &val); +	DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_CTL_3, &val); +	DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_USERCFG, &val); +	DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_DLY, &val); +	DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_CTL, &val); +	DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_TOP, &val); +	DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val); +	tfp410_readb(dvo, TFP410_DE_CNT_LO, &val); +	tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2); +	DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val); +	tfp410_readb(dvo, TFP410_DE_LIN_LO, &val); +	tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2); +	DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val); +	tfp410_readb(dvo, TFP410_H_RES_LO, &val); +	tfp410_readb(dvo, TFP410_H_RES_HI, &val2); +	DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val); +	tfp410_readb(dvo, TFP410_V_RES_LO, &val); +	tfp410_readb(dvo, TFP410_V_RES_HI, &val2); +	DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val); +} + +static void tfp410_save(struct intel_dvo_device *dvo) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; + +	if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1)) +		return; + +	if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2)) +		return; +} + +static void tfp410_restore(struct intel_dvo_device *dvo) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; + +	/* Restore it powered down initially */ +	tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1); + +	tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2); +	tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1); +} + +static void tfp410_destroy(struct intel_dvo_device *dvo) +{ +	struct tfp410_priv *tfp = dvo->dev_priv; + +	if (tfp) { +		kfree(tfp); +		dvo->dev_priv = NULL; +	} +} + +struct intel_dvo_dev_ops tfp410_ops = { +	.init = tfp410_init, +	.detect = tfp410_detect, +	.mode_valid = tfp410_mode_valid, +	.mode_set = tfp410_mode_set, +	.dpms = tfp410_dpms, +	.dump_regs = tfp410_dump_regs, +	.save = tfp410_save, +	.restore = tfp410_restore, +	.destroy = tfp410_destroy, +}; diff --git a/linux-core/intel_display.c b/linux-core/intel_display.c index 6aa61256..46abb90c 100644 --- a/linux-core/intel_display.c +++ b/linux-core/intel_display.c @@ -1396,7 +1396,8 @@ static void intel_setup_outputs(struct drm_device *dev)  	if (IS_I9XX(dev)) {  		intel_sdvo_init(dev, SDVOB);  		intel_sdvo_init(dev, SDVOC); -	} +	} else +		intel_dvo_init(dev);  	if (IS_I9XX(dev) && !IS_I915G(dev))  		intel_tv_init(dev); diff --git a/linux-core/intel_drv.h b/linux-core/intel_drv.h index 51c52c84..e0e6b797 100644 --- a/linux-core/intel_drv.h +++ b/linux-core/intel_drv.h @@ -69,6 +69,7 @@ extern bool intel_ddc_probe(struct drm_output *output);  extern void intel_crt_init(struct drm_device *dev);  extern void intel_sdvo_init(struct drm_device *dev, int output_device); +extern void intel_dvo_init(struct drm_device *dev);  extern void intel_tv_init(struct drm_device *dev);  extern void intel_lvds_init(struct drm_device *dev); diff --git a/linux-core/intel_dvo.c b/linux-core/intel_dvo.c new file mode 100644 index 00000000..423b7514 --- /dev/null +++ b/linux-core/intel_dvo.c @@ -0,0 +1,493 @@ +/* + * 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 <eric@anholt.net> + */ +/* + * Copyright 2006 Dave Airlie <airlied@linux.ie> + */ + +#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" +#include "dvo.h" + +#define SIL164_ADDR	0x38 +#define CH7xxx_ADDR	0x76 +#define TFP410_ADDR	0x38 + +extern struct intel_dvo_dev_ops sil164_ops; +extern struct intel_dvo_dev_ops ch7xxx_ops; +extern struct intel_dvo_dev_ops ivch_ops; +extern struct intel_dvo_dev_ops tfp410_ops; +extern struct intel_dvo_dev_ops ch7017_ops; + +struct intel_dvo_device intel_dvo_devices[] = { +	{ +		.type = INTEL_DVO_CHIP_TMDS, +		.name = "sil164", +		.dvo_reg = DVOC, +		.slave_addr = SIL164_ADDR, +		.dev_ops = &sil164_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_TMDS, +		.name = "ch7xxx", +		.dvo_reg = DVOC, +		.slave_addr = CH7xxx_ADDR, +		.dev_ops = &ch7xxx_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_LVDS, +		.name = "ivch", +		.dvo_reg = DVOA, +		.slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */ +		.dev_ops = &ivch_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_TMDS, +		.name = "tfp410", +		.dvo_reg = DVOC, +		.slave_addr = TFP410_ADDR, +		.dev_ops = &tfp410_ops, +	}, +	{ +		.type = INTEL_DVO_CHIP_LVDS, +		.name = "ch7017", +		.dvo_reg = DVOC, +		.slave_addr = 0x75, +		.gpio = GPIOE, +		.dev_ops = &ch7017_ops, +	} +}; + +static void intel_dvo_dpms(struct drm_output *output, int mode) +{ +	struct drm_i915_private *dev_priv = output->dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	u32 dvo_reg = dvo->dvo_reg; +	u32 temp = I915_READ(dvo_reg); + +	if (mode == DPMSModeOn) { +		I915_WRITE(dvo_reg, temp | DVO_ENABLE); +		I915_READ(dvo_reg); +		dvo->dev_ops->dpms(dvo, mode); +	} else { +		dvo->dev_ops->dpms(dvo, mode); +		I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); +		I915_READ(dvo_reg); +	} +} + +static void intel_dvo_save(struct drm_output *output) +{ +	struct drm_i915_private *dev_priv = output->dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	/* Each output should probably just save the registers it touches, +	 * but for now, use more overkill. +	 */ +	dev_priv->saveDVOA = I915_READ(DVOA); +	dev_priv->saveDVOB = I915_READ(DVOB); +	dev_priv->saveDVOC = I915_READ(DVOC); + +	dvo->dev_ops->save(dvo); +} + +static void intel_dvo_restore(struct drm_output *output) +{ +	struct drm_i915_private *dev_priv = output->dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	dvo->dev_ops->restore(dvo); + +	I915_WRITE(DVOA, dev_priv->saveDVOA); +	I915_WRITE(DVOB, dev_priv->saveDVOB); +	I915_WRITE(DVOC, dev_priv->saveDVOC); +} + +static int intel_dvo_mode_valid(struct drm_output *output, +				struct drm_display_mode *mode) +{ +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	if (mode->flags & V_DBLSCAN) +		return MODE_NO_DBLESCAN; + +	/* XXX: Validate clock range */ + +	if (dvo->panel_fixed_mode) { +		if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay) +			return MODE_PANEL; +		if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay) +			return MODE_PANEL; +	} + +	return dvo->dev_ops->mode_valid(dvo, mode); +} + +static bool intel_dvo_mode_fixup(struct drm_output *output, +				 struct drm_display_mode *mode, +				 struct drm_display_mode *adjusted_mode) +{ +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	/* 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 (dvo->panel_fixed_mode != NULL) { +#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x +		C(hdisplay); +		C(hsync_start); +		C(hsync_end); +		C(htotal); +		C(vdisplay); +		C(vsync_start); +		C(vsync_end); +		C(vtotal); +		C(clock); +		drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); +#undef C +	} + +	if (dvo->dev_ops->mode_fixup) +		return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode); + +	return true; +} + +static void intel_dvo_mode_set(struct drm_output *output, +			       struct drm_display_mode *mode, +			       struct drm_display_mode *adjusted_mode) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crtc *intel_crtc = output->crtc->driver_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	int pipe = intel_crtc->pipe; +	u32 dvo_val; +	u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg; +	int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + +	switch (dvo_reg) { +	case DVOA: +	default: +		dvo_srcdim_reg = DVOA_SRCDIM; +		break; +	case DVOB: +		dvo_srcdim_reg = DVOB_SRCDIM; +		break; +	case DVOC: +		dvo_srcdim_reg = DVOC_SRCDIM; +		break; +	} + +	dvo->dev_ops->mode_set(dvo, mode, adjusted_mode); + +	/* Save the data order, since I don't know what it should be set to. */ +	dvo_val = I915_READ(dvo_reg) & +		  (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG); +	dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE | +		   DVO_BLANK_ACTIVE_HIGH; + +	if (pipe == 1) +		dvo_val |= DVO_PIPE_B_SELECT; +	dvo_val |= DVO_PIPE_STALL; +	if (adjusted_mode->flags & V_PHSYNC) +		dvo_val |= DVO_HSYNC_ACTIVE_HIGH; +	if (adjusted_mode->flags & V_PVSYNC) +		dvo_val |= DVO_VSYNC_ACTIVE_HIGH; + +	I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED); + +	/*I915_WRITE(DVOB_SRCDIM, +	  (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | +	  (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/ +	I915_WRITE(dvo_srcdim_reg, +		   (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) | +		   (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT)); +	/*I915_WRITE(DVOB, dvo_val);*/ +	I915_WRITE(dvo_reg, dvo_val); +} + +/** + * Detect the output connection on our DVO device. + * + * Unimplemented. + */ +static enum drm_output_status intel_dvo_detect(struct drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	return dvo->dev_ops->detect(dvo); +} + +static int intel_dvo_get_modes(struct drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	/* We should probably have an i2c driver get_modes function for those +	 * devices which will have a fixed set of modes determined by the chip +	 * (TV-out, for example), but for now with just TMDS and LVDS, +	 * that's not the case. +	 */ +	intel_ddc_get_modes(output); +	if (!list_empty(&output->probed_modes)) +		return 1; + +#if 0 +	if (intel_output->i2c_drv->vid_rec->get_modes) +	{ +		modes = intel_output->i2c_drv->vid_rec->get_modes (intel_output->i2c_drv->dev_priv); +		if (modes != NULL) +			return modes; +	} +#endif + +	if (dvo->panel_fixed_mode != NULL) { +		struct drm_display_mode *mode; +		mode = drm_mode_duplicate(output->dev, dvo->panel_fixed_mode); +		if (mode) { +			drm_mode_probed_add(output, mode); +			return 1; +		} +	} +	return 0; +} + +static void intel_dvo_destroy (struct drm_output *output) +{ +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; + +	if (dvo) { +		if (dvo->dev_ops->destroy) +			dvo->dev_ops->destroy(dvo); +		if (dvo->panel_fixed_mode) +			kfree(dvo->panel_fixed_mode); +		/* no need, in i830_dvoices[] now */ +		//kfree(dvo); +	} +	if (intel_output) { +		if (intel_output->i2c_bus) +			intel_i2c_destroy(intel_output->i2c_bus); +		if (intel_output->ddc_bus) +			intel_i2c_destroy(intel_output->ddc_bus); +		kfree(intel_output); +		output->driver_private = NULL; +	} +} + +#ifdef RANDR_GET_CRTC_INTERFACE +static struct drm_crtc *intel_dvo_get_crtc(struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT); + +	return intel_pipe_to_crtc(pScrn, pipe); +} +#endif + + +static const struct drm_output_funcs intel_dvo_output_funcs = { +	.dpms = intel_dvo_dpms, +	.save = intel_dvo_save, +	.restore = intel_dvo_restore, +	.mode_valid = intel_dvo_mode_valid, +	.mode_fixup = intel_dvo_mode_fixup, +	.prepare = intel_output_prepare, +	.mode_set = intel_dvo_mode_set, +	.commit = intel_output_commit, +	.detect = intel_dvo_detect, +	.get_modes = intel_dvo_get_modes, +	.cleanup = intel_dvo_destroy +}; + +/** + * Attempts to get a fixed panel timing for LVDS (currently only the i830). + * + * Other chips with DVO LVDS will need to extend this to deal with the LVDS + * chip being on DVOB/C and having multiple pipes. + */ +static struct drm_display_mode * +intel_dvo_get_current_mode (struct drm_output *output) +{ +	struct drm_device *dev = output->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_output *intel_output = output->driver_private; +	struct intel_dvo_device *dvo = intel_output->dev_priv; +	uint32_t dvo_reg = dvo->dvo_reg; +	uint32_t dvo_val = I915_READ(dvo_reg); +	struct drm_display_mode *mode = NULL; + +	/* If the DVO port is active, that'll be the LVDS, so we can pull out +	 * its timings to get how the BIOS set up the panel. +	 */ +	if (dvo_val & DVO_ENABLE) { +		struct drm_crtc *crtc; +		int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0; + +		crtc = intel_get_crtc_from_pipe(dev, pipe); +		if (crtc) { +			mode = intel_crtc_mode_get(dev, crtc); + +			if (mode) { +				mode->type |= DRM_MODE_TYPE_PREFERRED; +				if (dvo_val & DVO_HSYNC_ACTIVE_HIGH) +					mode->flags |= V_PHSYNC; +				if (dvo_val & DVO_VSYNC_ACTIVE_HIGH) +					mode->flags |= V_PVSYNC; +			} +		} +	} +	return mode; +} + +void intel_dvo_init(struct drm_device *dev) +{ +	struct intel_output *intel_output; +	struct intel_dvo_device *dvo; +	struct intel_i2c_chan *i2cbus = NULL; +	int ret = 0; +	int i; +	int gpio_inited = 0; +	int connector = ConnectorUnknown; + +	intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL); +	if (!intel_output) +		return; + +	/* Set up the DDC bus */ +	intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D"); +	if (!intel_output->ddc_bus) +		goto free_intel; + +	/* Now, try to find a controller */ +	for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { +		struct drm_output *output = NULL; +		int gpio; + +		dvo = &intel_dvo_devices[i]; + +		/* Allow the I2C driver info to specify the GPIO to be used in +		 * special cases, but otherwise default to what's defined +		 * in the spec. +		 */ +		if (dvo->gpio != 0) +			gpio = dvo->gpio; +		else if (dvo->type == INTEL_DVO_CHIP_LVDS) +			gpio = GPIOB; +		else +			gpio = GPIOE; + +		/* Set up the I2C bus necessary for the chip we're probing. +		 * It appears that everything is on GPIOE except for panels +		 * on i830 laptops, which are on GPIOB (DVOA). +		 */ +		if (gpio_inited != gpio) { +			if (i2cbus != NULL) +				intel_i2c_destroy(i2cbus); +			if (!(i2cbus = intel_i2c_create(dev, gpio, +				gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) { +				continue; +			} +			gpio_inited = gpio; +		} + +		if (dvo->dev_ops!= NULL) +			ret = dvo->dev_ops->init(dvo, i2cbus); +		else +			ret = false; + +		if (!ret) +			continue; + +		intel_output->type = INTEL_OUTPUT_DVO; +		switch (dvo->type) { +		case INTEL_DVO_CHIP_TMDS: +			connector = ConnectorDVID; +			output = drm_output_create(dev, &intel_dvo_output_funcs, +					DRM_MODE_OUTPUT_TMDS); +			break; +		case INTEL_DVO_CHIP_LVDS: +			connector = ConnectorLVDS; +			output = drm_output_create(dev, &intel_dvo_output_funcs, +					DRM_MODE_OUTPUT_LVDS); +			break; +		} +		if (output == NULL) { +			dvo->dev_ops->destroy(dvo); +			goto free_i2c; +		} + +		output->driver_private = intel_output; +		output->display_info.subpixel_order = SubPixelHorizontalRGB; +		output->interlace_allowed = false; +		output->doublescan_allowed = false; + +		intel_output->dev_priv = dvo; +		intel_output->i2c_bus = i2cbus; + +		if (dvo->type == INTEL_DVO_CHIP_LVDS) { +			/* For our LVDS chipsets, we should hopefully be able +			 * to dig the fixed panel mode out of the BIOS data. +			 * However, it's in a different format from the BIOS +			 * data on chipsets with integrated LVDS (stored in AIM +			 * headers, likely), so for now, just get the current +			 * mode being output through DVO. +			 */ +			dvo->panel_fixed_mode = intel_dvo_get_current_mode(output); +			dvo->panel_wants_dither = true; +		} + +		drm_output_attach_property(output, +				dev->mode_config.connector_type_property, +				connector); +		return; +	} + +free_i2c: +	intel_i2c_destroy(intel_output->ddc_bus); +	/* Didn't find a chip, so tear down. */ +	if (i2cbus != NULL) +		intel_i2c_destroy(i2cbus); +free_intel: +	kfree(intel_output); +} | 
