18c2ecf20Sopenharmony_ci/************************************************************************** 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ciCopyright © 2006 Dave Airlie 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 98c2ecf20Sopenharmony_ci"Software"), to deal in the Software without restriction, including 108c2ecf20Sopenharmony_ciwithout limitation the rights to use, copy, modify, merge, publish, 118c2ecf20Sopenharmony_cidistribute, sub license, and/or sell copies of the Software, and to 128c2ecf20Sopenharmony_cipermit persons to whom the Software is furnished to do so, subject to 138c2ecf20Sopenharmony_cithe following conditions: 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciThe above copyright notice and this permission notice (including the 168c2ecf20Sopenharmony_cinext paragraph) shall be included in all copies or substantial portions 178c2ecf20Sopenharmony_ciof the Software. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 208c2ecf20Sopenharmony_ciOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 218c2ecf20Sopenharmony_ciMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 228c2ecf20Sopenharmony_ciIN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 238c2ecf20Sopenharmony_ciANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 248c2ecf20Sopenharmony_ciTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 258c2ecf20Sopenharmony_ciSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci**************************************************************************/ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "intel_display_types.h" 308c2ecf20Sopenharmony_ci#include "intel_dvo_dev.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define SIL164_VID 0x0001 338c2ecf20Sopenharmony_ci#define SIL164_DID 0x0006 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define SIL164_VID_LO 0x00 368c2ecf20Sopenharmony_ci#define SIL164_VID_HI 0x01 378c2ecf20Sopenharmony_ci#define SIL164_DID_LO 0x02 388c2ecf20Sopenharmony_ci#define SIL164_DID_HI 0x03 398c2ecf20Sopenharmony_ci#define SIL164_REV 0x04 408c2ecf20Sopenharmony_ci#define SIL164_RSVD 0x05 418c2ecf20Sopenharmony_ci#define SIL164_FREQ_LO 0x06 428c2ecf20Sopenharmony_ci#define SIL164_FREQ_HI 0x07 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define SIL164_REG8 0x08 458c2ecf20Sopenharmony_ci#define SIL164_8_VEN (1<<5) 468c2ecf20Sopenharmony_ci#define SIL164_8_HEN (1<<4) 478c2ecf20Sopenharmony_ci#define SIL164_8_DSEL (1<<3) 488c2ecf20Sopenharmony_ci#define SIL164_8_BSEL (1<<2) 498c2ecf20Sopenharmony_ci#define SIL164_8_EDGE (1<<1) 508c2ecf20Sopenharmony_ci#define SIL164_8_PD (1<<0) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define SIL164_REG9 0x09 538c2ecf20Sopenharmony_ci#define SIL164_9_VLOW (1<<7) 548c2ecf20Sopenharmony_ci#define SIL164_9_MSEL_MASK (0x7<<4) 558c2ecf20Sopenharmony_ci#define SIL164_9_TSEL (1<<3) 568c2ecf20Sopenharmony_ci#define SIL164_9_RSEN (1<<2) 578c2ecf20Sopenharmony_ci#define SIL164_9_HTPLG (1<<1) 588c2ecf20Sopenharmony_ci#define SIL164_9_MDI (1<<0) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define SIL164_REGC 0x0c 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct sil164_priv { 638c2ecf20Sopenharmony_ci //I2CDevRec d; 648c2ecf20Sopenharmony_ci bool quiet; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr)) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic bool sil164_readb(struct intel_dvo_device *dvo, int addr, u8 *ch) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct sil164_priv *sil = dvo->dev_priv; 728c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = dvo->i2c_bus; 738c2ecf20Sopenharmony_ci u8 out_buf[2]; 748c2ecf20Sopenharmony_ci u8 in_buf[2]; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 778c2ecf20Sopenharmony_ci { 788c2ecf20Sopenharmony_ci .addr = dvo->slave_addr, 798c2ecf20Sopenharmony_ci .flags = 0, 808c2ecf20Sopenharmony_ci .len = 1, 818c2ecf20Sopenharmony_ci .buf = out_buf, 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci { 848c2ecf20Sopenharmony_ci .addr = dvo->slave_addr, 858c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 868c2ecf20Sopenharmony_ci .len = 1, 878c2ecf20Sopenharmony_ci .buf = in_buf, 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci }; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci out_buf[0] = addr; 928c2ecf20Sopenharmony_ci out_buf[1] = 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (i2c_transfer(adapter, msgs, 2) == 2) { 958c2ecf20Sopenharmony_ci *ch = in_buf[0]; 968c2ecf20Sopenharmony_ci return true; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!sil->quiet) { 1008c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n", 1018c2ecf20Sopenharmony_ci addr, adapter->name, dvo->slave_addr); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci return false; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic bool sil164_writeb(struct intel_dvo_device *dvo, int addr, u8 ch) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct sil164_priv *sil = dvo->dev_priv; 1098c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = dvo->i2c_bus; 1108c2ecf20Sopenharmony_ci u8 out_buf[2]; 1118c2ecf20Sopenharmony_ci struct i2c_msg msg = { 1128c2ecf20Sopenharmony_ci .addr = dvo->slave_addr, 1138c2ecf20Sopenharmony_ci .flags = 0, 1148c2ecf20Sopenharmony_ci .len = 2, 1158c2ecf20Sopenharmony_ci .buf = out_buf, 1168c2ecf20Sopenharmony_ci }; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci out_buf[0] = addr; 1198c2ecf20Sopenharmony_ci out_buf[1] = ch; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (i2c_transfer(adapter, &msg, 1) == 1) 1228c2ecf20Sopenharmony_ci return true; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (!sil->quiet) { 1258c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n", 1268c2ecf20Sopenharmony_ci addr, adapter->name, dvo->slave_addr); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return false; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* Silicon Image 164 driver for chip on i2c bus */ 1338c2ecf20Sopenharmony_cistatic bool sil164_init(struct intel_dvo_device *dvo, 1348c2ecf20Sopenharmony_ci struct i2c_adapter *adapter) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci /* this will detect the SIL164 chip on the specified i2c bus */ 1378c2ecf20Sopenharmony_ci struct sil164_priv *sil; 1388c2ecf20Sopenharmony_ci unsigned char ch; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL); 1418c2ecf20Sopenharmony_ci if (sil == NULL) 1428c2ecf20Sopenharmony_ci return false; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci dvo->i2c_bus = adapter; 1458c2ecf20Sopenharmony_ci dvo->dev_priv = sil; 1468c2ecf20Sopenharmony_ci sil->quiet = true; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (!sil164_readb(dvo, SIL164_VID_LO, &ch)) 1498c2ecf20Sopenharmony_ci goto out; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (ch != (SIL164_VID & 0xff)) { 1528c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n", 1538c2ecf20Sopenharmony_ci ch, adapter->name, dvo->slave_addr); 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!sil164_readb(dvo, SIL164_DID_LO, &ch)) 1588c2ecf20Sopenharmony_ci goto out; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (ch != (SIL164_DID & 0xff)) { 1618c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n", 1628c2ecf20Sopenharmony_ci ch, adapter->name, dvo->slave_addr); 1638c2ecf20Sopenharmony_ci goto out; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci sil->quiet = false; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("init sil164 dvo controller successfully!\n"); 1688c2ecf20Sopenharmony_ci return true; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciout: 1718c2ecf20Sopenharmony_ci kfree(sil); 1728c2ecf20Sopenharmony_ci return false; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci u8 reg9; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci sil164_readb(dvo, SIL164_REG9, ®9); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (reg9 & SIL164_9_HTPLG) 1828c2ecf20Sopenharmony_ci return connector_status_connected; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci return connector_status_disconnected; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo, 1888c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci return MODE_OK; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void sil164_mode_set(struct intel_dvo_device *dvo, 1948c2ecf20Sopenharmony_ci const struct drm_display_mode *mode, 1958c2ecf20Sopenharmony_ci const struct drm_display_mode *adjusted_mode) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci /* As long as the basics are set up, since we don't have clock 1988c2ecf20Sopenharmony_ci * dependencies in the mode setup, we can just leave the 1998c2ecf20Sopenharmony_ci * registers alone and everything will work fine. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci /* recommended programming sequence from doc */ 2028c2ecf20Sopenharmony_ci /*sil164_writeb(sil, 0x08, 0x30); 2038c2ecf20Sopenharmony_ci sil164_writeb(sil, 0x09, 0x00); 2048c2ecf20Sopenharmony_ci sil164_writeb(sil, 0x0a, 0x90); 2058c2ecf20Sopenharmony_ci sil164_writeb(sil, 0x0c, 0x89); 2068c2ecf20Sopenharmony_ci sil164_writeb(sil, 0x08, 0x31);*/ 2078c2ecf20Sopenharmony_ci /* don't do much */ 2088c2ecf20Sopenharmony_ci return; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* set the SIL164 power state */ 2128c2ecf20Sopenharmony_cistatic void sil164_dpms(struct intel_dvo_device *dvo, bool enable) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int ret; 2158c2ecf20Sopenharmony_ci unsigned char ch; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = sil164_readb(dvo, SIL164_REG8, &ch); 2188c2ecf20Sopenharmony_ci if (ret == false) 2198c2ecf20Sopenharmony_ci return; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (enable) 2228c2ecf20Sopenharmony_ci ch |= SIL164_8_PD; 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci ch &= ~SIL164_8_PD; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci sil164_writeb(dvo, SIL164_REG8, ch); 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic bool sil164_get_hw_state(struct intel_dvo_device *dvo) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci int ret; 2338c2ecf20Sopenharmony_ci unsigned char ch; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = sil164_readb(dvo, SIL164_REG8, &ch); 2368c2ecf20Sopenharmony_ci if (ret == false) 2378c2ecf20Sopenharmony_ci return false; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (ch & SIL164_8_PD) 2408c2ecf20Sopenharmony_ci return true; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci return false; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void sil164_dump_regs(struct intel_dvo_device *dvo) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci u8 val; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci sil164_readb(dvo, SIL164_FREQ_LO, &val); 2508c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SIL164_FREQ_LO: 0x%02x\n", val); 2518c2ecf20Sopenharmony_ci sil164_readb(dvo, SIL164_FREQ_HI, &val); 2528c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SIL164_FREQ_HI: 0x%02x\n", val); 2538c2ecf20Sopenharmony_ci sil164_readb(dvo, SIL164_REG8, &val); 2548c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SIL164_REG8: 0x%02x\n", val); 2558c2ecf20Sopenharmony_ci sil164_readb(dvo, SIL164_REG9, &val); 2568c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SIL164_REG9: 0x%02x\n", val); 2578c2ecf20Sopenharmony_ci sil164_readb(dvo, SIL164_REGC, &val); 2588c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("SIL164_REGC: 0x%02x\n", val); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void sil164_destroy(struct intel_dvo_device *dvo) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct sil164_priv *sil = dvo->dev_priv; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (sil) { 2668c2ecf20Sopenharmony_ci kfree(sil); 2678c2ecf20Sopenharmony_ci dvo->dev_priv = NULL; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ciconst struct intel_dvo_dev_ops sil164_ops = { 2728c2ecf20Sopenharmony_ci .init = sil164_init, 2738c2ecf20Sopenharmony_ci .detect = sil164_detect, 2748c2ecf20Sopenharmony_ci .mode_valid = sil164_mode_valid, 2758c2ecf20Sopenharmony_ci .mode_set = sil164_mode_set, 2768c2ecf20Sopenharmony_ci .dpms = sil164_dpms, 2778c2ecf20Sopenharmony_ci .get_hw_state = sil164_get_hw_state, 2788c2ecf20Sopenharmony_ci .dump_regs = sil164_dump_regs, 2798c2ecf20Sopenharmony_ci .destroy = sil164_destroy, 2808c2ecf20Sopenharmony_ci}; 281