diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2013-02-10 18:24:23 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2013-02-20 01:01:04 -0500 |
commit | 476e84e126171d809f9c0b5d97137f5055f95ca8 (patch) | |
tree | 1bcabb855c82aa7e42572f4e8a6abe52c37030aa | |
parent | a2bc283f3905389ba53962a2bbb05ede0c16193d (diff) |
drm/nv50-/disp: initial supervisor support for off-chip encoders
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 124 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/init.c | 9 |
2 files changed, 100 insertions, 33 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 129815319974..314dda6d7cb8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | |||
@@ -626,7 +626,7 @@ nv50_disp_base_init(struct nouveau_object *object) | |||
626 | nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp); | 626 | nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp); |
627 | } | 627 | } |
628 | 628 | ||
629 | /* ... EXT caps */ | 629 | /* ... PIOR caps */ |
630 | for (i = 0; i < 3; i++) { | 630 | for (i = 0; i < 3; i++) { |
631 | tmp = nv_rd32(priv, 0x61e000 + (i * 0x800)); | 631 | tmp = nv_rd32(priv, 0x61e000 + (i * 0x800)); |
632 | nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp); | 632 | nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp); |
@@ -783,8 +783,8 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, | |||
783 | if (outp < 4) { | 783 | if (outp < 4) { |
784 | type = DCB_OUTPUT_ANALOG; | 784 | type = DCB_OUTPUT_ANALOG; |
785 | mask = 0; | 785 | mask = 0; |
786 | } else { | 786 | } else |
787 | outp -= 4; | 787 | if (outp < 8) { |
788 | switch (ctrl & 0x00000f00) { | 788 | switch (ctrl & 0x00000f00) { |
789 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; | 789 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; |
790 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; | 790 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; |
@@ -796,6 +796,17 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, | |||
796 | nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); | 796 | nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); |
797 | return 0x0000; | 797 | return 0x0000; |
798 | } | 798 | } |
799 | outp -= 4; | ||
800 | } else { | ||
801 | outp = outp - 8; | ||
802 | type = 0x0010; | ||
803 | mask = 0; | ||
804 | switch (ctrl & 0x00000f00) { | ||
805 | case 0x00000000: type |= priv->pior.type[outp]; break; | ||
806 | default: | ||
807 | nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl); | ||
808 | return 0x0000; | ||
809 | } | ||
799 | } | 810 | } |
800 | 811 | ||
801 | mask = 0x00c0 & (mask << 6); | 812 | mask = 0x00c0 & (mask << 6); |
@@ -806,6 +817,10 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, | |||
806 | if (!data) | 817 | if (!data) |
807 | return 0x0000; | 818 | return 0x0000; |
808 | 819 | ||
820 | /* off-chip encoders require matching the exact encoder type */ | ||
821 | if (dcb->location != 0) | ||
822 | type |= dcb->extdev << 8; | ||
823 | |||
809 | return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); | 824 | return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); |
810 | } | 825 | } |
811 | 826 | ||
@@ -820,9 +835,11 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) | |||
820 | u32 ctrl = 0x00000000; | 835 | u32 ctrl = 0x00000000; |
821 | int i; | 836 | int i; |
822 | 837 | ||
838 | /* DAC */ | ||
823 | for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) | 839 | for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) |
824 | ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); | 840 | ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); |
825 | 841 | ||
842 | /* SOR */ | ||
826 | if (!(ctrl & (1 << head))) { | 843 | if (!(ctrl & (1 << head))) { |
827 | if (nv_device(priv)->chipset < 0x90 || | 844 | if (nv_device(priv)->chipset < 0x90 || |
828 | nv_device(priv)->chipset == 0x92 || | 845 | nv_device(priv)->chipset == 0x92 || |
@@ -837,6 +854,13 @@ exec_script(struct nv50_disp_priv *priv, int head, int id) | |||
837 | } | 854 | } |
838 | } | 855 | } |
839 | 856 | ||
857 | /* PIOR */ | ||
858 | if (!(ctrl & (1 << head))) { | ||
859 | for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) | ||
860 | ctrl = nv_rd32(priv, 0x610b84 + (i * 8)); | ||
861 | i += 8; | ||
862 | } | ||
863 | |||
840 | if (!(ctrl & (1 << head))) | 864 | if (!(ctrl & (1 << head))) |
841 | return false; | 865 | return false; |
842 | i--; | 866 | i--; |
@@ -870,9 +894,11 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, | |||
870 | u32 data, conf = ~0; | 894 | u32 data, conf = ~0; |
871 | int i; | 895 | int i; |
872 | 896 | ||
897 | /* DAC */ | ||
873 | for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) | 898 | for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) |
874 | ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); | 899 | ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); |
875 | 900 | ||
901 | /* SOR */ | ||
876 | if (!(ctrl & (1 << head))) { | 902 | if (!(ctrl & (1 << head))) { |
877 | if (nv_device(priv)->chipset < 0x90 || | 903 | if (nv_device(priv)->chipset < 0x90 || |
878 | nv_device(priv)->chipset == 0x92 || | 904 | nv_device(priv)->chipset == 0x92 || |
@@ -887,6 +913,13 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, | |||
887 | } | 913 | } |
888 | } | 914 | } |
889 | 915 | ||
916 | /* PIOR */ | ||
917 | if (!(ctrl & (1 << head))) { | ||
918 | for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) | ||
919 | ctrl = nv_rd32(priv, 0x610b80 + (i * 8)); | ||
920 | i += 8; | ||
921 | } | ||
922 | |||
890 | if (!(ctrl & (1 << head))) | 923 | if (!(ctrl & (1 << head))) |
891 | return conf; | 924 | return conf; |
892 | i--; | 925 | i--; |
@@ -895,22 +928,27 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, | |||
895 | if (!data) | 928 | if (!data) |
896 | return conf; | 929 | return conf; |
897 | 930 | ||
898 | switch (outp->type) { | 931 | if (outp->location == 0) { |
899 | case DCB_OUTPUT_TMDS: | 932 | switch (outp->type) { |
900 | conf = (ctrl & 0x00000f00) >> 8; | 933 | case DCB_OUTPUT_TMDS: |
901 | if (pclk >= 165000) | 934 | conf = (ctrl & 0x00000f00) >> 8; |
902 | conf |= 0x0100; | 935 | if (pclk >= 165000) |
903 | break; | 936 | conf |= 0x0100; |
904 | case DCB_OUTPUT_LVDS: | 937 | break; |
905 | conf = priv->sor.lvdsconf; | 938 | case DCB_OUTPUT_LVDS: |
906 | break; | 939 | conf = priv->sor.lvdsconf; |
907 | case DCB_OUTPUT_DP: | 940 | break; |
941 | case DCB_OUTPUT_DP: | ||
942 | conf = (ctrl & 0x00000f00) >> 8; | ||
943 | break; | ||
944 | case DCB_OUTPUT_ANALOG: | ||
945 | default: | ||
946 | conf = 0x00ff; | ||
947 | break; | ||
948 | } | ||
949 | } else { | ||
908 | conf = (ctrl & 0x00000f00) >> 8; | 950 | conf = (ctrl & 0x00000f00) >> 8; |
909 | break; | 951 | pclk = pclk / 2; |
910 | case DCB_OUTPUT_ANALOG: | ||
911 | default: | ||
912 | conf = 0x00ff; | ||
913 | break; | ||
914 | } | 952 | } |
915 | 953 | ||
916 | data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); | 954 | data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); |
@@ -1057,7 +1095,6 @@ static void | |||
1057 | nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) | 1095 | nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) |
1058 | { | 1096 | { |
1059 | struct dcb_output outp; | 1097 | struct dcb_output outp; |
1060 | u32 addr, mask, data; | ||
1061 | int head; | 1098 | int head; |
1062 | 1099 | ||
1063 | /* finish detaching encoder? */ | 1100 | /* finish detaching encoder? */ |
@@ -1073,14 +1110,14 @@ nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) | |||
1073 | struct nouveau_clock *clk = nouveau_clock(priv); | 1110 | struct nouveau_clock *clk = nouveau_clock(priv); |
1074 | clk->pll_set(clk, PLL_VPLL0 + head, pclk); | 1111 | clk->pll_set(clk, PLL_VPLL0 + head, pclk); |
1075 | } | 1112 | } |
1076 | |||
1077 | nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000); | ||
1078 | } | 1113 | } |
1079 | 1114 | ||
1080 | /* (re)attach the relevant OR to the head */ | 1115 | /* (re)attach the relevant OR to the head */ |
1081 | head = ffs((super & 0x00000180) >> 7) - 1; | 1116 | head = ffs((super & 0x00000180) >> 7) - 1; |
1082 | if (head >= 0) { | 1117 | if (head >= 0) { |
1083 | u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; | 1118 | u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; |
1119 | u32 hval, hreg = 0x614200 + (head * 0x800); | ||
1120 | u32 oval, oreg; | ||
1084 | u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); | 1121 | u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); |
1085 | if (conf != ~0) { | 1122 | if (conf != ~0) { |
1086 | if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { | 1123 | if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { |
@@ -1103,19 +1140,25 @@ nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) | |||
1103 | 1140 | ||
1104 | exec_clkcmp(priv, head, 0, pclk, &outp); | 1141 | exec_clkcmp(priv, head, 0, pclk, &outp); |
1105 | 1142 | ||
1106 | if (outp.type == DCB_OUTPUT_ANALOG) { | 1143 | if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) { |
1107 | addr = 0x614280 + (ffs(outp.or) - 1) * 0x800; | 1144 | oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800; |
1108 | mask = 0xffffffff; | 1145 | oval = 0x00000000; |
1109 | data = 0x00000000; | 1146 | hval = 0x00000000; |
1110 | } else { | 1147 | } else |
1148 | if (!outp.location) { | ||
1111 | if (outp.type == DCB_OUTPUT_DP) | 1149 | if (outp.type == DCB_OUTPUT_DP) |
1112 | nv50_disp_intr_unk20_dp(priv, &outp, pclk); | 1150 | nv50_disp_intr_unk20_dp(priv, &outp, pclk); |
1113 | addr = 0x614300 + (ffs(outp.or) - 1) * 0x800; | 1151 | oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800; |
1114 | mask = 0x00000707; | 1152 | oval = (conf & 0x0100) ? 0x0101 : 0x0000; |
1115 | data = (conf & 0x0100) ? 0x0101 : 0x0000; | 1153 | hval = 0x00000000; |
1154 | } else { | ||
1155 | oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800; | ||
1156 | oval = 0x00000001; | ||
1157 | hval = 0x00000001; | ||
1116 | } | 1158 | } |
1117 | 1159 | ||
1118 | nv_mask(priv, addr, mask, data); | 1160 | nv_mask(priv, hreg, 0x0000000f, hval); |
1161 | nv_mask(priv, oreg, 0x00000707, oval); | ||
1119 | } | 1162 | } |
1120 | } | 1163 | } |
1121 | 1164 | ||
@@ -1151,9 +1194,28 @@ nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super) | |||
1151 | if (head >= 0) { | 1194 | if (head >= 0) { |
1152 | struct dcb_output outp; | 1195 | struct dcb_output outp; |
1153 | u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; | 1196 | u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; |
1154 | if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) | 1197 | if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) { |
1155 | if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS) | 1198 | if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS) |
1156 | nv50_disp_intr_unk40_tmds(priv, &outp); | 1199 | nv50_disp_intr_unk40_tmds(priv, &outp); |
1200 | else | ||
1201 | if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) { | ||
1202 | u32 soff = (ffs(outp.or) - 1) * 0x08; | ||
1203 | u32 ctrl = nv_rd32(priv, 0x610b84 + soff); | ||
1204 | u32 datarate; | ||
1205 | |||
1206 | switch ((ctrl & 0x000f0000) >> 16) { | ||
1207 | case 6: datarate = pclk * 30 / 8; break; | ||
1208 | case 5: datarate = pclk * 24 / 8; break; | ||
1209 | case 2: | ||
1210 | default: | ||
1211 | datarate = pclk * 18 / 8; | ||
1212 | break; | ||
1213 | } | ||
1214 | |||
1215 | nouveau_dp_train(&priv->base, priv->pior.dp, | ||
1216 | &outp, head, datarate); | ||
1217 | } | ||
1218 | } | ||
1157 | } | 1219 | } |
1158 | 1220 | ||
1159 | nv_wr32(priv, 0x610030, 0x80000000); | 1221 | nv_wr32(priv, 0x610030, 0x80000000); |
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 7d2ce11cf36a..2cc1e6a5eb6a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c | |||
@@ -231,6 +231,11 @@ init_i2c(struct nvbios_init *init, int index) | |||
231 | return NULL; | 231 | return NULL; |
232 | } | 232 | } |
233 | 233 | ||
234 | if (index == -2 && init->outp->location) { | ||
235 | index = NV_I2C_TYPE_EXTAUX(init->outp->extdev); | ||
236 | return i2c->find_type(i2c, index); | ||
237 | } | ||
238 | |||
234 | index = init->outp->i2c_index; | 239 | index = init->outp->i2c_index; |
235 | } | 240 | } |
236 | 241 | ||
@@ -258,7 +263,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val) | |||
258 | static int | 263 | static int |
259 | init_rdauxr(struct nvbios_init *init, u32 addr) | 264 | init_rdauxr(struct nvbios_init *init, u32 addr) |
260 | { | 265 | { |
261 | struct nouveau_i2c_port *port = init_i2c(init, -1); | 266 | struct nouveau_i2c_port *port = init_i2c(init, -2); |
262 | u8 data; | 267 | u8 data; |
263 | 268 | ||
264 | if (port && init_exec(init)) { | 269 | if (port && init_exec(init)) { |
@@ -274,7 +279,7 @@ init_rdauxr(struct nvbios_init *init, u32 addr) | |||
274 | static int | 279 | static int |
275 | init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) | 280 | init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) |
276 | { | 281 | { |
277 | struct nouveau_i2c_port *port = init_i2c(init, -1); | 282 | struct nouveau_i2c_port *port = init_i2c(init, -2); |
278 | if (port && init_exec(init)) | 283 | if (port && init_exec(init)) |
279 | return nv_wraux(port, addr, &data, 1); | 284 | return nv_wraux(port, addr, &data, 1); |
280 | return -ENODEV; | 285 | return -ENODEV; |