18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2002-2010, Intel Corporation. 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 ATRON electronic GmbH 48c2ecf20Sopenharmony_ci * Author: Jan Safrata <jan.nikitenko@gmail.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy 78c2ecf20Sopenharmony_ci * of this software and associated documentation files (the "Software"), to deal 88c2ecf20Sopenharmony_ci * in the Software without restriction, including without limitation the rights 98c2ecf20Sopenharmony_ci * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 108c2ecf20Sopenharmony_ci * copies of the Software, and to permit persons to whom the Software is 118c2ecf20Sopenharmony_ci * furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 148c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 198c2ecf20Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 208c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 218c2ecf20Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 228c2ecf20Sopenharmony_ci * THE SOFTWARE. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 288c2ecf20Sopenharmony_ci#include <linux/i2c.h> 298c2ecf20Sopenharmony_ci#include <linux/init.h> 308c2ecf20Sopenharmony_ci#include <linux/io.h> 318c2ecf20Sopenharmony_ci#include <linux/kernel.h> 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/pci.h> 348c2ecf20Sopenharmony_ci#include <linux/types.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "psb_drv.h" 378c2ecf20Sopenharmony_ci#include "psb_intel_reg.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * LPC GPIO based I2C bus for LVDS of Atom E6xx 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------------- 458c2ecf20Sopenharmony_ci * LPC Register Offsets. Used for LVDS GPIO Bit Bashing. Registers are part 468c2ecf20Sopenharmony_ci * Atom E6xx [D31:F0] 478c2ecf20Sopenharmony_ci ----------------------------------------------------------------------------*/ 488c2ecf20Sopenharmony_ci#define RGEN 0x20 498c2ecf20Sopenharmony_ci#define RGIO 0x24 508c2ecf20Sopenharmony_ci#define RGLVL 0x28 518c2ecf20Sopenharmony_ci#define RGTPE 0x2C 528c2ecf20Sopenharmony_ci#define RGTNE 0x30 538c2ecf20Sopenharmony_ci#define RGGPE 0x34 548c2ecf20Sopenharmony_ci#define RGSMI 0x38 558c2ecf20Sopenharmony_ci#define RGTS 0x3C 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* The LVDS GPIO clock lines are GPIOSUS[3] 588c2ecf20Sopenharmony_ci * The LVDS GPIO data lines are GPIOSUS[4] 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#define GPIO_CLOCK 0x08 618c2ecf20Sopenharmony_ci#define GPIO_DATA 0x10 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define LPC_READ_REG(chan, r) inl((chan)->reg + (r)) 648c2ecf20Sopenharmony_ci#define LPC_WRITE_REG(chan, r, val) outl((val), (chan)->reg + (r)) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int get_clock(void *data) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct psb_intel_i2c_chan *chan = data; 698c2ecf20Sopenharmony_ci u32 val, tmp; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGIO); 728c2ecf20Sopenharmony_ci val |= GPIO_CLOCK; 738c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGIO, val); 748c2ecf20Sopenharmony_ci tmp = LPC_READ_REG(chan, RGLVL); 758c2ecf20Sopenharmony_ci val = (LPC_READ_REG(chan, RGLVL) & GPIO_CLOCK) ? 1 : 0; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return val; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int get_data(void *data) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct psb_intel_i2c_chan *chan = data; 838c2ecf20Sopenharmony_ci u32 val, tmp; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGIO); 868c2ecf20Sopenharmony_ci val |= GPIO_DATA; 878c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGIO, val); 888c2ecf20Sopenharmony_ci tmp = LPC_READ_REG(chan, RGLVL); 898c2ecf20Sopenharmony_ci val = (LPC_READ_REG(chan, RGLVL) & GPIO_DATA) ? 1 : 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return val; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void set_clock(void *data, int state_high) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct psb_intel_i2c_chan *chan = data; 978c2ecf20Sopenharmony_ci u32 val; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (state_high) { 1008c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGIO); 1018c2ecf20Sopenharmony_ci val |= GPIO_CLOCK; 1028c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGIO, val); 1038c2ecf20Sopenharmony_ci } else { 1048c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGIO); 1058c2ecf20Sopenharmony_ci val &= ~GPIO_CLOCK; 1068c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGIO, val); 1078c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGLVL); 1088c2ecf20Sopenharmony_ci val &= ~GPIO_CLOCK; 1098c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGLVL, val); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void set_data(void *data, int state_high) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct psb_intel_i2c_chan *chan = data; 1168c2ecf20Sopenharmony_ci u32 val; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (state_high) { 1198c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGIO); 1208c2ecf20Sopenharmony_ci val |= GPIO_DATA; 1218c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGIO, val); 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGIO); 1248c2ecf20Sopenharmony_ci val &= ~GPIO_DATA; 1258c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGIO, val); 1268c2ecf20Sopenharmony_ci val = LPC_READ_REG(chan, RGLVL); 1278c2ecf20Sopenharmony_ci val &= ~GPIO_DATA; 1288c2ecf20Sopenharmony_ci LPC_WRITE_REG(chan, RGLVL, val); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid oaktrail_lvds_i2c_init(struct drm_encoder *encoder) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 1358c2ecf20Sopenharmony_ci struct gma_encoder *gma_encoder = to_gma_encoder(encoder); 1368c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv = dev->dev_private; 1378c2ecf20Sopenharmony_ci struct psb_intel_i2c_chan *chan; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!chan) 1418c2ecf20Sopenharmony_ci return; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci chan->drm_dev = dev; 1448c2ecf20Sopenharmony_ci chan->reg = dev_priv->lpc_gpio_base; 1458c2ecf20Sopenharmony_ci strncpy(chan->adapter.name, "gma500 LPC", I2C_NAME_SIZE - 1); 1468c2ecf20Sopenharmony_ci chan->adapter.owner = THIS_MODULE; 1478c2ecf20Sopenharmony_ci chan->adapter.algo_data = &chan->algo; 1488c2ecf20Sopenharmony_ci chan->adapter.dev.parent = &dev->pdev->dev; 1498c2ecf20Sopenharmony_ci chan->algo.setsda = set_data; 1508c2ecf20Sopenharmony_ci chan->algo.setscl = set_clock; 1518c2ecf20Sopenharmony_ci chan->algo.getsda = get_data; 1528c2ecf20Sopenharmony_ci chan->algo.getscl = get_clock; 1538c2ecf20Sopenharmony_ci chan->algo.udelay = 100; 1548c2ecf20Sopenharmony_ci chan->algo.timeout = usecs_to_jiffies(2200); 1558c2ecf20Sopenharmony_ci chan->algo.data = chan; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci i2c_set_adapdata(&chan->adapter, chan); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci set_data(chan, 1); 1608c2ecf20Sopenharmony_ci set_clock(chan, 1); 1618c2ecf20Sopenharmony_ci udelay(50); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (i2c_bit_add_bus(&chan->adapter)) { 1648c2ecf20Sopenharmony_ci kfree(chan); 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci gma_encoder->ddc_bus = chan; 1698c2ecf20Sopenharmony_ci} 170