PageRenderTime 2ms CodeModel.GetById 28ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/gpu/drm/i915/dvo_ch7xxx.c

https://bitbucket.org/cresqo/cm7-p500-kernel
C | 333 lines | 241 code | 58 blank | 34 comment | 28 complexity | d1ba56934be5f9c1ed036c34262fb1d7 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/**************************************************************************
  2
  3Copyright © 2006 Dave Airlie
  4
  5All Rights Reserved.
  6
  7Permission is hereby granted, free of charge, to any person obtaining a
  8copy of this software and associated documentation files (the
  9"Software"), to deal in the Software without restriction, including
 10without limitation the rights to use, copy, modify, merge, publish,
 11distribute, sub license, and/or sell copies of the Software, and to
 12permit persons to whom the Software is furnished to do so, subject to
 13the following conditions:
 14
 15The above copyright notice and this permission notice (including the
 16next paragraph) shall be included in all copies or substantial portions
 17of the Software.
 18
 19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 20OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 23ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 24TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 25SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 26
 27**************************************************************************/
 28
 29#include "dvo.h"
 30
 31#define CH7xxx_REG_VID		0x4a
 32#define CH7xxx_REG_DID		0x4b
 33
 34#define CH7011_VID		0x83 /* 7010 as well */
 35#define CH7009A_VID		0x84
 36#define CH7009B_VID		0x85
 37#define CH7301_VID		0x95
 38
 39#define CH7xxx_VID		0x84
 40#define CH7xxx_DID		0x17
 41
 42#define CH7xxx_NUM_REGS		0x4c
 43
 44#define CH7xxx_CM		0x1c
 45#define CH7xxx_CM_XCM		(1<<0)
 46#define CH7xxx_CM_MCP		(1<<2)
 47#define CH7xxx_INPUT_CLOCK	0x1d
 48#define CH7xxx_GPIO		0x1e
 49#define CH7xxx_GPIO_HPIR	(1<<3)
 50#define CH7xxx_IDF		0x1f
 51
 52#define CH7xxx_IDF_HSP		(1<<3)
 53#define CH7xxx_IDF_VSP		(1<<4)
 54
 55#define CH7xxx_CONNECTION_DETECT 0x20
 56#define CH7xxx_CDET_DVI		(1<<5)
 57
 58#define CH7301_DAC_CNTL		0x21
 59#define CH7301_HOTPLUG		0x23
 60#define CH7xxx_TCTL		0x31
 61#define CH7xxx_TVCO		0x32
 62#define CH7xxx_TPCP		0x33
 63#define CH7xxx_TPD		0x34
 64#define CH7xxx_TPVT		0x35
 65#define CH7xxx_TLPF		0x36
 66#define CH7xxx_TCT		0x37
 67#define CH7301_TEST_PATTERN	0x48
 68
 69#define CH7xxx_PM		0x49
 70#define CH7xxx_PM_FPD		(1<<0)
 71#define CH7301_PM_DACPD0	(1<<1)
 72#define CH7301_PM_DACPD1	(1<<2)
 73#define CH7301_PM_DACPD2	(1<<3)
 74#define CH7xxx_PM_DVIL		(1<<6)
 75#define CH7xxx_PM_DVIP		(1<<7)
 76
 77#define CH7301_SYNC_POLARITY	0x56
 78#define CH7301_SYNC_RGB_YUV	(1<<0)
 79#define CH7301_SYNC_POL_DVI	(1<<5)
 80
 81/** @file
 82 * driver for the Chrontel 7xxx DVI chip over DVO.
 83 */
 84
 85static struct ch7xxx_id_struct {
 86	uint8_t vid;
 87	char *name;
 88} ch7xxx_ids[] = {
 89	{ CH7011_VID, "CH7011" },
 90	{ CH7009A_VID, "CH7009A" },
 91	{ CH7009B_VID, "CH7009B" },
 92	{ CH7301_VID, "CH7301" },
 93};
 94
 95struct ch7xxx_priv {
 96	bool quiet;
 97};
 98
 99static char *ch7xxx_get_id(uint8_t vid)
