| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | /**************************************************************************
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  Copyright 2006 Dave Airlie <airlied@linux.ie> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | on 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 COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS 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 <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/fb.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/i2c.h>
 | 
					
						
							|  |  |  | #include <linux/i2c-id.h>
 | 
					
						
							|  |  |  | #include <linux/i2c-algo-bit.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "intelfb.h"
 | 
					
						
							|  |  |  | #include "intelfbhw.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* bit locations in the registers */ | 
					
						
							|  |  |  | #define SCL_DIR_MASK		0x0001
 | 
					
						
							|  |  |  | #define SCL_DIR			0x0002
 | 
					
						
							|  |  |  | #define SCL_VAL_MASK		0x0004
 | 
					
						
							|  |  |  | #define SCL_VAL_OUT		0x0008
 | 
					
						
							|  |  |  | #define SCL_VAL_IN		0x0010
 | 
					
						
							|  |  |  | #define SDA_DIR_MASK		0x0100
 | 
					
						
							|  |  |  | #define SDA_DIR			0x0200
 | 
					
						
							|  |  |  | #define SDA_VAL_MASK		0x0400
 | 
					
						
							|  |  |  | #define SDA_VAL_OUT		0x0800
 | 
					
						
							|  |  |  | #define SDA_VAL_IN		0x1000
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void intelfb_gpio_setscl(void *data, int state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct intelfb_i2c_chan *chan = data; | 
					
						
							|  |  |  | 	struct intelfb_info *dinfo = chan->dinfo; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | | 
					
						
							|  |  |  | 	       SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	val = INREG(chan->reg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void intelfb_gpio_setsda(void *data, int state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct intelfb_i2c_chan *chan = data; | 
					
						
							|  |  |  | 	struct intelfb_info *dinfo = chan->dinfo; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | | 
					
						
							|  |  |  | 	       SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	val = INREG(chan->reg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int intelfb_gpio_getscl(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct intelfb_i2c_chan *chan = data; | 
					
						
							|  |  |  | 	struct intelfb_info *dinfo = chan->dinfo; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	OUTREG(chan->reg, SCL_DIR_MASK); | 
					
						
							|  |  |  | 	OUTREG(chan->reg, 0); | 
					
						
							|  |  |  | 	val = INREG(chan->reg); | 
					
						
							|  |  |  | 	return ((val & SCL_VAL_IN) != 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int intelfb_gpio_getsda(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct intelfb_i2c_chan *chan = data; | 
					
						
							|  |  |  | 	struct intelfb_info *dinfo = chan->dinfo; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	OUTREG(chan->reg, SDA_DIR_MASK); | 
					
						
							|  |  |  | 	OUTREG(chan->reg, 0); | 
					
						
							|  |  |  | 	val = INREG(chan->reg); | 
					
						
							|  |  |  | 	return ((val & SDA_VAL_IN) != 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 				 struct intelfb_i2c_chan *chan, | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 				 const u32 reg, const char *name, | 
					
						
							|  |  |  | 				 int class) | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	chan->dinfo			= dinfo; | 
					
						
							|  |  |  | 	chan->reg			= reg; | 
					
						
							| 
									
										
										
										
											2007-05-01 23:26:28 +02:00
										 |  |  | 	snprintf(chan->adapter.name, sizeof(chan->adapter.name), | 
					
						
							|  |  |  | 		 "intelfb %s", name); | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 	chan->adapter.class		= class; | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	chan->adapter.owner		= THIS_MODULE; | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	chan->adapter.algo_data		= &chan->algo; | 
					
						
							|  |  |  | 	chan->adapter.dev.parent	= &chan->dinfo->pdev->dev; | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	chan->algo.setsda		= intelfb_gpio_setsda; | 
					
						
							|  |  |  | 	chan->algo.setscl		= intelfb_gpio_setscl; | 
					
						
							|  |  |  | 	chan->algo.getsda		= intelfb_gpio_getsda; | 
					
						
							|  |  |  | 	chan->algo.getscl		= intelfb_gpio_getscl; | 
					
						
							|  |  |  | 	chan->algo.udelay		= 40; | 
					
						
							|  |  |  | 	chan->algo.timeout		= 20; | 
					
						
							|  |  |  | 	chan->algo.data			= chan; | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	i2c_set_adapdata(&chan->adapter, chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Raise SCL and SDA */ | 
					
						
							|  |  |  | 	intelfb_gpio_setsda(chan, 1); | 
					
						
							|  |  |  | 	intelfb_gpio_setscl(chan, 1); | 
					
						
							|  |  |  | 	udelay(20); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = i2c_bit_add_bus(&chan->adapter); | 
					
						
							|  |  |  | 	if (rc == 0) | 
					
						
							|  |  |  | 		DBG_MSG("I2C bus %s registered.\n", name); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		WRN_MSG("Failed to register I2C bus %s.\n", name); | 
					
						
							|  |  |  | 	return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void intelfb_create_i2c_busses(struct intelfb_info *dinfo) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* everyone has at least a single analog output */ | 
					
						
							|  |  |  | 	dinfo->num_outputs = 1; | 
					
						
							|  |  |  | 	dinfo->output[i].type = INTELFB_OUTPUT_ANALOG; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* setup the DDC bus for analog output */ | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 			      "CRTDDC_A", I2C_CLASS_DDC); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	i++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 	/* need to add the output busses for each device
 | 
					
						
							|  |  |  | 	   - this function is very incomplete | 
					
						
							|  |  |  | 	   - i915GM has LVDS and TVOUT for example | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 	switch(dinfo->chipset) { | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	case INTEL_830M: | 
					
						
							|  |  |  | 	case INTEL_845G: | 
					
						
							| 
									
										
										
										
											2009-04-13 14:40:10 -07:00
										 |  |  | 	case INTEL_854: | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	case INTEL_855GM: | 
					
						
							|  |  |  | 	case INTEL_865G: | 
					
						
							|  |  |  | 		dinfo->output[i].type = INTELFB_OUTPUT_DVO; | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 		intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 				      GPIOD, "DVODDC_D", I2C_CLASS_DDC); | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 		intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 				      GPIOE, "DVOI2C_E", 0); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 		i++; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case INTEL_915G: | 
					
						
							|  |  |  | 	case INTEL_915GM: | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 		/* has some LVDS + tv-out */ | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 	case INTEL_945G: | 
					
						
							|  |  |  | 	case INTEL_945GM: | 
					
						
							| 
									
										
										
										
											2008-10-15 22:03:35 -07:00
										 |  |  | 	case INTEL_945GME: | 
					
						
							| 
									
										
										
										
											2008-04-28 02:15:43 -07:00
										 |  |  | 	case INTEL_965G: | 
					
						
							|  |  |  | 	case INTEL_965GM: | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 		/* SDVO ports have a single control bus - 2 devices */ | 
					
						
							|  |  |  | 		dinfo->output[i].type = INTELFB_OUTPUT_SDVO; | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 		intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 				      GPIOE, "SDVOCTRL_E", 0); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 		/* TODO: initialize the SDVO */ | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 		/* I830SDVOInit(pScrn, i, DVOB); */ | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 		i++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* set up SDVOC */ | 
					
						
							|  |  |  | 		dinfo->output[i].type = INTELFB_OUTPUT_SDVO; | 
					
						
							|  |  |  | 		dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus; | 
					
						
							|  |  |  | 		/* TODO: initialize the SDVO */ | 
					
						
							| 
									
										
										
										
											2007-10-16 01:29:31 -07:00
										 |  |  | 		/* I830SDVOInit(pScrn, i, DVOC); */ | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 		i++; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dinfo->num_outputs = i; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | void intelfb_delete_i2c_busses(struct intelfb_info *dinfo) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < MAX_OUTPUTS; i++) { | 
					
						
							|  |  |  | 		if (dinfo->output[i].i2c_bus.dinfo) { | 
					
						
							| 
									
										
										
										
											2006-12-10 21:21:33 +01:00
										 |  |  | 			i2c_del_adapter(&dinfo->output[i].i2c_bus.adapter); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 			dinfo->output[i].i2c_bus.dinfo = NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (dinfo->output[i].ddc_bus.dinfo) { | 
					
						
							| 
									
										
										
										
											2006-12-10 21:21:33 +01:00
										 |  |  | 			i2c_del_adapter(&dinfo->output[i].ddc_bus.adapter); | 
					
						
							| 
									
										
										
										
											2006-06-20 14:55:55 -04:00
										 |  |  | 			dinfo->output[i].ddc_bus.dinfo = NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |