18c2ecf20Sopenharmony_ci/************************************************************************** 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci Copyright 2006 Dave Airlie <airlied@linux.ie> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ciAll Rights Reserved. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ciPermission is hereby granted, free of charge, to any person obtaining a 88c2ecf20Sopenharmony_cicopy of this software and associated documentation files (the "Software"), 98c2ecf20Sopenharmony_cito deal in the Software without restriction, including without limitation 108c2ecf20Sopenharmony_cion the rights to use, copy, modify, merge, publish, distribute, sub 118c2ecf20Sopenharmony_cilicense, and/or sell copies of the Software, and to permit persons to whom 128c2ecf20Sopenharmony_cithe Software is furnished to do so, subject to the following conditions: 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ciThe above copyright notice and this permission notice (including the next 158c2ecf20Sopenharmony_ciparagraph) shall be included in all copies or substantial portions of the 168c2ecf20Sopenharmony_ciSoftware. 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 198c2ecf20Sopenharmony_ciIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 208c2ecf20Sopenharmony_ciFITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 218c2ecf20Sopenharmony_ciTHE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 228c2ecf20Sopenharmony_ciDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 238c2ecf20Sopenharmony_ciOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 248c2ecf20Sopenharmony_ciUSE OR OTHER DEALINGS IN THE SOFTWARE. 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci**************************************************************************/ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/kernel.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/pci.h> 328c2ecf20Sopenharmony_ci#include <linux/fb.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/i2c.h> 358c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <asm/io.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "intelfb.h" 408c2ecf20Sopenharmony_ci#include "intelfbhw.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* bit locations in the registers */ 438c2ecf20Sopenharmony_ci#define SCL_DIR_MASK 0x0001 448c2ecf20Sopenharmony_ci#define SCL_DIR 0x0002 458c2ecf20Sopenharmony_ci#define SCL_VAL_MASK 0x0004 468c2ecf20Sopenharmony_ci#define SCL_VAL_OUT 0x0008 478c2ecf20Sopenharmony_ci#define SCL_VAL_IN 0x0010 488c2ecf20Sopenharmony_ci#define SDA_DIR_MASK 0x0100 498c2ecf20Sopenharmony_ci#define SDA_DIR 0x0200 508c2ecf20Sopenharmony_ci#define SDA_VAL_MASK 0x0400 518c2ecf20Sopenharmony_ci#define SDA_VAL_OUT 0x0800 528c2ecf20Sopenharmony_ci#define SDA_VAL_IN 0x1000 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void intelfb_gpio_setscl(void *data, int state) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct intelfb_i2c_chan *chan = data; 578c2ecf20Sopenharmony_ci struct intelfb_info *dinfo = chan->dinfo; 588c2ecf20Sopenharmony_ci u32 val; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | 618c2ecf20Sopenharmony_ci SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK); 628c2ecf20Sopenharmony_ci val = INREG(chan->reg); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void intelfb_gpio_setsda(void *data, int state) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct intelfb_i2c_chan *chan = data; 688c2ecf20Sopenharmony_ci struct intelfb_info *dinfo = chan->dinfo; 698c2ecf20Sopenharmony_ci u32 val; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | 728c2ecf20Sopenharmony_ci SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK); 738c2ecf20Sopenharmony_ci val = INREG(chan->reg); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int intelfb_gpio_getscl(void *data) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct intelfb_i2c_chan *chan = data; 798c2ecf20Sopenharmony_ci struct intelfb_info *dinfo = chan->dinfo; 808c2ecf20Sopenharmony_ci u32 val; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci OUTREG(chan->reg, SCL_DIR_MASK); 838c2ecf20Sopenharmony_ci OUTREG(chan->reg, 0); 848c2ecf20Sopenharmony_ci val = INREG(chan->reg); 858c2ecf20Sopenharmony_ci return ((val & SCL_VAL_IN) != 0); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int intelfb_gpio_getsda(void *data) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct intelfb_i2c_chan *chan = data; 918c2ecf20Sopenharmony_ci struct intelfb_info *dinfo = chan->dinfo; 928c2ecf20Sopenharmony_ci u32 val; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci OUTREG(chan->reg, SDA_DIR_MASK); 958c2ecf20Sopenharmony_ci OUTREG(chan->reg, 0); 968c2ecf20Sopenharmony_ci val = INREG(chan->reg); 978c2ecf20Sopenharmony_ci return ((val & SDA_VAL_IN) != 0); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, 1018c2ecf20Sopenharmony_ci struct intelfb_i2c_chan *chan, 1028c2ecf20Sopenharmony_ci const u32 reg, const char *name, 1038c2ecf20Sopenharmony_ci int class) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int rc; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci chan->dinfo = dinfo; 1088c2ecf20Sopenharmony_ci chan->reg = reg; 1098c2ecf20Sopenharmony_ci snprintf(chan->adapter.name, sizeof(chan->adapter.name), 1108c2ecf20Sopenharmony_ci "intelfb %s", name); 1118c2ecf20Sopenharmony_ci chan->adapter.class = class; 1128c2ecf20Sopenharmony_ci chan->adapter.owner = THIS_MODULE; 1138c2ecf20Sopenharmony_ci chan->adapter.algo_data = &chan->algo; 1148c2ecf20Sopenharmony_ci chan->adapter.dev.parent = &chan->dinfo->pdev->dev; 1158c2ecf20Sopenharmony_ci chan->algo.setsda = intelfb_gpio_setsda; 1168c2ecf20Sopenharmony_ci chan->algo.setscl = intelfb_gpio_setscl; 1178c2ecf20Sopenharmony_ci chan->algo.getsda = intelfb_gpio_getsda; 1188c2ecf20Sopenharmony_ci chan->algo.getscl = intelfb_gpio_getscl; 1198c2ecf20Sopenharmony_ci chan->algo.udelay = 40; 1208c2ecf20Sopenharmony_ci chan->algo.timeout = 20; 1218c2ecf20Sopenharmony_ci chan->algo.data = chan; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci i2c_set_adapdata(&chan->adapter, chan); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Raise SCL and SDA */ 1268c2ecf20Sopenharmony_ci intelfb_gpio_setsda(chan, 1); 1278c2ecf20Sopenharmony_ci intelfb_gpio_setscl(chan, 1); 1288c2ecf20Sopenharmony_ci udelay(20); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rc = i2c_bit_add_bus(&chan->adapter); 1318c2ecf20Sopenharmony_ci if (rc == 0) 1328c2ecf20Sopenharmony_ci DBG_MSG("I2C bus %s registered.\n", name); 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci WRN_MSG("Failed to register I2C bus %s.\n", name); 1358c2ecf20Sopenharmony_ci return rc; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_civoid intelfb_create_i2c_busses(struct intelfb_info *dinfo) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int i = 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* everyone has at least a single analog output */ 1438c2ecf20Sopenharmony_ci dinfo->num_outputs = 1; 1448c2ecf20Sopenharmony_ci dinfo->output[i].type = INTELFB_OUTPUT_ANALOG; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* setup the DDC bus for analog output */ 1478c2ecf20Sopenharmony_ci intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, 1488c2ecf20Sopenharmony_ci "CRTDDC_A", I2C_CLASS_DDC); 1498c2ecf20Sopenharmony_ci i++; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* need to add the output busses for each device 1528c2ecf20Sopenharmony_ci - this function is very incomplete 1538c2ecf20Sopenharmony_ci - i915GM has LVDS and TVOUT for example 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci switch(dinfo->chipset) { 1568c2ecf20Sopenharmony_ci case INTEL_830M: 1578c2ecf20Sopenharmony_ci case INTEL_845G: 1588c2ecf20Sopenharmony_ci case INTEL_854: 1598c2ecf20Sopenharmony_ci case INTEL_855GM: 1608c2ecf20Sopenharmony_ci case INTEL_865G: 1618c2ecf20Sopenharmony_ci dinfo->output[i].type = INTELFB_OUTPUT_DVO; 1628c2ecf20Sopenharmony_ci intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, 1638c2ecf20Sopenharmony_ci GPIOD, "DVODDC_D", I2C_CLASS_DDC); 1648c2ecf20Sopenharmony_ci intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, 1658c2ecf20Sopenharmony_ci GPIOE, "DVOI2C_E", 0); 1668c2ecf20Sopenharmony_ci i++; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case INTEL_915G: 1698c2ecf20Sopenharmony_ci case INTEL_915GM: 1708c2ecf20Sopenharmony_ci /* has some LVDS + tv-out */ 1718c2ecf20Sopenharmony_ci case INTEL_945G: 1728c2ecf20Sopenharmony_ci case INTEL_945GM: 1738c2ecf20Sopenharmony_ci case INTEL_945GME: 1748c2ecf20Sopenharmony_ci case INTEL_965G: 1758c2ecf20Sopenharmony_ci case INTEL_965GM: 1768c2ecf20Sopenharmony_ci /* SDVO ports have a single control bus - 2 devices */ 1778c2ecf20Sopenharmony_ci dinfo->output[i].type = INTELFB_OUTPUT_SDVO; 1788c2ecf20Sopenharmony_ci intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, 1798c2ecf20Sopenharmony_ci GPIOE, "SDVOCTRL_E", 0); 1808c2ecf20Sopenharmony_ci /* TODO: initialize the SDVO */ 1818c2ecf20Sopenharmony_ci /* I830SDVOInit(pScrn, i, DVOB); */ 1828c2ecf20Sopenharmony_ci i++; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* set up SDVOC */ 1858c2ecf20Sopenharmony_ci dinfo->output[i].type = INTELFB_OUTPUT_SDVO; 1868c2ecf20Sopenharmony_ci dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus; 1878c2ecf20Sopenharmony_ci /* TODO: initialize the SDVO */ 1888c2ecf20Sopenharmony_ci /* I830SDVOInit(pScrn, i, DVOC); */ 1898c2ecf20Sopenharmony_ci i++; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci dinfo->num_outputs = i; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid intelfb_delete_i2c_busses(struct intelfb_info *dinfo) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci int i; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci for (i = 0; i < MAX_OUTPUTS; i++) { 2008c2ecf20Sopenharmony_ci if (dinfo->output[i].i2c_bus.dinfo) { 2018c2ecf20Sopenharmony_ci i2c_del_adapter(&dinfo->output[i].i2c_bus.adapter); 2028c2ecf20Sopenharmony_ci dinfo->output[i].i2c_bus.dinfo = NULL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci if (dinfo->output[i].ddc_bus.dinfo) { 2058c2ecf20Sopenharmony_ci i2c_del_adapter(&dinfo->output[i].ddc_bus.adapter); 2068c2ecf20Sopenharmony_ci dinfo->output[i].ddc_bus.dinfo = NULL; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci} 210