diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-03-10 10:28:48 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-03-13 03:15:04 -0400 |
commit | 6e83fda2c055f17780b2feef404f06803a49a261 (patch) | |
tree | d736720740ef69addd8911060629d987d1822705 /drivers/gpu/drm/nouveau | |
parent | f14d9a4dda65439d74326694db727c6d2a5df0ce (diff) |
drm/nvd0/disp: initial implementation of displayport
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvd0_display.c | 169 |
2 files changed, 171 insertions, 4 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7be4e06e8438..b3644651d89b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c | |||
@@ -364,10 +364,8 @@ dp_set_downspread(struct drm_device *dev, struct dp_state *dp, bool enable) | |||
364 | u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); | 364 | u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); |
365 | if (table) { | 365 | if (table) { |
366 | if (table[0] >= 0x20 && table[0] <= 0x30) { | 366 | if (table[0] >= 0x20 && table[0] <= 0x30) { |
367 | if (enable) | 367 | if (enable) script = ROM16(entry[12]); |
368 | script = ROM16(entry[12]); | 368 | else script = ROM16(entry[14]); |
369 | else | ||
370 | script = ROM16(entry[14]); | ||
371 | } | 369 | } |
372 | } | 370 | } |
373 | 371 | ||
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 296f205249fc..1723733ad9fa 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c | |||
@@ -1183,6 +1183,143 @@ nvd0_hdmi_disconnect(struct drm_encoder *encoder) | |||
1183 | /****************************************************************************** | 1183 | /****************************************************************************** |
1184 | * SOR | 1184 | * SOR |
1185 | *****************************************************************************/ | 1185 | *****************************************************************************/ |
1186 | static inline u32 | ||
1187 | nvd0_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) | ||
1188 | { | ||
1189 | static const u8 nvd0[] = { 16, 8, 0, 24 }; | ||
1190 | return nvd0[lane]; | ||
1191 | } | ||
1192 | |||
1193 | static void | ||
1194 | nvd0_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern) | ||
1195 | { | ||
1196 | const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
1197 | const u32 loff = (or * 0x800) + (link * 0x80); | ||
1198 | nv_mask(dev, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); | ||
1199 | } | ||
1200 | |||
1201 | static void | ||
1202 | nvd0_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb, | ||
1203 | u8 lane, u8 swing, u8 preem) | ||
1204 | { | ||
1205 | const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
1206 | const u32 loff = (or * 0x800) + (link * 0x80); | ||
1207 | u32 shift = nvd0_sor_dp_lane_map(dev, dcb, lane); | ||
1208 | u32 mask = 0x000000ff << shift; | ||
1209 | u8 *table, *entry, *config = NULL; | ||
1210 | |||
1211 | switch (swing) { | ||
1212 | case 0: preem += 0; break; | ||
1213 | case 1: preem += 4; break; | ||
1214 | case 2: preem += 7; break; | ||
1215 | case 3: preem += 9; break; | ||
1216 | } | ||
1217 | |||
1218 | table = nouveau_dp_bios_data(dev, dcb, &entry); | ||
1219 | if (table) { | ||
1220 | if (table[0] == 0x30) { | ||
1221 | config = entry + table[4]; | ||
1222 | config += table[5] * preem; | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | if (!config) { | ||
1227 | NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); | ||
1228 | return; | ||
1229 | } | ||
1230 | |||
1231 | nv_mask(dev, 0x61c118 + loff, mask, config[1] << shift); | ||
1232 | nv_mask(dev, 0x61c120 + loff, mask, config[2] << shift); | ||
1233 | nv_mask(dev, 0x61c130 + loff, 0x0000ff00, config[3] << 8); | ||
1234 | nv_mask(dev, 0x61c13c + loff, 0x00000000, 0x00000000); | ||
1235 | } | ||
1236 | |||
1237 | static void | ||
1238 | nvd0_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc, | ||
1239 | int link_nr, u32 link_bw, bool enhframe) | ||
1240 | { | ||
1241 | const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
1242 | const u32 loff = (or * 0x800) + (link * 0x80); | ||
1243 | const u32 soff = (or * 0x800); | ||
1244 | u32 dpctrl = nv_rd32(dev, 0x61c10c + loff) & ~0x001f4000; | ||
1245 | u32 clksor = nv_rd32(dev, 0x612300 + soff) & ~0x007c0000; | ||
1246 | u32 script = 0x0000, lane_mask = 0; | ||
1247 | u8 *table, *entry; | ||
1248 | int i; | ||
1249 | |||
1250 | link_bw /= 27000; | ||
1251 | |||
1252 | table = nouveau_dp_bios_data(dev, dcb, &entry); | ||
1253 | if (table) { | ||
1254 | if (table[0] == 0x30) entry = ROMPTR(dev, entry[10]); | ||
1255 | else entry = NULL; | ||
1256 | |||
1257 | while (entry) { | ||
1258 | if (entry[0] >= link_bw) | ||
1259 | break; | ||
1260 | entry += 3; | ||
1261 | } | ||
1262 | |||
1263 | nouveau_bios_run_init_table(dev, script, dcb, crtc); | ||
1264 | } | ||
1265 | |||
1266 | clksor |= link_bw << 18; | ||
1267 | dpctrl |= ((1 << link_nr) - 1) << 16; | ||
1268 | if (enhframe) | ||
1269 | dpctrl |= 0x00004000; | ||
1270 | |||
1271 | for (i = 0; i < link_nr; i++) | ||
1272 | lane_mask |= 1 << (nvd0_sor_dp_lane_map(dev, dcb, i) >> 3); | ||
1273 | |||
1274 | nv_wr32(dev, 0x612300 + soff, clksor); | ||
1275 | nv_wr32(dev, 0x61c10c + loff, dpctrl); | ||
1276 | nv_mask(dev, 0x61c130 + loff, 0x0000000f, lane_mask); | ||
1277 | } | ||
1278 | |||
1279 | static void | ||
1280 | nvd0_sor_dp_link_get(struct drm_device *dev, struct dcb_entry *dcb, | ||
1281 | u32 *link_nr, u32 *link_bw) | ||
1282 | { | ||
1283 | const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
1284 | const u32 loff = (or * 0x800) + (link * 0x80); | ||
1285 | const u32 soff = (or * 0x800); | ||
1286 | u32 dpctrl = nv_rd32(dev, 0x61c10c + loff) & 0x000f0000; | ||
1287 | u32 clksor = nv_rd32(dev, 0x612300 + soff); | ||
1288 | |||
1289 | if (dpctrl > 0x00030000) *link_nr = 4; | ||
1290 | else if (dpctrl > 0x00010000) *link_nr = 2; | ||
1291 | else *link_nr = 1; | ||
1292 | |||
1293 | *link_bw = (clksor & 0x007c0000) >> 18; | ||
1294 | *link_bw *= 27000; | ||
1295 | } | ||
1296 | |||
1297 | static void | ||
1298 | nvd0_sor_dp_calc_tu(struct drm_device *dev, struct dcb_entry *dcb, | ||
1299 | u32 crtc, u32 datarate) | ||
1300 | { | ||
1301 | const u32 symbol = 100000; | ||
1302 | const u32 TU = 64; | ||
1303 | u32 link_nr, link_bw; | ||
1304 | u64 ratio, value; | ||
1305 | |||
1306 | nvd0_sor_dp_link_get(dev, dcb, &link_nr, &link_bw); | ||
1307 | |||
1308 | ratio = datarate; | ||
1309 | ratio *= symbol; | ||
1310 | do_div(ratio, link_nr * link_bw); | ||
1311 | |||
1312 | value = (symbol - ratio) * TU; | ||
1313 | value *= ratio; | ||
1314 | do_div(value, symbol); | ||
1315 | do_div(value, symbol); | ||
1316 | |||
1317 | value += 5; | ||
1318 | value |= 0x08000000; | ||
1319 | |||
1320 | nv_wr32(dev, 0x616610 + (crtc * 0x800), value); | ||
1321 | } | ||
1322 | |||
1186 | static void | 1323 | static void |
1187 | nvd0_sor_dpms(struct drm_encoder *encoder, int mode) | 1324 | nvd0_sor_dpms(struct drm_encoder *encoder, int mode) |
1188 | { | 1325 | { |
@@ -1215,6 +1352,16 @@ nvd0_sor_dpms(struct drm_encoder *encoder, int mode) | |||
1215 | nv_mask(dev, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl); | 1352 | nv_mask(dev, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl); |
1216 | nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); | 1353 | nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); |
1217 | nv_wait(dev, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000); | 1354 | nv_wait(dev, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000); |
1355 | |||
1356 | if (nv_encoder->dcb->type == OUTPUT_DP) { | ||
1357 | struct dp_train_func func = { | ||
1358 | .link_set = nvd0_sor_dp_link_set, | ||
1359 | .train_set = nvd0_sor_dp_train_set, | ||
1360 | .train_adj = nvd0_sor_dp_train_adj | ||
1361 | }; | ||
1362 | |||
1363 | nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); | ||
1364 | } | ||
1218 | } | 1365 | } |
1219 | 1366 | ||
1220 | static bool | 1367 | static bool |
@@ -1306,6 +1453,19 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, | |||
1306 | 1453 | ||
1307 | } | 1454 | } |
1308 | break; | 1455 | break; |
1456 | case OUTPUT_DP: | ||
1457 | if (nv_connector->base.display_info.bpc == 6) | ||
1458 | nv_encoder->dp.datarate = mode->clock * 18 / 8; | ||
1459 | else | ||
1460 | nv_encoder->dp.datarate = mode->clock * 24 / 8; | ||
1461 | |||
1462 | if (nv_encoder->dcb->sorconf.link & 1) | ||
1463 | mode_ctrl |= 0x00000800; | ||
1464 | else | ||
1465 | mode_ctrl |= 0x00000900; | ||
1466 | |||
1467 | or_config = (mode_ctrl & 0x00000f00) >> 8; | ||
1468 | break; | ||
1309 | default: | 1469 | default: |
1310 | BUG_ON(1); | 1470 | BUG_ON(1); |
1311 | break; | 1471 | break; |
@@ -1313,6 +1473,11 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, | |||
1313 | 1473 | ||
1314 | nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); | 1474 | nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); |
1315 | 1475 | ||
1476 | if (nv_encoder->dcb->type == OUTPUT_DP) { | ||
1477 | nvd0_sor_dp_calc_tu(dev, nv_encoder->dcb, nv_crtc->index, | ||
1478 | nv_encoder->dp.datarate); | ||
1479 | } | ||
1480 | |||
1316 | push = evo_wait(dev, EVO_MASTER, 4); | 1481 | push = evo_wait(dev, EVO_MASTER, 4); |
1317 | if (push) { | 1482 | if (push) { |
1318 | evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); | 1483 | evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2); |
@@ -1413,6 +1578,8 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc) | |||
1413 | case 0x00000100: type = OUTPUT_TMDS; break; | 1578 | case 0x00000100: type = OUTPUT_TMDS; break; |
1414 | case 0x00000200: type = OUTPUT_TMDS; break; | 1579 | case 0x00000200: type = OUTPUT_TMDS; break; |
1415 | case 0x00000500: type = OUTPUT_TMDS; break; | 1580 | case 0x00000500: type = OUTPUT_TMDS; break; |
1581 | case 0x00000800: type = OUTPUT_DP; break; | ||
1582 | case 0x00000900: type = OUTPUT_DP; break; | ||
1416 | default: | 1583 | default: |
1417 | NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc); | 1584 | NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc); |
1418 | return NULL; | 1585 | return NULL; |
@@ -1498,6 +1665,7 @@ nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask) | |||
1498 | break; | 1665 | break; |
1499 | case OUTPUT_TMDS: | 1666 | case OUTPUT_TMDS: |
1500 | case OUTPUT_LVDS: | 1667 | case OUTPUT_LVDS: |
1668 | case OUTPUT_DP: | ||
1501 | if (cfg & 0x00000100) | 1669 | if (cfg & 0x00000100) |
1502 | tmp = 0x00000101; | 1670 | tmp = 0x00000101; |
1503 | else | 1671 | else |
@@ -1798,6 +1966,7 @@ nvd0_display_create(struct drm_device *dev) | |||
1798 | switch (dcbe->type) { | 1966 | switch (dcbe->type) { |
1799 | case OUTPUT_TMDS: | 1967 | case OUTPUT_TMDS: |
1800 | case OUTPUT_LVDS: | 1968 | case OUTPUT_LVDS: |
1969 | case OUTPUT_DP: | ||
1801 | nvd0_sor_create(connector, dcbe); | 1970 | nvd0_sor_create(connector, dcbe); |
1802 | break; | 1971 | break; |
1803 | case OUTPUT_ANALOG: | 1972 | case OUTPUT_ANALOG: |