| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Version: 1.64 2002/06/10 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * See matroxfb_base.c for contributors. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "matroxfb_base.h"
 | 
					
						
							|  |  |  | #include "matroxfb_maven.h"
 | 
					
						
							|  |  |  | #include <linux/i2c.h>
 | 
					
						
							|  |  |  | #include <linux/i2c-algo-bit.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* MGA-TVO I2C for G200, G400 */ | 
					
						
							|  |  |  | #define MAT_CLK		0x20
 | 
					
						
							|  |  |  | #define MAT_DATA	0x10
 | 
					
						
							|  |  |  | /* primary head DDC for Mystique(?), G100, G200, G400 */ | 
					
						
							|  |  |  | #define DDC1_CLK	0x08
 | 
					
						
							|  |  |  | #define DDC1_DATA	0x02
 | 
					
						
							|  |  |  | /* primary head DDC for Millennium, Millennium II */ | 
					
						
							|  |  |  | #define DDC1B_CLK	0x10
 | 
					
						
							|  |  |  | #define DDC1B_DATA	0x04
 | 
					
						
							|  |  |  | /* secondary head DDC for G400 */ | 
					
						
							|  |  |  | #define DDC2_CLK	0x04
 | 
					
						
							|  |  |  | #define DDC2_DATA	0x01
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /******************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct matroxfb_dh_maven_info { | 
					
						
							|  |  |  | 	struct i2c_bit_adapter	maven; | 
					
						
							|  |  |  | 	struct i2c_bit_adapter	ddc1; | 
					
						
							|  |  |  | 	struct i2c_bit_adapter	ddc2; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int matroxfb_read_gpio(struct matrox_fb_info* minfo) { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	int v; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	matroxfb_DAC_lock_irqsave(flags); | 
					
						
							|  |  |  | 	v = matroxfb_DAC_in(PMINFO DAC_XGENIODATA); | 
					
						
							|  |  |  | 	matroxfb_DAC_unlock_irqrestore(flags); | 
					
						
							|  |  |  | 	return v; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	int v; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	matroxfb_DAC_lock_irqsave(flags); | 
					
						
							|  |  |  | 	v = (matroxfb_DAC_in(PMINFO DAC_XGENIOCTRL) & mask) | val; | 
					
						
							|  |  |  | 	matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, v); | 
					
						
							|  |  |  | 	/* We must reset GENIODATA very often... XFree plays with this register */ | 
					
						
							|  |  |  | 	matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0x00); | 
					
						
							|  |  |  | 	matroxfb_DAC_unlock_irqrestore(flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* software I2C functions */ | 
					
						
							|  |  |  | static inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) { | 
					
						
							|  |  |  | 	if (state) | 
					
						
							|  |  |  | 		state = 0; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		state = mask; | 
					
						
							|  |  |  | 	matroxfb_set_gpio(minfo, ~mask, state); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void matroxfb_gpio_setsda(void* data, int state) { | 
					
						
							|  |  |  | 	struct i2c_bit_adapter* b = data; | 
					
						
							|  |  |  | 	matroxfb_i2c_set(b->minfo, b->mask.data, state); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void matroxfb_gpio_setscl(void* data, int state) { | 
					
						
							|  |  |  | 	struct i2c_bit_adapter* b = data; | 
					
						
							|  |  |  | 	matroxfb_i2c_set(b->minfo, b->mask.clock, state); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int matroxfb_gpio_getsda(void* data) { | 
					
						
							|  |  |  | 	struct i2c_bit_adapter* b = data; | 
					
						
							|  |  |  | 	return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int matroxfb_gpio_getscl(void* data) { | 
					
						
							|  |  |  | 	struct i2c_bit_adapter* b = data; | 
					
						
							|  |  |  | 	return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-12 15:08:54 -07:00
										 |  |  | static const struct i2c_algo_bit_data matrox_i2c_algo_template = | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2006-08-13 23:40:09 +02:00
										 |  |  | 	.setsda		= matroxfb_gpio_setsda, | 
					
						
							|  |  |  | 	.setscl		= matroxfb_gpio_setscl, | 
					
						
							|  |  |  | 	.getsda		= matroxfb_gpio_getsda, | 
					
						
							|  |  |  | 	.getscl		= matroxfb_gpio_getscl, | 
					
						
							|  |  |  | 	.udelay		= 10, | 
					
						
							|  |  |  | 	.timeout	= 100, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,  | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 		unsigned int data, unsigned int clock, const char *name, | 
					
						
							|  |  |  | 		int class) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b->minfo = minfo; | 
					
						
							|  |  |  | 	b->mask.data = data; | 
					
						
							|  |  |  | 	b->mask.clock = clock; | 
					
						
							| 
									
										
										
										
											2008-08-12 15:08:54 -07:00
										 |  |  | 	b->adapter.owner = THIS_MODULE; | 
					
						
							| 
									
										
										
										
											2007-05-01 23:26:28 +02:00
										 |  |  | 	snprintf(b->adapter.name, sizeof(b->adapter.name), name, | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		minfo->fbcon.node); | 
					
						
							|  |  |  | 	i2c_set_adapdata(&b->adapter, b); | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 	b->adapter.class = class; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	b->adapter.algo_data = &b->bac; | 
					
						
							| 
									
										
										
										
											2007-02-13 22:09:03 +01:00
										 |  |  | 	b->adapter.dev.parent = &ACCESS_FBINFO(pcidev)->dev; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	b->bac = matrox_i2c_algo_template; | 
					
						
							|  |  |  | 	b->bac.data = b; | 
					
						
							|  |  |  | 	err = i2c_bit_add_bus(&b->adapter); | 
					
						
							|  |  |  | 	b->initialized = !err; | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void i2c_bit_bus_del(struct i2c_bit_adapter* b) { | 
					
						
							|  |  |  | 	if (b->initialized) { | 
					
						
							| 
									
										
										
										
											2006-12-10 21:21:33 +01:00
										 |  |  | 		i2c_del_adapter(&b->adapter); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		b->initialized = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) { | 
					
						
							|  |  |  | 	i2c_bit_bus_del(&minfo2->maven); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) { | 
					
						
							|  |  |  | 	i2c_bit_bus_del(&minfo2->ddc1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) { | 
					
						
							|  |  |  | 	i2c_bit_bus_del(&minfo2->ddc2); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	struct matroxfb_dh_maven_info* m2info; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-02-12 00:55:07 -08:00
										 |  |  | 	m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if (!m2info) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	matroxfb_DAC_lock_irqsave(flags); | 
					
						
							|  |  |  | 	matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0xFF); | 
					
						
							|  |  |  | 	matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, 0x00); | 
					
						
							|  |  |  | 	matroxfb_DAC_unlock_irqrestore(flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (ACCESS_FBINFO(chip)) { | 
					
						
							|  |  |  | 		case MGA_2064: | 
					
						
							|  |  |  | 		case MGA_2164: | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 			err = i2c_bus_reg(&m2info->ddc1, minfo, | 
					
						
							|  |  |  | 					  DDC1B_DATA, DDC1B_CLK, | 
					
						
							|  |  |  | 					  "DDC:fb%u #0", I2C_CLASS_DDC); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 			err = i2c_bus_reg(&m2info->ddc1, minfo, | 
					
						
							|  |  |  | 					  DDC1_DATA, DDC1_CLK, | 
					
						
							|  |  |  | 					  "DDC:fb%u #0", I2C_CLASS_DDC); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto fail_ddc1; | 
					
						
							|  |  |  | 	if (ACCESS_FBINFO(devflags.dualhead)) { | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 		err = i2c_bus_reg(&m2info->ddc2, minfo, | 
					
						
							|  |  |  | 				  DDC2_DATA, DDC2_CLK, | 
					
						
							|  |  |  | 				  "DDC:fb%u #1", I2C_CLASS_DDC); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err == -ENODEV) { | 
					
						
							|  |  |  | 			printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); | 
					
						
							|  |  |  | 		} else if (err) | 
					
						
							|  |  |  | 			printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); | 
					
						
							|  |  |  | 		/* Register maven bus even on G450/G550 */ | 
					
						
							| 
									
										
										
										
											2008-07-14 22:38:28 +02:00
										 |  |  | 		err = i2c_bus_reg(&m2info->maven, minfo, | 
					
						
							|  |  |  | 				  MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if (err) | 
					
						
							|  |  |  | 			printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); | 
					
						
							| 
									
										
										
										
											2008-08-12 15:08:55 -07:00
										 |  |  | 		else { | 
					
						
							|  |  |  | 			struct i2c_board_info maven_info = { | 
					
						
							|  |  |  | 				I2C_BOARD_INFO("maven", 0x1b), | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			unsigned short const addr_list[2] = { | 
					
						
							|  |  |  | 				0x1b, I2C_CLIENT_END | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			i2c_new_probed_device(&m2info->maven.adapter, | 
					
						
							|  |  |  | 					      &maven_info, addr_list); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return m2info; | 
					
						
							|  |  |  | fail_ddc1:; | 
					
						
							|  |  |  | 	kfree(m2info); | 
					
						
							|  |  |  | 	printk(KERN_ERR "i2c-matroxfb: Could not register primary adapter DDC bus.\n"); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void i2c_matroxfb_remove(struct matrox_fb_info* minfo, void* data) { | 
					
						
							|  |  |  | 	struct matroxfb_dh_maven_info* m2info = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i2c_maven_done(m2info); | 
					
						
							|  |  |  | 	i2c_ddc2_done(m2info); | 
					
						
							|  |  |  | 	i2c_ddc1_done(m2info); | 
					
						
							|  |  |  | 	kfree(m2info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct matroxfb_driver i2c_matroxfb = { | 
					
						
							|  |  |  | 	.node =		LIST_HEAD_INIT(i2c_matroxfb.node), | 
					
						
							|  |  |  | 	.name =		"i2c-matroxfb", | 
					
						
							|  |  |  | 	.probe = 	i2c_matroxfb_probe, | 
					
						
							|  |  |  | 	.remove =	i2c_matroxfb_remove, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init i2c_matroxfb_init(void) { | 
					
						
							|  |  |  | 	if (matroxfb_register_driver(&i2c_matroxfb)) { | 
					
						
							|  |  |  | 		printk(KERN_ERR "i2c-matroxfb: failed to register driver\n"); | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit i2c_matroxfb_exit(void) { | 
					
						
							|  |  |  | 	matroxfb_unregister_driver(&i2c_matroxfb); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(i2c_matroxfb_init); | 
					
						
							|  |  |  | module_exit(i2c_matroxfb_exit); | 
					
						
							|  |  |  | /* no __setup required */ | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |