Subversion Repositories Kolibri OS

Rev

Rev 6084 | Details | Compare with Previous | Last modification | View Log | RSS feed

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