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, &reg9);
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