randr12: common tmds access functions
[nouveau] / src / nv50_output.c
1 /*
2  * Copyright (c) 2007 NVIDIA, 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
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 #include <string.h>
25
26 #include "nv_include.h"
27 #include "nv50_type.h"
28 #include "nv50_display.h"
29 #include "nv50_output.h"
30
31 #include <xf86DDC.h>
32
33 static Bool NV50ReadPortMapping(int scrnIndex, NVPtr pNv)
34 {
35          unsigned const char *VBIOS = (unsigned const char *)pNv->VBIOS.data;
36          unsigned char *table2;
37          unsigned char headerSize, entries;
38          int i;
39          CARD16 a;
40          CARD32 b;
41
42          if (!VBIOS)
43                 goto fail;
44
45          /* Clear the i2c map to invalid */
46          for (i = 0; i < 4; i++)
47                 pNv->i2cMap[i].dac = pNv->i2cMap[i].sor = -1;
48
49         if (*(CARD16*)VBIOS != 0xaa55) goto fail;
50
51         a = *(CARD16*)(VBIOS + 0x36);
52         table2 = (unsigned char*)VBIOS + a;
53
54         if (table2[0] != 0x40) goto fail;
55
56         b = *(CARD32*)(table2 + 6);
57         if (b != 0x4edcbdcb) goto fail;
58
59         headerSize = table2[1];
60         entries = table2[2];
61
62         for(i = 0; i < entries; i++) {
63                 int type, port;
64                 ORNum or;
65
66                 b = *(CARD32*)&table2[headerSize + 8*i];
67                 type = b & 0xf;
68                 port = (b >> 4) & 0xf;
69                 or = ffs((b >> 24) & 0xf) - 1;
70
71                 if (type == 0xe)
72                         break;
73
74                 if(type < 4) {
75                         switch(type) {
76                                 case 0: /* CRT */
77                                         if(pNv->i2cMap[port].dac != -1) {
78                                                 xf86DrvMsg(scrnIndex, X_WARNING,
79                                                         "DDC routing table corrupt!  DAC %i -> %i "
80                                                         "for port %i\n",
81                                                         or, pNv->i2cMap[port].dac, port);
82                                         }
83                                         pNv->i2cMap[port].dac = or;
84                                         break;
85                                 case 1: /* TV */
86                                         break;
87                                 case 2: /* TMDS */
88                                         if(pNv->i2cMap[port].sor != -1)
89                                                 xf86DrvMsg(scrnIndex, X_WARNING,
90                                                         "DDC routing table corrupt!  SOR %i -> %i "
91                                                         "for port %i\n",
92                                                         or, pNv->i2cMap[port].sor, port);
93                                         pNv->i2cMap[port].sor = or;
94                                         break;
95                                 case 3: /* LVDS */
96                                         pNv->lvds.present = TRUE;
97                                         pNv->lvds.or = or;
98                                         break;
99                         }
100                 }
101         }
102
103         xf86DrvMsg(scrnIndex, X_PROBED, "Connector map:\n");
104         if (pNv->lvds.present) {
105                 xf86DrvMsg(scrnIndex, X_PROBED,
106                         "  [N/A] -> SOR%i (LVDS)\n", pNv->lvds.or);
107         }
108          for(i = 0; i < 4; i++) {
109                 if(pNv->i2cMap[i].dac != -1)
110                         xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> DAC%i\n", i, pNv->i2cMap[i].dac);
111                 if(pNv->i2cMap[i].sor != -1)
112                         xf86DrvMsg(scrnIndex, X_PROBED, "  Bus %i -> SOR%i\n", i, pNv->i2cMap[i].sor);
113         }
114
115         return TRUE;
116
117         fail:
118                 xf86DrvMsg(scrnIndex, X_ERROR, "Couldn't find the DDC routing table.  "
119                         "Mode setting will probably fail!\n");
120                 return FALSE;
121 }
122
123 static void NV50_I2CPutBits(I2CBusPtr b, int clock, int data)
124 {
125         NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
126         const int off = b->DriverPrivate.val * 0x18;
127
128         pNv->REGS[(0x0000E138+off)/4] = 4 | clock | data << 1;
129 }
130
131 static void NV50_I2CGetBits(I2CBusPtr b, int *clock, int *data)
132 {
133         NVPtr pNv = NVPTR(xf86Screens[b->scrnIndex]);
134         const int off = b->DriverPrivate.val * 0x18;
135         unsigned char val;
136
137         val = pNv->REGS[(0x0000E138+off)/4];
138         *clock = !!(val & 1);
139         *data = !!(val & 2);
140 }
141
142 static I2CBusPtr
143 NV50I2CInit(ScrnInfoPtr pScrn, const char *name, const int port)
144 {
145         I2CBusPtr i2c;
146
147         /* Allocate the I2C bus structure */
148         i2c = xf86CreateI2CBusRec();
149         if(!i2c) return NULL;
150
151         i2c->BusName = strdup(name);
152         i2c->scrnIndex = pScrn->scrnIndex;
153         i2c->I2CPutBits = NV50_I2CPutBits;
154         i2c->I2CGetBits = NV50_I2CGetBits;
155         i2c->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
156         i2c->StartTimeout = 550;
157         i2c->BitTimeout = 40;
158         i2c->ByteTimeout = 40;
159         i2c->AcknTimeout = 40;
160         i2c->DriverPrivate.val = port;
161
162         if(xf86I2CBusInit(i2c)) {
163                 return i2c;
164         } else {
165                 xfree(i2c);
166                 return NULL;
167         }
168 }
169
170 void
171 NV50OutputSetPClk(xf86OutputPtr output, int pclk)
172 {
173         NVOutputPrivatePtr nv_output = output->driver_private;
174
175         if (nv_output->type == OUTPUT_TMDS)
176                 NV50SorSetPClk(output, pclk);
177
178         if (nv_output->type == OUTPUT_ANALOG)
179                 NV50DacSetPClk(output, pclk);
180 }
181
182 int
183 NV50OutputModeValid(xf86OutputPtr output, DisplayModePtr mode)
184 {
185         if (mode->Clock > 400000)
186                 return MODE_CLOCK_HIGH;
187         if (mode->Clock < 25000)
188                 return MODE_CLOCK_LOW;
189
190         return MODE_OK;
191 }
192
193 void
194 NV50OutputPrepare(xf86OutputPtr output)
195 {
196 }
197
198 void
199 NV50OutputCommit(xf86OutputPtr output)
200 {
201 }
202
203 static xf86MonPtr
204 ProbeDDC(I2CBusPtr i2c)
205 {
206         ScrnInfoPtr pScrn = xf86Screens[i2c->scrnIndex];
207         NVPtr pNv = NVPTR(pScrn);
208         xf86MonPtr monInfo = NULL;
209         const int bus = i2c->DriverPrivate.val, off = bus * 0x18;
210
211         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
212                 "Probing for EDID on I2C bus %i...\n", bus);
213         pNv->REGS[(0x0000E138+off)/4] = 7;
214         /* Should probably use xf86OutputGetEDID here */
215         monInfo = xf86DoEDID_DDC2(pScrn->scrnIndex, i2c);
216         pNv->REGS[(0x0000E138+off)/4] = 3;
217
218         if(monInfo) {
219                 xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
220                         "DDC detected a %s:\n", monInfo->features.input_type ?
221                         "DFP" : "CRT");
222                 xf86PrintEDID(monInfo);
223         } else {
224                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  ... none found\n");
225         }
226
227         return monInfo;
228 }
229
230 DisplayModePtr
231 NV50OutputGetDDCModes(xf86OutputPtr output)
232 {
233         /* The EDID is read as part of the detect step */
234         output->funcs->detect(output);
235         return xf86OutputGetEDIDModes(output);
236 }
237
238 void
239 NV50OutputDestroy(xf86OutputPtr output)
240 {
241         NVOutputPrivatePtr nv_output = output->driver_private;
242
243         if (nv_output->pDDCBus)
244                 xf86DestroyI2CBusRec(nv_output->pDDCBus, TRUE, TRUE);
245
246         nv_output->pDDCBus = NULL;
247 }
248
249 Bool
250 NV50CreateOutputs(ScrnInfoPtr pScrn)
251 {
252         NVPtr pNv = NVPTR(pScrn);
253         xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
254         int i;
255
256         if(!NV50ReadPortMapping(pScrn->scrnIndex, pNv))
257                 return FALSE;
258
259         /* For each DDC port, create an output for the attached ORs */
260         for (i = 0; i < 4; i++) {
261                 xf86OutputPtr dac = NULL, sor = NULL;
262                 I2CBusPtr i2c;
263                 char i2cName[16];
264
265                 if(pNv->i2cMap[i].dac == -1 && pNv->i2cMap[i].sor == -1) {
266                         /* No outputs on this port */
267                         continue;
268                 }
269
270                 snprintf(i2cName, sizeof(i2cName), "I2C%i", i);
271                 i2c = NV50I2CInit(pScrn, i2cName, i);
272                 if (!i2c) {
273                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
274                                 "Failed to initialize I2C for port %i.\n",
275                                 i);
276                         continue;
277                 }
278
279                 if (pNv->i2cMap[i].dac != -1)
280                         dac = NV50CreateDac(pScrn, pNv->i2cMap[i].dac);
281                 if (pNv->i2cMap[i].sor != -1)
282                         sor = NV50CreateSor(pScrn, pNv->i2cMap[i].sor, OUTPUT_TMDS);
283
284                 if (dac) {
285                         NVOutputPrivatePtr nv_output = dac->driver_private;
286
287                         nv_output->pDDCBus = i2c;
288                         nv_output->scaling_mode = SCALE_PANEL;
289                 }
290                 if (sor) {
291                         NVOutputPrivatePtr nv_output = sor->driver_private;
292
293                         nv_output->pDDCBus = i2c;
294                         nv_output->scaling_mode = SCALE_ASPECT;
295                 }
296         }
297
298         if (pNv->lvds.present) {
299                 xf86OutputPtr lvds = NV50CreateSor(pScrn, pNv->lvds.or, OUTPUT_LVDS);
300                 NVOutputPrivatePtr nv_output = lvds->driver_private;
301
302                 nv_output->scaling_mode = SCALE_ASPECT;
303         }
304
305         /* For each output, set the crtc and clone masks */
306         for(i = 0; i < xf86_config->num_output; i++) {
307                 xf86OutputPtr output = xf86_config->output[i];
308
309                 /* Any output can connect to any head */
310                 output->possible_crtcs = 0x3;
311                 output->possible_clones = 0;
312         }
313
314         return TRUE;
315 }
316