]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blame - drivers/gpu/drm/nouveau/nvkm/subdev/mxm/nv50.c
drm/nouveau/bios: convert to new-style nvkm_subdev
[mirror_ubuntu-eoan-kernel.git] / drivers / gpu / drm / nouveau / nvkm / subdev / mxm / nv50.c
CommitLineData
d38ac521
BS
1/*
2 * Copyright 2011 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
be83cd4e 24#include "mxms.h"
d38ac521 25
d38ac521
BS
26#include <subdev/bios.h>
27#include <subdev/bios/conn.h>
28#include <subdev/bios/dcb.h>
29#include <subdev/bios/mxm.h>
30
d38ac521
BS
31struct context {
32 u32 *outp;
33 struct mxms_odev desc;
34};
35
36static bool
be83cd4e 37mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info)
d38ac521
BS
38{
39 struct context *ctx = info;
40 struct mxms_odev desc;
41
42 mxms_output_device(mxm, data, &desc);
43 if (desc.outp_type == 2 &&
44 desc.dig_conn == ctx->desc.dig_conn)
45 return false;
46 return true;
47}
48
49static bool
be83cd4e 50mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info)
d38ac521 51{
46484438 52 struct nvkm_bios *bios = mxm->subdev.device->bios;
d38ac521
BS
53 struct context *ctx = info;
54 u64 desc = *(u64 *)data;
55
56 mxms_output_device(mxm, data, &ctx->desc);
57
58 /* match dcb encoder type to mxm-ods device type */
59 if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
60 return true;
61
62 /* digital output, have some extra stuff to match here, there's a
63 * table in the vbios that provides a mapping from the mxm digital
64 * connection enum values to SOR/link
65 */
66 if ((desc & 0x00000000000000f0) >= 0x20) {
67 /* check against sor index */
68 u8 link = mxm_sor_map(bios, ctx->desc.dig_conn);
69 if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24)
70 return true;
71
72 /* check dcb entry has a compatible link field */
73 link = (link & 0x30) >> 4;
74 if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
75 return true;
76 }
77
78 /* mark this descriptor accounted for by setting invalid device type,
79 * except of course some manufactures don't follow specs properly and
80 * we need to avoid killing off the TMDS function on DP connectors
81 * if MXM-SIS is missing an entry for it.
82 */
83 data[0] &= ~0xf0;
84 if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 &&
85 mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) {
86 data[0] |= 0x20; /* modify descriptor to match TMDS now */
87 } else {
88 data[0] |= 0xf0;
89 }
90
91 return false;
92}
93
94static int
be83cd4e 95mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb)
d38ac521 96{
be83cd4e 97 struct nvkm_mxm *mxm = data;
d38ac521
BS
98 struct context ctx = { .outp = (u32 *)(bios->data + pdcb) };
99 u8 type, i2cidx, link, ver, len;
100 u8 *conn;
101
102 /* look for an output device structure that matches this dcb entry.
103 * if one isn't found, disable it.
104 */
105 if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) {
27cc60a1
BS
106 nvkm_debug(&mxm->subdev, "disable %d: %08x %08x\n",
107 idx, ctx.outp[0], ctx.outp[1]);
d38ac521
BS
108 ctx.outp[0] |= 0x0000000f;
109 return 0;
110 }
111
112 /* modify the output's ddc/aux port, there's a pointer to a table
113 * with the mapping from mxm ddc/aux port to dcb i2c_index in the
114 * vbios mxm table
115 */
116 i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port);
117 if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP)
118 i2cidx = (i2cidx & 0x0f) << 4;
119 else
120 i2cidx = (i2cidx & 0xf0);
121
122 if (i2cidx != 0xf0) {
123 ctx.outp[0] &= ~0x000000f0;
124 ctx.outp[0] |= i2cidx;
125 }
126
127 /* override dcb sorconf.link, based on what mxm data says */
128 switch (ctx.desc.outp_type) {
129 case 0x00: /* Analog CRT */
130 case 0x01: /* Analog TV/HDTV */
131 break;
132 default:
133 link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30;
134 ctx.outp[1] &= ~0x00000030;
135 ctx.outp[1] |= link;
136 break;
137 }
138
139 /* we may need to fixup various other vbios tables based on what
140 * the descriptor says the connector type should be.
141 *
142 * in a lot of cases, the vbios tables will claim DVI-I is possible,
143 * and the mxm data says the connector is really HDMI. another
144 * common example is DP->eDP.
145 */
146 conn = bios->data;
20014cbe 147 conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
d38ac521
BS
148 type = conn[0];
149 switch (ctx.desc.conn_type) {
150 case 0x01: /* LVDS */
151 ctx.outp[1] |= 0x00000004; /* use_power_scripts */
152 /* XXX: modify default link width in LVDS table */
153 break;
154 case 0x02: /* HDMI */
155 type = DCB_CONNECTOR_HDMI_1;
156 break;
157 case 0x03: /* DVI-D */
158 type = DCB_CONNECTOR_DVI_D;
159 break;
160 case 0x0e: /* eDP, falls through to DPint */
161 ctx.outp[1] |= 0x00010000;
162 case 0x07: /* DP internal, wtf is this?? HP8670w */
163 ctx.outp[1] |= 0x00000004; /* use_power_scripts? */
164 type = DCB_CONNECTOR_eDP;
165 break;
166 default:
167 break;
168 }
169
170 if (mxms_version(mxm) >= 0x0300)
171 conn[0] = type;
172
173 return 0;
174}
175
176static bool
be83cd4e 177mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info)
d38ac521 178{
27cc60a1 179 struct nvkm_subdev *subdev = &mxm->subdev;
d38ac521
BS
180 u64 desc = *(u64 *)data;
181 if ((desc & 0xf0) != 0xf0)
27cc60a1 182 nvkm_info(subdev, "unmatched output device %016llx\n", desc);
d38ac521
BS
183 return true;
184}
185
186static void
be83cd4e 187mxm_dcb_sanitise(struct nvkm_mxm *mxm)
d38ac521 188{
27cc60a1
BS
189 struct nvkm_subdev *subdev = &mxm->subdev;
190 struct nvkm_bios *bios = subdev->device->bios;
d38ac521
BS
191 u8 ver, hdr, cnt, len;
192 u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len);
193 if (dcb == 0x0000 || ver != 0x40) {
27cc60a1 194 nvkm_debug(subdev, "unsupported DCB version\n");
d38ac521
BS
195 return;
196 }
197
72de1823 198 dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry);
d38ac521
BS
199 mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL);
200}
201
202static int
be83cd4e
BS
203nv50_mxm_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
204 struct nvkm_oclass *oclass, void *data, u32 size,
205 struct nvkm_object **pobject)
d38ac521 206{
2d9d5889 207 struct nvkm_mxm *mxm;
d38ac521
BS
208 int ret;
209
2d9d5889
BS
210 ret = nvkm_mxm_create(parent, engine, oclass, &mxm);
211 *pobject = nv_object(mxm);
d38ac521
BS
212 if (ret)
213 return ret;
214
2d9d5889
BS
215 if (mxm->action & MXM_SANITISE_DCB)
216 mxm_dcb_sanitise(mxm);
d38ac521
BS
217 return 0;
218}
219
be83cd4e 220struct nvkm_oclass
d38ac521
BS
221nv50_mxm_oclass = {
222 .handle = NV_SUBDEV(MXM, 0x50),
be83cd4e 223 .ofuncs = &(struct nvkm_ofuncs) {
d38ac521 224 .ctor = nv50_mxm_ctor,
be83cd4e
BS
225 .dtor = _nvkm_mxm_dtor,
226 .init = _nvkm_mxm_init,
227 .fini = _nvkm_mxm_fini,
d38ac521
BS
228 },
229};