summaryrefslogtreecommitdiff
path: root/src/i830_sdvo.c
diff options
context:
space:
mode:
authorEric Anholt <anholt@FreeBSD.org>2006-04-19 14:23:45 -0700
committerEric Anholt <anholt@FreeBSD.org>2006-04-19 14:23:45 -0700
commit88bb4b578857588f34ac84b7a20577139eccab6d (patch)
treedc0f908bdaffce0d1ebddad10cab827d84213519 /src/i830_sdvo.c
parentd8f7dfac769d7b03f069306b1296bb2e1e08b009 (diff)
Add more SDVO code. It's taken from airlied's driver, but with magic numbers
replaced by symbolic names in many places. I tried to restrain myself from functional changes in airlied's code in this pass.
Diffstat (limited to 'src/i830_sdvo.c')
-rw-r--r--src/i830_sdvo.c479
1 files changed, 477 insertions, 2 deletions
diff --git a/src/i830_sdvo.c b/src/i830_sdvo.c
index 7f5549c9..3b3efa15 100644
--- a/src/i830_sdvo.c
+++ b/src/i830_sdvo.c
@@ -28,6 +28,9 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "xf86_OSproc.h"
#include "compiler.h"
#include "i830.h"
+#include "i830_sdvo_regs.h"
+
+CARD16 curr_table[6];
/* SDVO support for i9xx chipsets */
static Bool sReadByte(I830SDVOPtr s, int addr, unsigned char *ch)
@@ -41,7 +44,6 @@ static Bool sReadByte(I830SDVOPtr s, int addr, unsigned char *ch)
return TRUE;
}
-#if 0
static Bool sWriteByte(I830SDVOPtr s, int addr, unsigned char ch)
{
if (!xf86I2CWriteByte(&s->d, addr, ch)) {
@@ -52,8 +54,481 @@ static Bool sWriteByte(I830SDVOPtr s, int addr, unsigned char ch)
}
return TRUE;
}
+
+/* following on from tracing the intel BIOS i2c routines */
+static void
+I830SDVOWriteOutputs(I830SDVOPtr s, int num_out)
+{
+ int i;
+
+ ErrorF("SDVO: W: ");
+ for (i = num_out; i <= SDVO_I2C_ARG_0; i++)
+ ErrorF("%02X ", s->sdvo_regs[i]);
+ ErrorF("\n");
+
+ /* blast the output regs */
+ for (i = SDVO_I2C_ARG_0; i >= num_out; i--) {
+ sWriteByte(s, i, s->sdvo_regs[i]);
+ }
+ /* blast the command reg */
+ sWriteByte(s, SDVO_I2C_OPCODE, s->sdvo_regs[SDVO_I2C_OPCODE]);
+}
+
+static void
+I830SDVOReadInputRegs(I830SDVOPtr s)
+{
+ int i;
+
+ /* follow BIOS ordering */
+ sReadByte(s, SDVO_I2C_CMD_STATUS, &s->sdvo_regs[SDVO_I2C_CMD_STATUS]);
+
+ sReadByte(s, SDVO_I2C_RETURN_3, &s->sdvo_regs[SDVO_I2C_RETURN_3]);
+ sReadByte(s, SDVO_I2C_RETURN_2, &s->sdvo_regs[SDVO_I2C_RETURN_2]);
+ sReadByte(s, SDVO_I2C_RETURN_1, &s->sdvo_regs[SDVO_I2C_RETURN_1]);
+ sReadByte(s, SDVO_I2C_RETURN_0, &s->sdvo_regs[SDVO_I2C_RETURN_0]);
+ sReadByte(s, SDVO_I2C_RETURN_7, &s->sdvo_regs[SDVO_I2C_RETURN_7]);
+ sReadByte(s, SDVO_I2C_RETURN_6, &s->sdvo_regs[SDVO_I2C_RETURN_6]);
+ sReadByte(s, SDVO_I2C_RETURN_5, &s->sdvo_regs[SDVO_I2C_RETURN_5]);
+ sReadByte(s, SDVO_I2C_RETURN_4, &s->sdvo_regs[SDVO_I2C_RETURN_4]);
+
+ ErrorF("SDVO: R: ");
+ for (i = SDVO_I2C_CMD_STATUS; i <= SDVO_I2C_RETURN_7; i++)
+ ErrorF("%02X ", s->sdvo_regs[i]);
+ ErrorF("\n");
+}
+
+Bool
+I830SDVOSetupDDC(I830SDVOPtr s, int enable)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_SET_CONTROL_BUS_SWITCH;
+ s->sdvo_regs[SDVO_I2C_ARG_0] = SDVO_CONTROL_BUS_DDC2;
+
+ I830SDVOWriteOutputs(s, 7);
+
+ sReadByte(s, SDVO_I2C_CMD_STATUS, &s->sdvo_regs[SDVO_I2C_CMD_STATUS]);
+
+ ErrorF("SDVO: R: ");
+ ErrorF("%02X ", s->sdvo_regs[SDVO_I2C_CMD_STATUS]);
+ ErrorF("\n");
+ return TRUE;
+}
+
+static Bool
+I830SDVOSetTargetInput(I830SDVOPtr s)
+{
+ /* write out 0x10 */
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_SET_TARGET_INPUT;
+
+ I830SDVOWriteOutputs(s, 0);
+
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOGetTrainedInputs(I830SDVOPtr s, int on)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_GET_TRAINED_INPUTS;
+
+ /* XXX: I don't believe we need to set anything here --anholt */
+ s->sdvo_regs[0x07] = on ? 0x80 : 0x00;
+ s->sdvo_regs[0x04] = on ? 0x80 : 0x00;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOGetActiveOutputs(I830SDVOPtr s, int on)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_GET_ACTIVE_OUTPUTS;
+
+ s->sdvo_regs[0x07] = on ? 0x01 : 0x00;
+ s->sdvo_regs[0x03] = 0x1;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOSetActiveOutputs(I830SDVOPtr s, int on)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_SET_ACTIVE_OUTPUTS;
+
+ /* XXX: This should be touching args 0,1, I believe. --anholt */
+ s->sdvo_regs[0x07] = on ? 0x01 : 0x00;
+ s->sdvo_regs[0x03] = on ? 0x01 : 0x00;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOGetInputPixelClockRange(I830SDVOPtr s, CARD16 clock, CARD16 height)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE;
+
+ /* XXX: SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE shouldn't be taking args. */
+
+ /* set clock regs */
+ s->sdvo_regs[0x06] = (clock >> 8) & 0xff;
+ s->sdvo_regs[0x07] = clock & 0xff;
+
+ /* set height regs */
+ s->sdvo_regs[0x02] = (height >> 8) & 0xff;
+ s->sdvo_regs[0x03] = height & 0xff;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOSetTargetOutput(I830SDVOPtr s)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_SET_TARGET_OUTPUT;
+
+ s->sdvo_regs[SDVO_I2C_ARG_0] = 0x1; /* Enable */
+ s->sdvo_regs[SDVO_I2C_ARG_1] = 0x0; /* Disable */
+
+ I830SDVOWriteOutputs(s, 0); /* XXX: Only write these two */
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+#if 0
+static Bool
+I830SDVOGetOutputTimingsPart1(I830SDVOPtr s)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_GET_OUTPUT_TIMINGS_PART1;
+
+ /* XXX: No args */
+ s->sdvo_regs[0x07] = 0x0;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOGetOutputTimingsPart2(I830SDVOPtr s)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_GET_OUTPUT_TIMINGS_PART2;
+
+ /* XXX: No args */
+ s->sdvo_regs[0x07] = 0x0;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
#endif
+static Bool
+I830SDVOSetTimingsPart1(I830SDVOPtr s, char cmd, CARD16 clock, CARD16 magic1,
+ CARD16 magic2, CARD16 magic3)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = cmd;
+
+ /* set clock regs */
+ s->sdvo_regs[SDVO_I2C_ARG_0] = clock & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_1] = (clock >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_2] = magic3 & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_3] = (magic3 >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_4] = magic2 & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_5] = (magic2 >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_6] = magic1 & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_7] = (magic1 >> 8) & 0xff;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOSetInputTimingsPart1(I830SDVOPtr s, CARD16 clock,
+ CARD16 magic1, CARD16 magic2, CARD16 magic3)
+{
+ return I830SDVOSetTimingsPart1(s, SDVO_CMD_SET_INPUT_TIMINGS_PART1,
+ clock, magic1, magic2, magic3);
+}
+
+static Bool
+I830SDVOSetOutputTimingsPart1(I830SDVOPtr s, CARD16 clock, CARD16 magic1,
+ CARD16 magic2, CARD16 magic3)
+{
+ return I830SDVOSetTimingsPart1(s, SDVO_CMD_SET_OUTPUT_TIMINGS_PART1,
+ clock, magic1, magic2, magic3);
+}
+
+static Bool
+I830SDVOGetPreferredInputTimingPart2(I830SDVOPtr s, CARD16 clock,
+ CARD16 magic1, CARD16 magic2,
+ CARD16 magic3)
+{
+ Bool ok;
+
+ /* XXX: This is a rather different command */
+ ok = I830SDVOSetTimingsPart1(s, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
+ clock, magic1, magic2, magic3);
+
+ curr_table[3] = s->sdvo_regs[SDVO_I2C_RETURN_0] |
+ (s->sdvo_regs[SDVO_I2C_RETURN_1] << 8);
+ curr_table[4] = s->sdvo_regs[SDVO_I2C_RETURN_2] |
+ (s->sdvo_regs[SDVO_I2C_RETURN_3] << 8);
+ curr_table[5] = 0x1e;
+
+ return ok;
+}
+
+static Bool
+I830SDVOSetTimingsPart2(I830SDVOPtr s, CARD8 cmd, CARD16 magic4, CARD16 magic5,
+ CARD16 magic6)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = cmd;
+
+ /* set clock regs */
+ s->sdvo_regs[SDVO_I2C_ARG_0] = magic4 & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_1] = (magic4 >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_2] = magic5 & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_3] = (magic5 >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_4] = magic6 & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_5] = (magic6 >> 8) & 0xff;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOSetInputTimingsPart2(I830SDVOPtr s, CARD16 magic4, CARD16 magic5,
+ CARD16 magic6)
+{
+ return I830SDVOSetTimingsPart2(s, SDVO_CMD_SET_INPUT_TIMINGS_PART2, magic4,
+ magic5, magic6);
+}
+
+static Bool
+I830SDVOSetOutputTimingsPart2(I830SDVOPtr s, CARD16 magic4, CARD16 magic5,
+ CARD16 magic6)
+{
+ return I830SDVOSetTimingsPart2(s, SDVO_CMD_SET_OUTPUT_TIMINGS_PART2, magic4,
+ magic5, magic6);
+}
+
+static Bool
+I830SDVOCreatePreferredInputTiming(I830SDVOPtr s, CARD16 clock, CARD16 width,
+ CARD16 height)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING;
+
+ s->sdvo_regs[SDVO_I2C_ARG_0] = clock & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_1] = (clock >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_2] = width & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_3] = (width >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_4] = (height >> 8) & 0xff;
+ s->sdvo_regs[SDVO_I2C_ARG_5] = height & 0xff;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOGetPreferredInputTimingPart1(I830SDVOPtr s)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1;
+
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ curr_table[0] = s->sdvo_regs[SDVO_I2C_RETURN_6] |
+ (s->sdvo_regs[SDVO_I2C_RETURN_7] << 8);
+ curr_table[1] = s->sdvo_regs[SDVO_I2C_RETURN_4] |
+ (s->sdvo_regs[SDVO_I2C_RETURN_5] << 8);
+ curr_table[2] = s->sdvo_regs[SDVO_I2C_RETURN_2] |
+ (s->sdvo_regs[SDVO_I2C_RETURN_3] << 8);
+
+ return TRUE;
+}
+
+static Bool
+I830SDVOSetClockRateMult(I830SDVOPtr s, CARD8 val)
+{
+ memset(s->sdvo_regs, 0, 9);
+
+ s->sdvo_regs[SDVO_I2C_OPCODE] = SDVO_CMD_SET_CLOCK_RATE_MULT;
+
+ s->sdvo_regs[SDVO_I2C_ARG_0] = val;
+ I830SDVOWriteOutputs(s, 0);
+ I830SDVOReadInputRegs(s);
+
+ return TRUE;
+}
+
+Bool
+I830SDVOPreSetMode(I830SDVOPtr s, DisplayModePtr mode)
+{
+ CARD16 clock = mode->Clock/10, width = mode->CrtcHDisplay;
+ CARD16 height = mode->CrtcVDisplay;
+ CARD16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+ CARD16 h_sync_offset, v_sync_offset;
+ CARD16 sync_flags;
+ CARD8 c16a[8];
+ CARD8 c17a[8];
+ CARD16 out_timings[6];
+
+ /* do some mode translations */
+ h_blank_len = mode->CrtcHBlankEnd - mode->CrtcHBlankStart;
+ h_sync_len = mode->CrtcHSyncEnd - mode->CrtcHSyncStart;
+
+ v_blank_len = mode->CrtcVBlankEnd - mode->CrtcVBlankStart;
+ v_sync_len = mode->CrtcVSyncEnd - mode->CrtcVSyncStart;
+
+ h_sync_offset = mode->CrtcHSyncStart - mode->CrtcHBlankStart;
+ v_sync_offset = mode->CrtcVSyncStart - mode->CrtcVBlankStart;
+
+ sync_flags = 0x18;
+ if (mode->Flags & V_PHSYNC)
+ sync_flags |= 0x2;
+ if (mode->Flags & V_PVSYNC)
+ sync_flags |= 0x4;
+ /* high bits of 0 */
+ c16a[7] = clock & 0xff;
+ c16a[6] = (clock >> 8) & 0xff;
+ c16a[5] = (width & 0xff);
+ c16a[4] = (h_blank_len & 0xff);
+ c16a[3] = (((width >> 8) & 0xf) << 4) | ((h_blank_len >> 8) & 0xf);
+ c16a[2] = (height & 0xff);
+ c16a[1] = (v_blank_len & 0xff);
+ c16a[0] = (((height >> 8) & 0xf) << 4) | ((v_blank_len >> 8) & 0xf);
+
+ c17a[7] = h_sync_offset;
+ c17a[6] = h_sync_len & 0xff;
+ c17a[5] = (v_sync_offset & 0xf) << 4 | (v_sync_len & 0xf);
+ c17a[4] = 0;
+ c17a[3] = sync_flags;
+ c17a[2] = 0;
+ out_timings[0] = c16a[1] | ((short)c16a[0] << 8);
+ out_timings[1] = c16a[3] | ((short)c16a[2] << 8);
+ out_timings[2] = c16a[5] | ((short)c16a[4] << 8);
+ out_timings[3] = c17a[7] | ((short)c17a[6] << 8);
+ out_timings[4] = c17a[5] | ((short)c17a[4] << 8);
+ out_timings[5] = c17a[3] | ((short)c17a[2] << 8);
+
+ I830SDVOSetTargetInput(s);
+ I830SDVOGetInputPixelClockRange(s, clock, height);
+
+ I830SDVOGetActiveOutputs(s, 0);
+ I830SDVOSetActiveOutputs(s, 0);
+
+ I830SDVOSetTargetOutput(s);
+ I830SDVOSetOutputTimingsPart1(s, clock, out_timings[0], out_timings[1],
+ out_timings[2]);
+
+ I830SDVOSetTargetOutput(s);
+ I830SDVOSetOutputTimingsPart2(s, out_timings[3], out_timings[4],
+ out_timings[5]);
+
+ I830SDVOSetTargetInput(s);
+
+ I830SDVOCreatePreferredInputTiming(s, clock, width, height);
+ I830SDVOSetTargetInput(s);
+
+ I830SDVOGetPreferredInputTimingPart1(s);
+ I830SDVOSetTargetInput(s);
+
+ I830SDVOGetPreferredInputTimingPart2(s, clock, out_timings[0], out_timings[1],
+ out_timings[2]);
+ I830SDVOSetTargetInput(s);
+
+ I830SDVOSetInputTimingsPart1(s, clock, curr_table[0], curr_table[1],
+ curr_table[2]);
+
+ I830SDVOSetTargetInput(s);
+ I830SDVOSetInputTimingsPart2(s, curr_table[3], curr_table[4],
+ out_timings[5]);
+
+ I830SDVOSetTargetInput(s);
+ /*if (mode->PrivFlags & I830_MFLAG_DOUBLE)
+ I830SDVOSetClockRateMult(s, 0x02);
+ else */
+ I830SDVOSetClockRateMult(s, 0x01);
+
+ return TRUE;
+}
+
+Bool
+I830SDVOPostSetMode(I830SDVOPtr s, DisplayModePtr mode)
+{
+ int clock = mode->Clock/10, height=mode->CrtcVDisplay;
+ Bool ret = TRUE;
+
+ /* the BIOS writes out 6 commands post mode set */
+ /* two 03s, 04 05, 10, 1d */
+ /* these contain the height and mode clock / 10 by the looks of it */
+
+ I830SDVOGetTrainedInputs(s, 1);
+ I830SDVOGetTrainedInputs(s, 0);
+
+ /* THIS IS A DIRTY HACK - sometimes for some reason on startup
+ * the BIOS doesn't find my DVI monitor -
+ * without this hack the driver doesn't work.. this causes the modesetting
+ * to be re-run
+ */
+ if (s->sdvo_regs[SDVO_I2C_RETURN_0] != 0x1) {
+ ret = FALSE;
+ }
+
+ I830SDVOGetActiveOutputs(s, 1);
+ I830SDVOSetActiveOutputs(s, 1);
+
+ I830SDVOSetTargetInput(s);
+ I830SDVOGetInputPixelClockRange(s, clock, height);
+
+ return ret;
+}
+
I830SDVOPtr
I830SDVOInit(I2CBusPtr b)
{
@@ -92,7 +567,7 @@ I830I2CDetectSDVOController(ScrnInfoPtr pScrn, int output_index)
if (sdvo == NULL)
return FALSE;
- for (i=0; i<0x40; i++) {
+ for (i = 0; i < 0x40; i++) {
if (!sReadByte(sdvo, i, &ch[i]))
return FALSE;
}