2 * Copyright (c) 2007 NVIDIA, Corporation
3 * Copyright (c) 2008 Maarten Maathuis
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:
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
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.
27 #include "nv_include.h"
31 #include "nouveau_crtc.h"
32 #include "nouveau_output.h"
33 #include "nouveau_connector.h"
36 NV50OrOffset(nouveauOutputPtr output)
38 return ffs(output->dcb->or) - 1;
42 NV50OutputInit(ScrnInfoPtr pScrn, int dcb_entry, char *outputname, int bus_count)
44 NVPtr pNv = NVPTR(pScrn);
47 int i2c_index = pNv->dcb_table.entry[dcb_entry].i2c_index;
48 int bus = pNv->dcb_table.entry[dcb_entry].bus;
50 char connector_name[20];
52 /* I2C buses belong to the connector, but can only inited once we know the outputs. */
53 sprintf(connector_name, "Connector-%d", bus);
55 /* Give the connectors better names if possible. */
56 switch (pNv->dcb_table.entry[dcb_entry].type) {
58 sprintf(connector_name, "LVDS-%d", bus);
61 sprintf(connector_name, "DVI-%d", bus);
64 if (bus_count > 1) /* DVI-I */
65 sprintf(connector_name, "DVI-%d", bus);
67 sprintf(connector_name, "VGA-%d", bus);
70 sprintf(connector_name, "TV-%d", bus);
76 xfree(pNv->connector[bus]->name);
77 pNv->connector[bus]->name = xstrdup(connector_name);
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));
82 pNv->connector[bus]->i2c_index = i2c_index;
83 pNv->connector[bus]->pDDCBus = pNv->pI2CBus[i2c_index];
85 if (pNv->dcb_table.entry[dcb_entry].type == OUTPUT_TV)
86 return; /* unsupported */
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;
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 */
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);
104 /* Put ourselves in the main output list. */
106 pNv->output = output;
108 nouveauOutputPtr output_link = pNv->output;
109 if (output_link->next) {
111 output_link = output_link->next;
112 } while (output_link->next);
114 output_link->next = output;
117 /* Output property for tmds and lvds. */
118 output->dithering = (pNv->FPDither || (output->type == OUTPUT_LVDS && !pNv->VBIOS.fp.if_is_24bit));
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;
126 output->scale_mode = SCALE_PANEL;
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 */
135 /* NV5x scaling hardware seems to work fine for analog too. */
136 if (output->type == OUTPUT_ANALOG) {
137 output->scale_mode = SCALE_PANEL;
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 */
146 /* Usually 3, which means both crtc's. */
147 output->allowed_crtc = output->dcb->heads;
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);
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);
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);
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);
175 NV50OutputSetup(ScrnInfoPtr pScrn)
177 NVPtr pNv = NVPTR(pScrn);
178 int i, type, i2c_index, bus, bus_count[0xf];
182 memset(pNv->pI2CBus, 0, sizeof(pNv->pI2CBus));
183 memset(bus_count, 0, sizeof(bus_count));
185 for (i = 0 ; i < pNv->dcb_table.entries; i++)
186 bus_count[pNv->dcb_table.entry[i].bus]++;
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;
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);
196 /* SOR-0, SOR-1, DAC-0, DAC-1 or DAC-2. */
197 index = ffs(pNv->dcb_table.entry[i].or) - 1;
201 sprintf(outputname, "DAC-%d", index);
204 sprintf(outputname, "SOR-%d", index);
206 case OUTPUT_TV: /* this does not handle shared dac's yet. */
207 sprintf(outputname, "DAC-%d", index);
210 sprintf(outputname, "SOR-%d", index);
213 xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DCB type %d not known\n", type);
217 if (type < OUTPUT_NONE)
218 NV50OutputInit(pScrn, i, outputname, bus_count[bus]);
223 NV50OutputDestroy(ScrnInfoPtr pScrn)
225 NVPtr pNv = NVPTR(pScrn);
226 nouveauOutputPtr output, next;
228 for (output = pNv->output; output != NULL; output = next) {