Subversion Repositories Kolibri OS

Rev

Rev 2330 | Rev 3031 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2330 Serge 1
/*
2
 * Copyright © 2006 Intel Corporation
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a
5
 * copy of this software and associated documentation files (the "Software"),
6
 * to deal in the Software without restriction, including without limitation
7
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
 * and/or sell copies of the Software, and to permit persons to whom the
9
 * Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice (including the next
12
 * paragraph) shall be included in all copies or substantial portions of the
13
 * Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 *
23
 * Authors:
24
 *    Eric Anholt 
25
 *
26
 */
27
 
28
#include "dvo.h"
29
 
30
/*
31
 * register definitions for the i82807aa.
32
 *
33
 * Documentation on this chipset can be found in datasheet #29069001 at
34
 * intel.com.
35
 */
36
 
37
/*
38
 * VCH Revision & GMBus Base Addr
39
 */
40
#define VR00		0x00
41
# define VR00_BASE_ADDRESS_MASK		0x007f
42
 
43
/*
44
 * Functionality Enable
45
 */
46
#define VR01		0x01
47
 
48
/*
49
 * Enable the panel fitter
50
 */
51
# define VR01_PANEL_FIT_ENABLE		(1 << 3)
52
/*
53
 * Enables the LCD display.
54
 *
55
 * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
56
 */
57
# define VR01_LCD_ENABLE		(1 << 2)
58
/** Enables the DVO repeater. */
59
# define VR01_DVO_BYPASS_ENABLE		(1 << 1)
60
/** Enables the DVO clock */
61
# define VR01_DVO_ENABLE		(1 << 0)
62
 
63
/*
64
 * LCD Interface Format
65
 */
66
#define VR10		0x10
67
/** Enables LVDS output instead of CMOS */
68
# define VR10_LVDS_ENABLE		(1 << 4)
69
/** Enables 18-bit LVDS output. */
70
# define VR10_INTERFACE_1X18		(0 << 2)
71
/** Enables 24-bit LVDS or CMOS output */
72
# define VR10_INTERFACE_1X24		(1 << 2)
73
/** Enables 2x18-bit LVDS or CMOS output. */
74
# define VR10_INTERFACE_2X18		(2 << 2)
75
/** Enables 2x24-bit LVDS output */
76
# define VR10_INTERFACE_2X24		(3 << 2)
77
 
78
/*
79
 * VR20 LCD Horizontal Display Size
80
 */
81
#define VR20	0x20
82
 
83
/*
84
 * LCD Vertical Display Size
85
 */
86
#define VR21	0x20
87
 
88
/*
89
 * Panel power down status
90
 */
91
#define VR30		0x30
92
/** Read only bit indicating that the panel is not in a safe poweroff state. */
93
# define VR30_PANEL_ON			(1 << 15)
94
 
95
#define VR40		0x40
96
# define VR40_STALL_ENABLE		(1 << 13)
97
# define VR40_VERTICAL_INTERP_ENABLE	(1 << 12)
98
# define VR40_ENHANCED_PANEL_FITTING	(1 << 11)
99
# define VR40_HORIZONTAL_INTERP_ENABLE	(1 << 10)
100
# define VR40_AUTO_RATIO_ENABLE		(1 << 9)
101
# define VR40_CLOCK_GATING_ENABLE	(1 << 8)
102
 
103
/*
104
 * Panel Fitting Vertical Ratio
105
 * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
106
 */
107
#define VR41		0x41
108
 
109
/*
110
 * Panel Fitting Horizontal Ratio
111
 * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
112
 */
113
#define VR42		0x42
114
 
115
/*
116
 * Horizontal Image Size
117
 */
118
#define VR43		0x43
119
 
120
/* VR80 GPIO 0
121
 */
122
#define VR80	    0x80
123
#define VR81	    0x81
124
#define VR82	    0x82
125
#define VR83	    0x83
126
#define VR84	    0x84
127
#define VR85	    0x85
128
#define VR86	    0x86
129
#define VR87	    0x87
130
 
131
/* VR88 GPIO 8
132
 */
133
#define VR88	    0x88
134
 
135
/* Graphics BIOS scratch 0
136
 */
137
#define VR8E	    0x8E
138
# define VR8E_PANEL_TYPE_MASK		(0xf << 0)
139
# define VR8E_PANEL_INTERFACE_CMOS	(0 << 4)
140
# define VR8E_PANEL_INTERFACE_LVDS	(1 << 4)
141
# define VR8E_FORCE_DEFAULT_PANEL	(1 << 5)
142
 
143
/* Graphics BIOS scratch 1
144
 */
145
#define VR8F	    0x8F
146
# define VR8F_VCH_PRESENT		(1 << 0)
147
# define VR8F_DISPLAY_CONN		(1 << 1)
148
# define VR8F_POWER_MASK		(0x3c)
149
# define VR8F_POWER_POS			(2)
150
 
