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