18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Version: 1.64 2002/06/10 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * See matroxfb_base.c for contributors. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "matroxfb_base.h" 158c2ecf20Sopenharmony_ci#include "matroxfb_maven.h" 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* MGA-TVO I2C for G200, G400 */ 218c2ecf20Sopenharmony_ci#define MAT_CLK 0x20 228c2ecf20Sopenharmony_ci#define MAT_DATA 0x10 238c2ecf20Sopenharmony_ci/* primary head DDC for Mystique(?), G100, G200, G400 */ 248c2ecf20Sopenharmony_ci#define DDC1_CLK 0x08 258c2ecf20Sopenharmony_ci#define DDC1_DATA 0x02 268c2ecf20Sopenharmony_ci/* primary head DDC for Millennium, Millennium II */ 278c2ecf20Sopenharmony_ci#define DDC1B_CLK 0x10 288c2ecf20Sopenharmony_ci#define DDC1B_DATA 0x04 298c2ecf20Sopenharmony_ci/* secondary head DDC for G400 */ 308c2ecf20Sopenharmony_ci#define DDC2_CLK 0x04 318c2ecf20Sopenharmony_ci#define DDC2_DATA 0x01 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/******************************************************/ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct matroxfb_dh_maven_info { 368c2ecf20Sopenharmony_ci struct i2c_bit_adapter maven; 378c2ecf20Sopenharmony_ci struct i2c_bit_adapter ddc1; 388c2ecf20Sopenharmony_ci struct i2c_bit_adapter ddc2; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int matroxfb_read_gpio(struct matrox_fb_info* minfo) { 428c2ecf20Sopenharmony_ci unsigned long flags; 438c2ecf20Sopenharmony_ci int v; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 468c2ecf20Sopenharmony_ci v = matroxfb_DAC_in(minfo, DAC_XGENIODATA); 478c2ecf20Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 488c2ecf20Sopenharmony_ci return v; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) { 528c2ecf20Sopenharmony_ci unsigned long flags; 538c2ecf20Sopenharmony_ci int v; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 568c2ecf20Sopenharmony_ci v = (matroxfb_DAC_in(minfo, DAC_XGENIOCTRL) & mask) | val; 578c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, v); 588c2ecf20Sopenharmony_ci /* We must reset GENIODATA very often... XFree plays with this register */ 598c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0x00); 608c2ecf20Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* software I2C functions */ 648c2ecf20Sopenharmony_cistatic inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) { 658c2ecf20Sopenharmony_ci if (state) 668c2ecf20Sopenharmony_ci state = 0; 678c2ecf20Sopenharmony_ci else 688c2ecf20Sopenharmony_ci state = mask; 698c2ecf20Sopenharmony_ci matroxfb_set_gpio(minfo, ~mask, state); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void matroxfb_gpio_setsda(void* data, int state) { 738c2ecf20Sopenharmony_ci struct i2c_bit_adapter* b = data; 748c2ecf20Sopenharmony_ci matroxfb_i2c_set(b->minfo, b->mask.data, state); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void matroxfb_gpio_setscl(void* data, int state) { 788c2ecf20Sopenharmony_ci struct i2c_bit_adapter* b = data; 798c2ecf20Sopenharmony_ci matroxfb_i2c_set(b->minfo, b->mask.clock, state); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int matroxfb_gpio_getsda(void* data) { 838c2ecf20Sopenharmony_ci struct i2c_bit_adapter* b = data; 848c2ecf20Sopenharmony_ci return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int matroxfb_gpio_getscl(void* data) { 888c2ecf20Sopenharmony_ci struct i2c_bit_adapter* b = data; 898c2ecf20Sopenharmony_ci return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct i2c_algo_bit_data matrox_i2c_algo_template = 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci .setsda = matroxfb_gpio_setsda, 958c2ecf20Sopenharmony_ci .setscl = matroxfb_gpio_setscl, 968c2ecf20Sopenharmony_ci .getsda = matroxfb_gpio_getsda, 978c2ecf20Sopenharmony_ci .getscl = matroxfb_gpio_getscl, 988c2ecf20Sopenharmony_ci .udelay = 10, 998c2ecf20Sopenharmony_ci .timeout = 100, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, 1038c2ecf20Sopenharmony_ci unsigned int data, unsigned int clock, const char *name, 1048c2ecf20Sopenharmony_ci int class) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int err; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci b->minfo = minfo; 1098c2ecf20Sopenharmony_ci b->mask.data = data; 1108c2ecf20Sopenharmony_ci b->mask.clock = clock; 1118c2ecf20Sopenharmony_ci b->adapter.owner = THIS_MODULE; 1128c2ecf20Sopenharmony_ci snprintf(b->adapter.name, sizeof(b->adapter.name), name, 1138c2ecf20Sopenharmony_ci minfo->fbcon.node); 1148c2ecf20Sopenharmony_ci i2c_set_adapdata(&b->adapter, b); 1158c2ecf20Sopenharmony_ci b->adapter.class = class; 1168c2ecf20Sopenharmony_ci b->adapter.algo_data = &b->bac; 1178c2ecf20Sopenharmony_ci b->adapter.dev.parent = &minfo->pcidev->dev; 1188c2ecf20Sopenharmony_ci b->bac = matrox_i2c_algo_template; 1198c2ecf20Sopenharmony_ci b->bac.data = b; 1208c2ecf20Sopenharmony_ci err = i2c_bit_add_bus(&b->adapter); 1218c2ecf20Sopenharmony_ci b->initialized = !err; 1228c2ecf20Sopenharmony_ci return err; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void i2c_bit_bus_del(struct i2c_bit_adapter* b) { 1268c2ecf20Sopenharmony_ci if (b->initialized) { 1278c2ecf20Sopenharmony_ci i2c_del_adapter(&b->adapter); 1288c2ecf20Sopenharmony_ci b->initialized = 0; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) { 1338c2ecf20Sopenharmony_ci i2c_bit_bus_del(&minfo2->maven); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) { 1378c2ecf20Sopenharmony_ci i2c_bit_bus_del(&minfo2->ddc1); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) { 1418c2ecf20Sopenharmony_ci i2c_bit_bus_del(&minfo2->ddc2); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { 1458c2ecf20Sopenharmony_ci int err; 1468c2ecf20Sopenharmony_ci unsigned long flags; 1478c2ecf20Sopenharmony_ci struct matroxfb_dh_maven_info* m2info; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!m2info) 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci matroxfb_DAC_lock_irqsave(flags); 1548c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIODATA, 0xFF); 1558c2ecf20Sopenharmony_ci matroxfb_DAC_out(minfo, DAC_XGENIOCTRL, 0x00); 1568c2ecf20Sopenharmony_ci matroxfb_DAC_unlock_irqrestore(flags); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci switch (minfo->chip) { 1598c2ecf20Sopenharmony_ci case MGA_2064: 1608c2ecf20Sopenharmony_ci case MGA_2164: 1618c2ecf20Sopenharmony_ci err = i2c_bus_reg(&m2info->ddc1, minfo, 1628c2ecf20Sopenharmony_ci DDC1B_DATA, DDC1B_CLK, 1638c2ecf20Sopenharmony_ci "DDC:fb%u #0", I2C_CLASS_DDC); 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci err = i2c_bus_reg(&m2info->ddc1, minfo, 1678c2ecf20Sopenharmony_ci DDC1_DATA, DDC1_CLK, 1688c2ecf20Sopenharmony_ci "DDC:fb%u #0", I2C_CLASS_DDC); 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci if (err) 1728c2ecf20Sopenharmony_ci goto fail_ddc1; 1738c2ecf20Sopenharmony_ci if (minfo->devflags.dualhead) { 1748c2ecf20Sopenharmony_ci err = i2c_bus_reg(&m2info->ddc2, minfo, 1758c2ecf20Sopenharmony_ci DDC2_DATA, DDC2_CLK, 1768c2ecf20Sopenharmony_ci "DDC:fb%u #1", I2C_CLASS_DDC); 1778c2ecf20Sopenharmony_ci if (err == -ENODEV) { 1788c2ecf20Sopenharmony_ci printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n"); 1798c2ecf20Sopenharmony_ci } else if (err) 1808c2ecf20Sopenharmony_ci printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n"); 1818c2ecf20Sopenharmony_ci /* Register maven bus even on G450/G550 */ 1828c2ecf20Sopenharmony_ci err = i2c_bus_reg(&m2info->maven, minfo, 1838c2ecf20Sopenharmony_ci MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0); 1848c2ecf20Sopenharmony_ci if (err) 1858c2ecf20Sopenharmony_ci printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n"); 1868c2ecf20Sopenharmony_ci else { 1878c2ecf20Sopenharmony_ci struct i2c_board_info maven_info = { 1888c2ecf20Sopenharmony_ci I2C_BOARD_INFO("maven", 0x1b), 1898c2ecf20Sopenharmony_ci }; 1908c2ecf20Sopenharmony_ci unsigned short const addr_list[2] = { 1918c2ecf20Sopenharmony_ci 0x1b, I2C_CLIENT_END 1928c2ecf20Sopenharmony_ci }; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci i2c_new_scanned_device(&m2info->maven.adapter, 1958c2ecf20Sopenharmony_ci &maven_info, addr_list, NULL); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci return m2info; 1998c2ecf20Sopenharmony_cifail_ddc1:; 2008c2ecf20Sopenharmony_ci kfree(m2info); 2018c2ecf20Sopenharmony_ci printk(KERN_ERR "i2c-matroxfb: Could not register primary adapter DDC bus.\n"); 2028c2ecf20Sopenharmony_ci return NULL; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void i2c_matroxfb_remove(struct matrox_fb_info* minfo, void* data) { 2068c2ecf20Sopenharmony_ci struct matroxfb_dh_maven_info* m2info = data; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci i2c_maven_done(m2info); 2098c2ecf20Sopenharmony_ci i2c_ddc2_done(m2info); 2108c2ecf20Sopenharmony_ci i2c_ddc1_done(m2info); 2118c2ecf20Sopenharmony_ci kfree(m2info); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic struct matroxfb_driver i2c_matroxfb = { 2158c2ecf20Sopenharmony_ci .node = LIST_HEAD_INIT(i2c_matroxfb.node), 2168c2ecf20Sopenharmony_ci .name = "i2c-matroxfb", 2178c2ecf20Sopenharmony_ci .probe = i2c_matroxfb_probe, 2188c2ecf20Sopenharmony_ci .remove = i2c_matroxfb_remove, 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int __init i2c_matroxfb_init(void) { 2228c2ecf20Sopenharmony_ci if (matroxfb_register_driver(&i2c_matroxfb)) { 2238c2ecf20Sopenharmony_ci printk(KERN_ERR "i2c-matroxfb: failed to register driver\n"); 2248c2ecf20Sopenharmony_ci return -ENXIO; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void __exit i2c_matroxfb_exit(void) { 2308c2ecf20Sopenharmony_ci matroxfb_unregister_driver(&i2c_matroxfb); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 2348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards"); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cimodule_init(i2c_matroxfb_init); 2378c2ecf20Sopenharmony_cimodule_exit(i2c_matroxfb_exit); 2388c2ecf20Sopenharmony_ci/* no __setup required */ 2398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 240