151
 
152
struct ivch_priv {
153
	bool quiet;
154
 
155
	uint16_t width, height;
156
};
157
 
158
 
159
static void ivch_dump_regs(struct intel_dvo_device *dvo);
160
 
161
/**
162
 * Reads a register on the ivch.
163
 *
164
 * Each of the 256 registers are 16 bits long.
165
 */
166
static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
167
{
168
	struct ivch_priv *priv = dvo->dev_priv;
169
	struct i2c_adapter *adapter = dvo->i2c_bus;
170
	u8 out_buf[1];
171
	u8 in_buf[2];
172
 
173
	struct i2c_msg msgs[] = {
174
		{
175
			.addr = dvo->slave_addr,
176
			.flags = I2C_M_RD,
177
			.len = 0,
178
		},
179
		{
180
			.addr = 0,
181
			.flags = I2C_M_NOSTART,
182
			.len = 1,
183
			.buf = out_buf,
184
		},
185
		{
186
			.addr = dvo->slave_addr,
187
			.flags = I2C_M_RD | I2C_M_NOSTART,
188
			.len = 2,
189
			.buf = in_buf,
190
		}
191
	};
192
 
193
	out_buf[0] = addr;
194
 
195
	if (i2c_transfer(adapter, msgs, 3) == 3) {
196
		*data = (in_buf[1] << 8) | in_buf[0];
197
		return true;
198
	};
199
 
200
	if (!priv->quiet) {
201
		DRM_DEBUG_KMS("Unable to read register 0x%02x from "
202
				"%s:%02x.\n",
203
			  addr, adapter->name, dvo->slave_addr);
204
	}
205
	return false;
206
}
207
 
208
/** Writes a 16-bit register on the ivch */
209
static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
210
{
211
	struct ivch_priv *priv = dvo->dev_priv;
212
	struct i2c_adapter *adapter = dvo->i2c_bus;
213
	u8 out_buf[3];
214
	struct i2c_msg msg = {
215
		.addr = dvo->slave_addr,
216
		.flags = 0,
217
		.len = 3,
218
		.buf = out_buf,
219
	};
220
 
221
	out_buf[0] = addr;
222
	out_buf[1] = data & 0xff;
223
	out_buf[2] = data >> 8;
224
 
225
	if (i2c_transfer(adapter, &msg, 1) == 1)
226
		return true;
227
 
228
	if (!priv->quiet) {
229
		DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
230
			  addr, adapter->name, dvo->slave_addr);
231
	}
232
 
233
	return false;
234
}
235
 
236
/** Probes the given bus and slave address for an ivch */
237
static bool ivch_init(struct intel_dvo_device *dvo,
238
		      struct i2c_adapter *adapter)
239
{
240
	struct ivch_priv *priv;
241
	uint16_t temp;
242
 
243
	priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
244
	if (priv == NULL)
245
		return false;
246
 
247
	dvo->i2c_bus = adapter;
248
	dvo->dev_priv = priv;
249
	priv->quiet = true;
250
 
251
	if (!ivch_read(dvo, VR00, &temp))
252
		goto out;
253
	priv->quiet = false;
254
 
255
	/* Since the identification bits are probably zeroes, which doesn't seem
256
	 * very unique, check that the value in the base address field matches
257
	 * the address it's responding on.
258
	 */
259
	if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
260
		DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
261
			  "(%d vs %d)\n",
262
			  (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
263
		goto out;
264
	}
265
 
266
	ivch_read(dvo, VR20, &priv->width);
267
	ivch_read(dvo, VR21, &priv->height);
268
 
269
	return true;
270
 
271
out:
272
	kfree(priv);
273
	return false;
274
}
275
 
276
static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
277
{
278
	return connector_status_connected;
279
}
280
 
281
static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
282
					    struct drm_display_mode *mode)
283
{
284
	if (mode->clock > 112000)
285
		return MODE_CLOCK_HIGH;
286
 
287
	return MODE_OK;
288
}
289
 
290
/** Sets the power state of the panel connected to the ivch */
291
static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
292
{
293
	int i;
294
	uint16_t vr01, vr30, backlight;
295
 
296
	/* Set the new power state of the panel. */
297
	if (!ivch_read(dvo, VR01, &vr01))
298
		return;
299
 
300
	if (mode == DRM_MODE_DPMS_ON)
301
		backlight = 1;
302
	else
303
		backlight = 0;
304
	ivch_write(dvo, VR80, backlight);
305
 
306
	if (mode == DRM_MODE_DPMS_ON)
307
		vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
308
	else
309
		vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
310
 
311
	ivch_write(dvo, VR01, vr01);
312
 
313
	/* Wait for the panel to make its state transition */
314
	for (i = 0; i < 100; i++) {
315
		if (!ivch_read(dvo, VR30, &vr30))
316
			break;
317
 
318
		if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON))
