Rename relevant functions, sizes and offsets to PRM.IO from P.IO, in keeping with...
[nouveau] / src / nv50_output.c
1 /*
2  * Copyright (c) 2007 NVIDIA, Corporation
3  * Copyright (c) 2008 Maarten Maathuis
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24
25 #include <string.h>
26
27 #include "nv_include.h"
28
29 #include <xf86DDC.h>
30
31 #include "nouveau_crtc.h"
32 #include "nouveau_output.h"
33 #include "nouveau_connector.h"
34
35 int
36 NV50OrOffset(nouveauOutputPtr output)
37 {
38         return ffs(output->dcb->or) - 1;
39 }
40
41 static void
42 NV50OutputInit(ScrnInfoPtr pScrn, int dcb_entry, char *outputname, int bus_count)
43 {
44         NVPtr pNv = NVPTR(pScrn);
45         int i;
46
47         int i2c_index = pNv->dcb_table.entry[dcb_entry].i2c_index;
48         int bus = pNv->dcb_table.entry[dcb_entry].bus;
49
50         char connector_name[20];
51
52         /* I2C buses belong to the connector, but can only inited once we know the outputs. */
53         sprintf(connector_name, "Connector-%d", bus);
54
55         /* Give the connectors better names if possible. */
56         switch (pNv->dcb_table.entry[dcb_entry].type) {
57                 case OUTPUT_LVDS:
58                         sprintf(connector_name, "LVDS-%d", bus);
59                         break;
60                 case OUTPUT_TMDS:
61                         sprintf(connector_name, "DVI-%d", bus);
62                         break;
63                 case OUTPUT_ANALOG:
64                         if (bus_count > 1) /* DVI-I */
65                                 sprintf(connector_name, "DVI-%d", bus);
66                         else
67                                 sprintf(connector_name, "VGA-%d", bus);
68                         break;
69                 case OUTPUT_TV:
70                         sprintf(connector_name, "TV-%d", bus);
71                         break;
72                 default:
73                         break;
74         }
75
76         xfree(pNv->connector[bus]->name);
77         pNv->connector[bus]->name = xstrdup(connector_name);
78
79         if (i2c_index < 0x10 && pNv->pI2CBus[i2c_index] == NULL)
80                 NV_I2CInit(pScrn, &pNv->pI2CBus[i2c_index], pNv->dcb_table.i2c_read[i2c_index], xstrdup(connector_name));
81
82         pNv->connector[bus]->i2c_index = i2c_index;
83         pNv->connector[bus]->pDDCBus = pNv->pI2CBus[i2c_index];
84
85         if (pNv->dcb_table.entry[dcb_entry].type == OUTPUT_TV)
86                 return; /* unsupported */
87
88         /* Create output. */
89         nouveauOutputPtr output = xnfcalloc(sizeof(nouveauOutputRec), 1);
90         output->name = xstrdup(outputname);
91         output->dcb = &pNv->dcb_table.entry[dcb_entry];
92         output->type = pNv->dcb_table.entry[dcb_entry].type;
93         output->scrn = pScrn;
94
95         /* Put the output in the connector's list of outputs. */
96         for (i = 0; i < MAX_OUTPUTS_PER_CONNECTOR; i++) {
97                 if (pNv->connector[bus]->outputs[i]) /* filled */
98                         continue;
99                 pNv->connector[bus]->outputs[i] = output;
100                 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "%s attached with index %d to %s\n", outputname, i, connector_name);
101                 break;
102         }
103
104         /* Put ourselves in the main output list. */
105         if (!pNv->output) {
106                 pNv->output = output;
107         } else {
108                 nouveauOutputPtr output_link = pNv->output;
109                 if (output_link->next) {
110                         do {
111                                 output_link = output_link->next;
112                         } while (output_link->next);
113                 }
114                 output_link->next = output;
115         }
116
117         /* Output property for tmds and lvds. */
118         output->dithering = (pNv->FPDither || (output->type == OUTPUT_LVDS && !pNv->VBIOS.fp.if_is_24bit));
119
120         if (output->type == OUTPUT_LVDS || output->type == OUTPUT_TMDS) {
121                 if (pNv->fpScaler) /* GPU Scaling */
122                         output->scale_mode = SCALE_ASPECT;
123                 else if (output->type == OUTPUT_LVDS)
124                         output->scale_mode = SCALE_NOSCALE;
125                 else
126                         output->scale_mode = SCALE_PANEL;
127
128                 if (xf86GetOptValString(pNv->Options, OPTION_SCALING_MODE)) {
129                         output->scale_mode = nv_scaling_mode_lookup(xf86GetOptValString(pNv->Options, OPTION_SCALING_MODE), -1);
130                         if (output->scale_mode == SCALE_INVALID)
131                                 output->scale_mode = SCALE_ASPECT; /* default */
132                 }
133         }
134
135         /* NV5x scaling hardware seems to work fine for analog too. */
136         if (output->type == OUTPUT_ANALOG) {
137                 output->scale_mode = SCALE_PANEL;
138
139                 if (xf86GetOptValString(pNv->Options, OPTION_SCALING_MODE)) {
140                         output->scale_mode = nv_scaling_mode_lookup(xf86GetOptValString(pNv->Options, OPTION_SCALING_MODE), -1);
141                         if (output->scale_mode == SCALE_INVALID)
142                                 output->scale_mode = SCALE_PANEL; /* default */
143                 }
144         }
145
146         /* Usually 3, which means both crtc's. */
147         output->allowed_crtc = output->dcb->heads;
148
149         if (output->type == OUTPUT_TMDS) {
150                 NVWrite(pNv, NV50_SOR0_UNK00C + NV50OrOffset(output) * 0x800, 0x03010700);
151                 NVWrite(pNv, NV50_SOR0_UNK010 + NV50OrOffset(output) * 0x800, 0x0000152f);
152                 NVWrite(pNv, NV50_SOR0_UNK014 + NV50OrOffset(output) * 0x800, 0x00000000);
153                 NVWrite(pNv, NV50_SOR0_UNK018 + NV50OrOffset(output) * 0x800, 0x00245af8);
154         }
155
156         if (output->type == OUTPUT_LVDS) {
157                 /* now fpindex should be known, so reread the table here. */
158                 /* not that we do a lot with the information. */
159                 parse_lvds_manufacturer_table(pScrn, &pNv->VBIOS, 0);
160         }
161
162         /* This needs to be handled in the same way as pre-NV5x on the long run. */
163         //if (output->type == OUTPUT_LVDS)
164         //      pNv->VBIOS.fp.native_mode = GetLVDSNativeMode(pScrn);
165
166         /* Function pointers. */
167         if (output->type == OUTPUT_TMDS || output->type == OUTPUT_LVDS) {
168                 NV50SorSetFunctionPointers(output);
169         } else if (output->type == OUTPUT_ANALOG || output->type == OUTPUT_TV) {
170                 NV50DacSetFunctionPointers(output);
171         }
172 }
173
174 void
175 NV50OutputSetup(ScrnInfoPtr pScrn)
176 {
177         NVPtr pNv = NVPTR(pScrn);
178         int i, type, i2c_index, bus, bus_count[0xf];
179         char outputname[20];
180         uint32_t index;
181
182         memset(pNv->pI2CBus, 0, sizeof(pNv->pI2CBus));
183         memset(bus_count, 0, sizeof(bus_count));
184
185         for (i = 0 ; i < pNv->dcb_table.entries; i++)
186                 bus_count[pNv->dcb_table.entry[i].bus]++;
187
188         /* we setup the outputs up from the BIOS table */
189         for (i = 0 ; i < pNv->dcb_table.entries; i++) {
190                 type = pNv->dcb_table.entry[i].type;
191                 i2c_index = pNv->dcb_table.entry[i].i2c_index;
192                 bus = pNv->dcb_table.entry[i].bus;
193
194                 xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "DCB entry %d: type: %d, i2c_index: %d, heads: %d, bus: %d, or: %d\n", i, type, pNv->dcb_table.entry[i].i2c_index, pNv->dcb_table.entry[i].heads, pNv->dcb_table.entry[i].bus, pNv->dcb_table.entry[i].or);
195
196                 /* SOR-0, SOR-1, DAC-0, DAC-1 or DAC-2. */
197                 index = ffs(pNv->dcb_table.entry[i].or) - 1;
198
199                 switch (type) {
200                 case OUTPUT_ANALOG:
201                         sprintf(outputname, "DAC-%d", index);
202                         break;
203                 case OUTPUT_TMDS:
204                         sprintf(outputname, "SOR-%d", index);
205                         break;
206                 case OUTPUT_TV: /* this does not handle shared dac's yet. */
207                         sprintf(outputname, "DAC-%d", index);
208                         break;
209                 case OUTPUT_LVDS:
210                         sprintf(outputname, "SOR-%d", index);
211                         break;
212                 default:
213                         xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DCB type %d not known\n", type);
214                         break;
215                 }
216
217                 if (type < OUTPUT_NONE)
218                         NV50OutputInit(pScrn, i, outputname, bus_count[bus]);
219         }
220 }
221
222 void
223 NV50OutputDestroy(ScrnInfoPtr pScrn)
224 {
225         NVPtr pNv = NVPTR(pScrn);
226         nouveauOutputPtr output, next;
227
228         for (output = pNv->output; output != NULL; output = next) {
229                 next = output->next;
230                 xfree(output->name);
231                 xfree(output);
232         }
233
234         pNv->output = NULL;
235 }