162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Version: 1.64 2002/06/10 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * See matroxfb_base.c for contributors. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "matroxfb_base.h" 1562306a36Sopenharmony_ci#include "matroxfb_maven.h" 1662306a36Sopenharmony_ci#include <linux/i2c.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/i2c-algo-bit.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* MGA-TVO I2C for G200, G400 */ 2162306a36Sopenharmony_ci#define MAT_CLK 0x20 2262306a36Sopenharmony_ci#define MAT_DATA 0x10 2362306a36Sopenharmony_ci/* primary head DDC for Mystique(?), G100, G200, G400 */ 2462306a36Sopenharmony_ci#define DDC1_CLK 0x08 2562306a36Sopenharmony_ci#define DDC1_DATA 0x02 2662306a36Sopenharmony_ci/* primary head DDC for Millennium, Millennium II */ 2762306a36Sopenharmony_ci#define DDC1B_CLK 0x10 2862306a36Sopenharmony_ci#define DDC1B_DATA 0x04 2962306a36Sopenharmony_ci/* secondary head DDC for G400 */ 3062306a36Sopenharmony_ci#define DDC2_CLK 0x04 3162306a36Sopenharmony_ci#define DDC2_DATA 0x01 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/******************************************************/ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct matroxfb_dh_maven_info { 3662306a36Sopenharmony_ci struct i2c_bit_adapter maven; 3762306a36Sopenharmony_ci struct i2c_bit_adapter ddc1; 3862306a36Sopenharmony_ci struct i2c_bit_adapter ddc2; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int matroxfb_read_gpio(struct matrox_fb_info* minfo) { 4262306a36Sopenharmony_ci unsigned long flags; 4362306a36Sopenharmony_ci int v; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 4662306a36Sopenharmony_ci v = matroxfb_DAC_in(minfo, DAC_XGENIODATA); 4762306a36Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 4862306a36Sopenharmony_ci return v; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { 5262306a36Sopenharmony_ci unsigned long flags; 5362306a36Sopenharmony_ci int v; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 5662306a36Sopenharmony_ci v = (matroxfb_DAC_in(minfo, DAC_XGENIOCTRL) & mask) | val; 5762306a36Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, v); 5862306a36Sopenharmony_ci /* We must reset GENIODATA very often... XFree plays with this register */ 5962306a36Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0x00); 6062306a36Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* software I2C functions */ 6462306a36Sopenharmony_cistatic inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) { 6562306a36Sopenharmony_ci if (state) 6662306a36Sopenharmony_ci state = 0; 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci state = mask; 6962306a36Sopenharmony_ci matroxfb_set_gpio(minfo, ~mask, state); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void matroxfb_gpio_setsda(void* data, int state) { 7362306a36Sopenharmony_ci struct i2c_bit_adapter* b = data; 7462306a36Sopenharmony_ci matroxfb_i2c_set(b->minfo, b->mask.data, state); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void matroxfb_gpio_setscl(void* data, int state) { 7862306a36Sopenharmony_ci struct i2c_bit_adapter* b = data; 7962306a36Sopenharmony_ci matroxfb_i2c_set(b->minfo, b->mask.clock, state); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int matroxfb_gpio_getsda(void* data) { 8362306a36Sopenharmony_ci struct i2c_bit_adapter* b = data; 8462306a36Sopenharmony_ci return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int matroxfb_gpio_getscl(void* data) { 8862306a36Sopenharmony_ci struct i2c_bit_adapter* b = data; 8962306a36Sopenharmony_ci return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic const struct i2c_algo_bit_data matrox_i2c_algo_template = 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci .setsda = matroxfb_gpio_setsda, 9562306a36Sopenharmony_ci .setscl = matroxfb_gpio_setscl, 9662306a36Sopenharmony_ci .getsda = matroxfb_gpio_getsda, 9762306a36Sopenharmony_ci .getscl = matroxfb_gpio_getscl, 9862306a36Sopenharmony_ci .udelay = 10, 9962306a36Sopenharmony_ci .timeout = 100, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, 10362306a36Sopenharmony_ci unsigned int data, unsigned int clock, const char *name, 10462306a36Sopenharmony_ci int class) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci int err; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci b->minfo = minfo; 10962306a36Sopenharmony_ci b->mask.data = data; 11062306a36Sopenharmony_ci b->mask.clock = clock; 11162306a36Sopenharmony_ci b->adapter.owner = THIS_MODULE; 11262306a36Sopenharmony_ci snprintf(b->adapter.name, sizeof(b->adapter.name), name, 11362306a36Sopenharmony_ci minfo->fbcon.node); 11462306a36Sopenharmony_ci i2c_set_adapdata(&b->adapter, b); 11562306a36Sopenharmony_ci b->adapter.class = class; 11662306a36Sopenharmony_ci b->adapter.algo_data = &b->bac; 11762306a36Sopenharmony_ci b->adapter.dev.parent = &minfo->pcidev->dev; 11862306a36Sopenharmony_ci b->bac = matrox_i2c_algo_template; 11962306a36Sopenharmony_ci b->bac.data = b; 12062306a36Sopenharmony_ci err = i2c_bit_add_bus(&b->adapter); 12162306a36Sopenharmony_ci b->initialized = !err; 12262306a36Sopenharmony_ci return err; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void i2c_bit_bus_del(struct i2c_bit_adapter* b) { 12662306a36Sopenharmony_ci if (b->initialized) { 12762306a36Sopenharmony_ci i2c_del_adapter(&b->adapter); 12862306a36Sopenharmony_ci b->initialized = 0; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) { 13362306a36Sopenharmony_ci i2c_bit_bus_del(&minfo2->maven); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) { 13762306a36Sopenharmony_ci i2c_bit_bus_del(&minfo2->ddc1); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) { 14162306a36Sopenharmony_ci i2c_bit_bus_del(&minfo2->ddc2); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { 14562306a36Sopenharmony_ci int err; 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci struct matroxfb_dh_maven_info* m2info; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); 15062306a36Sopenharmony_ci if (!m2info) 15162306a36Sopenharmony_ci return NULL; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 15462306a36Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0xFF); 15562306a36Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, 0x00); 15662306a36Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci switch (minfo->chip) { 15962306a36Sopenharmony_ci case MGA_2064: 16062306a36Sopenharmony_ci case MGA_2164: 16162306a36Sopenharmony_ci err = i2c_bus_reg(&m2info->ddc1, minfo, 16262306a36Sopenharmony_ci DDC1B_DATA, DDC1B_CLK, 16362306a36Sopenharmony_ci "DDC:fb%u #0", I2C_CLASS_DDC); 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci err = i2c_bus_reg(&m2info->ddc1, minfo, 16762306a36Sopenharmony_ci DDC1_DATA, DDC1_CLK, 16862306a36Sopenharmony_ci "DDC:fb%u #0", I2C_CLASS_DDC); 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci if (err) 17262306a36Sopenharmony_ci goto fail_ddc1; 17362306a36Sopenharmony_ci if (minfo->devflags.dualhead) { 17462306a36Sopenharmony_ci err = i2c_bus_reg(&m2info->ddc2, minfo, 17562306a36Sopenharmony_ci DDC2_DATA, DDC2_CLK, 17662306a36Sopenharmony_ci "DDC:fb%u #1", I2C_CLASS_DDC); 17762306a36Sopenharmony_ci if (err == -ENODEV) { 17862306a36Sopenharmony_ci printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); 17962306a36Sopenharmony_ci } else if (err) 18062306a36Sopenharmony_ci printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); 18162306a36Sopenharmony_ci /* Register maven bus even on G450/G550 */ 18262306a36Sopenharmony_ci err = i2c_bus_reg(&m2info->maven, minfo, 18362306a36Sopenharmony_ci MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); 18462306a36Sopenharmony_ci if (err) 18562306a36Sopenharmony_ci printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); 18662306a36Sopenharmony_ci else { 18762306a36Sopenharmony_ci struct i2c_board_info maven_info = { 18862306a36Sopenharmony_ci I2C_BOARD_INFO("maven", 0x1b), 18962306a36Sopenharmony_ci }; 19062306a36Sopenharmony_ci unsigned short const addr_list[2] = { 19162306a36Sopenharmony_ci 0x1b, I2C_CLIENT_END 19262306a36Sopenharmony_ci }; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci i2c_new_scanned_device(&m2info->maven.adapter, 19562306a36Sopenharmony_ci &maven_info, addr_list, NULL); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci return m2info; 19962306a36Sopenharmony_cifail_ddc1:; 20062306a36Sopenharmony_ci kfree(m2info); 20162306a36Sopenharmony_ci printk(KERN_ERR "i2c-matroxfb: Could not register primary adapter DDC bus.\n"); 20262306a36Sopenharmony_ci return NULL; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void i2c_matroxfb_remove(struct matrox_fb_info* minfo, void* data) { 20662306a36Sopenharmony_ci struct matroxfb_dh_maven_info* m2info = data; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci i2c_maven_done(m2info); 20962306a36Sopenharmony_ci i2c_ddc2_done(m2info); 21062306a36Sopenharmony_ci i2c_ddc1_done(m2info); 21162306a36Sopenharmony_ci kfree(m2info); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic struct matroxfb_driver i2c_matroxfb = { 21562306a36Sopenharmony_ci .node = LIST_HEAD_INIT(i2c_matroxfb.node), 21662306a36Sopenharmony_ci .name = "i2c-matroxfb", 21762306a36Sopenharmony_ci .probe = i2c_matroxfb_probe, 21862306a36Sopenharmony_ci .remove = i2c_matroxfb_remove, 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int __init i2c_matroxfb_init(void) { 22262306a36Sopenharmony_ci if (matroxfb_register_driver(&i2c_matroxfb)) { 22362306a36Sopenharmony_ci printk(KERN_ERR "i2c-matroxfb: failed to register driver\n"); 22462306a36Sopenharmony_ci return -ENXIO; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void __exit i2c_matroxfb_exit(void) { 23062306a36Sopenharmony_ci matroxfb_unregister_driver(&i2c_matroxfb); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciMODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 23462306a36Sopenharmony_ciMODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards"); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cimodule_init(i2c_matroxfb_init); 23762306a36Sopenharmony_cimodule_exit(i2c_matroxfb_exit); 23862306a36Sopenharmony_ci/* no __setup required */ 23962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 240