319
			break;
320
		udelay(1000);
321
	}
322
	/* wait some more; vch may fail to resync sometimes without this */
323
	udelay(16 * 1000);
324
}
325
 
326
static void ivch_mode_set(struct intel_dvo_device *dvo,
327
			  struct drm_display_mode *mode,
328
			  struct drm_display_mode *adjusted_mode)
329
{
330
	uint16_t vr40 = 0;
331
	uint16_t vr01;
332
 
333
	vr01 = 0;
334
	vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
335
		VR40_HORIZONTAL_INTERP_ENABLE);
336
 
337
	if (mode->hdisplay != adjusted_mode->hdisplay ||
338
	    mode->vdisplay != adjusted_mode->vdisplay) {
339
		uint16_t x_ratio, y_ratio;
340
 
341
		vr01 |= VR01_PANEL_FIT_ENABLE;
342
		vr40 |= VR40_CLOCK_GATING_ENABLE;
343
		x_ratio = (((mode->hdisplay - 1) << 16) /
344
			   (adjusted_mode->hdisplay - 1)) >> 2;
345
		y_ratio = (((mode->vdisplay - 1) << 16) /
346
			   (adjusted_mode->vdisplay - 1)) >> 2;
2342 Serge 347
		ivch_write(dvo, VR42, x_ratio);
348
		ivch_write(dvo, VR41, y_ratio);
2330 Serge 349
	} else {
350
		vr01 &= ~VR01_PANEL_FIT_ENABLE;
351
		vr40 &= ~VR40_CLOCK_GATING_ENABLE;
352
	}
353
	vr40 &= ~VR40_AUTO_RATIO_ENABLE;
354
 
355
	ivch_write(dvo, VR01, vr01);
356
	ivch_write(dvo, VR40, vr40);
357
 
358
	ivch_dump_regs(dvo);
359
}
360
 
361
static void ivch_dump_regs(struct intel_dvo_device *dvo)
362
{
363
	uint16_t val;
364
 
365
	ivch_read(dvo, VR00, &val);
366
	DRM_LOG_KMS("VR00: 0x%04x\n", val);
367
	ivch_read(dvo, VR01, &val);
368
	DRM_LOG_KMS("VR01: 0x%04x\n", val);
369
	ivch_read(dvo, VR30, &val);
370
	DRM_LOG_KMS("VR30: 0x%04x\n", val);
371
	ivch_read(dvo, VR40, &val);
372
	DRM_LOG_KMS("VR40: 0x%04x\n", val);
373
 
374
	/* GPIO registers */
375
	ivch_read(dvo, VR80, &val);
376
	DRM_LOG_KMS("VR80: 0x%04x\n", val);
377
	ivch_read(dvo, VR81, &val);
378
	DRM_LOG_KMS("VR81: 0x%04x\n", val);
379
	ivch_read(dvo, VR82, &val);
380
	DRM_LOG_KMS("VR82: 0x%04x\n", val);
381
	ivch_read(dvo, VR83, &val);
382
	DRM_LOG_KMS("VR83: 0x%04x\n", val);
383
	ivch_read(dvo, VR84, &val);
384
	DRM_LOG_KMS("VR84: 0x%04x\n", val);
385
	ivch_read(dvo, VR85, &val);
386
	DRM_LOG_KMS("VR85: 0x%04x\n", val);
387
	ivch_read(dvo, VR86, &val);
388
	DRM_LOG_KMS("VR86: 0x%04x\n", val);
389
	ivch_read(dvo, VR87, &val);
390
	DRM_LOG_KMS("VR87: 0x%04x\n", val);
391
	ivch_read(dvo, VR88, &val);
392
	DRM_LOG_KMS("VR88: 0x%04x\n", val);
393
 
394
	/* Scratch register 0 - AIM Panel type */
395
	ivch_read(dvo, VR8E, &val);
396
	DRM_LOG_KMS("VR8E: 0x%04x\n", val);
397
 
398
	/* Scratch register 1 - Status register */
399
	ivch_read(dvo, VR8F, &val);
400
	DRM_LOG_KMS("VR8F: 0x%04x\n", val);
401
}
402
 
403
static void ivch_destroy(struct intel_dvo_device *dvo)
404
{
405
	struct ivch_priv *priv = dvo->dev_priv;
406
 
407
	if (priv) {
408
		kfree(priv);
409
		dvo->dev_priv = NULL;
410
	}
411
}
412
 
2342 Serge 413
struct intel_dvo_dev_ops ivch_ops = {
2330 Serge 414
	.init = ivch_init,
415
	.dpms = ivch_dpms,
416
	.mode_valid = ivch_mode_valid,
417
	.mode_set = ivch_mode_set,
418
	.detect = ivch_detect,
419
	.dump_regs = ivch_dump_regs,
420
	.destroy = ivch_destroy,
421
};