100{
101	int i;
102
103	for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
104		if (ch7xxx_ids[i].vid == vid)
105			return ch7xxx_ids[i].name;
106	}
107
108	return NULL;
109}
110
111/** Reads an 8 bit register */
112static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
113{
114	struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
115	struct i2c_adapter *adapter = dvo->i2c_bus;
116	struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
117	u8 out_buf[2];
118	u8 in_buf[2];
119
120	struct i2c_msg msgs[] = {
121		{
122			.addr = dvo->slave_addr,
123			.flags = 0,
124			.len = 1,
125			.buf = out_buf,
126		},
127		{
128			.addr = dvo->slave_addr,
129			.flags = I2C_M_RD,
130			.len = 1,
131			.buf = in_buf,
132		}
133	};
134
135	out_buf[0] = addr;
136	out_buf[1] = 0;
137
138	if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
139		*ch = in_buf[0];
140		return true;
141	};
142
143	if (!ch7xxx->quiet) {
144		DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
145			  addr, i2cbus->adapter.name, dvo->slave_addr);
146	}
147	return false;
148}
149
150/** Writes an 8 bit register */
151static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
152{
153	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
154	struct i2c_adapter *adapter = dvo->i2c_bus;
155	struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
156	uint8_t out_buf[2];
157	struct i2c_msg msg = {
158		.addr = dvo->slave_addr,
159		.flags = 0,
160		.len = 2,
161		.buf = out_buf,
162	};
163
164	out_buf[0] = addr;
165	out_buf[1] = ch;
166
167	if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
168		return true;
169
170	if (!ch7xxx->quiet) {
171		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
172			  addr, i2cbus->adapter.name, dvo->slave_addr);
173	}
174
175	return false;
176}
177
178static bool ch7xxx_init(struct intel_dvo_device *dvo,
179			struct i2c_adapter *adapter)
180{
181	/* this will detect the CH7xxx chip on the specified i2c bus */
182	struct ch7xxx_priv *ch7xxx;
183	uint8_t vendor, device;
184	char *name;
185
186	ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
187	if (ch7xxx == NULL)
188		return false;
189
190	dvo->i2c_bus = adapter;
191	dvo->dev_priv = ch7xxx;
192	ch7xxx->quiet = true;
193
194	if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
195		goto out;
196
197	name = ch7xxx_get_id(vendor);
198	if (!name) {
199		DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
200				"slave %d.\n",
201			  vendor, adapter->name, dvo->slave_addr);
202		goto out;
203	}
204
205
206	if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
207		goto out;
208
209	if (device != CH7xxx_DID) {
210		DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
211				"slave %d.\n",
212			  vendor, adapter->name, dvo->slave_addr);
213		goto out;
214	}
215
216	ch7xxx->quiet = false;
217	DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
218		  name, vendor, device);
219	return true;
220out:
221	kfree(ch7xxx);
222	return false;
223}
224
225static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
226{
227	uint8_t cdet, orig_pm, pm;
228
229	ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
230
231	pm = orig_pm;
232	pm &= ~CH7xxx_PM_FPD;
233	pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
234
235	ch7xxx_writeb(dvo, CH7xxx_PM, pm);
236
237	ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
238
239	ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
240
241	if (cdet & CH7xxx_CDET_DVI)
242		return connector_status_connected;
243	return connector_status_disconnected;
244}
245
246static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
247					      struct drm_display_mode *mode)
248{
249	if (mode->clock > 165000)
250		return MODE_CLOCK_HIGH;
251
252	return MODE_OK;
253}
254
255static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
256			    struct drm_display_mode *mode,
257			    struct drm_display_mode *adjusted_mode)
258{
259	uint8_t tvco, tpcp, tpd, tlpf, idf;
260
261	if (mode->clock <= 65000) {
262		tvco = 0x23;
263		tpcp = 0x08;
264		tpd = 0x16;
265		tlpf = 0x60;
266	} else {
267		tvco = 0x2d;
268		tpcp = 0x06;
269		tpd = 0x26;
270		tlpf = 0xa0;
271	}
272
273	ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
274	ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
275	ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
276	ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
277	ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
278	ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
279	ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
280
281	ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
282
283	idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
284	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
285		idf |= CH7xxx_IDF_HSP;
286
287	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
288		idf |= CH7xxx_IDF_HSP;
289
290	ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
291}
292
293/* set the CH7xxx power state */
294static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
295{
296	if (mode == DRM_MODE_DPMS_ON)
297		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
298	else
299		ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
300}
301
302static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
303{
304	int i;
305
306	for (i = 0; i < CH7xxx_NUM_REGS; i++) {
307		uint8_t val;
308		if ((i % 8) == 0 )
309			DRM_LOG_KMS("\n %02X: ", i);
310		ch7xxx_readb(dvo, i, &val);
311		DRM_LOG_KMS("%02X ", val);
312	}
313}
314
315static void ch7xxx_destroy(struct intel_dvo_device *dvo)
316{
317	struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
318
319	if (ch7xxx) {
320		kfree(ch7xxx);
321		dvo->dev_priv = NULL;
322	}
323}
324
325struct intel_dvo_dev_ops ch7xxx_ops = {
326	.init = ch7xxx_init,
327	.detect = ch7xxx_detect,
328	.mode_valid = ch7xxx_mode_valid,
329	.mode_set = ch7xxx_mode_set,
330	.dpms = ch7xxx_dpms,
331	.dump_regs = ch7xxx_dump_regs,
332	.destroy = ch7xxx_destroy,
333};