Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpu/drm/i915/dvo_ivch.c
15113 views
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 <[email protected]>
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;
347
ivch_write (dvo, VR42, x_ratio);
348
ivch_write (dvo, VR41, y_ratio);
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
413
struct intel_dvo_dev_ops ivch_ops= {
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
};
422
423