1fa225cbcSrjs/*
2fa225cbcSrjs * Copyright © 2006 Intel Corporation
3fa225cbcSrjs *
4fa225cbcSrjs * Permission is hereby granted, free of charge, to any person obtaining a
5fa225cbcSrjs * copy of this software and associated documentation files (the "Software"),
6fa225cbcSrjs * to deal in the Software without restriction, including without limitation
7fa225cbcSrjs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8fa225cbcSrjs * and/or sell copies of the Software, and to permit persons to whom the
9fa225cbcSrjs * Software is furnished to do so, subject to the following conditions:
10fa225cbcSrjs *
11fa225cbcSrjs * The above copyright notice and this permission notice (including the next
12fa225cbcSrjs * paragraph) shall be included in all copies or substantial portions of the
13fa225cbcSrjs * Software.
14fa225cbcSrjs *
15fa225cbcSrjs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16fa225cbcSrjs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17fa225cbcSrjs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18fa225cbcSrjs * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19fa225cbcSrjs * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20fa225cbcSrjs * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21fa225cbcSrjs * DEALINGS IN THE SOFTWARE.
22fa225cbcSrjs *
23fa225cbcSrjs * Authors:
24fa225cbcSrjs *    Eric Anholt <eric@anholt.net>
25fa225cbcSrjs *
26fa225cbcSrjs */
27fa225cbcSrjs
28fa225cbcSrjs#ifdef HAVE_CONFIG_H
29fa225cbcSrjs#include "config.h"
30fa225cbcSrjs#endif
31fa225cbcSrjs
32fa225cbcSrjs#include "xf86.h"
33fa225cbcSrjs#include "xf86_OSproc.h"
34fa225cbcSrjs#include "compiler.h"
35fa225cbcSrjs#include "miscstruct.h"
36fa225cbcSrjs#include "xf86i2c.h"
37fa225cbcSrjs#include "xf86Crtc.h"
38fa225cbcSrjs#ifdef HAVE_XEXTPROTO_71
39fa225cbcSrjs#include <X11/extensions/dpmsconst.h>
40fa225cbcSrjs#else
41fa225cbcSrjs#define DPMS_SERVER
42fa225cbcSrjs#include <X11/extensions/dpms.h>
43fa225cbcSrjs#endif
44fa225cbcSrjs
45fa225cbcSrjs#include <unistd.h>
46fa225cbcSrjs
47fa225cbcSrjs#include "../i2c_vid.h"
48fa225cbcSrjs#include "../i830_bios.h"
49fa225cbcSrjs#include "ivch_reg.h"
50fa225cbcSrjs
51fa225cbcSrjsstruct ivch_priv {
52fa225cbcSrjs    I2CDevRec	    d;
53fa225cbcSrjs
54fa225cbcSrjs    xf86OutputPtr   output;
55fa225cbcSrjs    Bool quiet;
56fa225cbcSrjs
57fa225cbcSrjs    uint16_t width, height;
58fa225cbcSrjs
59fa225cbcSrjs    uint16_t save_VR01;
60fa225cbcSrjs    uint16_t save_VR40;
61fa225cbcSrjs};
62fa225cbcSrjs
63fa225cbcSrjsstruct vch_capabilities {
64fa225cbcSrjs    struct aimdb_block	aimdb_block;
65fa225cbcSrjs    uint8_t		panel_type;
66fa225cbcSrjs    uint8_t		set_panel_type;
67fa225cbcSrjs    uint8_t		slave_address;
68fa225cbcSrjs    uint8_t		capabilities;
69fa225cbcSrjs#define VCH_PANEL_FITTING_SUPPORT	(0x3 << 0)
70fa225cbcSrjs#define VCH_PANEL_FITTING_TEXT		(1 << 2)
71fa225cbcSrjs#define VCH_PANEL_FITTING_GRAPHICS	(1 << 3)
72fa225cbcSrjs#define VCH_PANEL_FITTING_RATIO		(1 << 4)
73fa225cbcSrjs#define VCH_DITHERING			(1 << 5)
74fa225cbcSrjs    uint8_t		backlight_gpio;
75fa225cbcSrjs    uint8_t		set_panel_type_us_gpios;
76fa225cbcSrjs} __attribute__ ((packed));
77fa225cbcSrjs
78fa225cbcSrjsstatic void
79fa225cbcSrjsivch_dump_regs(I2CDevPtr d);
80fa225cbcSrjs
81fa225cbcSrjs/**
82fa225cbcSrjs * Reads a register on the ivch.
83fa225cbcSrjs *
84fa225cbcSrjs * Each of the 256 registers are 16 bits long.
85fa225cbcSrjs */
86fa225cbcSrjsstatic Bool
87fa225cbcSrjsivch_read(struct ivch_priv *priv, int addr, uint16_t *data)
88fa225cbcSrjs{
89fa225cbcSrjs    I2CBusPtr b = priv->d.pI2CBus;
90fa225cbcSrjs    I2CByte *p = (I2CByte *) data;
91fa225cbcSrjs
92fa225cbcSrjs    if (!b->I2CStart(b, priv->d.StartTimeout))
93fa225cbcSrjs	goto fail;
94fa225cbcSrjs
95fa225cbcSrjs    if (!b->I2CPutByte(&priv->d, priv->d.SlaveAddr | 1))
96fa225cbcSrjs	goto fail;
97fa225cbcSrjs
98fa225cbcSrjs    if (!b->I2CPutByte(&priv->d, addr))
99fa225cbcSrjs	goto fail;
100fa225cbcSrjs
101fa225cbcSrjs    if (!b->I2CGetByte(&priv->d, p++, FALSE))
102fa225cbcSrjs	goto fail;
103fa225cbcSrjs
104fa225cbcSrjs    if (!b->I2CGetByte(&priv->d, p++, TRUE))
105fa225cbcSrjs	goto fail;
106fa225cbcSrjs
107fa225cbcSrjs    b->I2CStop(&priv->d);
108fa225cbcSrjs
109fa225cbcSrjs    return TRUE;
110fa225cbcSrjs
111fa225cbcSrjs fail:
112fa225cbcSrjs    if (!priv->quiet) {
113fa225cbcSrjs	xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR,
114fa225cbcSrjs		   "ivch: Unable to read register 0x%02x from %s:%02x.\n",
115fa225cbcSrjs		   addr, priv->d.pI2CBus->BusName, priv->d.SlaveAddr);
116fa225cbcSrjs    }
117fa225cbcSrjs    b->I2CStop(&priv->d);
118fa225cbcSrjs
119fa225cbcSrjs    return FALSE;
120fa225cbcSrjs}
121fa225cbcSrjs
122fa225cbcSrjs/** Writes a 16-bit register on the ivch */
123fa225cbcSrjsstatic Bool
124fa225cbcSrjsivch_write(struct ivch_priv *priv, int addr, uint16_t data)
125fa225cbcSrjs{
126fa225cbcSrjs    I2CBusPtr b = priv->d.pI2CBus;
127fa225cbcSrjs
128fa225cbcSrjs    if (!b->I2CStart(b, priv->d.StartTimeout))
129fa225cbcSrjs	goto fail;
130fa225cbcSrjs
131fa225cbcSrjs    if (!b->I2CPutByte(&priv->d, priv->d.SlaveAddr))
132fa225cbcSrjs	goto fail;
133fa225cbcSrjs
134fa225cbcSrjs    if (!b->I2CPutByte(&priv->d, addr))
135fa225cbcSrjs	goto fail;
136fa225cbcSrjs
137fa225cbcSrjs    if (!b->I2CPutByte(&priv->d, data & 0xff))
138fa225cbcSrjs	goto fail;
139fa225cbcSrjs
140fa225cbcSrjs    if (!b->I2CPutByte(&priv->d, data >> 8))
141fa225cbcSrjs	goto fail;
142fa225cbcSrjs
143fa225cbcSrjs    b->I2CStop(&priv->d);
144fa225cbcSrjs
145fa225cbcSrjs    return TRUE;
146fa225cbcSrjs
147fa225cbcSrjs fail:
148fa225cbcSrjs    b->I2CStop(&priv->d);
149fa225cbcSrjs
150fa225cbcSrjs    if (!priv->quiet) {
151fa225cbcSrjs	xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR,
152fa225cbcSrjs		   "Unable to write register 0x%02x to %s:%d.\n",
153fa225cbcSrjs		   addr, priv->d.pI2CBus->BusName, priv->d.SlaveAddr);
154fa225cbcSrjs    }
155fa225cbcSrjs
156fa225cbcSrjs    return FALSE;
157fa225cbcSrjs}
158fa225cbcSrjs
159fa225cbcSrjs/** Probes the given bus and slave address for an ivch */
160fa225cbcSrjsstatic void *
161fa225cbcSrjsivch_init(I2CBusPtr b, I2CSlaveAddr addr)
162fa225cbcSrjs{
163fa225cbcSrjs    struct	ivch_priv *priv;
164fa225cbcSrjs    uint16_t	temp;
165fa225cbcSrjs
166fa225cbcSrjs    priv = xcalloc(1, sizeof(struct ivch_priv));
167fa225cbcSrjs    if (priv == NULL)
168fa225cbcSrjs	return NULL;
169fa225cbcSrjs
170fa225cbcSrjs    priv->output = NULL;
171fa225cbcSrjs    priv->d.DevName = "i82807aa \"ivch\" LVDS/CMOS panel controller";
172fa225cbcSrjs    priv->d.SlaveAddr = addr;
173fa225cbcSrjs    priv->d.pI2CBus = b;
174fa225cbcSrjs    priv->d.StartTimeout = b->StartTimeout;
175fa225cbcSrjs    priv->d.BitTimeout = b->BitTimeout;
176fa225cbcSrjs    priv->d.AcknTimeout = b->AcknTimeout;
177fa225cbcSrjs    priv->d.ByteTimeout = b->ByteTimeout;
178fa225cbcSrjs    priv->d.DriverPrivate.ptr = priv;
179fa225cbcSrjs    priv->quiet = TRUE;
180fa225cbcSrjs
181fa225cbcSrjs    if (!ivch_read(priv, VR00, &temp))
182fa225cbcSrjs	goto out;
183fa225cbcSrjs    priv->quiet = FALSE;
184fa225cbcSrjs
185fa225cbcSrjs    /* Since the identification bits are probably zeroes, which doesn't seem
186fa225cbcSrjs     * very unique, check that the value in the base address field matches
187fa225cbcSrjs     * the address it's responding on.
188fa225cbcSrjs     */
189fa225cbcSrjs    if ((temp & VR00_BASE_ADDRESS_MASK) != (priv->d.SlaveAddr >> 1)) {
190fa225cbcSrjs	xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_ERROR,
191fa225cbcSrjs		   "ivch detect failed due to address mismatch "
192fa225cbcSrjs		   "(%d vs %d)\n",
193fa225cbcSrjs		   (temp & VR00_BASE_ADDRESS_MASK), priv->d.SlaveAddr >> 1);
194fa225cbcSrjs	goto out;
195fa225cbcSrjs    }
196fa225cbcSrjs
197fa225cbcSrjs    if (!xf86I2CDevInit(&priv->d)) {
198fa225cbcSrjs	goto out;
199fa225cbcSrjs    }
200fa225cbcSrjs
201fa225cbcSrjs    ivch_read(priv, VR20, &priv->width);
202fa225cbcSrjs    ivch_read(priv, VR21, &priv->height);
203fa225cbcSrjs
204fa225cbcSrjs    return priv;
205fa225cbcSrjs
206fa225cbcSrjsout:
207fa225cbcSrjs    xfree(priv);
208fa225cbcSrjs    return NULL;
209fa225cbcSrjs}
210fa225cbcSrjs
211fa225cbcSrjsstatic xf86OutputStatus
212fa225cbcSrjsivch_detect(I2CDevPtr d)
213fa225cbcSrjs{
214fa225cbcSrjs    return XF86OutputStatusConnected;
215fa225cbcSrjs}
216fa225cbcSrjs
217fa225cbcSrjsstatic ModeStatus
218fa225cbcSrjsivch_mode_valid(I2CDevPtr d, DisplayModePtr mode)
219fa225cbcSrjs{
220fa225cbcSrjs    if (mode->Clock > 112000)
221fa225cbcSrjs	return MODE_CLOCK_HIGH;
222fa225cbcSrjs
223fa225cbcSrjs    return MODE_OK;
224fa225cbcSrjs}
225fa225cbcSrjs
226fa225cbcSrjs/** Sets the power state of the panel connected to the ivch */
227fa225cbcSrjsstatic void
228fa225cbcSrjsivch_dpms(I2CDevPtr d, int mode)
229fa225cbcSrjs{
230fa225cbcSrjs    struct ivch_priv *priv = d->DriverPrivate.ptr;
231fa225cbcSrjs    int i;
232fa225cbcSrjs    uint16_t vr01, vr30, backlight;
233fa225cbcSrjs
234fa225cbcSrjs    /* Set the new power state of the panel. */
235fa225cbcSrjs    if (!ivch_read(priv, VR01, &vr01))
236fa225cbcSrjs	return;
237fa225cbcSrjs
238fa225cbcSrjs    if (mode == DPMSModeOn)
239fa225cbcSrjs	backlight = 1;
240fa225cbcSrjs    else
241fa225cbcSrjs	backlight = 0;
242fa225cbcSrjs    ivch_write(priv, VR80, backlight);
243fa225cbcSrjs
244fa225cbcSrjs    if (mode == DPMSModeOn)
245fa225cbcSrjs	vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
246fa225cbcSrjs    else
247fa225cbcSrjs	vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
248fa225cbcSrjs
249fa225cbcSrjs    ivch_write(priv, VR01, vr01);
250fa225cbcSrjs
251fa225cbcSrjs    /* Wait for the panel to make its state transition */
252fa225cbcSrjs    for (i = 0; i < 100; i++) {
253fa225cbcSrjs	if (!ivch_read(priv, VR30, &vr30))
254fa225cbcSrjs	    break;
255fa225cbcSrjs
256fa225cbcSrjs	if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DPMSModeOn))
257fa225cbcSrjs	    break;
258fa225cbcSrjs	usleep (1000);
259fa225cbcSrjs    }
260fa225cbcSrjs    /* And wait some more; without this, the vch fails to resync sometimes */
261fa225cbcSrjs    usleep (16 * 1000);
262fa225cbcSrjs}
263fa225cbcSrjs
264fa225cbcSrjsstatic void
265fa225cbcSrjsivch_mode_set(I2CDevPtr d, DisplayModePtr mode, DisplayModePtr adjusted_mode)
266fa225cbcSrjs{
267fa225cbcSrjs    struct ivch_priv	*priv = d->DriverPrivate.ptr;
268fa225cbcSrjs    uint16_t		vr40 = 0;
269fa225cbcSrjs    uint16_t		vr01;
270fa225cbcSrjs
271fa225cbcSrjs    vr01 = 0;
272fa225cbcSrjs    vr40 = (VR40_STALL_ENABLE |
273fa225cbcSrjs	    VR40_VERTICAL_INTERP_ENABLE |
274fa225cbcSrjs	    VR40_HORIZONTAL_INTERP_ENABLE);
275fa225cbcSrjs
276fa225cbcSrjs    if (mode->HDisplay != adjusted_mode->HDisplay ||
277fa225cbcSrjs	mode->VDisplay != adjusted_mode->VDisplay)
278fa225cbcSrjs    {
279fa225cbcSrjs	uint16_t	x_ratio, y_ratio;
280fa225cbcSrjs
281fa225cbcSrjs	vr01 |= VR01_PANEL_FIT_ENABLE;
282fa225cbcSrjs	vr40 |= VR40_CLOCK_GATING_ENABLE;
283fa225cbcSrjs	x_ratio = (((mode->HDisplay - 1) << 16) / (adjusted_mode->HDisplay - 1)) >> 2;
284fa225cbcSrjs	y_ratio = (((mode->VDisplay - 1) << 16) / (adjusted_mode->VDisplay - 1)) >> 2;
285fa225cbcSrjs	ivch_write (priv, VR42, x_ratio);
286fa225cbcSrjs	ivch_write (priv, VR41, y_ratio);
287fa225cbcSrjs    }
288fa225cbcSrjs    else
289fa225cbcSrjs    {
290fa225cbcSrjs	vr01 &= ~VR01_PANEL_FIT_ENABLE;
291fa225cbcSrjs	vr40 &= ~VR40_CLOCK_GATING_ENABLE;
292fa225cbcSrjs    }
293fa225cbcSrjs    vr40 &= ~VR40_AUTO_RATIO_ENABLE;
294fa225cbcSrjs
295fa225cbcSrjs    ivch_write(priv, VR01, vr01);
296fa225cbcSrjs    ivch_write(priv, VR40, vr40);
297fa225cbcSrjs
298fa225cbcSrjs    ivch_dump_regs(d);
299fa225cbcSrjs}
300fa225cbcSrjs
301fa225cbcSrjsstatic void
302fa225cbcSrjsivch_dump_regs(I2CDevPtr d)
303fa225cbcSrjs{
304fa225cbcSrjs    struct ivch_priv *priv = d->DriverPrivate.ptr;
305fa225cbcSrjs    uint16_t val;
306fa225cbcSrjs
307fa225cbcSrjs    ivch_read(priv, VR00, &val);
308fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR00: 0x%04x\n", val);
309fa225cbcSrjs    ivch_read(priv, VR01, &val);
310fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR01: 0x%04x\n", val);
311fa225cbcSrjs    ivch_read(priv, VR30, &val);
312fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR30: 0x%04x\n", val);
313fa225cbcSrjs    ivch_read(priv, VR40, &val);
314fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR40: 0x%04x\n", val);
315fa225cbcSrjs
316fa225cbcSrjs    /* GPIO registers */
317fa225cbcSrjs    ivch_read(priv, VR80, &val);
318fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR80: 0x%04x\n", val);
319fa225cbcSrjs    ivch_read(priv, VR81, &val);
320fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR81: 0x%04x\n", val);
321fa225cbcSrjs    ivch_read(priv, VR82, &val);
322fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR82: 0x%04x\n", val);
323fa225cbcSrjs    ivch_read(priv, VR83, &val);
324fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR83: 0x%04x\n", val);
325fa225cbcSrjs    ivch_read(priv, VR84, &val);
326fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR84: 0x%04x\n", val);
327fa225cbcSrjs    ivch_read(priv, VR85, &val);
328fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR85: 0x%04x\n", val);
329fa225cbcSrjs    ivch_read(priv, VR86, &val);
330fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR86: 0x%04x\n", val);
331fa225cbcSrjs    ivch_read(priv, VR87, &val);
332fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR87: 0x%04x\n", val);
333fa225cbcSrjs    ivch_read(priv, VR88, &val);
334fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR88: 0x%04x\n", val);
335fa225cbcSrjs
336fa225cbcSrjs    /* Scratch register 0 - AIM Panel type */
337fa225cbcSrjs    ivch_read(priv, VR8E, &val);
338fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR8E: 0x%04x\n", val);
339fa225cbcSrjs
340fa225cbcSrjs    /* Scratch register 1 - Status register */
341fa225cbcSrjs    ivch_read(priv, VR8F, &val);
342fa225cbcSrjs    xf86DrvMsg(priv->d.pI2CBus->scrnIndex, X_INFO, "VR8F: 0x%04x\n", val);
343fa225cbcSrjs}
344fa225cbcSrjs
345fa225cbcSrjsstatic void
346fa225cbcSrjsivch_save(I2CDevPtr d)
347fa225cbcSrjs{
348fa225cbcSrjs    struct ivch_priv *priv = d->DriverPrivate.ptr;
349fa225cbcSrjs
350fa225cbcSrjs    ivch_read(priv, VR01, &priv->save_VR01);
351fa225cbcSrjs    ivch_read(priv, VR40, &priv->save_VR40);
352fa225cbcSrjs}
353fa225cbcSrjs
354fa225cbcSrjsstatic void
355fa225cbcSrjsivch_restore(I2CDevPtr d)
356fa225cbcSrjs{
357fa225cbcSrjs    struct ivch_priv *priv = d->DriverPrivate.ptr;
358fa225cbcSrjs
359fa225cbcSrjs    ivch_write(priv, VR01, priv->save_VR01);
360fa225cbcSrjs    ivch_write(priv, VR40, priv->save_VR40);
361fa225cbcSrjs}
362fa225cbcSrjs
363fa225cbcSrjs
364fa225cbcSrjs_X_EXPORT I830I2CVidOutputRec ivch_methods = {
365fa225cbcSrjs    .init = ivch_init,
366fa225cbcSrjs    .dpms = ivch_dpms,
367fa225cbcSrjs    .save = ivch_save,
368fa225cbcSrjs    .restore = ivch_restore,
369fa225cbcSrjs    .mode_valid = ivch_mode_valid,
370fa225cbcSrjs    .mode_set = ivch_mode_set,
371fa225cbcSrjs    .detect = ivch_detect,
372fa225cbcSrjs    .dump_regs = ivch_dump_regs,
373fa225cbcSrjs};
374