diff options
91 files changed, 4531 insertions, 4018 deletions
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h index bea4543554ba..74a8795c2c2b 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h | |||
@@ -169,18 +169,10 @@ static inline void | |||
169 | nouveau_bios_run_init_table(struct drm_device *dev, u16 table, | 169 | nouveau_bios_run_init_table(struct drm_device *dev, u16 table, |
170 | struct dcb_output *outp, int crtc) | 170 | struct dcb_output *outp, int crtc) |
171 | { | 171 | { |
172 | struct nouveau_drm *drm = nouveau_drm(dev); | 172 | nvbios_init(&nvxx_bios(&nouveau_drm(dev)->client.device)->subdev, table, |
173 | struct nvkm_bios *bios = nvxx_bios(&drm->client.device); | 173 | init.outp = outp; |
174 | struct nvbios_init init = { | 174 | init.head = crtc; |
175 | .subdev = &bios->subdev, | 175 | ); |
176 | .bios = bios, | ||
177 | .offset = table, | ||
178 | .outp = outp, | ||
179 | .crtc = crtc, | ||
180 | .execute = 1, | ||
181 | }; | ||
182 | |||
183 | nvbios_exec(&init); | ||
184 | } | 176 | } |
185 | 177 | ||
186 | #endif | 178 | #endif |
diff --git a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h index ae49dfd1f97b..542d95145a67 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/cl5070.h +++ b/drivers/gpu/drm/nouveau/include/nvif/cl5070.h | |||
@@ -27,29 +27,25 @@ struct nv50_disp_scanoutpos_v0 { | |||
27 | 27 | ||
28 | struct nv50_disp_mthd_v1 { | 28 | struct nv50_disp_mthd_v1 { |
29 | __u8 version; | 29 | __u8 version; |
30 | #define NV50_DISP_MTHD_V1_DAC_PWR 0x10 | 30 | #define NV50_DISP_MTHD_V1_ACQUIRE 0x01 |
31 | #define NV50_DISP_MTHD_V1_RELEASE 0x02 | ||
31 | #define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 | 32 | #define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 |
32 | #define NV50_DISP_MTHD_V1_SOR_PWR 0x20 | ||
33 | #define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 | 33 | #define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 |
34 | #define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 | 34 | #define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 |
35 | #define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT 0x23 | 35 | #define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT 0x23 |
36 | #define NV50_DISP_MTHD_V1_SOR_DP_PWR 0x24 | ||
37 | #define NV50_DISP_MTHD_V1_SOR_DP_MST_LINK 0x25 | 36 | #define NV50_DISP_MTHD_V1_SOR_DP_MST_LINK 0x25 |
38 | #define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26 | 37 | #define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26 |
39 | #define NV50_DISP_MTHD_V1_PIOR_PWR 0x30 | ||
40 | __u8 method; | 38 | __u8 method; |
41 | __u16 hasht; | 39 | __u16 hasht; |
42 | __u16 hashm; | 40 | __u16 hashm; |
43 | __u8 pad06[2]; | 41 | __u8 pad06[2]; |
44 | }; | 42 | }; |
45 | 43 | ||
46 | struct nv50_disp_dac_pwr_v0 { | 44 | struct nv50_disp_acquire_v0 { |
47 | __u8 version; | 45 | __u8 version; |
48 | __u8 state; | 46 | __u8 or; |
49 | __u8 data; | 47 | __u8 link; |
50 | __u8 vsync; | 48 | __u8 pad03[5]; |
51 | __u8 hsync; | ||
52 | __u8 pad05[3]; | ||
53 | }; | 49 | }; |
54 | 50 | ||
55 | struct nv50_disp_dac_load_v0 { | 51 | struct nv50_disp_dac_load_v0 { |
@@ -59,12 +55,6 @@ struct nv50_disp_dac_load_v0 { | |||
59 | __u32 data; | 55 | __u32 data; |
60 | }; | 56 | }; |
61 | 57 | ||
62 | struct nv50_disp_sor_pwr_v0 { | ||
63 | __u8 version; | ||
64 | __u8 state; | ||
65 | __u8 pad02[6]; | ||
66 | }; | ||
67 | |||
68 | struct nv50_disp_sor_hda_eld_v0 { | 58 | struct nv50_disp_sor_hda_eld_v0 { |
69 | __u8 version; | 59 | __u8 version; |
70 | __u8 pad01[7]; | 60 | __u8 pad01[7]; |
@@ -76,7 +66,9 @@ struct nv50_disp_sor_hdmi_pwr_v0 { | |||
76 | __u8 state; | 66 | __u8 state; |
77 | __u8 max_ac_packet; | 67 | __u8 max_ac_packet; |
78 | __u8 rekey; | 68 | __u8 rekey; |
79 | __u8 pad04[4]; | 69 | __u8 avi_infoframe_length; |
70 | __u8 vendor_infoframe_length; | ||
71 | __u8 pad06[2]; | ||
80 | }; | 72 | }; |
81 | 73 | ||
82 | struct nv50_disp_sor_lvds_script_v0 { | 74 | struct nv50_disp_sor_lvds_script_v0 { |
@@ -86,12 +78,6 @@ struct nv50_disp_sor_lvds_script_v0 { | |||
86 | __u8 pad04[4]; | 78 | __u8 pad04[4]; |
87 | }; | 79 | }; |
88 | 80 | ||
89 | struct nv50_disp_sor_dp_pwr_v0 { | ||
90 | __u8 version; | ||
91 | __u8 state; | ||
92 | __u8 pad02[6]; | ||
93 | }; | ||
94 | |||
95 | struct nv50_disp_sor_dp_mst_link_v0 { | 81 | struct nv50_disp_sor_dp_mst_link_v0 { |
96 | __u8 version; | 82 | __u8 version; |
97 | __u8 state; | 83 | __u8 state; |
@@ -106,11 +92,4 @@ struct nv50_disp_sor_dp_mst_vcpi_v0 { | |||
106 | __u16 pbn; | 92 | __u16 pbn; |
107 | __u16 aligned_pbn; | 93 | __u16 aligned_pbn; |
108 | }; | 94 | }; |
109 | |||
110 | struct nv50_disp_pior_pwr_v0 { | ||
111 | __u8 version; | ||
112 | __u8 state; | ||
113 | __u8 type; | ||
114 | __u8 pad03[5]; | ||
115 | }; | ||
116 | #endif | 95 | #endif |
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h index 970ae753968a..05f9c13ab8c3 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h | |||
@@ -8,17 +8,15 @@ struct nvkm_disp { | |||
8 | const struct nvkm_disp_func *func; | 8 | const struct nvkm_disp_func *func; |
9 | struct nvkm_engine engine; | 9 | struct nvkm_engine engine; |
10 | 10 | ||
11 | struct nvkm_oproxy *client; | 11 | struct list_head head; |
12 | 12 | struct list_head ior; | |
13 | struct list_head outp; | 13 | struct list_head outp; |
14 | struct list_head conn; | 14 | struct list_head conn; |
15 | 15 | ||
16 | struct nvkm_event hpd; | 16 | struct nvkm_event hpd; |
17 | struct nvkm_event vblank; | 17 | struct nvkm_event vblank; |
18 | 18 | ||
19 | struct { | 19 | struct nvkm_oproxy *client; |
20 | int nr; | ||
21 | } head; | ||
22 | }; | 20 | }; |
23 | 21 | ||
24 | int nv04_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 22 | int nv04_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
@@ -26,7 +24,9 @@ int nv50_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | |||
26 | int g84_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 24 | int g84_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
27 | int gt200_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 25 | int gt200_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
28 | int g94_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 26 | int g94_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
27 | int mcp77_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | ||
29 | int gt215_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 28 | int gt215_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
29 | int mcp89_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | ||
30 | int gf119_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 30 | int gf119_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
31 | int gk104_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 31 | int gk104_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
32 | int gk110_disp_new(struct nvkm_device *, int, struct nvkm_disp **); | 32 | int gk110_disp_new(struct nvkm_device *, int, struct nvkm_disp **); |
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h index 4dc1c8af840c..06ab48052128 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h | |||
@@ -3,19 +3,34 @@ | |||
3 | 3 | ||
4 | struct nvbios_init { | 4 | struct nvbios_init { |
5 | struct nvkm_subdev *subdev; | 5 | struct nvkm_subdev *subdev; |
6 | struct nvkm_bios *bios; | 6 | u32 offset; |
7 | u16 offset; | 7 | |
8 | struct dcb_output *outp; | 8 | struct dcb_output *outp; |
9 | int crtc; | 9 | int or; |
10 | int link; | ||
11 | int head; | ||
10 | 12 | ||
11 | /* internal state used during parsing */ | 13 | /* internal state used during parsing */ |
12 | u8 execute; | 14 | u8 execute; |
13 | u32 nested; | 15 | u32 nested; |
14 | u16 repeat; | 16 | u32 repeat; |
15 | u16 repend; | 17 | u32 repend; |
16 | u32 ramcfg; | 18 | u32 ramcfg; |
17 | }; | 19 | }; |
18 | 20 | ||
21 | #define nvbios_init(s,o,ARGS...) ({ \ | ||
22 | struct nvbios_init init = { \ | ||
23 | .subdev = (s), \ | ||
24 | .offset = (o), \ | ||
25 | .or = -1, \ | ||
26 | .link = 0, \ | ||
27 | .head = -1, \ | ||
28 | .execute = 1, \ | ||
29 | }; \ | ||
30 | ARGS \ | ||
31 | nvbios_exec(&init); \ | ||
32 | }) | ||
19 | int nvbios_exec(struct nvbios_init *); | 33 | int nvbios_exec(struct nvbios_init *); |
20 | int nvbios_init(struct nvkm_subdev *, bool execute); | 34 | |
35 | int nvbios_post(struct nvkm_subdev *, bool execute); | ||
21 | #endif | 36 | #endif |
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h index 820a4805916f..ff0709652f80 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h | |||
@@ -26,7 +26,6 @@ struct nvkm_timer { | |||
26 | 26 | ||
27 | u64 nvkm_timer_read(struct nvkm_timer *); | 27 | u64 nvkm_timer_read(struct nvkm_timer *); |
28 | void nvkm_timer_alarm(struct nvkm_timer *, u32 nsec, struct nvkm_alarm *); | 28 | void nvkm_timer_alarm(struct nvkm_timer *, u32 nsec, struct nvkm_alarm *); |
29 | void nvkm_timer_alarm_cancel(struct nvkm_timer *, struct nvkm_alarm *); | ||
30 | 29 | ||
31 | /* Delay based on GPU time (ie. PTIMER). | 30 | /* Delay based on GPU time (ie. PTIMER). |
32 | * | 31 | * |
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 9a0772ad495a..b998c33af18a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c | |||
@@ -1533,7 +1533,8 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, | |||
1533 | if (conf & 0x100000) | 1533 | if (conf & 0x100000) |
1534 | entry->i2c_upper_default = true; | 1534 | entry->i2c_upper_default = true; |
1535 | 1535 | ||
1536 | entry->hasht = (entry->location << 4) | entry->type; | 1536 | entry->hasht = (entry->extdev << 8) | (entry->location << 4) | |
1537 | entry->type; | ||
1537 | entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; | 1538 | entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; |
1538 | return true; | 1539 | return true; |
1539 | } | 1540 | } |
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index f802bcd94457..147b22163f9f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c | |||
@@ -1045,6 +1045,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector, | |||
1045 | return MODE_BAD; | 1045 | return MODE_BAD; |
1046 | } | 1046 | } |
1047 | 1047 | ||
1048 | if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) | ||
1049 | clock *= 2; | ||
1050 | |||
1048 | if (clock < min_clock) | 1051 | if (clock < min_clock) |
1049 | return MODE_CLOCK_LOW; | 1052 | return MODE_CLOCK_LOW; |
1050 | 1053 | ||
@@ -1321,6 +1324,13 @@ nouveau_connector_create(struct drm_device *dev, int index) | |||
1321 | break; | 1324 | break; |
1322 | } | 1325 | } |
1323 | 1326 | ||
1327 | /* HDMI 3D support */ | ||
1328 | if ((disp->disp.oclass >= G82_DISP) | ||
1329 | && ((type == DRM_MODE_CONNECTOR_DisplayPort) | ||
1330 | || (type == DRM_MODE_CONNECTOR_eDP) | ||
1331 | || (type == DRM_MODE_CONNECTOR_HDMIA))) | ||
1332 | connector->stereo_allowed = true; | ||
1333 | |||
1324 | /* defaults, will get overridden in detect() */ | 1334 | /* defaults, will get overridden in detect() */ |
1325 | connector->interlace_allowed = false; | 1335 | connector->interlace_allowed = false; |
1326 | connector->doublescan_allowed = false; | 1336 | connector->doublescan_allowed = false; |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 7acd1cf8c5a6..90757af9bc73 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c | |||
@@ -727,7 +727,7 @@ nouveau_pmops_thaw(struct device *dev) | |||
727 | } | 727 | } |
728 | 728 | ||
729 | bool | 729 | bool |
730 | nouveau_pmops_runtime() | 730 | nouveau_pmops_runtime(void) |
731 | { | 731 | { |
732 | if (nouveau_runtime_pm == -1) | 732 | if (nouveau_runtime_pm == -1) |
733 | return nouveau_is_optimus() || nouveau_is_v1_dsm(); | 733 | return nouveau_is_optimus() || nouveau_is_v1_dsm(); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 198e5f27682f..e28d966946a1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h | |||
@@ -42,6 +42,7 @@ struct nouveau_encoder { | |||
42 | 42 | ||
43 | struct dcb_output *dcb; | 43 | struct dcb_output *dcb; |
44 | int or; | 44 | int or; |
45 | int link; | ||
45 | 46 | ||
46 | struct i2c_adapter *i2c; | 47 | struct i2c_adapter *i2c; |
47 | struct nvkm_i2c_aux *aux; | 48 | struct nvkm_i2c_aux *aux; |
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c index 23b1670c1c2f..7c965648df80 100644 --- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c +++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c | |||
@@ -38,21 +38,6 @@ | |||
38 | #include <nvkm/subdev/volt.h> | 38 | #include <nvkm/subdev/volt.h> |
39 | 39 | ||
40 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) | 40 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
41 | static ssize_t | ||
42 | nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) | ||
43 | { | ||
44 | struct drm_device *dev = dev_get_drvdata(d); | ||
45 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
46 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
47 | int temp = nvkm_therm_temp_get(therm); | ||
48 | |||
49 | if (temp < 0) | ||
50 | return temp; | ||
51 | |||
52 | return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000); | ||
53 | } | ||
54 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, | ||
55 | NULL, 0); | ||
56 | 41 | ||
57 | static ssize_t | 42 | static ssize_t |
58 | nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, | 43 | nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, |
@@ -60,7 +45,7 @@ nouveau_hwmon_show_temp1_auto_point1_pwm(struct device *d, | |||
60 | { | 45 | { |
61 | return snprintf(buf, PAGE_SIZE, "%d\n", 100); | 46 | return snprintf(buf, PAGE_SIZE, "%d\n", 100); |
62 | } | 47 | } |
63 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO, | 48 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_pwm, 0444, |
64 | nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0); | 49 | nouveau_hwmon_show_temp1_auto_point1_pwm, NULL, 0); |
65 | 50 | ||
66 | static ssize_t | 51 | static ssize_t |
@@ -92,7 +77,7 @@ nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d, | |||
92 | 77 | ||
93 | return count; | 78 | return count; |
94 | } | 79 | } |
95 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO | S_IWUSR, | 80 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, 0644, |
96 | nouveau_hwmon_temp1_auto_point1_temp, | 81 | nouveau_hwmon_temp1_auto_point1_temp, |
97 | nouveau_hwmon_set_temp1_auto_point1_temp, 0); | 82 | nouveau_hwmon_set_temp1_auto_point1_temp, 0); |
98 | 83 | ||
@@ -125,572 +110,595 @@ nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d, | |||
125 | 110 | ||
126 | return count; | 111 | return count; |
127 | } | 112 | } |
128 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, | 113 | static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp_hyst, 0644, |
129 | nouveau_hwmon_temp1_auto_point1_temp_hyst, | 114 | nouveau_hwmon_temp1_auto_point1_temp_hyst, |
130 | nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0); | 115 | nouveau_hwmon_set_temp1_auto_point1_temp_hyst, 0); |
131 | 116 | ||
132 | static ssize_t | 117 | static ssize_t |
133 | nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) | 118 | nouveau_hwmon_get_pwm1_max(struct device *d, |
134 | { | 119 | struct device_attribute *a, char *buf) |
135 | struct drm_device *dev = dev_get_drvdata(d); | ||
136 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
137 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
138 | |||
139 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
140 | therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) * 1000); | ||
141 | } | ||
142 | static ssize_t | ||
143 | nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, | ||
144 | const char *buf, size_t count) | ||
145 | { | 120 | { |
146 | struct drm_device *dev = dev_get_drvdata(d); | 121 | struct drm_device *dev = dev_get_drvdata(d); |
147 | struct nouveau_drm *drm = nouveau_drm(dev); | 122 | struct nouveau_drm *drm = nouveau_drm(dev); |
148 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 123 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
149 | long value; | 124 | int ret; |
150 | |||
151 | if (kstrtol(buf, 10, &value) == -EINVAL) | ||
152 | return count; | ||
153 | 125 | ||
154 | therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK, value / 1000); | 126 | ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY); |
127 | if (ret < 0) | ||
128 | return ret; | ||
155 | 129 | ||
156 | return count; | 130 | return sprintf(buf, "%i\n", ret); |
157 | } | 131 | } |
158 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, | ||
159 | nouveau_hwmon_set_max_temp, | ||
160 | 0); | ||
161 | 132 | ||
162 | static ssize_t | 133 | static ssize_t |
163 | nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a, | 134 | nouveau_hwmon_get_pwm1_min(struct device *d, |
164 | char *buf) | 135 | struct device_attribute *a, char *buf) |
165 | { | 136 | { |
166 | struct drm_device *dev = dev_get_drvdata(d); | 137 | struct drm_device *dev = dev_get_drvdata(d); |
167 | struct nouveau_drm *drm = nouveau_drm(dev); | 138 | struct nouveau_drm *drm = nouveau_drm(dev); |
168 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 139 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
140 | int ret; | ||
169 | 141 | ||
170 | return snprintf(buf, PAGE_SIZE, "%d\n", | 142 | ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY); |
171 | therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000); | 143 | if (ret < 0) |
144 | return ret; | ||
145 | |||
146 | return sprintf(buf, "%i\n", ret); | ||
172 | } | 147 | } |
148 | |||
173 | static ssize_t | 149 | static ssize_t |
174 | nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a, | 150 | nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, |
175 | const char *buf, size_t count) | 151 | const char *buf, size_t count) |
176 | { | 152 | { |
177 | struct drm_device *dev = dev_get_drvdata(d); | 153 | struct drm_device *dev = dev_get_drvdata(d); |
178 | struct nouveau_drm *drm = nouveau_drm(dev); | 154 | struct nouveau_drm *drm = nouveau_drm(dev); |
179 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 155 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
180 | long value; | 156 | long value; |
157 | int ret; | ||
181 | 158 | ||
182 | if (kstrtol(buf, 10, &value) == -EINVAL) | 159 | if (kstrtol(buf, 10, &value) == -EINVAL) |
183 | return count; | 160 | return -EINVAL; |
184 | 161 | ||
185 | therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST, | 162 | ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY, value); |
186 | value / 1000); | 163 | if (ret < 0) |
164 | return ret; | ||
187 | 165 | ||
188 | return count; | 166 | return count; |
189 | } | 167 | } |
190 | static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, | 168 | static SENSOR_DEVICE_ATTR(pwm1_min, 0644, |
191 | nouveau_hwmon_max_temp_hyst, | 169 | nouveau_hwmon_get_pwm1_min, |
192 | nouveau_hwmon_set_max_temp_hyst, 0); | 170 | nouveau_hwmon_set_pwm1_min, 0); |
193 | |||
194 | static ssize_t | ||
195 | nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, | ||
196 | char *buf) | ||
197 | { | ||
198 | struct drm_device *dev = dev_get_drvdata(d); | ||
199 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
200 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
201 | 171 | ||
202 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
203 | therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) * 1000); | ||
204 | } | ||
205 | static ssize_t | 172 | static ssize_t |
206 | nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, | 173 | nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, |
207 | const char *buf, | 174 | const char *buf, size_t count) |
208 | size_t count) | ||
209 | { | 175 | { |
210 | struct drm_device *dev = dev_get_drvdata(d); | 176 | struct drm_device *dev = dev_get_drvdata(d); |
211 | struct nouveau_drm *drm = nouveau_drm(dev); | 177 | struct nouveau_drm *drm = nouveau_drm(dev); |
212 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 178 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
213 | long value; | 179 | long value; |
180 | int ret; | ||
214 | 181 | ||
215 | if (kstrtol(buf, 10, &value) == -EINVAL) | 182 | if (kstrtol(buf, 10, &value) == -EINVAL) |
216 | return count; | 183 | return -EINVAL; |
217 | 184 | ||
218 | therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL, value / 1000); | 185 | ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY, value); |
186 | if (ret < 0) | ||
187 | return ret; | ||
219 | 188 | ||
220 | return count; | 189 | return count; |
221 | } | 190 | } |
222 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, | 191 | static SENSOR_DEVICE_ATTR(pwm1_max, 0644, |
223 | nouveau_hwmon_critical_temp, | 192 | nouveau_hwmon_get_pwm1_max, |
224 | nouveau_hwmon_set_critical_temp, | 193 | nouveau_hwmon_set_pwm1_max, 0); |
225 | 0); | ||
226 | 194 | ||
227 | static ssize_t | 195 | static struct attribute *pwm_fan_sensor_attrs[] = { |
228 | nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a, | 196 | &sensor_dev_attr_pwm1_min.dev_attr.attr, |
229 | char *buf) | 197 | &sensor_dev_attr_pwm1_max.dev_attr.attr, |
230 | { | 198 | NULL |
231 | struct drm_device *dev = dev_get_drvdata(d); | 199 | }; |
232 | struct nouveau_drm *drm = nouveau_drm(dev); | 200 | static const struct attribute_group pwm_fan_sensor_group = { |
233 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 201 | .attrs = pwm_fan_sensor_attrs, |
202 | }; | ||
234 | 203 | ||
235 | return snprintf(buf, PAGE_SIZE, "%d\n", | 204 | static struct attribute *temp1_auto_point_sensor_attrs[] = { |
236 | therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) * 1000); | 205 | &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, |
237 | } | 206 | &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, |
238 | static ssize_t | 207 | &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, |
239 | nouveau_hwmon_set_critical_temp_hyst(struct device *d, | 208 | NULL |
240 | struct device_attribute *a, | 209 | }; |
241 | const char *buf, | 210 | static const struct attribute_group temp1_auto_point_sensor_group = { |
242 | size_t count) | 211 | .attrs = temp1_auto_point_sensor_attrs, |
243 | { | 212 | }; |
244 | struct drm_device *dev = dev_get_drvdata(d); | ||
245 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
246 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
247 | long value; | ||
248 | 213 | ||
249 | if (kstrtol(buf, 10, &value) == -EINVAL) | 214 | #define N_ATTR_GROUPS 3 |
250 | return count; | ||
251 | 215 | ||
252 | therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST, | 216 | static const u32 nouveau_config_chip[] = { |
253 | value / 1000); | 217 | HWMON_C_UPDATE_INTERVAL, |
218 | 0 | ||
219 | }; | ||
254 | 220 | ||
255 | return count; | 221 | static const u32 nouveau_config_in[] = { |
256 | } | 222 | HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_LABEL, |
257 | static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, | 223 | 0 |
258 | nouveau_hwmon_critical_temp_hyst, | 224 | }; |
259 | nouveau_hwmon_set_critical_temp_hyst, 0); | ||
260 | static ssize_t | ||
261 | nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a, | ||
262 | char *buf) | ||
263 | { | ||
264 | struct drm_device *dev = dev_get_drvdata(d); | ||
265 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
266 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
267 | 225 | ||
268 | return snprintf(buf, PAGE_SIZE, "%d\n", | 226 | static const u32 nouveau_config_temp[] = { |
269 | therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) * 1000); | 227 | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | |
270 | } | 228 | HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_EMERGENCY | |
271 | static ssize_t | 229 | HWMON_T_EMERGENCY_HYST, |
272 | nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a, | 230 | 0 |
273 | const char *buf, | 231 | }; |
274 | size_t count) | ||
275 | { | ||
276 | struct drm_device *dev = dev_get_drvdata(d); | ||
277 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
278 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
279 | long value; | ||
280 | 232 | ||
281 | if (kstrtol(buf, 10, &value) == -EINVAL) | 233 | static const u32 nouveau_config_fan[] = { |
282 | return count; | 234 | HWMON_F_INPUT, |
235 | 0 | ||
236 | }; | ||
283 | 237 | ||
284 | therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN, value / 1000); | 238 | static const u32 nouveau_config_pwm[] = { |
239 | HWMON_PWM_INPUT | HWMON_PWM_ENABLE, | ||
240 | 0 | ||
241 | }; | ||
285 | 242 | ||
286 | return count; | 243 | static const u32 nouveau_config_power[] = { |
287 | } | 244 | HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT, |
288 | static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO | S_IWUSR, | 245 | 0 |
289 | nouveau_hwmon_emergency_temp, | 246 | }; |
290 | nouveau_hwmon_set_emergency_temp, | ||
291 | 0); | ||
292 | 247 | ||
293 | static ssize_t | 248 | static const struct hwmon_channel_info nouveau_chip = { |
294 | nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a, | 249 | .type = hwmon_chip, |
295 | char *buf) | 250 | .config = nouveau_config_chip, |
296 | { | 251 | }; |
297 | struct drm_device *dev = dev_get_drvdata(d); | ||
298 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
299 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
300 | 252 | ||
301 | return snprintf(buf, PAGE_SIZE, "%d\n", | 253 | static const struct hwmon_channel_info nouveau_temp = { |
302 | therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000); | 254 | .type = hwmon_temp, |
303 | } | 255 | .config = nouveau_config_temp, |
304 | static ssize_t | 256 | }; |
305 | nouveau_hwmon_set_emergency_temp_hyst(struct device *d, | ||
306 | struct device_attribute *a, | ||
307 | const char *buf, | ||
308 | size_t count) | ||
309 | { | ||
310 | struct drm_device *dev = dev_get_drvdata(d); | ||
311 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
312 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
313 | long value; | ||
314 | 257 | ||
315 | if (kstrtol(buf, 10, &value) == -EINVAL) | 258 | static const struct hwmon_channel_info nouveau_fan = { |
316 | return count; | 259 | .type = hwmon_fan, |
260 | .config = nouveau_config_fan, | ||
261 | }; | ||
317 | 262 | ||
318 | therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST, | 263 | static const struct hwmon_channel_info nouveau_in = { |
319 | value / 1000); | 264 | .type = hwmon_in, |
265 | .config = nouveau_config_in, | ||
266 | }; | ||
320 | 267 | ||
321 | return count; | 268 | static const struct hwmon_channel_info nouveau_pwm = { |
322 | } | 269 | .type = hwmon_pwm, |
323 | static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO | S_IWUSR, | 270 | .config = nouveau_config_pwm, |
324 | nouveau_hwmon_emergency_temp_hyst, | 271 | }; |
325 | nouveau_hwmon_set_emergency_temp_hyst, | 272 | |
326 | 0); | 273 | static const struct hwmon_channel_info nouveau_power = { |
327 | 274 | .type = hwmon_power, | |
328 | static ssize_t nouveau_hwmon_show_name(struct device *dev, | 275 | .config = nouveau_config_power, |
329 | struct device_attribute *attr, | 276 | }; |
330 | char *buf) | 277 | |
278 | static const struct hwmon_channel_info *nouveau_info[] = { | ||
279 | &nouveau_chip, | ||
280 | &nouveau_temp, | ||
281 | &nouveau_fan, | ||
282 | &nouveau_in, | ||
283 | &nouveau_pwm, | ||
284 | &nouveau_power, | ||
285 | NULL | ||
286 | }; | ||
287 | |||
288 | static umode_t | ||
289 | nouveau_chip_is_visible(const void *data, u32 attr, int channel) | ||
331 | { | 290 | { |
332 | return sprintf(buf, "nouveau\n"); | 291 | switch (attr) { |
292 | case hwmon_chip_update_interval: | ||
293 | return 0444; | ||
294 | default: | ||
295 | return 0; | ||
296 | } | ||
333 | } | 297 | } |
334 | static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); | ||
335 | 298 | ||
336 | static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, | 299 | static umode_t |
337 | struct device_attribute *attr, | 300 | nouveau_power_is_visible(const void *data, u32 attr, int channel) |
338 | char *buf) | ||
339 | { | 301 | { |
340 | return sprintf(buf, "1000\n"); | 302 | struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); |
303 | struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); | ||
304 | |||
305 | if (!iccsense || !iccsense->data_valid || list_empty(&iccsense->rails)) | ||
306 | return 0; | ||
307 | |||
308 | switch (attr) { | ||
309 | case hwmon_power_input: | ||
310 | return 0444; | ||
311 | case hwmon_power_max: | ||
312 | if (iccsense->power_w_max) | ||
313 | return 0444; | ||
314 | return 0; | ||
315 | case hwmon_power_crit: | ||
316 | if (iccsense->power_w_crit) | ||
317 | return 0444; | ||
318 | return 0; | ||
319 | default: | ||
320 | return 0; | ||
321 | } | ||
341 | } | 322 | } |
342 | static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, | ||
343 | nouveau_hwmon_show_update_rate, | ||
344 | NULL, 0); | ||
345 | 323 | ||
346 | static ssize_t | 324 | static umode_t |
347 | nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr, | 325 | nouveau_temp_is_visible(const void *data, u32 attr, int channel) |
348 | char *buf) | ||
349 | { | 326 | { |
350 | struct drm_device *dev = dev_get_drvdata(d); | 327 | struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); |
351 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
352 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 328 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
353 | 329 | ||
354 | return snprintf(buf, PAGE_SIZE, "%d\n", nvkm_therm_fan_sense(therm)); | 330 | if (therm && therm->attr_get && nvkm_therm_temp_get(therm) < 0) |
331 | return 0; | ||
332 | |||
333 | switch (attr) { | ||
334 | case hwmon_temp_input: | ||
335 | return 0444; | ||
336 | case hwmon_temp_max: | ||
337 | case hwmon_temp_max_hyst: | ||
338 | case hwmon_temp_crit: | ||
339 | case hwmon_temp_crit_hyst: | ||
340 | case hwmon_temp_emergency: | ||
341 | case hwmon_temp_emergency_hyst: | ||
342 | return 0644; | ||
343 | default: | ||
344 | return 0; | ||
345 | } | ||
355 | } | 346 | } |
356 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, nouveau_hwmon_show_fan1_input, | ||
357 | NULL, 0); | ||
358 | 347 | ||
359 | static ssize_t | 348 | static umode_t |
360 | nouveau_hwmon_get_pwm1_enable(struct device *d, | 349 | nouveau_pwm_is_visible(const void *data, u32 attr, int channel) |
361 | struct device_attribute *a, char *buf) | ||
362 | { | 350 | { |
363 | struct drm_device *dev = dev_get_drvdata(d); | 351 | struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); |
364 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
365 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 352 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
366 | int ret; | ||
367 | 353 | ||
368 | ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE); | 354 | if (therm && therm->attr_get && therm->fan_get && |
369 | if (ret < 0) | 355 | therm->fan_get(therm) < 0) |
370 | return ret; | 356 | return 0; |
371 | 357 | ||
372 | return sprintf(buf, "%i\n", ret); | 358 | switch (attr) { |
359 | case hwmon_pwm_enable: | ||
360 | case hwmon_pwm_input: | ||
361 | return 0644; | ||
362 | default: | ||
363 | return 0; | ||
364 | } | ||
373 | } | 365 | } |
374 | 366 | ||
375 | static ssize_t | 367 | static umode_t |
376 | nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a, | 368 | nouveau_input_is_visible(const void *data, u32 attr, int channel) |
377 | const char *buf, size_t count) | ||
378 | { | 369 | { |
379 | struct drm_device *dev = dev_get_drvdata(d); | 370 | struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); |
380 | struct nouveau_drm *drm = nouveau_drm(dev); | 371 | struct nvkm_volt *volt = nvxx_volt(&drm->client.device); |
381 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | ||
382 | long value; | ||
383 | int ret; | ||
384 | |||
385 | ret = kstrtol(buf, 10, &value); | ||
386 | if (ret) | ||
387 | return ret; | ||
388 | 372 | ||
389 | ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MODE, value); | 373 | if (!volt || nvkm_volt_get(volt) < 0) |
390 | if (ret) | 374 | return 0; |
391 | return ret; | 375 | |
392 | else | 376 | switch (attr) { |
393 | return count; | 377 | case hwmon_in_input: |
378 | case hwmon_in_label: | ||
379 | case hwmon_in_min: | ||
380 | case hwmon_in_max: | ||
381 | return 0444; | ||
382 | default: | ||
383 | return 0; | ||
384 | } | ||
394 | } | 385 | } |
395 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, | ||
396 | nouveau_hwmon_get_pwm1_enable, | ||
397 | nouveau_hwmon_set_pwm1_enable, 0); | ||
398 | 386 | ||
399 | static ssize_t | 387 | static umode_t |
400 | nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf) | 388 | nouveau_fan_is_visible(const void *data, u32 attr, int channel) |
401 | { | 389 | { |
402 | struct drm_device *dev = dev_get_drvdata(d); | 390 | struct nouveau_drm *drm = nouveau_drm((struct drm_device *)data); |
403 | struct nouveau_drm *drm = nouveau_drm(dev); | ||
404 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 391 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
405 | int ret; | ||
406 | 392 | ||
407 | ret = therm->fan_get(therm); | 393 | if (!therm || !therm->attr_get || nvkm_therm_fan_sense(therm) < 0) |
408 | if (ret < 0) | 394 | return 0; |
409 | return ret; | ||
410 | 395 | ||
411 | return sprintf(buf, "%i\n", ret); | 396 | switch (attr) { |
397 | case hwmon_fan_input: | ||
398 | return 0444; | ||
399 | default: | ||
400 | return 0; | ||
401 | } | ||
412 | } | 402 | } |
413 | 403 | ||
414 | static ssize_t | 404 | static int |
415 | nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a, | 405 | nouveau_chip_read(struct device *dev, u32 attr, int channel, long *val) |
416 | const char *buf, size_t count) | ||
417 | { | 406 | { |
418 | struct drm_device *dev = dev_get_drvdata(d); | 407 | switch (attr) { |
419 | struct nouveau_drm *drm = nouveau_drm(dev); | 408 | case hwmon_chip_update_interval: |
420 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 409 | *val = 1000; |
421 | int ret = -ENODEV; | 410 | break; |
422 | long value; | 411 | default: |
423 | 412 | return -EOPNOTSUPP; | |
424 | if (kstrtol(buf, 10, &value) == -EINVAL) | 413 | } |
425 | return -EINVAL; | ||
426 | |||
427 | ret = therm->fan_set(therm, value); | ||
428 | if (ret) | ||
429 | return ret; | ||
430 | 414 | ||
431 | return count; | 415 | return 0; |
432 | } | 416 | } |
433 | 417 | ||
434 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, | 418 | static int |
435 | nouveau_hwmon_get_pwm1, | 419 | nouveau_temp_read(struct device *dev, u32 attr, int channel, long *val) |
436 | nouveau_hwmon_set_pwm1, 0); | ||
437 | |||
438 | static ssize_t | ||
439 | nouveau_hwmon_get_pwm1_min(struct device *d, | ||
440 | struct device_attribute *a, char *buf) | ||
441 | { | 420 | { |
442 | struct drm_device *dev = dev_get_drvdata(d); | 421 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
443 | struct nouveau_drm *drm = nouveau_drm(dev); | 422 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
444 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 423 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
445 | int ret; | 424 | int ret; |
446 | 425 | ||
447 | ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY); | 426 | if (!therm || !therm->attr_get) |
448 | if (ret < 0) | 427 | return -EOPNOTSUPP; |
449 | return ret; | 428 | |
429 | switch (attr) { | ||
430 | case hwmon_temp_input: | ||
431 | ret = nvkm_therm_temp_get(therm); | ||
432 | *val = ret < 0 ? ret : (ret * 1000); | ||
433 | break; | ||
434 | case hwmon_temp_max: | ||
435 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK) | ||
436 | * 1000; | ||
437 | break; | ||
438 | case hwmon_temp_max_hyst: | ||
439 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST) | ||
440 | * 1000; | ||
441 | break; | ||
442 | case hwmon_temp_crit: | ||
443 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL) | ||
444 | * 1000; | ||
445 | break; | ||
446 | case hwmon_temp_crit_hyst: | ||
447 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST) | ||
448 | * 1000; | ||
449 | break; | ||
450 | case hwmon_temp_emergency: | ||
451 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN) | ||
452 | * 1000; | ||
453 | break; | ||
454 | case hwmon_temp_emergency_hyst: | ||
455 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST) | ||
456 | * 1000; | ||
457 | break; | ||
458 | default: | ||
459 | return -EOPNOTSUPP; | ||
460 | } | ||
450 | 461 | ||
451 | return sprintf(buf, "%i\n", ret); | 462 | return 0; |
452 | } | 463 | } |
453 | 464 | ||
454 | static ssize_t | 465 | static int |
455 | nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a, | 466 | nouveau_fan_read(struct device *dev, u32 attr, int channel, long *val) |
456 | const char *buf, size_t count) | ||
457 | { | 467 | { |
458 | struct drm_device *dev = dev_get_drvdata(d); | 468 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
459 | struct nouveau_drm *drm = nouveau_drm(dev); | 469 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
460 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 470 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
461 | long value; | ||
462 | int ret; | ||
463 | 471 | ||
464 | if (kstrtol(buf, 10, &value) == -EINVAL) | 472 | if (!therm) |
465 | return -EINVAL; | 473 | return -EOPNOTSUPP; |
466 | 474 | ||
467 | ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MIN_DUTY, value); | 475 | switch (attr) { |
468 | if (ret < 0) | 476 | case hwmon_fan_input: |
469 | return ret; | 477 | *val = nvkm_therm_fan_sense(therm); |
478 | break; | ||
479 | default: | ||
480 | return -EOPNOTSUPP; | ||
481 | } | ||
470 | 482 | ||
471 | return count; | 483 | return 0; |
472 | } | 484 | } |
473 | 485 | ||
474 | static SENSOR_DEVICE_ATTR(pwm1_min, S_IRUGO | S_IWUSR, | 486 | static int |
475 | nouveau_hwmon_get_pwm1_min, | 487 | nouveau_in_read(struct device *dev, u32 attr, int channel, long *val) |
476 | nouveau_hwmon_set_pwm1_min, 0); | ||
477 | |||
478 | static ssize_t | ||
479 | nouveau_hwmon_get_pwm1_max(struct device *d, | ||
480 | struct device_attribute *a, char *buf) | ||
481 | { | 488 | { |
482 | struct drm_device *dev = dev_get_drvdata(d); | 489 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
483 | struct nouveau_drm *drm = nouveau_drm(dev); | 490 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
484 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 491 | struct nvkm_volt *volt = nvxx_volt(&drm->client.device); |
485 | int ret; | 492 | int ret; |
486 | 493 | ||
487 | ret = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY); | 494 | if (!volt) |
488 | if (ret < 0) | 495 | return -EOPNOTSUPP; |
489 | return ret; | 496 | |
497 | switch (attr) { | ||
498 | case hwmon_in_input: | ||
499 | ret = nvkm_volt_get(volt); | ||
500 | *val = ret < 0 ? ret : (ret / 1000); | ||
501 | break; | ||
502 | case hwmon_in_min: | ||
503 | *val = volt->min_uv > 0 ? (volt->min_uv / 1000) : -ENODEV; | ||
504 | break; | ||
505 | case hwmon_in_max: | ||
506 | *val = volt->max_uv > 0 ? (volt->max_uv / 1000) : -ENODEV; | ||
507 | break; | ||
508 | default: | ||
509 | return -EOPNOTSUPP; | ||
510 | } | ||
490 | 511 | ||
491 | return sprintf(buf, "%i\n", ret); | 512 | return 0; |
492 | } | 513 | } |
493 | 514 | ||
494 | static ssize_t | 515 | static int |
495 | nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a, | 516 | nouveau_pwm_read(struct device *dev, u32 attr, int channel, long *val) |
496 | const char *buf, size_t count) | ||
497 | { | 517 | { |
498 | struct drm_device *dev = dev_get_drvdata(d); | 518 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
499 | struct nouveau_drm *drm = nouveau_drm(dev); | 519 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
500 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 520 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
501 | long value; | ||
502 | int ret; | ||
503 | 521 | ||
504 | if (kstrtol(buf, 10, &value) == -EINVAL) | 522 | if (!therm || !therm->attr_get || !therm->fan_get) |
505 | return -EINVAL; | 523 | return -EOPNOTSUPP; |
506 | 524 | ||
507 | ret = therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MAX_DUTY, value); | 525 | switch (attr) { |
508 | if (ret < 0) | 526 | case hwmon_pwm_enable: |
509 | return ret; | 527 | *val = therm->attr_get(therm, NVKM_THERM_ATTR_FAN_MODE); |
528 | break; | ||
529 | case hwmon_pwm_input: | ||
530 | *val = therm->fan_get(therm); | ||
531 | break; | ||
532 | default: | ||
533 | return -EOPNOTSUPP; | ||
534 | } | ||
510 | 535 | ||
511 | return count; | 536 | return 0; |
512 | } | 537 | } |
513 | 538 | ||
514 | static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR, | 539 | static int |
515 | nouveau_hwmon_get_pwm1_max, | 540 | nouveau_power_read(struct device *dev, u32 attr, int channel, long *val) |
516 | nouveau_hwmon_set_pwm1_max, 0); | ||
517 | |||
518 | static ssize_t | ||
519 | nouveau_hwmon_get_in0_input(struct device *d, | ||
520 | struct device_attribute *a, char *buf) | ||
521 | { | 541 | { |
522 | struct drm_device *dev = dev_get_drvdata(d); | 542 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
523 | struct nouveau_drm *drm = nouveau_drm(dev); | 543 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
524 | struct nvkm_volt *volt = nvxx_volt(&drm->client.device); | 544 | struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); |
525 | int ret; | ||
526 | 545 | ||
527 | ret = nvkm_volt_get(volt); | 546 | if (!iccsense) |
528 | if (ret < 0) | 547 | return -EOPNOTSUPP; |
529 | return ret; | 548 | |
549 | switch (attr) { | ||
550 | case hwmon_power_input: | ||
551 | *val = nvkm_iccsense_read_all(iccsense); | ||
552 | break; | ||
553 | case hwmon_power_max: | ||
554 | *val = iccsense->power_w_max; | ||
555 | break; | ||
556 | case hwmon_power_crit: | ||
557 | *val = iccsense->power_w_crit; | ||
558 | break; | ||
559 | default: | ||
560 | return -EOPNOTSUPP; | ||
561 | } | ||
530 | 562 | ||
531 | return sprintf(buf, "%i\n", ret / 1000); | 563 | return 0; |
532 | } | 564 | } |
533 | 565 | ||
534 | static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, | 566 | static int |
535 | nouveau_hwmon_get_in0_input, NULL, 0); | 567 | nouveau_temp_write(struct device *dev, u32 attr, int channel, long val) |
536 | |||
537 | static ssize_t | ||
538 | nouveau_hwmon_get_in0_min(struct device *d, | ||
539 | struct device_attribute *a, char *buf) | ||
540 | { | 568 | { |
541 | struct drm_device *dev = dev_get_drvdata(d); | 569 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
542 | struct nouveau_drm *drm = nouveau_drm(dev); | 570 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
543 | struct nvkm_volt *volt = nvxx_volt(&drm->client.device); | 571 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
544 | |||
545 | if (!volt || !volt->min_uv) | ||
546 | return -ENODEV; | ||
547 | 572 | ||
548 | return sprintf(buf, "%i\n", volt->min_uv / 1000); | 573 | if (!therm || !therm->attr_set) |
574 | return -EOPNOTSUPP; | ||
575 | |||
576 | switch (attr) { | ||
577 | case hwmon_temp_max: | ||
578 | return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK, | ||
579 | val / 1000); | ||
580 | case hwmon_temp_max_hyst: | ||
581 | return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_DOWN_CLK_HYST, | ||
582 | val / 1000); | ||
583 | case hwmon_temp_crit: | ||
584 | return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL, | ||
585 | val / 1000); | ||
586 | case hwmon_temp_crit_hyst: | ||
587 | return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_CRITICAL_HYST, | ||
588 | val / 1000); | ||
589 | case hwmon_temp_emergency: | ||
590 | return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN, | ||
591 | val / 1000); | ||
592 | case hwmon_temp_emergency_hyst: | ||
593 | return therm->attr_set(therm, NVKM_THERM_ATTR_THRS_SHUTDOWN_HYST, | ||
594 | val / 1000); | ||
595 | default: | ||
596 | return -EOPNOTSUPP; | ||
597 | } | ||
549 | } | 598 | } |
550 | 599 | ||
551 | static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO, | 600 | static int |
552 | nouveau_hwmon_get_in0_min, NULL, 0); | 601 | nouveau_pwm_write(struct device *dev, u32 attr, int channel, long val) |
553 | |||
554 | static ssize_t | ||
555 | nouveau_hwmon_get_in0_max(struct device *d, | ||
556 | struct device_attribute *a, char *buf) | ||
557 | { | 602 | { |
558 | struct drm_device *dev = dev_get_drvdata(d); | 603 | struct drm_device *drm_dev = dev_get_drvdata(dev); |
559 | struct nouveau_drm *drm = nouveau_drm(dev); | 604 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
560 | struct nvkm_volt *volt = nvxx_volt(&drm->client.device); | 605 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
561 | 606 | ||
562 | if (!volt || !volt->max_uv) | 607 | if (!therm || !therm->attr_set) |
563 | return -ENODEV; | 608 | return -EOPNOTSUPP; |
564 | 609 | ||
565 | return sprintf(buf, "%i\n", volt->max_uv / 1000); | 610 | switch (attr) { |
611 | case hwmon_pwm_input: | ||
612 | return therm->fan_set(therm, val); | ||
613 | case hwmon_pwm_enable: | ||
614 | return therm->attr_set(therm, NVKM_THERM_ATTR_FAN_MODE, val); | ||
615 | default: | ||
616 | return -EOPNOTSUPP; | ||
617 | } | ||
566 | } | 618 | } |
567 | 619 | ||
568 | static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO, | 620 | static umode_t |
569 | nouveau_hwmon_get_in0_max, NULL, 0); | 621 | nouveau_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, |
570 | 622 | int channel) | |
571 | static ssize_t | 623 | { |
572 | nouveau_hwmon_get_in0_label(struct device *d, | 624 | switch (type) { |
573 | struct device_attribute *a, char *buf) | 625 | case hwmon_chip: |
574 | { | 626 | return nouveau_chip_is_visible(data, attr, channel); |
575 | return sprintf(buf, "GPU core\n"); | 627 | case hwmon_temp: |
628 | return nouveau_temp_is_visible(data, attr, channel); | ||
629 | case hwmon_fan: | ||
630 | return nouveau_fan_is_visible(data, attr, channel); | ||
631 | case hwmon_in: | ||
632 | return nouveau_input_is_visible(data, attr, channel); | ||
633 | case hwmon_pwm: | ||
634 | return nouveau_pwm_is_visible(data, attr, channel); | ||
635 | case hwmon_power: | ||
636 | return nouveau_power_is_visible(data, attr, channel); | ||
637 | default: | ||
638 | return 0; | ||
639 | } | ||
576 | } | 640 | } |
577 | 641 | ||
578 | static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, | 642 | static const char input_label[] = "GPU core"; |
579 | nouveau_hwmon_get_in0_label, NULL, 0); | ||
580 | 643 | ||
581 | static ssize_t | 644 | static int |
582 | nouveau_hwmon_get_power1_input(struct device *d, struct device_attribute *a, | 645 | nouveau_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
583 | char *buf) | 646 | int channel, const char **buf) |
584 | { | 647 | { |
585 | struct drm_device *dev = dev_get_drvdata(d); | 648 | if (type == hwmon_in && attr == hwmon_in_label) { |
586 | struct nouveau_drm *drm = nouveau_drm(dev); | 649 | *buf = input_label; |
587 | struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); | 650 | return 0; |
588 | int result = nvkm_iccsense_read_all(iccsense); | 651 | } |
589 | |||
590 | if (result < 0) | ||
591 | return result; | ||
592 | |||
593 | return sprintf(buf, "%i\n", result); | ||
594 | } | ||
595 | |||
596 | static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, | ||
597 | nouveau_hwmon_get_power1_input, NULL, 0); | ||
598 | 652 | ||
599 | static ssize_t | 653 | return -EOPNOTSUPP; |
600 | nouveau_hwmon_get_power1_max(struct device *d, struct device_attribute *a, | 654 | } |
601 | char *buf) | 655 | |
602 | { | 656 | static int |
603 | struct drm_device *dev = dev_get_drvdata(d); | 657 | nouveau_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
604 | struct nouveau_drm *drm = nouveau_drm(dev); | 658 | int channel, long *val) |
605 | struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); | 659 | { |
606 | return sprintf(buf, "%i\n", iccsense->power_w_max); | 660 | switch (type) { |
661 | case hwmon_chip: | ||
662 | return nouveau_chip_read(dev, attr, channel, val); | ||
663 | case hwmon_temp: | ||
664 | return nouveau_temp_read(dev, attr, channel, val); | ||
665 | case hwmon_fan: | ||
666 | return nouveau_fan_read(dev, attr, channel, val); | ||
667 | case hwmon_in: | ||
668 | return nouveau_in_read(dev, attr, channel, val); | ||
669 | case hwmon_pwm: | ||
670 | return nouveau_pwm_read(dev, attr, channel, val); | ||
671 | case hwmon_power: | ||
672 | return nouveau_power_read(dev, attr, channel, val); | ||
673 | default: | ||
674 | return -EOPNOTSUPP; | ||
675 | } | ||
607 | } | 676 | } |
608 | 677 | ||
609 | static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO, | 678 | static int |
610 | nouveau_hwmon_get_power1_max, NULL, 0); | 679 | nouveau_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
611 | 680 | int channel, long val) | |
612 | static ssize_t | ||
613 | nouveau_hwmon_get_power1_crit(struct device *d, struct device_attribute *a, | ||
614 | char *buf) | ||
615 | { | 681 | { |
616 | struct drm_device *dev = dev_get_drvdata(d); | 682 | switch (type) { |
617 | struct nouveau_drm *drm = nouveau_drm(dev); | 683 | case hwmon_temp: |
618 | struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); | 684 | return nouveau_temp_write(dev, attr, channel, val); |
619 | return sprintf(buf, "%i\n", iccsense->power_w_crit); | 685 | case hwmon_pwm: |
686 | return nouveau_pwm_write(dev, attr, channel, val); | ||
687 | default: | ||
688 | return -EOPNOTSUPP; | ||
689 | } | ||
620 | } | 690 | } |
621 | 691 | ||
622 | static SENSOR_DEVICE_ATTR(power1_crit, S_IRUGO, | 692 | static const struct hwmon_ops nouveau_hwmon_ops = { |
623 | nouveau_hwmon_get_power1_crit, NULL, 0); | 693 | .is_visible = nouveau_is_visible, |
624 | 694 | .read = nouveau_read, | |
625 | static struct attribute *hwmon_default_attributes[] = { | 695 | .read_string = nouveau_read_string, |
626 | &sensor_dev_attr_name.dev_attr.attr, | 696 | .write = nouveau_write, |
627 | &sensor_dev_attr_update_rate.dev_attr.attr, | ||
628 | NULL | ||
629 | }; | ||
630 | static struct attribute *hwmon_temp_attributes[] = { | ||
631 | &sensor_dev_attr_temp1_input.dev_attr.attr, | ||
632 | &sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr, | ||
633 | &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, | ||
634 | &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr, | ||
635 | &sensor_dev_attr_temp1_max.dev_attr.attr, | ||
636 | &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, | ||
637 | &sensor_dev_attr_temp1_crit.dev_attr.attr, | ||
638 | &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, | ||
639 | &sensor_dev_attr_temp1_emergency.dev_attr.attr, | ||
640 | &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, | ||
641 | NULL | ||
642 | }; | ||
643 | static struct attribute *hwmon_fan_rpm_attributes[] = { | ||
644 | &sensor_dev_attr_fan1_input.dev_attr.attr, | ||
645 | NULL | ||
646 | }; | ||
647 | static struct attribute *hwmon_pwm_fan_attributes[] = { | ||
648 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
649 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
650 | &sensor_dev_attr_pwm1_min.dev_attr.attr, | ||
651 | &sensor_dev_attr_pwm1_max.dev_attr.attr, | ||
652 | NULL | ||
653 | }; | ||
654 | |||
655 | static struct attribute *hwmon_in0_attributes[] = { | ||
656 | &sensor_dev_attr_in0_input.dev_attr.attr, | ||
657 | &sensor_dev_attr_in0_min.dev_attr.attr, | ||
658 | &sensor_dev_attr_in0_max.dev_attr.attr, | ||
659 | &sensor_dev_attr_in0_label.dev_attr.attr, | ||
660 | NULL | ||
661 | }; | 697 | }; |
662 | 698 | ||
663 | static struct attribute *hwmon_power_attributes[] = { | 699 | static const struct hwmon_chip_info nouveau_chip_info = { |
664 | &sensor_dev_attr_power1_input.dev_attr.attr, | 700 | .ops = &nouveau_hwmon_ops, |
665 | NULL | 701 | .info = nouveau_info, |
666 | }; | ||
667 | |||
668 | static struct attribute *hwmon_power_caps_attributes[] = { | ||
669 | &sensor_dev_attr_power1_max.dev_attr.attr, | ||
670 | &sensor_dev_attr_power1_crit.dev_attr.attr, | ||
671 | NULL | ||
672 | }; | ||
673 | |||
674 | static const struct attribute_group hwmon_default_attrgroup = { | ||
675 | .attrs = hwmon_default_attributes, | ||
676 | }; | ||
677 | static const struct attribute_group hwmon_temp_attrgroup = { | ||
678 | .attrs = hwmon_temp_attributes, | ||
679 | }; | ||
680 | static const struct attribute_group hwmon_fan_rpm_attrgroup = { | ||
681 | .attrs = hwmon_fan_rpm_attributes, | ||
682 | }; | ||
683 | static const struct attribute_group hwmon_pwm_fan_attrgroup = { | ||
684 | .attrs = hwmon_pwm_fan_attributes, | ||
685 | }; | ||
686 | static const struct attribute_group hwmon_in0_attrgroup = { | ||
687 | .attrs = hwmon_in0_attributes, | ||
688 | }; | ||
689 | static const struct attribute_group hwmon_power_attrgroup = { | ||
690 | .attrs = hwmon_power_attributes, | ||
691 | }; | ||
692 | static const struct attribute_group hwmon_power_caps_attrgroup = { | ||
693 | .attrs = hwmon_power_caps_attributes, | ||
694 | }; | 702 | }; |
695 | #endif | 703 | #endif |
696 | 704 | ||
@@ -700,90 +708,36 @@ nouveau_hwmon_init(struct drm_device *dev) | |||
700 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) | 708 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
701 | struct nouveau_drm *drm = nouveau_drm(dev); | 709 | struct nouveau_drm *drm = nouveau_drm(dev); |
702 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); | 710 | struct nvkm_therm *therm = nvxx_therm(&drm->client.device); |
703 | struct nvkm_volt *volt = nvxx_volt(&drm->client.device); | 711 | const struct attribute_group *special_groups[N_ATTR_GROUPS]; |
704 | struct nvkm_iccsense *iccsense = nvxx_iccsense(&drm->client.device); | ||
705 | struct nouveau_hwmon *hwmon; | 712 | struct nouveau_hwmon *hwmon; |
706 | struct device *hwmon_dev; | 713 | struct device *hwmon_dev; |
707 | int ret = 0; | 714 | int ret = 0; |
715 | int i = 0; | ||
708 | 716 | ||
709 | hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL); | 717 | hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL); |
710 | if (!hwmon) | 718 | if (!hwmon) |
711 | return -ENOMEM; | 719 | return -ENOMEM; |
712 | hwmon->dev = dev; | 720 | hwmon->dev = dev; |
713 | 721 | ||
714 | hwmon_dev = hwmon_device_register(dev->dev); | 722 | if (therm && therm->attr_get && therm->attr_set) { |
723 | if (nvkm_therm_temp_get(therm) >= 0) | ||
724 | special_groups[i++] = &temp1_auto_point_sensor_group; | ||
725 | if (therm->fan_get && therm->fan_get(therm) >= 0) | ||
726 | special_groups[i++] = &pwm_fan_sensor_group; | ||
727 | } | ||
728 | |||
729 | special_groups[i] = 0; | ||
730 | hwmon_dev = hwmon_device_register_with_info(dev->dev, "nouveau", dev, | ||
731 | &nouveau_chip_info, | ||
732 | special_groups); | ||
715 | if (IS_ERR(hwmon_dev)) { | 733 | if (IS_ERR(hwmon_dev)) { |
716 | ret = PTR_ERR(hwmon_dev); | 734 | ret = PTR_ERR(hwmon_dev); |
717 | NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret); | 735 | NV_ERROR(drm, "Unable to register hwmon device: %d\n", ret); |
718 | return ret; | 736 | return ret; |
719 | } | 737 | } |
720 | dev_set_drvdata(hwmon_dev, dev); | ||
721 | |||
722 | /* set the default attributes */ | ||
723 | ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup); | ||
724 | if (ret) | ||
725 | goto error; | ||
726 | |||
727 | if (therm && therm->attr_get && therm->attr_set) { | ||
728 | /* if the card has a working thermal sensor */ | ||
729 | if (nvkm_therm_temp_get(therm) >= 0) { | ||
730 | ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); | ||
731 | if (ret) | ||
732 | goto error; | ||
733 | } | ||
734 | |||
735 | /* if the card has a pwm fan */ | ||
736 | /*XXX: incorrect, need better detection for this, some boards have | ||
737 | * the gpio entries for pwm fan control even when there's no | ||
738 | * actual fan connected to it... therm table? */ | ||
739 | if (therm->fan_get && therm->fan_get(therm) >= 0) { | ||
740 | ret = sysfs_create_group(&hwmon_dev->kobj, | ||
741 | &hwmon_pwm_fan_attrgroup); | ||
742 | if (ret) | ||
743 | goto error; | ||
744 | } | ||
745 | } | ||
746 | |||
747 | /* if the card can read the fan rpm */ | ||
748 | if (therm && nvkm_therm_fan_sense(therm) >= 0) { | ||
749 | ret = sysfs_create_group(&hwmon_dev->kobj, | ||
750 | &hwmon_fan_rpm_attrgroup); | ||
751 | if (ret) | ||
752 | goto error; | ||
753 | } | ||
754 | |||
755 | if (volt && nvkm_volt_get(volt) >= 0) { | ||
756 | ret = sysfs_create_group(&hwmon_dev->kobj, | ||
757 | &hwmon_in0_attrgroup); | ||
758 | |||
759 | if (ret) | ||
760 | goto error; | ||
761 | } | ||
762 | |||
763 | if (iccsense && iccsense->data_valid && !list_empty(&iccsense->rails)) { | ||
764 | ret = sysfs_create_group(&hwmon_dev->kobj, | ||
765 | &hwmon_power_attrgroup); | ||
766 | |||
767 | if (ret) | ||
768 | goto error; | ||
769 | |||
770 | if (iccsense->power_w_max && iccsense->power_w_crit) { | ||
771 | ret = sysfs_create_group(&hwmon_dev->kobj, | ||
772 | &hwmon_power_caps_attrgroup); | ||
773 | if (ret) | ||
774 | goto error; | ||
775 | } | ||
776 | } | ||
777 | 738 | ||
778 | hwmon->hwmon = hwmon_dev; | 739 | hwmon->hwmon = hwmon_dev; |
779 | |||
780 | return 0; | 740 | return 0; |
781 | |||
782 | error: | ||
783 | NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret); | ||
784 | hwmon_device_unregister(hwmon_dev); | ||
785 | hwmon->hwmon = NULL; | ||
786 | return ret; | ||
787 | #else | 741 | #else |
788 | return 0; | 742 | return 0; |
789 | #endif | 743 | #endif |
@@ -795,17 +749,8 @@ nouveau_hwmon_fini(struct drm_device *dev) | |||
795 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) | 749 | #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE)) |
796 | struct nouveau_hwmon *hwmon = nouveau_hwmon(dev); | 750 | struct nouveau_hwmon *hwmon = nouveau_hwmon(dev); |
797 | 751 | ||
798 | if (hwmon->hwmon) { | 752 | if (hwmon->hwmon) |
799 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup); | ||
800 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup); | ||
801 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup); | ||
802 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup); | ||
803 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_in0_attrgroup); | ||
804 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_power_attrgroup); | ||
805 | sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_power_caps_attrgroup); | ||
806 | |||
807 | hwmon_device_unregister(hwmon->hwmon); | 753 | hwmon_device_unregister(hwmon->hwmon); |
808 | } | ||
809 | 754 | ||
810 | nouveau_drm(dev)->hwmon = NULL; | 755 | nouveau_drm(dev)->hwmon = NULL; |
811 | kfree(hwmon); | 756 | kfree(hwmon); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 02fe0efb9e16..48393a4f6331 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c | |||
@@ -111,6 +111,10 @@ nouveau_vga_fini(struct nouveau_drm *drm) | |||
111 | struct drm_device *dev = drm->dev; | 111 | struct drm_device *dev = drm->dev; |
112 | bool runtime = nouveau_pmops_runtime(); | 112 | bool runtime = nouveau_pmops_runtime(); |
113 | 113 | ||
114 | /* only relevant for PCI devices */ | ||
115 | if (!dev->pdev) | ||
116 | return; | ||
117 | |||
114 | vga_client_register(dev->pdev, NULL, NULL, NULL); | 118 | vga_client_register(dev->pdev, NULL, NULL, NULL); |
115 | 119 | ||
116 | if (pci_is_thunderbolt_attached(dev->pdev)) | 120 | if (pci_is_thunderbolt_attached(dev->pdev)) |
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 775c10015dbe..e3132a2ce34d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c | |||
@@ -23,6 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/dma-mapping.h> | 25 | #include <linux/dma-mapping.h> |
26 | #include <linux/hdmi.h> | ||
26 | 27 | ||
27 | #include <drm/drmP.h> | 28 | #include <drm/drmP.h> |
28 | #include <drm/drm_atomic.h> | 29 | #include <drm/drm_atomic.h> |
@@ -31,6 +32,7 @@ | |||
31 | #include <drm/drm_dp_helper.h> | 32 | #include <drm/drm_dp_helper.h> |
32 | #include <drm/drm_fb_helper.h> | 33 | #include <drm/drm_fb_helper.h> |
33 | #include <drm/drm_plane_helper.h> | 34 | #include <drm/drm_plane_helper.h> |
35 | #include <drm/drm_edid.h> | ||
34 | 36 | ||
35 | #include <nvif/class.h> | 37 | #include <nvif/class.h> |
36 | #include <nvif/cl0002.h> | 38 | #include <nvif/cl0002.h> |
@@ -1965,6 +1967,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, | |||
1965 | struct drm_display_mode *umode = &asyh->state.mode; | 1967 | struct drm_display_mode *umode = &asyh->state.mode; |
1966 | int mode = asyc->scaler.mode; | 1968 | int mode = asyc->scaler.mode; |
1967 | struct edid *edid; | 1969 | struct edid *edid; |
1970 | int umode_vdisplay, omode_hdisplay, omode_vdisplay; | ||
1968 | 1971 | ||
1969 | if (connector->edid_blob_ptr) | 1972 | if (connector->edid_blob_ptr) |
1970 | edid = (struct edid *)connector->edid_blob_ptr->data; | 1973 | edid = (struct edid *)connector->edid_blob_ptr->data; |
@@ -1979,12 +1982,18 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, | |||
1979 | mode = DRM_MODE_SCALE_FULLSCREEN; | 1982 | mode = DRM_MODE_SCALE_FULLSCREEN; |
1980 | } | 1983 | } |
1981 | 1984 | ||
1985 | /* For the user-specified mode, we must ignore doublescan and | ||
1986 | * the like, but honor frame packing. | ||
1987 | */ | ||
1988 | umode_vdisplay = umode->vdisplay; | ||
1989 | if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) | ||
1990 | umode_vdisplay += umode->vtotal; | ||
1982 | asyh->view.iW = umode->hdisplay; | 1991 | asyh->view.iW = umode->hdisplay; |
1983 | asyh->view.iH = umode->vdisplay; | 1992 | asyh->view.iH = umode_vdisplay; |
1984 | asyh->view.oW = omode->hdisplay; | 1993 | /* For the output mode, we can just use the stock helper. */ |
1985 | asyh->view.oH = omode->vdisplay; | 1994 | drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay); |
1986 | if (omode->flags & DRM_MODE_FLAG_DBLSCAN) | 1995 | asyh->view.oW = omode_hdisplay; |
1987 | asyh->view.oH *= 2; | 1996 | asyh->view.oH = omode_vdisplay; |
1988 | 1997 | ||
1989 | /* Add overscan compensation if necessary, will keep the aspect | 1998 | /* Add overscan compensation if necessary, will keep the aspect |
1990 | * ratio the same as the backend mode unless overridden by the | 1999 | * ratio the same as the backend mode unless overridden by the |
@@ -2014,7 +2023,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh, | |||
2014 | switch (mode) { | 2023 | switch (mode) { |
2015 | case DRM_MODE_SCALE_CENTER: | 2024 | case DRM_MODE_SCALE_CENTER: |
2016 | asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW); | 2025 | asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW); |
2017 | asyh->view.oH = min((u16)umode->vdisplay, asyh->view.oH); | 2026 | asyh->view.oH = min((u16)umode_vdisplay, asyh->view.oH); |
2018 | /* fall-through */ | 2027 | /* fall-through */ |
2019 | case DRM_MODE_SCALE_ASPECT: | 2028 | case DRM_MODE_SCALE_ASPECT: |
2020 | if (asyh->view.oH < asyh->view.oW) { | 2029 | if (asyh->view.oH < asyh->view.oW) { |
@@ -2036,34 +2045,37 @@ static void | |||
2036 | nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) | 2045 | nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) |
2037 | { | 2046 | { |
2038 | struct drm_display_mode *mode = &asyh->state.adjusted_mode; | 2047 | struct drm_display_mode *mode = &asyh->state.adjusted_mode; |
2039 | u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; | ||
2040 | u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; | ||
2041 | u32 hbackp = mode->htotal - mode->hsync_end; | ||
2042 | u32 vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; | ||
2043 | u32 hfrontp = mode->hsync_start - mode->hdisplay; | ||
2044 | u32 vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; | ||
2045 | u32 blankus; | ||
2046 | struct nv50_head_mode *m = &asyh->mode; | 2048 | struct nv50_head_mode *m = &asyh->mode; |
2049 | u32 blankus; | ||
2047 | 2050 | ||
2048 | m->h.active = mode->htotal; | 2051 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE); |
2049 | m->h.synce = mode->hsync_end - mode->hsync_start - 1; | ||
2050 | m->h.blanke = m->h.synce + hbackp; | ||
2051 | m->h.blanks = mode->htotal - hfrontp - 1; | ||
2052 | 2052 | ||
2053 | m->v.active = mode->vtotal * vscan / ilace; | 2053 | /* |
2054 | m->v.synce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; | 2054 | * DRM modes are defined in terms of a repeating interval |
2055 | m->v.blanke = m->v.synce + vbackp; | 2055 | * starting with the active display area. The hardware modes |
2056 | m->v.blanks = m->v.active - vfrontp - 1; | 2056 | * are defined in terms of a repeating interval starting one |
2057 | * unit (pixel or line) into the sync pulse. So, add bias. | ||
2058 | */ | ||
2059 | |||
2060 | m->h.active = mode->crtc_htotal; | ||
2061 | m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1; | ||
2062 | m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1; | ||
2063 | m->h.blanks = m->h.blanke + mode->crtc_hdisplay; | ||
2064 | |||
2065 | m->v.active = mode->crtc_vtotal; | ||
2066 | m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1; | ||
2067 | m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1; | ||
2068 | m->v.blanks = m->v.blanke + mode->crtc_vdisplay; | ||
2057 | 2069 | ||
2058 | /*XXX: Safe underestimate, even "0" works */ | 2070 | /*XXX: Safe underestimate, even "0" works */ |
2059 | blankus = (m->v.active - mode->vdisplay - 2) * m->h.active; | 2071 | blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active; |
2060 | blankus *= 1000; | 2072 | blankus *= 1000; |
2061 | blankus /= mode->clock; | 2073 | blankus /= mode->crtc_clock; |
2062 | m->v.blankus = blankus; | 2074 | m->v.blankus = blankus; |
2063 | 2075 | ||
2064 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) { | 2076 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) { |
2065 | m->v.blank2e = m->v.active + m->v.synce + vbackp; | 2077 | m->v.blank2e = m->v.active + m->v.blanke; |
2066 | m->v.blank2s = m->v.blank2e + (mode->vdisplay * vscan / ilace); | 2078 | m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay; |
2067 | m->v.active = (m->v.active * 2) + 1; | 2079 | m->v.active = (m->v.active * 2) + 1; |
2068 | m->interlace = true; | 2080 | m->interlace = true; |
2069 | } else { | 2081 | } else { |
@@ -2071,9 +2083,8 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh) | |||
2071 | m->v.blank2s = 1; | 2083 | m->v.blank2s = 1; |
2072 | m->interlace = false; | 2084 | m->interlace = false; |
2073 | } | 2085 | } |
2074 | m->clock = mode->clock; | 2086 | m->clock = mode->crtc_clock; |
2075 | 2087 | ||
2076 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
2077 | asyh->set.mode = true; | 2088 | asyh->set.mode = true; |
2078 | } | 2089 | } |
2079 | 2090 | ||
@@ -2392,6 +2403,51 @@ out: | |||
2392 | /****************************************************************************** | 2403 | /****************************************************************************** |
2393 | * Output path helpers | 2404 | * Output path helpers |
2394 | *****************************************************************************/ | 2405 | *****************************************************************************/ |
2406 | static void | ||
2407 | nv50_outp_release(struct nouveau_encoder *nv_encoder) | ||
2408 | { | ||
2409 | struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev); | ||
2410 | struct { | ||
2411 | struct nv50_disp_mthd_v1 base; | ||
2412 | } args = { | ||
2413 | .base.version = 1, | ||
2414 | .base.method = NV50_DISP_MTHD_V1_RELEASE, | ||
2415 | .base.hasht = nv_encoder->dcb->hasht, | ||
2416 | .base.hashm = nv_encoder->dcb->hashm, | ||
2417 | }; | ||
2418 | |||
2419 | nvif_mthd(disp->disp, 0, &args, sizeof(args)); | ||
2420 | nv_encoder->or = -1; | ||
2421 | nv_encoder->link = 0; | ||
2422 | } | ||
2423 | |||
2424 | static int | ||
2425 | nv50_outp_acquire(struct nouveau_encoder *nv_encoder) | ||
2426 | { | ||
2427 | struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); | ||
2428 | struct nv50_disp *disp = nv50_disp(drm->dev); | ||
2429 | struct { | ||
2430 | struct nv50_disp_mthd_v1 base; | ||
2431 | struct nv50_disp_acquire_v0 info; | ||
2432 | } args = { | ||
2433 | .base.version = 1, | ||
2434 | .base.method = NV50_DISP_MTHD_V1_ACQUIRE, | ||
2435 | .base.hasht = nv_encoder->dcb->hasht, | ||
2436 | .base.hashm = nv_encoder->dcb->hashm, | ||
2437 | }; | ||
2438 | int ret; | ||
2439 | |||
2440 | ret = nvif_mthd(disp->disp, 0, &args, sizeof(args)); | ||
2441 | if (ret) { | ||
2442 | NV_ERROR(drm, "error acquiring output path: %d\n", ret); | ||
2443 | return ret; | ||
2444 | } | ||
2445 | |||
2446 | nv_encoder->or = args.info.or; | ||
2447 | nv_encoder->link = args.info.link; | ||
2448 | return 0; | ||
2449 | } | ||
2450 | |||
2395 | static int | 2451 | static int |
2396 | nv50_outp_atomic_check_view(struct drm_encoder *encoder, | 2452 | nv50_outp_atomic_check_view(struct drm_encoder *encoder, |
2397 | struct drm_crtc_state *crtc_state, | 2453 | struct drm_crtc_state *crtc_state, |
@@ -2449,30 +2505,6 @@ nv50_outp_atomic_check(struct drm_encoder *encoder, | |||
2449 | * DAC | 2505 | * DAC |
2450 | *****************************************************************************/ | 2506 | *****************************************************************************/ |
2451 | static void | 2507 | static void |
2452 | nv50_dac_dpms(struct drm_encoder *encoder, int mode) | ||
2453 | { | ||
2454 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
2455 | struct nv50_disp *disp = nv50_disp(encoder->dev); | ||
2456 | struct { | ||
2457 | struct nv50_disp_mthd_v1 base; | ||
2458 | struct nv50_disp_dac_pwr_v0 pwr; | ||
2459 | } args = { | ||
2460 | .base.version = 1, | ||
2461 | .base.method = NV50_DISP_MTHD_V1_DAC_PWR, | ||
2462 | .base.hasht = nv_encoder->dcb->hasht, | ||
2463 | .base.hashm = nv_encoder->dcb->hashm, | ||
2464 | .pwr.state = 1, | ||
2465 | .pwr.data = 1, | ||
2466 | .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND && | ||
2467 | mode != DRM_MODE_DPMS_OFF), | ||
2468 | .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY && | ||
2469 | mode != DRM_MODE_DPMS_OFF), | ||
2470 | }; | ||
2471 | |||
2472 | nvif_mthd(disp->disp, 0, &args, sizeof(args)); | ||
2473 | } | ||
2474 | |||
2475 | static void | ||
2476 | nv50_dac_disable(struct drm_encoder *encoder) | 2508 | nv50_dac_disable(struct drm_encoder *encoder) |
2477 | { | 2509 | { |
2478 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 2510 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
@@ -2495,6 +2527,7 @@ nv50_dac_disable(struct drm_encoder *encoder) | |||
2495 | } | 2527 | } |
2496 | 2528 | ||
2497 | nv_encoder->crtc = NULL; | 2529 | nv_encoder->crtc = NULL; |
2530 | nv50_outp_release(nv_encoder); | ||
2498 | } | 2531 | } |
2499 | 2532 | ||
2500 | static void | 2533 | static void |
@@ -2506,6 +2539,8 @@ nv50_dac_enable(struct drm_encoder *encoder) | |||
2506 | struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode; | 2539 | struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode; |
2507 | u32 *push; | 2540 | u32 *push; |
2508 | 2541 | ||
2542 | nv50_outp_acquire(nv_encoder); | ||
2543 | |||
2509 | push = evo_wait(mast, 8); | 2544 | push = evo_wait(mast, 8); |
2510 | if (push) { | 2545 | if (push) { |
2511 | if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { | 2546 | if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) { |
@@ -2573,7 +2608,6 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) | |||
2573 | 2608 | ||
2574 | static const struct drm_encoder_helper_funcs | 2609 | static const struct drm_encoder_helper_funcs |
2575 | nv50_dac_help = { | 2610 | nv50_dac_help = { |
2576 | .dpms = nv50_dac_dpms, | ||
2577 | .atomic_check = nv50_outp_atomic_check, | 2611 | .atomic_check = nv50_outp_atomic_check, |
2578 | .enable = nv50_dac_enable, | 2612 | .enable = nv50_dac_enable, |
2579 | .disable = nv50_dac_disable, | 2613 | .disable = nv50_dac_disable, |
@@ -2606,7 +2640,6 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) | |||
2606 | if (!nv_encoder) | 2640 | if (!nv_encoder) |
2607 | return -ENOMEM; | 2641 | return -ENOMEM; |
2608 | nv_encoder->dcb = dcbe; | 2642 | nv_encoder->dcb = dcbe; |
2609 | nv_encoder->or = ffs(dcbe->or) - 1; | ||
2610 | 2643 | ||
2611 | bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); | 2644 | bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); |
2612 | if (bus) | 2645 | if (bus) |
@@ -2708,6 +2741,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) | |||
2708 | struct { | 2741 | struct { |
2709 | struct nv50_disp_mthd_v1 base; | 2742 | struct nv50_disp_mthd_v1 base; |
2710 | struct nv50_disp_sor_hdmi_pwr_v0 pwr; | 2743 | struct nv50_disp_sor_hdmi_pwr_v0 pwr; |
2744 | u8 infoframes[2 * 17]; /* two frames, up to 17 bytes each */ | ||
2711 | } args = { | 2745 | } args = { |
2712 | .base.version = 1, | 2746 | .base.version = 1, |
2713 | .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, | 2747 | .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, |
@@ -2719,17 +2753,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) | |||
2719 | }; | 2753 | }; |
2720 | struct nouveau_connector *nv_connector; | 2754 | struct nouveau_connector *nv_connector; |
2721 | u32 max_ac_packet; | 2755 | u32 max_ac_packet; |
2756 | union hdmi_infoframe avi_frame; | ||
2757 | union hdmi_infoframe vendor_frame; | ||
2758 | int ret; | ||
2759 | int size; | ||
2722 | 2760 | ||
2723 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | 2761 | nv_connector = nouveau_encoder_connector_get(nv_encoder); |
2724 | if (!drm_detect_hdmi_monitor(nv_connector->edid)) | 2762 | if (!drm_detect_hdmi_monitor(nv_connector->edid)) |
2725 | return; | 2763 | return; |
2726 | 2764 | ||
2765 | ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode); | ||
2766 | if (!ret) { | ||
2767 | /* We have an AVI InfoFrame, populate it to the display */ | ||
2768 | args.pwr.avi_infoframe_length | ||
2769 | = hdmi_infoframe_pack(&avi_frame, args.infoframes, 17); | ||
2770 | } | ||
2771 | |||
2772 | ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode); | ||
2773 | if (!ret) { | ||
2774 | /* We have a Vendor InfoFrame, populate it to the display */ | ||
2775 | args.pwr.vendor_infoframe_length | ||
2776 | = hdmi_infoframe_pack(&vendor_frame, | ||
2777 | args.infoframes | ||
2778 | + args.pwr.avi_infoframe_length, | ||
2779 | 17); | ||
2780 | } | ||
2781 | |||
2727 | max_ac_packet = mode->htotal - mode->hdisplay; | 2782 | max_ac_packet = mode->htotal - mode->hdisplay; |
2728 | max_ac_packet -= args.pwr.rekey; | 2783 | max_ac_packet -= args.pwr.rekey; |
2729 | max_ac_packet -= 18; /* constant from tegra */ | 2784 | max_ac_packet -= 18; /* constant from tegra */ |
2730 | args.pwr.max_ac_packet = max_ac_packet / 32; | 2785 | args.pwr.max_ac_packet = max_ac_packet / 32; |
2731 | 2786 | ||
2732 | nvif_mthd(disp->disp, 0, &args, sizeof(args)); | 2787 | size = sizeof(args.base) |
2788 | + sizeof(args.pwr) | ||
2789 | + args.pwr.avi_infoframe_length | ||
2790 | + args.pwr.vendor_infoframe_length; | ||
2791 | nvif_mthd(disp->disp, 0, &args, size); | ||
2733 | nv50_audio_enable(encoder, mode); | 2792 | nv50_audio_enable(encoder, mode); |
2734 | } | 2793 | } |
2735 | 2794 | ||
@@ -2747,6 +2806,8 @@ struct nv50_mstm { | |||
2747 | struct nv50_msto *msto[4]; | 2806 | struct nv50_msto *msto[4]; |
2748 | 2807 | ||
2749 | bool modified; | 2808 | bool modified; |
2809 | bool disabled; | ||
2810 | int links; | ||
2750 | }; | 2811 | }; |
2751 | 2812 | ||
2752 | struct nv50_mstc { | 2813 | struct nv50_mstc { |
@@ -2895,7 +2956,10 @@ nv50_msto_enable(struct drm_encoder *encoder) | |||
2895 | r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots); | 2956 | r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots); |
2896 | WARN_ON(!r); | 2957 | WARN_ON(!r); |
2897 | 2958 | ||
2898 | if (mstm->outp->dcb->sorconf.link & 1) | 2959 | if (!mstm->links++) |
2960 | nv50_outp_acquire(mstm->outp); | ||
2961 | |||
2962 | if (mstm->outp->link & 1) | ||
2899 | proto = 0x8; | 2963 | proto = 0x8; |
2900 | else | 2964 | else |
2901 | proto = 0x9; | 2965 | proto = 0x9; |
@@ -2927,6 +2991,8 @@ nv50_msto_disable(struct drm_encoder *encoder) | |||
2927 | 2991 | ||
2928 | mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); | 2992 | mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); |
2929 | mstm->modified = true; | 2993 | mstm->modified = true; |
2994 | if (!--mstm->links) | ||
2995 | mstm->disabled = true; | ||
2930 | msto->disabled = true; | 2996 | msto->disabled = true; |
2931 | } | 2997 | } |
2932 | 2998 | ||
@@ -3142,6 +3208,12 @@ nv50_mstm_prepare(struct nv50_mstm *mstm) | |||
3142 | nv50_msto_prepare(msto); | 3208 | nv50_msto_prepare(msto); |
3143 | } | 3209 | } |
3144 | } | 3210 | } |
3211 | |||
3212 | if (mstm->disabled) { | ||
3213 | if (!mstm->links) | ||
3214 | nv50_outp_release(mstm->outp); | ||
3215 | mstm->disabled = false; | ||
3216 | } | ||
3145 | } | 3217 | } |
3146 | 3218 | ||
3147 | static void | 3219 | static void |
@@ -3369,25 +3441,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, | |||
3369 | * SOR | 3441 | * SOR |
3370 | *****************************************************************************/ | 3442 | *****************************************************************************/ |
3371 | static void | 3443 | static void |
3372 | nv50_sor_dpms(struct drm_encoder *encoder, int mode) | ||
3373 | { | ||
3374 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
3375 | struct nv50_disp *disp = nv50_disp(encoder->dev); | ||
3376 | struct { | ||
3377 | struct nv50_disp_mthd_v1 base; | ||
3378 | struct nv50_disp_sor_pwr_v0 pwr; | ||
3379 | } args = { | ||
3380 | .base.version = 1, | ||
3381 | .base.method = NV50_DISP_MTHD_V1_SOR_PWR, | ||
3382 | .base.hasht = nv_encoder->dcb->hasht, | ||
3383 | .base.hashm = nv_encoder->dcb->hashm, | ||
3384 | .pwr.state = mode == DRM_MODE_DPMS_ON, | ||
3385 | }; | ||
3386 | |||
3387 | nvif_mthd(disp->disp, 0, &args, sizeof(args)); | ||
3388 | } | ||
3389 | |||
3390 | static void | ||
3391 | nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, | 3444 | nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, |
3392 | struct drm_display_mode *mode, u8 proto, u8 depth) | 3445 | struct drm_display_mode *mode, u8 proto, u8 depth) |
3393 | { | 3446 | { |
@@ -3459,6 +3512,7 @@ nv50_sor_disable(struct drm_encoder *encoder) | |||
3459 | nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); | 3512 | nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); |
3460 | nv50_audio_disable(encoder, nv_crtc); | 3513 | nv50_audio_disable(encoder, nv_crtc); |
3461 | nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); | 3514 | nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); |
3515 | nv50_outp_release(nv_encoder); | ||
3462 | } | 3516 | } |
3463 | } | 3517 | } |
3464 | 3518 | ||
@@ -3487,10 +3541,11 @@ nv50_sor_enable(struct drm_encoder *encoder) | |||
3487 | 3541 | ||
3488 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | 3542 | nv_connector = nouveau_encoder_connector_get(nv_encoder); |
3489 | nv_encoder->crtc = encoder->crtc; | 3543 | nv_encoder->crtc = encoder->crtc; |
3544 | nv50_outp_acquire(nv_encoder); | ||
3490 | 3545 | ||
3491 | switch (nv_encoder->dcb->type) { | 3546 | switch (nv_encoder->dcb->type) { |
3492 | case DCB_OUTPUT_TMDS: | 3547 | case DCB_OUTPUT_TMDS: |
3493 | if (nv_encoder->dcb->sorconf.link & 1) { | 3548 | if (nv_encoder->link & 1) { |
3494 | proto = 0x1; | 3549 | proto = 0x1; |
3495 | /* Only enable dual-link if: | 3550 | /* Only enable dual-link if: |
3496 | * - Need to (i.e. rate > 165MHz) | 3551 | * - Need to (i.e. rate > 165MHz) |
@@ -3548,7 +3603,7 @@ nv50_sor_enable(struct drm_encoder *encoder) | |||
3548 | else | 3603 | else |
3549 | depth = 0x6; | 3604 | depth = 0x6; |
3550 | 3605 | ||
3551 | if (nv_encoder->dcb->sorconf.link & 1) | 3606 | if (nv_encoder->link & 1) |
3552 | proto = 0x8; | 3607 | proto = 0x8; |
3553 | else | 3608 | else |
3554 | proto = 0x9; | 3609 | proto = 0x9; |
@@ -3565,7 +3620,6 @@ nv50_sor_enable(struct drm_encoder *encoder) | |||
3565 | 3620 | ||
3566 | static const struct drm_encoder_helper_funcs | 3621 | static const struct drm_encoder_helper_funcs |
3567 | nv50_sor_help = { | 3622 | nv50_sor_help = { |
3568 | .dpms = nv50_sor_dpms, | ||
3569 | .atomic_check = nv50_outp_atomic_check, | 3623 | .atomic_check = nv50_outp_atomic_check, |
3570 | .enable = nv50_sor_enable, | 3624 | .enable = nv50_sor_enable, |
3571 | .disable = nv50_sor_disable, | 3625 | .disable = nv50_sor_disable, |
@@ -3608,7 +3662,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) | |||
3608 | if (!nv_encoder) | 3662 | if (!nv_encoder) |
3609 | return -ENOMEM; | 3663 | return -ENOMEM; |
3610 | nv_encoder->dcb = dcbe; | 3664 | nv_encoder->dcb = dcbe; |
3611 | nv_encoder->or = ffs(dcbe->or) - 1; | ||
3612 | nv_encoder->update = nv50_sor_update; | 3665 | nv_encoder->update = nv50_sor_update; |
3613 | 3666 | ||
3614 | encoder = to_drm_encoder(nv_encoder); | 3667 | encoder = to_drm_encoder(nv_encoder); |
@@ -3649,26 +3702,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) | |||
3649 | /****************************************************************************** | 3702 | /****************************************************************************** |
3650 | * PIOR | 3703 | * PIOR |
3651 | *****************************************************************************/ | 3704 | *****************************************************************************/ |
3652 | static void | ||
3653 | nv50_pior_dpms(struct drm_encoder *encoder, int mode) | ||
3654 | { | ||
3655 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
3656 | struct nv50_disp *disp = nv50_disp(encoder->dev); | ||
3657 | struct { | ||
3658 | struct nv50_disp_mthd_v1 base; | ||
3659 | struct nv50_disp_pior_pwr_v0 pwr; | ||
3660 | } args = { | ||
3661 | .base.version = 1, | ||
3662 | .base.method = NV50_DISP_MTHD_V1_PIOR_PWR, | ||
3663 | .base.hasht = nv_encoder->dcb->hasht, | ||
3664 | .base.hashm = nv_encoder->dcb->hashm, | ||
3665 | .pwr.state = mode == DRM_MODE_DPMS_ON, | ||
3666 | .pwr.type = nv_encoder->dcb->type, | ||
3667 | }; | ||
3668 | |||
3669 | nvif_mthd(disp->disp, 0, &args, sizeof(args)); | ||
3670 | } | ||
3671 | |||
3672 | static int | 3705 | static int |
3673 | nv50_pior_atomic_check(struct drm_encoder *encoder, | 3706 | nv50_pior_atomic_check(struct drm_encoder *encoder, |
3674 | struct drm_crtc_state *crtc_state, | 3707 | struct drm_crtc_state *crtc_state, |
@@ -3701,6 +3734,7 @@ nv50_pior_disable(struct drm_encoder *encoder) | |||
3701 | } | 3734 | } |
3702 | 3735 | ||
3703 | nv_encoder->crtc = NULL; | 3736 | nv_encoder->crtc = NULL; |
3737 | nv50_outp_release(nv_encoder); | ||
3704 | } | 3738 | } |
3705 | 3739 | ||
3706 | static void | 3740 | static void |
@@ -3715,6 +3749,8 @@ nv50_pior_enable(struct drm_encoder *encoder) | |||
3715 | u8 proto, depth; | 3749 | u8 proto, depth; |
3716 | u32 *push; | 3750 | u32 *push; |
3717 | 3751 | ||
3752 | nv50_outp_acquire(nv_encoder); | ||
3753 | |||
3718 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | 3754 | nv_connector = nouveau_encoder_connector_get(nv_encoder); |
3719 | switch (nv_connector->base.display_info.bpc) { | 3755 | switch (nv_connector->base.display_info.bpc) { |
3720 | case 10: depth = 0x6; break; | 3756 | case 10: depth = 0x6; break; |
@@ -3753,7 +3789,6 @@ nv50_pior_enable(struct drm_encoder *encoder) | |||
3753 | 3789 | ||
3754 | static const struct drm_encoder_helper_funcs | 3790 | static const struct drm_encoder_helper_funcs |
3755 | nv50_pior_help = { | 3791 | nv50_pior_help = { |
3756 | .dpms = nv50_pior_dpms, | ||
3757 | .atomic_check = nv50_pior_atomic_check, | 3792 | .atomic_check = nv50_pior_atomic_check, |
3758 | .enable = nv50_pior_enable, | 3793 | .enable = nv50_pior_enable, |
3759 | .disable = nv50_pior_disable, | 3794 | .disable = nv50_pior_disable, |
@@ -3803,7 +3838,6 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) | |||
3803 | if (!nv_encoder) | 3838 | if (!nv_encoder) |
3804 | return -ENOMEM; | 3839 | return -ENOMEM; |
3805 | nv_encoder->dcb = dcbe; | 3840 | nv_encoder->dcb = dcbe; |
3806 | nv_encoder->or = ffs(dcbe->or) - 1; | ||
3807 | nv_encoder->i2c = ddc; | 3841 | nv_encoder->i2c = ddc; |
3808 | nv_encoder->aux = aux; | 3842 | nv_encoder->aux = aux; |
3809 | 3843 | ||
@@ -4318,14 +4352,8 @@ nv50_display_init(struct drm_device *dev) | |||
4318 | 4352 | ||
4319 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 4353 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
4320 | if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { | 4354 | if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { |
4321 | const struct drm_encoder_helper_funcs *help; | 4355 | struct nouveau_encoder *nv_encoder = |
4322 | struct nouveau_encoder *nv_encoder; | 4356 | nouveau_encoder(encoder); |
4323 | |||
4324 | nv_encoder = nouveau_encoder(encoder); | ||
4325 | help = encoder->helper_private; | ||
4326 | if (help && help->dpms) | ||
4327 | help->dpms(encoder, DRM_MODE_DPMS_ON); | ||
4328 | |||
4329 | nv50_mstm_init(nv_encoder->dp.mstm); | 4357 | nv50_mstm_init(nv_encoder->dp.mstm); |
4330 | } | 4358 | } |
4331 | } | 4359 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index b690bc12a5b7..7bdc7a5ae723 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c | |||
@@ -1257,7 +1257,7 @@ nvaa_chipset = { | |||
1257 | .therm = g84_therm_new, | 1257 | .therm = g84_therm_new, |
1258 | .timer = nv41_timer_new, | 1258 | .timer = nv41_timer_new, |
1259 | .volt = nv40_volt_new, | 1259 | .volt = nv40_volt_new, |
1260 | .disp = g94_disp_new, | 1260 | .disp = mcp77_disp_new, |
1261 | .dma = nv50_dma_new, | 1261 | .dma = nv50_dma_new, |
1262 | .fifo = g84_fifo_new, | 1262 | .fifo = g84_fifo_new, |
1263 | .gr = gt200_gr_new, | 1263 | .gr = gt200_gr_new, |
@@ -1289,7 +1289,7 @@ nvac_chipset = { | |||
1289 | .therm = g84_therm_new, | 1289 | .therm = g84_therm_new, |
1290 | .timer = nv41_timer_new, | 1290 | .timer = nv41_timer_new, |
1291 | .volt = nv40_volt_new, | 1291 | .volt = nv40_volt_new, |
1292 | .disp = g94_disp_new, | 1292 | .disp = mcp77_disp_new, |
1293 | .dma = nv50_dma_new, | 1293 | .dma = nv50_dma_new, |
1294 | .fifo = g84_fifo_new, | 1294 | .fifo = g84_fifo_new, |
1295 | .gr = mcp79_gr_new, | 1295 | .gr = mcp79_gr_new, |
@@ -1323,7 +1323,7 @@ nvaf_chipset = { | |||
1323 | .timer = nv41_timer_new, | 1323 | .timer = nv41_timer_new, |
1324 | .volt = nv40_volt_new, | 1324 | .volt = nv40_volt_new, |
1325 | .ce[0] = gt215_ce_new, | 1325 | .ce[0] = gt215_ce_new, |
1326 | .disp = gt215_disp_new, | 1326 | .disp = mcp89_disp_new, |
1327 | .dma = nv50_dma_new, | 1327 | .dma = nv50_dma_new, |
1328 | .fifo = g84_fifo_new, | 1328 | .fifo = g84_fifo_new, |
1329 | .gr = mcp89_gr_new, | 1329 | .gr = mcp89_gr_new, |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c index 6474bd2a6d07..189ed80e21ff 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c | |||
@@ -51,10 +51,12 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev) | |||
51 | reset_control_assert(tdev->rst); | 51 | reset_control_assert(tdev->rst); |
52 | udelay(10); | 52 | udelay(10); |
53 | 53 | ||
54 | ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D); | 54 | if (!tdev->pdev->dev.pm_domain) { |
55 | if (ret) | 55 | ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D); |
56 | goto err_clamp; | 56 | if (ret) |
57 | udelay(10); | 57 | goto err_clamp; |
58 | udelay(10); | ||
59 | } | ||
58 | 60 | ||
59 | reset_control_deassert(tdev->rst); | 61 | reset_control_deassert(tdev->rst); |
60 | udelay(10); | 62 | udelay(10); |
@@ -80,9 +82,6 @@ nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev) | |||
80 | { | 82 | { |
81 | int ret; | 83 | int ret; |
82 | 84 | ||
83 | reset_control_assert(tdev->rst); | ||
84 | udelay(10); | ||
85 | |||
86 | clk_disable_unprepare(tdev->clk_pwr); | 85 | clk_disable_unprepare(tdev->clk_pwr); |
87 | if (tdev->clk_ref) | 86 | if (tdev->clk_ref) |
88 | clk_disable_unprepare(tdev->clk_ref); | 87 | clk_disable_unprepare(tdev->clk_ref); |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild index fa05d16ae948..48ce6699183e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild | |||
@@ -4,7 +4,9 @@ nvkm-y += nvkm/engine/disp/nv50.o | |||
4 | nvkm-y += nvkm/engine/disp/g84.o | 4 | nvkm-y += nvkm/engine/disp/g84.o |
5 | nvkm-y += nvkm/engine/disp/g94.o | 5 | nvkm-y += nvkm/engine/disp/g94.o |
6 | nvkm-y += nvkm/engine/disp/gt200.o | 6 | nvkm-y += nvkm/engine/disp/gt200.o |
7 | nvkm-y += nvkm/engine/disp/mcp77.o | ||
7 | nvkm-y += nvkm/engine/disp/gt215.o | 8 | nvkm-y += nvkm/engine/disp/gt215.o |
9 | nvkm-y += nvkm/engine/disp/mcp89.o | ||
8 | nvkm-y += nvkm/engine/disp/gf119.o | 10 | nvkm-y += nvkm/engine/disp/gf119.o |
9 | nvkm-y += nvkm/engine/disp/gk104.o | 11 | nvkm-y += nvkm/engine/disp/gk104.o |
10 | nvkm-y += nvkm/engine/disp/gk110.o | 12 | nvkm-y += nvkm/engine/disp/gk110.o |
@@ -12,29 +14,41 @@ nvkm-y += nvkm/engine/disp/gm107.o | |||
12 | nvkm-y += nvkm/engine/disp/gm200.o | 14 | nvkm-y += nvkm/engine/disp/gm200.o |
13 | nvkm-y += nvkm/engine/disp/gp100.o | 15 | nvkm-y += nvkm/engine/disp/gp100.o |
14 | nvkm-y += nvkm/engine/disp/gp102.o | 16 | nvkm-y += nvkm/engine/disp/gp102.o |
17 | nvkm-y += nvkm/engine/disp/vga.o | ||
15 | 18 | ||
16 | nvkm-y += nvkm/engine/disp/outp.o | 19 | nvkm-y += nvkm/engine/disp/head.o |
17 | nvkm-y += nvkm/engine/disp/outpdp.o | 20 | nvkm-y += nvkm/engine/disp/headnv04.o |
21 | nvkm-y += nvkm/engine/disp/headnv50.o | ||
22 | nvkm-y += nvkm/engine/disp/headgf119.o | ||
23 | |||
24 | nvkm-y += nvkm/engine/disp/ior.o | ||
18 | nvkm-y += nvkm/engine/disp/dacnv50.o | 25 | nvkm-y += nvkm/engine/disp/dacnv50.o |
26 | nvkm-y += nvkm/engine/disp/dacgf119.o | ||
19 | nvkm-y += nvkm/engine/disp/piornv50.o | 27 | nvkm-y += nvkm/engine/disp/piornv50.o |
20 | nvkm-y += nvkm/engine/disp/sornv50.o | 28 | nvkm-y += nvkm/engine/disp/sornv50.o |
29 | nvkm-y += nvkm/engine/disp/sorg84.o | ||
21 | nvkm-y += nvkm/engine/disp/sorg94.o | 30 | nvkm-y += nvkm/engine/disp/sorg94.o |
31 | nvkm-y += nvkm/engine/disp/sormcp77.o | ||
32 | nvkm-y += nvkm/engine/disp/sorgt215.o | ||
33 | nvkm-y += nvkm/engine/disp/sormcp89.o | ||
22 | nvkm-y += nvkm/engine/disp/sorgf119.o | 34 | nvkm-y += nvkm/engine/disp/sorgf119.o |
35 | nvkm-y += nvkm/engine/disp/sorgk104.o | ||
23 | nvkm-y += nvkm/engine/disp/sorgm107.o | 36 | nvkm-y += nvkm/engine/disp/sorgm107.o |
24 | nvkm-y += nvkm/engine/disp/sorgm200.o | 37 | nvkm-y += nvkm/engine/disp/sorgm200.o |
25 | nvkm-y += nvkm/engine/disp/dport.o | ||
26 | 38 | ||
27 | nvkm-y += nvkm/engine/disp/conn.o | 39 | nvkm-y += nvkm/engine/disp/outp.o |
40 | nvkm-y += nvkm/engine/disp/dp.o | ||
28 | 41 | ||
29 | nvkm-y += nvkm/engine/disp/hdagt215.o | 42 | nvkm-y += nvkm/engine/disp/hdagt215.o |
30 | nvkm-y += nvkm/engine/disp/hdagf119.o | 43 | nvkm-y += nvkm/engine/disp/hdagf119.o |
31 | 44 | ||
45 | nvkm-y += nvkm/engine/disp/hdmi.o | ||
32 | nvkm-y += nvkm/engine/disp/hdmig84.o | 46 | nvkm-y += nvkm/engine/disp/hdmig84.o |
33 | nvkm-y += nvkm/engine/disp/hdmigt215.o | 47 | nvkm-y += nvkm/engine/disp/hdmigt215.o |
34 | nvkm-y += nvkm/engine/disp/hdmigf119.o | 48 | nvkm-y += nvkm/engine/disp/hdmigf119.o |
35 | nvkm-y += nvkm/engine/disp/hdmigk104.o | 49 | nvkm-y += nvkm/engine/disp/hdmigk104.o |
36 | 50 | ||
37 | nvkm-y += nvkm/engine/disp/vga.o | 51 | nvkm-y += nvkm/engine/disp/conn.o |
38 | 52 | ||
39 | nvkm-y += nvkm/engine/disp/rootnv04.o | 53 | nvkm-y += nvkm/engine/disp/rootnv04.o |
40 | nvkm-y += nvkm/engine/disp/rootnv50.o | 54 | nvkm-y += nvkm/engine/disp/rootnv50.o |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index 1efe91b1e22b..c7c84d34d97e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c | |||
@@ -23,6 +23,9 @@ | |||
23 | */ | 23 | */ |
24 | #include "priv.h" | 24 | #include "priv.h" |
25 | #include "conn.h" | 25 | #include "conn.h" |
26 | #include "dp.h" | ||
27 | #include "head.h" | ||
28 | #include "ior.h" | ||
26 | #include "outp.h" | 29 | #include "outp.h" |
27 | 30 | ||
28 | #include <core/client.h> | 31 | #include <core/client.h> |
@@ -37,17 +40,21 @@ | |||
37 | #include <nvif/unpack.h> | 40 | #include <nvif/unpack.h> |
38 | 41 | ||
39 | static void | 42 | static void |
40 | nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int head) | 43 | nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id) |
41 | { | 44 | { |
42 | struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); | 45 | struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); |
43 | disp->func->head.vblank_fini(disp, head); | 46 | struct nvkm_head *head = nvkm_head_find(disp, id); |
47 | if (head) | ||
48 | head->func->vblank_put(head); | ||
44 | } | 49 | } |
45 | 50 | ||
46 | static void | 51 | static void |
47 | nvkm_disp_vblank_init(struct nvkm_event *event, int type, int head) | 52 | nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id) |
48 | { | 53 | { |
49 | struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); | 54 | struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); |
50 | disp->func->head.vblank_init(disp, head); | 55 | struct nvkm_head *head = nvkm_head_find(disp, id); |
56 | if (head) | ||
57 | head->func->vblank_get(head); | ||
51 | } | 58 | } |
52 | 59 | ||
53 | static int | 60 | static int |
@@ -96,7 +103,7 @@ nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size, | |||
96 | union { | 103 | union { |
97 | struct nvif_notify_conn_req_v0 v0; | 104 | struct nvif_notify_conn_req_v0 v0; |
98 | } *req = data; | 105 | } *req = data; |
99 | struct nvkm_output *outp; | 106 | struct nvkm_outp *outp; |
100 | int ret = -ENOSYS; | 107 | int ret = -ENOSYS; |
101 | 108 | ||
102 | if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) { | 109 | if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) { |
@@ -210,15 +217,15 @@ static int | |||
210 | nvkm_disp_fini(struct nvkm_engine *engine, bool suspend) | 217 | nvkm_disp_fini(struct nvkm_engine *engine, bool suspend) |
211 | { | 218 | { |
212 | struct nvkm_disp *disp = nvkm_disp(engine); | 219 | struct nvkm_disp *disp = nvkm_disp(engine); |
213 | struct nvkm_connector *conn; | 220 | struct nvkm_conn *conn; |
214 | struct nvkm_output *outp; | 221 | struct nvkm_outp *outp; |
215 | 222 | ||
216 | list_for_each_entry(outp, &disp->outp, head) { | 223 | list_for_each_entry(outp, &disp->outp, head) { |
217 | nvkm_output_fini(outp); | 224 | nvkm_outp_fini(outp); |
218 | } | 225 | } |
219 | 226 | ||
220 | list_for_each_entry(conn, &disp->conn, head) { | 227 | list_for_each_entry(conn, &disp->conn, head) { |
221 | nvkm_connector_fini(conn); | 228 | nvkm_conn_fini(conn); |
222 | } | 229 | } |
223 | 230 | ||
224 | return 0; | 231 | return 0; |
@@ -228,129 +235,70 @@ static int | |||
228 | nvkm_disp_init(struct nvkm_engine *engine) | 235 | nvkm_disp_init(struct nvkm_engine *engine) |
229 | { | 236 | { |
230 | struct nvkm_disp *disp = nvkm_disp(engine); | 237 | struct nvkm_disp *disp = nvkm_disp(engine); |
231 | struct nvkm_connector *conn; | 238 | struct nvkm_conn *conn; |
232 | struct nvkm_output *outp; | 239 | struct nvkm_outp *outp; |
233 | 240 | ||
234 | list_for_each_entry(conn, &disp->conn, head) { | 241 | list_for_each_entry(conn, &disp->conn, head) { |
235 | nvkm_connector_init(conn); | 242 | nvkm_conn_init(conn); |
236 | } | 243 | } |
237 | 244 | ||
238 | list_for_each_entry(outp, &disp->outp, head) { | 245 | list_for_each_entry(outp, &disp->outp, head) { |
239 | nvkm_output_init(outp); | 246 | nvkm_outp_init(outp); |
240 | } | 247 | } |
241 | 248 | ||
242 | return 0; | 249 | return 0; |
243 | } | 250 | } |
244 | 251 | ||
245 | static void * | 252 | static int |
246 | nvkm_disp_dtor(struct nvkm_engine *engine) | 253 | nvkm_disp_oneinit(struct nvkm_engine *engine) |
247 | { | 254 | { |
248 | struct nvkm_disp *disp = nvkm_disp(engine); | 255 | struct nvkm_disp *disp = nvkm_disp(engine); |
249 | struct nvkm_connector *conn; | 256 | struct nvkm_subdev *subdev = &disp->engine.subdev; |
250 | struct nvkm_output *outp; | 257 | struct nvkm_bios *bios = subdev->device->bios; |
251 | void *data = disp; | 258 | struct nvkm_outp *outp, *outt, *pair; |
252 | 259 | struct nvkm_conn *conn; | |
253 | if (disp->func->dtor) | 260 | struct nvkm_head *head; |
254 | data = disp->func->dtor(disp); | ||
255 | |||
256 | nvkm_event_fini(&disp->vblank); | ||
257 | nvkm_event_fini(&disp->hpd); | ||
258 | |||
259 | while (!list_empty(&disp->outp)) { | ||
260 | outp = list_first_entry(&disp->outp, typeof(*outp), head); | ||
261 | list_del(&outp->head); | ||
262 | nvkm_output_del(&outp); | ||
263 | } | ||
264 | |||
265 | while (!list_empty(&disp->conn)) { | ||
266 | conn = list_first_entry(&disp->conn, typeof(*conn), head); | ||
267 | list_del(&conn->head); | ||
268 | nvkm_connector_del(&conn); | ||
269 | } | ||
270 | |||
271 | return data; | ||
272 | } | ||
273 | |||
274 | static const struct nvkm_engine_func | ||
275 | nvkm_disp = { | ||
276 | .dtor = nvkm_disp_dtor, | ||
277 | .init = nvkm_disp_init, | ||
278 | .fini = nvkm_disp_fini, | ||
279 | .intr = nvkm_disp_intr, | ||
280 | .base.sclass = nvkm_disp_class_get, | ||
281 | }; | ||
282 | |||
283 | int | ||
284 | nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | ||
285 | int index, int heads, struct nvkm_disp *disp) | ||
286 | { | ||
287 | struct nvkm_bios *bios = device->bios; | ||
288 | struct nvkm_output *outp, *outt, *pair; | ||
289 | struct nvkm_connector *conn; | ||
290 | struct nvbios_connE connE; | 261 | struct nvbios_connE connE; |
291 | struct dcb_output dcbE; | 262 | struct dcb_output dcbE; |
292 | u8 hpd = 0, ver, hdr; | 263 | u8 hpd = 0, ver, hdr; |
293 | u32 data; | 264 | u32 data; |
294 | int ret, i; | 265 | int ret, i; |
295 | 266 | ||
296 | INIT_LIST_HEAD(&disp->outp); | 267 | /* Create output path objects for each VBIOS display path. */ |
297 | INIT_LIST_HEAD(&disp->conn); | ||
298 | disp->func = func; | ||
299 | disp->head.nr = heads; | ||
300 | |||
301 | ret = nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine); | ||
302 | if (ret) | ||
303 | return ret; | ||
304 | |||
305 | /* create output objects for each display path in the vbios */ | ||
306 | i = -1; | 268 | i = -1; |
307 | while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { | 269 | while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { |
308 | const struct nvkm_disp_func_outp *outps; | ||
309 | int (*ctor)(struct nvkm_disp *, int, struct dcb_output *, | ||
310 | struct nvkm_output **); | ||
311 | |||
312 | if (dcbE.type == DCB_OUTPUT_UNUSED) | 270 | if (dcbE.type == DCB_OUTPUT_UNUSED) |
313 | continue; | 271 | continue; |
314 | if (dcbE.type == DCB_OUTPUT_EOL) | 272 | if (dcbE.type == DCB_OUTPUT_EOL) |
315 | break; | 273 | break; |
316 | outp = NULL; | 274 | outp = NULL; |
317 | 275 | ||
318 | switch (dcbE.location) { | ||
319 | case 0: outps = &disp->func->outp.internal; break; | ||
320 | case 1: outps = &disp->func->outp.external; break; | ||
321 | default: | ||
322 | nvkm_warn(&disp->engine.subdev, | ||
323 | "dcb %d locn %d unknown\n", i, dcbE.location); | ||
324 | continue; | ||
325 | } | ||
326 | |||
327 | switch (dcbE.type) { | 276 | switch (dcbE.type) { |
328 | case DCB_OUTPUT_ANALOG: ctor = outps->crt ; break; | 277 | case DCB_OUTPUT_ANALOG: |
329 | case DCB_OUTPUT_TV : ctor = outps->tv ; break; | 278 | case DCB_OUTPUT_TV: |
330 | case DCB_OUTPUT_TMDS : ctor = outps->tmds; break; | 279 | case DCB_OUTPUT_TMDS: |
331 | case DCB_OUTPUT_LVDS : ctor = outps->lvds; break; | 280 | case DCB_OUTPUT_LVDS: |
332 | case DCB_OUTPUT_DP : ctor = outps->dp ; break; | 281 | ret = nvkm_outp_new(disp, i, &dcbE, &outp); |
282 | break; | ||
283 | case DCB_OUTPUT_DP: | ||
284 | ret = nvkm_dp_new(disp, i, &dcbE, &outp); | ||
285 | break; | ||
333 | default: | 286 | default: |
334 | nvkm_warn(&disp->engine.subdev, | 287 | nvkm_warn(subdev, "dcb %d type %d unknown\n", |
335 | "dcb %d type %d unknown\n", i, dcbE.type); | 288 | i, dcbE.type); |
336 | continue; | 289 | continue; |
337 | } | 290 | } |
338 | 291 | ||
339 | if (ctor) | ||
340 | ret = ctor(disp, i, &dcbE, &outp); | ||
341 | else | ||
342 | ret = -ENODEV; | ||
343 | |||
344 | if (ret) { | 292 | if (ret) { |
345 | if (ret == -ENODEV) { | 293 | if (outp) { |
346 | nvkm_debug(&disp->engine.subdev, | 294 | if (ret != -ENODEV) |
347 | "dcb %d %d/%d not supported\n", | 295 | OUTP_ERR(outp, "ctor failed: %d", ret); |
348 | i, dcbE.location, dcbE.type); | 296 | else |
297 | OUTP_DBG(outp, "not supported"); | ||
298 | nvkm_outp_del(&outp); | ||
349 | continue; | 299 | continue; |
350 | } | 300 | } |
351 | nvkm_error(&disp->engine.subdev, | 301 | nvkm_error(subdev, "failed to create outp %d\n", i); |
352 | "failed to create output %d\n", i); | ||
353 | nvkm_output_del(&outp); | ||
354 | continue; | 302 | continue; |
355 | } | 303 | } |
356 | 304 | ||
@@ -358,18 +306,18 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | |||
358 | hpd = max(hpd, (u8)(dcbE.connector + 1)); | 306 | hpd = max(hpd, (u8)(dcbE.connector + 1)); |
359 | } | 307 | } |
360 | 308 | ||
361 | /* create connector objects based on the outputs we support */ | 309 | /* Create connector objects based on available output paths. */ |
362 | list_for_each_entry_safe(outp, outt, &disp->outp, head) { | 310 | list_for_each_entry_safe(outp, outt, &disp->outp, head) { |
363 | /* bios data *should* give us the most useful information */ | 311 | /* VBIOS data *should* give us the most useful information. */ |
364 | data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, | 312 | data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, |
365 | &connE); | 313 | &connE); |
366 | 314 | ||
367 | /* no bios connector data... */ | 315 | /* No bios connector data... */ |
368 | if (!data) { | 316 | if (!data) { |
369 | /* heuristic: anything with the same ccb index is | 317 | /* Heuristic: anything with the same ccb index is |
370 | * considered to be on the same connector, any | 318 | * considered to be on the same connector, any |
371 | * output path without an associated ccb entry will | 319 | * output path without an associated ccb entry will |
372 | * be put on its own connector | 320 | * be put on its own connector. |
373 | */ | 321 | */ |
374 | int ccb_index = outp->info.i2c_index; | 322 | int ccb_index = outp->info.i2c_index; |
375 | if (ccb_index != 0xf) { | 323 | if (ccb_index != 0xf) { |
@@ -381,7 +329,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | |||
381 | } | 329 | } |
382 | } | 330 | } |
383 | 331 | ||
384 | /* connector shared with another output path */ | 332 | /* Connector shared with another output path. */ |
385 | if (outp->conn) | 333 | if (outp->conn) |
386 | continue; | 334 | continue; |
387 | 335 | ||
@@ -392,7 +340,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | |||
392 | i = outp->info.connector; | 340 | i = outp->info.connector; |
393 | } | 341 | } |
394 | 342 | ||
395 | /* check that we haven't already created this connector */ | 343 | /* Check that we haven't already created this connector. */ |
396 | list_for_each_entry(conn, &disp->conn, head) { | 344 | list_for_each_entry(conn, &disp->conn, head) { |
397 | if (conn->index == outp->info.connector) { | 345 | if (conn->index == outp->info.connector) { |
398 | outp->conn = conn; | 346 | outp->conn = conn; |
@@ -403,15 +351,15 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | |||
403 | if (outp->conn) | 351 | if (outp->conn) |
404 | continue; | 352 | continue; |
405 | 353 | ||
406 | /* apparently we need to create a new one! */ | 354 | /* Apparently we need to create a new one! */ |
407 | ret = nvkm_connector_new(disp, i, &connE, &outp->conn); | 355 | ret = nvkm_conn_new(disp, i, &connE, &outp->conn); |
408 | if (ret) { | 356 | if (ret) { |
409 | nvkm_error(&disp->engine.subdev, | 357 | nvkm_error(&disp->engine.subdev, |
410 | "failed to create output %d conn: %d\n", | 358 | "failed to create outp %d conn: %d\n", |
411 | outp->index, ret); | 359 | outp->index, ret); |
412 | nvkm_connector_del(&outp->conn); | 360 | nvkm_conn_del(&outp->conn); |
413 | list_del(&outp->head); | 361 | list_del(&outp->head); |
414 | nvkm_output_del(&outp); | 362 | nvkm_outp_del(&outp); |
415 | continue; | 363 | continue; |
416 | } | 364 | } |
417 | 365 | ||
@@ -422,18 +370,81 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | |||
422 | if (ret) | 370 | if (ret) |
423 | return ret; | 371 | return ret; |
424 | 372 | ||
425 | ret = nvkm_event_init(&nvkm_disp_vblank_func, 1, heads, &disp->vblank); | 373 | i = 0; |
426 | if (ret) | 374 | list_for_each_entry(head, &disp->head, head) |
427 | return ret; | 375 | i = max(i, head->id + 1); |
428 | 376 | ||
429 | return 0; | 377 | return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank); |
378 | } | ||
379 | |||
380 | static void * | ||
381 | nvkm_disp_dtor(struct nvkm_engine *engine) | ||
382 | { | ||
383 | struct nvkm_disp *disp = nvkm_disp(engine); | ||
384 | struct nvkm_conn *conn; | ||
385 | struct nvkm_outp *outp; | ||
386 | void *data = disp; | ||
387 | |||
388 | if (disp->func->dtor) | ||
389 | data = disp->func->dtor(disp); | ||
390 | |||
391 | nvkm_event_fini(&disp->vblank); | ||
392 | nvkm_event_fini(&disp->hpd); | ||
393 | |||
394 | while (!list_empty(&disp->conn)) { | ||
395 | conn = list_first_entry(&disp->conn, typeof(*conn), head); | ||
396 | list_del(&conn->head); | ||
397 | nvkm_conn_del(&conn); | ||
398 | } | ||
399 | |||
400 | while (!list_empty(&disp->outp)) { | ||
401 | outp = list_first_entry(&disp->outp, typeof(*outp), head); | ||
402 | list_del(&outp->head); | ||
403 | nvkm_outp_del(&outp); | ||
404 | } | ||
405 | |||
406 | while (!list_empty(&disp->ior)) { | ||
407 | struct nvkm_ior *ior = | ||
408 | list_first_entry(&disp->ior, typeof(*ior), head); | ||
409 | nvkm_ior_del(&ior); | ||
410 | } | ||
411 | |||
412 | while (!list_empty(&disp->head)) { | ||
413 | struct nvkm_head *head = | ||
414 | list_first_entry(&disp->head, typeof(*head), head); | ||
415 | nvkm_head_del(&head); | ||
416 | } | ||
417 | |||
418 | return data; | ||
419 | } | ||
420 | |||
421 | static const struct nvkm_engine_func | ||
422 | nvkm_disp = { | ||
423 | .dtor = nvkm_disp_dtor, | ||
424 | .oneinit = nvkm_disp_oneinit, | ||
425 | .init = nvkm_disp_init, | ||
426 | .fini = nvkm_disp_fini, | ||
427 | .intr = nvkm_disp_intr, | ||
428 | .base.sclass = nvkm_disp_class_get, | ||
429 | }; | ||
430 | |||
431 | int | ||
432 | nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device, | ||
433 | int index, struct nvkm_disp *disp) | ||
434 | { | ||
435 | disp->func = func; | ||
436 | INIT_LIST_HEAD(&disp->head); | ||
437 | INIT_LIST_HEAD(&disp->ior); | ||
438 | INIT_LIST_HEAD(&disp->outp); | ||
439 | INIT_LIST_HEAD(&disp->conn); | ||
440 | return nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine); | ||
430 | } | 441 | } |
431 | 442 | ||
432 | int | 443 | int |
433 | nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device, | 444 | nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device, |
434 | int index, int heads, struct nvkm_disp **pdisp) | 445 | int index, struct nvkm_disp **pdisp) |
435 | { | 446 | { |
436 | if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL))) | 447 | if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL))) |
437 | return -ENOMEM; | 448 | return -ENOMEM; |
438 | return nvkm_disp_ctor(func, device, index, heads, *pdisp); | 449 | return nvkm_disp_ctor(func, device, index, *pdisp); |
439 | } | 450 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c index 83f152300ec0..f1d6b820d482 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/basenv50.c | |||
@@ -22,6 +22,7 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "dmacnv50.h" | 24 | #include "dmacnv50.h" |
25 | #include "head.h" | ||
25 | #include "rootnv50.h" | 26 | #include "rootnv50.h" |
26 | 27 | ||
27 | #include <core/client.h> | 28 | #include <core/client.h> |
@@ -50,7 +51,7 @@ nv50_disp_base_new(const struct nv50_disp_dmac_func *func, | |||
50 | nvif_ioctl(parent, "create disp base channel dma vers %d " | 51 | nvif_ioctl(parent, "create disp base channel dma vers %d " |
51 | "pushbuf %016llx head %d\n", | 52 | "pushbuf %016llx head %d\n", |
52 | args->v0.version, args->v0.pushbuf, args->v0.head); | 53 | args->v0.version, args->v0.pushbuf, args->v0.head); |
53 | if (args->v0.head > disp->base.head.nr) | 54 | if (!nvkm_head_find(&disp->base, args->v0.head)) |
54 | return -EINVAL; | 55 | return -EINVAL; |
55 | push = args->v0.pushbuf; | 56 | push = args->v0.pushbuf; |
56 | head = args->v0.head; | 57 | head = args->v0.head; |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c index 524a24eae1a0..0c0310498afd 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include "rootnv50.h" | 25 | #include "rootnv50.h" |
26 | 26 | ||
27 | #include <core/client.h> | 27 | #include <core/client.h> |
28 | #include <core/notify.h> | ||
28 | #include <core/ramht.h> | 29 | #include <core/ramht.h> |
29 | #include <engine/dma.h> | 30 | #include <engine/dma.h> |
30 | 31 | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c index c6910d644a3d..febc5c274488 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c | |||
@@ -30,9 +30,9 @@ | |||
30 | #include <nvif/event.h> | 30 | #include <nvif/event.h> |
31 | 31 | ||
32 | static int | 32 | static int |
33 | nvkm_connector_hpd(struct nvkm_notify *notify) | 33 | nvkm_conn_hpd(struct nvkm_notify *notify) |
34 | { | 34 | { |
35 | struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd); | 35 | struct nvkm_conn *conn = container_of(notify, typeof(*conn), hpd); |
36 | struct nvkm_disp *disp = conn->disp; | 36 | struct nvkm_disp *disp = conn->disp; |
37 | struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; | 37 | struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; |
38 | const struct nvkm_gpio_ntfy_rep *line = notify->data; | 38 | const struct nvkm_gpio_ntfy_rep *line = notify->data; |
@@ -52,21 +52,21 @@ nvkm_connector_hpd(struct nvkm_notify *notify) | |||
52 | } | 52 | } |
53 | 53 | ||
54 | void | 54 | void |
55 | nvkm_connector_fini(struct nvkm_connector *conn) | 55 | nvkm_conn_fini(struct nvkm_conn *conn) |
56 | { | 56 | { |
57 | nvkm_notify_put(&conn->hpd); | 57 | nvkm_notify_put(&conn->hpd); |
58 | } | 58 | } |
59 | 59 | ||
60 | void | 60 | void |
61 | nvkm_connector_init(struct nvkm_connector *conn) | 61 | nvkm_conn_init(struct nvkm_conn *conn) |
62 | { | 62 | { |
63 | nvkm_notify_get(&conn->hpd); | 63 | nvkm_notify_get(&conn->hpd); |
64 | } | 64 | } |
65 | 65 | ||
66 | void | 66 | void |
67 | nvkm_connector_del(struct nvkm_connector **pconn) | 67 | nvkm_conn_del(struct nvkm_conn **pconn) |
68 | { | 68 | { |
69 | struct nvkm_connector *conn = *pconn; | 69 | struct nvkm_conn *conn = *pconn; |
70 | if (conn) { | 70 | if (conn) { |
71 | nvkm_notify_fini(&conn->hpd); | 71 | nvkm_notify_fini(&conn->hpd); |
72 | kfree(*pconn); | 72 | kfree(*pconn); |
@@ -75,8 +75,8 @@ nvkm_connector_del(struct nvkm_connector **pconn) | |||
75 | } | 75 | } |
76 | 76 | ||
77 | static void | 77 | static void |
78 | nvkm_connector_ctor(struct nvkm_disp *disp, int index, | 78 | nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info, |
79 | struct nvbios_connE *info, struct nvkm_connector *conn) | 79 | struct nvkm_conn *conn) |
80 | { | 80 | { |
81 | static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; | 81 | static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 }; |
82 | struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; | 82 | struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio; |
@@ -105,7 +105,7 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index, | |||
105 | return; | 105 | return; |
106 | } | 106 | } |
107 | 107 | ||
108 | ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd, | 108 | ret = nvkm_notify_init(NULL, &gpio->event, nvkm_conn_hpd, |
109 | true, &(struct nvkm_gpio_ntfy_req) { | 109 | true, &(struct nvkm_gpio_ntfy_req) { |
110 | .mask = NVKM_GPIO_TOGGLED, | 110 | .mask = NVKM_GPIO_TOGGLED, |
111 | .line = func.line, | 111 | .line = func.line, |
@@ -122,11 +122,11 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index, | |||
122 | } | 122 | } |
123 | 123 | ||
124 | int | 124 | int |
125 | nvkm_connector_new(struct nvkm_disp *disp, int index, | 125 | nvkm_conn_new(struct nvkm_disp *disp, int index, struct nvbios_connE *info, |
126 | struct nvbios_connE *info, struct nvkm_connector **pconn) | 126 | struct nvkm_conn **pconn) |
127 | { | 127 | { |
128 | if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL))) | 128 | if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL))) |
129 | return -ENOMEM; | 129 | return -ENOMEM; |
130 | nvkm_connector_ctor(disp, index, info, *pconn); | 130 | nvkm_conn_ctor(disp, index, info, *pconn); |
131 | return 0; | 131 | return 0; |
132 | } | 132 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h index ed32fe7f1864..de962b7b026d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h | |||
@@ -6,7 +6,7 @@ | |||
6 | #include <subdev/bios.h> | 6 | #include <subdev/bios.h> |
7 | #include <subdev/bios/conn.h> | 7 | #include <subdev/bios/conn.h> |
8 | 8 | ||
9 | struct nvkm_connector { | 9 | struct nvkm_conn { |
10 | struct nvkm_disp *disp; | 10 | struct nvkm_disp *disp; |
11 | int index; | 11 | int index; |
12 | struct nvbios_connE info; | 12 | struct nvbios_connE info; |
@@ -16,14 +16,14 @@ struct nvkm_connector { | |||
16 | struct list_head head; | 16 | struct list_head head; |
17 | }; | 17 | }; |
18 | 18 | ||
19 | int nvkm_connector_new(struct nvkm_disp *, int index, struct nvbios_connE *, | 19 | int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *, |
20 | struct nvkm_connector **); | 20 | struct nvkm_conn **); |
21 | void nvkm_connector_del(struct nvkm_connector **); | 21 | void nvkm_conn_del(struct nvkm_conn **); |
22 | void nvkm_connector_init(struct nvkm_connector *); | 22 | void nvkm_conn_init(struct nvkm_conn *); |
23 | void nvkm_connector_fini(struct nvkm_connector *); | 23 | void nvkm_conn_fini(struct nvkm_conn *); |
24 | 24 | ||
25 | #define CONN_MSG(c,l,f,a...) do { \ | 25 | #define CONN_MSG(c,l,f,a...) do { \ |
26 | struct nvkm_connector *_conn = (c); \ | 26 | struct nvkm_conn *_conn = (c); \ |
27 | nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \ | 27 | nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \ |
28 | _conn->index, _conn->info.location, _conn->info.type, ##a); \ | 28 | _conn->index, _conn->info.location, _conn->info.type, ##a); \ |
29 | } while(0) | 29 | } while(0) |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c index 82ff82d8c1ab..ab51121b7982 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/cursnv50.c | |||
@@ -22,6 +22,7 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "channv50.h" | 24 | #include "channv50.h" |
25 | #include "head.h" | ||
25 | #include "rootnv50.h" | 26 | #include "rootnv50.h" |
26 | 27 | ||
27 | #include <core/client.h> | 28 | #include <core/client.h> |
@@ -48,7 +49,7 @@ nv50_disp_curs_new(const struct nv50_disp_chan_func *func, | |||
48 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 49 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { |
49 | nvif_ioctl(parent, "create disp cursor vers %d head %d\n", | 50 | nvif_ioctl(parent, "create disp cursor vers %d head %d\n", |
50 | args->v0.version, args->v0.head); | 51 | args->v0.version, args->v0.head); |
51 | if (args->v0.head > disp->base.head.nr) | 52 | if (!nvkm_head_find(&disp->base, args->v0.head)) |
52 | return -EINVAL; | 53 | return -EINVAL; |
53 | head = args->v0.head; | 54 | head = args->v0.head; |
54 | } else | 55 | } else |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c new file mode 100644 index 000000000000..dbd032ef352a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacgf119.c | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "ior.h" | ||
23 | |||
24 | static void | ||
25 | gf119_dac_clock(struct nvkm_ior *dac) | ||
26 | { | ||
27 | struct nvkm_device *device = dac->disp->engine.subdev.device; | ||
28 | const u32 doff = nv50_ior_base(dac); | ||
29 | nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000); | ||
30 | } | ||
31 | |||
32 | static void | ||
33 | gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) | ||
34 | { | ||
35 | struct nvkm_device *device = dac->disp->engine.subdev.device; | ||
36 | const u32 coff = (state == &dac->asy) * 0x20000 + dac->id * 0x20; | ||
37 | u32 ctrl = nvkm_rd32(device, 0x640180 + coff); | ||
38 | |||
39 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | ||
40 | switch (state->proto_evo) { | ||
41 | case 0: state->proto = CRT; break; | ||
42 | default: | ||
43 | state->proto = UNKNOWN; | ||
44 | break; | ||
45 | } | ||
46 | |||
47 | state->head = ctrl & 0x0000000f; | ||
48 | } | ||
49 | |||
50 | static const struct nvkm_ior_func | ||
51 | gf119_dac = { | ||
52 | .state = gf119_dac_state, | ||
53 | .power = nv50_dac_power, | ||
54 | .sense = nv50_dac_sense, | ||
55 | .clock = gf119_dac_clock, | ||
56 | }; | ||
57 | |||
58 | int | ||
59 | gf119_dac_new(struct nvkm_disp *disp, int id) | ||
60 | { | ||
61 | struct nvkm_device *device = disp->engine.subdev.device; | ||
62 | if (!(nvkm_rd32(device, 0x612004) & (0x00000010 << id))) | ||
63 | return 0; | ||
64 | return nvkm_ior_new_(&gf119_dac, disp, DAC, id); | ||
65 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c index c9b78b8f9c87..85e692b12260 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dacnv50.c | |||
@@ -21,106 +21,96 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outp.h" | ||
26 | 25 | ||
27 | #include <core/client.h> | ||
28 | #include <subdev/timer.h> | 26 | #include <subdev/timer.h> |
29 | 27 | ||
30 | #include <nvif/cl5070.h> | 28 | static void |
31 | #include <nvif/unpack.h> | 29 | nv50_dac_clock(struct nvkm_ior *dac) |
32 | |||
33 | int | ||
34 | nv50_dac_power(NV50_DISP_MTHD_V1) | ||
35 | { | 30 | { |
36 | struct nvkm_device *device = disp->base.engine.subdev.device; | 31 | struct nvkm_device *device = dac->disp->engine.subdev.device; |
37 | const u32 doff = outp->or * 0x800; | 32 | const u32 doff = nv50_ior_base(dac); |
38 | union { | 33 | nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000); |
39 | struct nv50_disp_dac_pwr_v0 v0; | ||
40 | } *args = data; | ||
41 | u32 stat; | ||
42 | int ret = -ENOSYS; | ||
43 | |||
44 | nvif_ioctl(object, "disp dac pwr size %d\n", size); | ||
45 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
46 | nvif_ioctl(object, "disp dac pwr vers %d state %d data %d " | ||
47 | "vsync %d hsync %d\n", | ||
48 | args->v0.version, args->v0.state, args->v0.data, | ||
49 | args->v0.vsync, args->v0.hsync); | ||
50 | stat = 0x00000040 * !args->v0.state; | ||
51 | stat |= 0x00000010 * !args->v0.data; | ||
52 | stat |= 0x00000004 * !args->v0.vsync; | ||
53 | stat |= 0x00000001 * !args->v0.hsync; | ||
54 | } else | ||
55 | return ret; | ||
56 | |||
57 | nvkm_msec(device, 2000, | ||
58 | if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) | ||
59 | break; | ||
60 | ); | ||
61 | nvkm_mask(device, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat); | ||
62 | nvkm_msec(device, 2000, | ||
63 | if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) | ||
64 | break; | ||
65 | ); | ||
66 | return 0; | ||
67 | } | 34 | } |
68 | 35 | ||
69 | int | 36 | int |
70 | nv50_dac_sense(NV50_DISP_MTHD_V1) | 37 | nv50_dac_sense(struct nvkm_ior *dac, u32 loadval) |
71 | { | 38 | { |
72 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 39 | struct nvkm_device *device = dac->disp->engine.subdev.device; |
73 | struct nvkm_device *device = subdev->device; | 40 | const u32 doff = nv50_ior_base(dac); |
74 | union { | ||
75 | struct nv50_disp_dac_load_v0 v0; | ||
76 | } *args = data; | ||
77 | const u32 doff = outp->or * 0x800; | ||
78 | u32 loadval; | ||
79 | int ret = -ENOSYS; | ||
80 | 41 | ||
81 | nvif_ioctl(object, "disp dac load size %d\n", size); | 42 | dac->func->power(dac, false, true, false, false, false); |
82 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
83 | nvif_ioctl(object, "disp dac load vers %d data %08x\n", | ||
84 | args->v0.version, args->v0.data); | ||
85 | if (args->v0.data & 0xfff00000) | ||
86 | return -EINVAL; | ||
87 | loadval = args->v0.data; | ||
88 | } else | ||
89 | return ret; | ||
90 | |||
91 | nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80150000); | ||
92 | nvkm_msec(device, 2000, | ||
93 | if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) | ||
94 | break; | ||
95 | ); | ||
96 | 43 | ||
97 | nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval); | 44 | nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval); |
98 | mdelay(9); | 45 | mdelay(9); |
99 | udelay(500); | 46 | udelay(500); |
100 | loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000); | 47 | loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000); |
101 | 48 | ||
102 | nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80550000); | 49 | dac->func->power(dac, false, false, false, false, false); |
50 | if (!(loadval & 0x80000000)) | ||
51 | return -ETIMEDOUT; | ||
52 | |||
53 | return (loadval & 0x38000000) >> 27; | ||
54 | } | ||
55 | |||
56 | static void | ||
57 | nv50_dac_power_wait(struct nvkm_device *device, const u32 doff) | ||
58 | { | ||
103 | nvkm_msec(device, 2000, | 59 | nvkm_msec(device, 2000, |
104 | if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) | 60 | if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000)) |
105 | break; | 61 | break; |
106 | ); | 62 | ); |
63 | } | ||
107 | 64 | ||
108 | nvkm_debug(subdev, "DAC%d sense: %08x\n", outp->or, loadval); | 65 | void |
109 | if (!(loadval & 0x80000000)) | 66 | nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu, |
110 | return -ETIMEDOUT; | 67 | bool data, bool vsync, bool hsync) |
68 | { | ||
69 | struct nvkm_device *device = dac->disp->engine.subdev.device; | ||
70 | const u32 doff = nv50_ior_base(dac); | ||
71 | const u32 shift = normal ? 0 : 16; | ||
72 | const u32 state = 0x80000000 | (0x00000040 * ! pu | | ||
73 | 0x00000010 * ! data | | ||
74 | 0x00000004 * ! vsync | | ||
75 | 0x00000001 * ! hsync) << shift; | ||
76 | const u32 field = 0xc0000000 | (0x00000055 << shift); | ||
77 | |||
78 | nv50_dac_power_wait(device, doff); | ||
79 | nvkm_mask(device, 0x61a004 + doff, field, state); | ||
80 | nv50_dac_power_wait(device, doff); | ||
81 | } | ||
82 | |||
83 | static void | ||
84 | nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state) | ||
85 | { | ||
86 | struct nvkm_device *device = dac->disp->engine.subdev.device; | ||
87 | const u32 coff = dac->id * 8 + (state == &dac->arm) * 4; | ||
88 | u32 ctrl = nvkm_rd32(device, 0x610b58 + coff); | ||
89 | |||
90 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | ||
91 | switch (state->proto_evo) { | ||
92 | case 0: state->proto = CRT; break; | ||
93 | default: | ||
94 | state->proto = UNKNOWN; | ||
95 | break; | ||
96 | } | ||
111 | 97 | ||
112 | args->v0.load = (loadval & 0x38000000) >> 27; | 98 | state->head = ctrl & 0x00000003; |
113 | return 0; | ||
114 | } | 99 | } |
115 | 100 | ||
116 | static const struct nvkm_output_func | 101 | static const struct nvkm_ior_func |
117 | nv50_dac_output_func = { | 102 | nv50_dac = { |
103 | .state = nv50_dac_state, | ||
104 | .power = nv50_dac_power, | ||
105 | .sense = nv50_dac_sense, | ||
106 | .clock = nv50_dac_clock, | ||
118 | }; | 107 | }; |
119 | 108 | ||
120 | int | 109 | int |
121 | nv50_dac_output_new(struct nvkm_disp *disp, int index, | 110 | nv50_dac_new(struct nvkm_disp *disp, int id) |
122 | struct dcb_output *dcbE, struct nvkm_output **poutp) | ||
123 | { | 111 | { |
124 | return nvkm_output_new_(&nv50_dac_output_func, disp, | 112 | struct nvkm_device *device = disp->engine.subdev.device; |
125 | index, dcbE, poutp); | 113 | if (!(nvkm_rd32(device, 0x610184) & (0x00100000 << id))) |
114 | return 0; | ||
115 | return nvkm_ior_new_(&nv50_dac, disp, DAC, id); | ||
126 | } | 116 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c new file mode 100644 index 000000000000..7c5bed29ffef --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | |||
@@ -0,0 +1,652 @@ | |||
1 | /* | ||
2 | * Copyright 2014 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 | */ | ||
24 | #include "dp.h" | ||
25 | #include "conn.h" | ||
26 | #include "head.h" | ||
27 | #include "ior.h" | ||
28 | |||
29 | #include <subdev/bios.h> | ||
30 | #include <subdev/bios/init.h> | ||
31 | #include <subdev/i2c.h> | ||
32 | |||
33 | #include <nvif/event.h> | ||
34 | |||
35 | struct lt_state { | ||
36 | struct nvkm_dp *dp; | ||
37 | u8 stat[6]; | ||
38 | u8 conf[4]; | ||
39 | bool pc2; | ||
40 | u8 pc2stat; | ||
41 | u8 pc2conf[2]; | ||
42 | }; | ||
43 | |||
44 | static int | ||
45 | nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay) | ||
46 | { | ||
47 | struct nvkm_dp *dp = lt->dp; | ||
48 | int ret; | ||
49 | |||
50 | if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) | ||
51 | mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); | ||
52 | else | ||
53 | udelay(delay); | ||
54 | |||
55 | ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6); | ||
56 | if (ret) | ||
57 | return ret; | ||
58 | |||
59 | if (pc) { | ||
60 | ret = nvkm_rdaux(dp->aux, DPCD_LS0C, <->pc2stat, 1); | ||
61 | if (ret) | ||
62 | lt->pc2stat = 0x00; | ||
63 | OUTP_TRACE(&dp->outp, "status %6ph pc2 %02x", | ||
64 | lt->stat, lt->pc2stat); | ||
65 | } else { | ||
66 | OUTP_TRACE(&dp->outp, "status %6ph", lt->stat); | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int | ||
73 | nvkm_dp_train_drive(struct lt_state *lt, bool pc) | ||
74 | { | ||
75 | struct nvkm_dp *dp = lt->dp; | ||
76 | struct nvkm_ior *ior = dp->outp.ior; | ||
77 | struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios; | ||
78 | struct nvbios_dpout info; | ||
79 | struct nvbios_dpcfg ocfg; | ||
80 | u8 ver, hdr, cnt, len; | ||
81 | u32 data; | ||
82 | int ret, i; | ||
83 | |||
84 | for (i = 0; i < ior->dp.nr; i++) { | ||
85 | u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; | ||
86 | u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3; | ||
87 | u8 lpre = (lane & 0x0c) >> 2; | ||
88 | u8 lvsw = (lane & 0x03) >> 0; | ||
89 | u8 hivs = 3 - lpre; | ||
90 | u8 hipe = 3; | ||
91 | u8 hipc = 3; | ||
92 | |||
93 | if (lpc2 >= hipc) | ||
94 | lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; | ||
95 | if (lpre >= hipe) { | ||
96 | lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ | ||
97 | lvsw = hivs = 3 - (lpre & 3); | ||
98 | } else | ||
99 | if (lvsw >= hivs) { | ||
100 | lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; | ||
101 | } | ||
102 | |||
103 | lt->conf[i] = (lpre << 3) | lvsw; | ||
104 | lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); | ||
105 | |||
106 | OUTP_TRACE(&dp->outp, "config lane %d %02x %02x", | ||
107 | i, lt->conf[i], lpc2); | ||
108 | |||
109 | data = nvbios_dpout_match(bios, dp->outp.info.hasht, | ||
110 | dp->outp.info.hashm, | ||
111 | &ver, &hdr, &cnt, &len, &info); | ||
112 | if (!data) | ||
113 | continue; | ||
114 | |||
115 | data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3, | ||
116 | lpre & 3, &ver, &hdr, &cnt, &len, | ||
117 | &ocfg); | ||
118 | if (!data) | ||
119 | continue; | ||
120 | |||
121 | ior->func->dp.drive(ior, i, ocfg.pc, ocfg.dc, | ||
122 | ocfg.pe, ocfg.tx_pu); | ||
123 | } | ||
124 | |||
125 | ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4); | ||
126 | if (ret) | ||
127 | return ret; | ||
128 | |||
129 | if (pc) { | ||
130 | ret = nvkm_wraux(dp->aux, DPCD_LC0F, lt->pc2conf, 2); | ||
131 | if (ret) | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern) | ||
140 | { | ||
141 | struct nvkm_dp *dp = lt->dp; | ||
142 | u8 sink_tp; | ||
143 | |||
144 | OUTP_TRACE(&dp->outp, "training pattern %d", pattern); | ||
145 | dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern); | ||
146 | |||
147 | nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1); | ||
148 | sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; | ||
149 | sink_tp |= pattern; | ||
150 | nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1); | ||
151 | } | ||
152 | |||
153 | static int | ||
154 | nvkm_dp_train_eq(struct lt_state *lt) | ||
155 | { | ||
156 | bool eq_done = false, cr_done = true; | ||
157 | int tries = 0, i; | ||
158 | |||
159 | if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED) | ||
160 | nvkm_dp_train_pattern(lt, 3); | ||
161 | else | ||
162 | nvkm_dp_train_pattern(lt, 2); | ||
163 | |||
164 | do { | ||
165 | if ((tries && | ||
166 | nvkm_dp_train_drive(lt, lt->pc2)) || | ||
167 | nvkm_dp_train_sense(lt, lt->pc2, 400)) | ||
168 | break; | ||
169 | |||
170 | eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); | ||
171 | for (i = 0; i < lt->dp->outp.ior->dp.nr && eq_done; i++) { | ||
172 | u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
173 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) | ||
174 | cr_done = false; | ||
175 | if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | ||
176 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) | ||
177 | eq_done = false; | ||
178 | } | ||
179 | } while (!eq_done && cr_done && ++tries <= 5); | ||
180 | |||
181 | return eq_done ? 0 : -1; | ||
182 | } | ||
183 | |||
184 | static int | ||
185 | nvkm_dp_train_cr(struct lt_state *lt) | ||
186 | { | ||
187 | bool cr_done = false, abort = false; | ||
188 | int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | ||
189 | int tries = 0, i; | ||
190 | |||
191 | nvkm_dp_train_pattern(lt, 1); | ||
192 | |||
193 | do { | ||
194 | if (nvkm_dp_train_drive(lt, false) || | ||
195 | nvkm_dp_train_sense(lt, false, 100)) | ||
196 | break; | ||
197 | |||
198 | cr_done = true; | ||
199 | for (i = 0; i < lt->dp->outp.ior->dp.nr; i++) { | ||
200 | u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
201 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { | ||
202 | cr_done = false; | ||
203 | if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED) | ||
204 | abort = true; | ||
205 | break; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { | ||
210 | voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | ||
211 | tries = 0; | ||
212 | } | ||
213 | } while (!cr_done && !abort && ++tries < 5); | ||
214 | |||
215 | return cr_done ? 0 : -1; | ||
216 | } | ||
217 | |||
218 | static int | ||
219 | nvkm_dp_train_links(struct nvkm_dp *dp) | ||
220 | { | ||
221 | struct nvkm_ior *ior = dp->outp.ior; | ||
222 | struct nvkm_disp *disp = dp->outp.disp; | ||
223 | struct nvkm_subdev *subdev = &disp->engine.subdev; | ||
224 | struct nvkm_bios *bios = subdev->device->bios; | ||
225 | struct lt_state lt = { | ||
226 | .dp = dp, | ||
227 | }; | ||
228 | u32 lnkcmp; | ||
229 | u8 sink[2]; | ||
230 | int ret; | ||
231 | |||
232 | OUTP_DBG(&dp->outp, "training %d x %d MB/s", | ||
233 | ior->dp.nr, ior->dp.bw * 27); | ||
234 | |||
235 | /* Intersect misc. capabilities of the OR and sink. */ | ||
236 | if (disp->engine.subdev.device->chipset < 0xd0) | ||
237 | dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED; | ||
238 | lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED; | ||
239 | |||
240 | /* Set desired link configuration on the source. */ | ||
241 | if ((lnkcmp = lt.dp->info.lnkcmp)) { | ||
242 | if (dp->version < 0x30) { | ||
243 | while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp)) | ||
244 | lnkcmp += 4; | ||
245 | lnkcmp = nvbios_rd16(bios, lnkcmp + 2); | ||
246 | } else { | ||
247 | while (ior->dp.bw < nvbios_rd08(bios, lnkcmp)) | ||
248 | lnkcmp += 3; | ||
249 | lnkcmp = nvbios_rd16(bios, lnkcmp + 1); | ||
250 | } | ||
251 | |||
252 | nvbios_init(subdev, lnkcmp, | ||
253 | init.outp = &dp->outp.info; | ||
254 | init.or = ior->id; | ||
255 | init.link = ior->asy.link; | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | ret = ior->func->dp.links(ior, dp->aux); | ||
260 | if (ret) { | ||
261 | if (ret < 0) { | ||
262 | OUTP_ERR(&dp->outp, "train failed with %d", ret); | ||
263 | return ret; | ||
264 | } | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | ior->func->dp.power(ior, ior->dp.nr); | ||
269 | |||
270 | /* Set desired link configuration on the sink. */ | ||
271 | sink[0] = ior->dp.bw; | ||
272 | sink[1] = ior->dp.nr; | ||
273 | if (ior->dp.ef) | ||
274 | sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; | ||
275 | |||
276 | ret = nvkm_wraux(dp->aux, DPCD_LC00_LINK_BW_SET, sink, 2); | ||
277 | if (ret) | ||
278 | return ret; | ||
279 | |||
280 | /* Attempt to train the link in this configuration. */ | ||
281 | memset(lt.stat, 0x00, sizeof(lt.stat)); | ||
282 | ret = nvkm_dp_train_cr(<); | ||
283 | if (ret == 0) | ||
284 | ret = nvkm_dp_train_eq(<); | ||
285 | nvkm_dp_train_pattern(<, 0); | ||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | static void | ||
290 | nvkm_dp_train_fini(struct nvkm_dp *dp) | ||
291 | { | ||
292 | /* Execute AfterLinkTraining script from DP Info table. */ | ||
293 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[1], | ||
294 | init.outp = &dp->outp.info; | ||
295 | init.or = dp->outp.ior->id; | ||
296 | init.link = dp->outp.ior->asy.link; | ||
297 | ); | ||
298 | } | ||
299 | |||
300 | static void | ||
301 | nvkm_dp_train_init(struct nvkm_dp *dp) | ||
302 | { | ||
303 | /* Execute EnableSpread/DisableSpread script from DP Info table. */ | ||
304 | if (dp->dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) { | ||
305 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[2], | ||
306 | init.outp = &dp->outp.info; | ||
307 | init.or = dp->outp.ior->id; | ||
308 | init.link = dp->outp.ior->asy.link; | ||
309 | ); | ||
310 | } else { | ||
311 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[3], | ||
312 | init.outp = &dp->outp.info; | ||
313 | init.or = dp->outp.ior->id; | ||
314 | init.link = dp->outp.ior->asy.link; | ||
315 | ); | ||
316 | } | ||
317 | |||
318 | /* Execute BeforeLinkTraining script from DP Info table. */ | ||
319 | nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0], | ||
320 | init.outp = &dp->outp.info; | ||
321 | init.or = dp->outp.ior->id; | ||
322 | init.link = dp->outp.ior->asy.link; | ||
323 | ); | ||
324 | } | ||
325 | |||
326 | static const struct dp_rates { | ||
327 | u32 rate; | ||
328 | u8 bw; | ||
329 | u8 nr; | ||
330 | } nvkm_dp_rates[] = { | ||
331 | { 2160000, 0x14, 4 }, | ||
332 | { 1080000, 0x0a, 4 }, | ||
333 | { 1080000, 0x14, 2 }, | ||
334 | { 648000, 0x06, 4 }, | ||
335 | { 540000, 0x0a, 2 }, | ||
336 | { 540000, 0x14, 1 }, | ||
337 | { 324000, 0x06, 2 }, | ||
338 | { 270000, 0x0a, 1 }, | ||
339 | { 162000, 0x06, 1 }, | ||
340 | {} | ||
341 | }; | ||
342 | |||
343 | static int | ||
344 | nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) | ||
345 | { | ||
346 | struct nvkm_ior *ior = dp->outp.ior; | ||
347 | const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; | ||
348 | const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; | ||
349 | const u8 outp_nr = dp->outp.info.dpconf.link_nr; | ||
350 | const u8 outp_bw = dp->outp.info.dpconf.link_bw; | ||
351 | const struct dp_rates *failsafe = NULL, *cfg; | ||
352 | int ret = -EINVAL; | ||
353 | u8 pwr; | ||
354 | |||
355 | /* Find the lowest configuration of the OR that can support | ||
356 | * the required link rate. | ||
357 | * | ||
358 | * We will refuse to program the OR to lower rates, even if | ||
359 | * link training fails at higher rates (or even if the sink | ||
360 | * can't support the rate at all, though the DD is supposed | ||
361 | * to prevent such situations from happening). | ||
362 | * | ||
363 | * Attempting to do so can cause the entire display to hang, | ||
364 | * and it's better to have a failed modeset than that. | ||
365 | */ | ||
366 | for (cfg = nvkm_dp_rates; cfg->rate; cfg++) { | ||
367 | if (cfg->nr <= outp_nr && cfg->nr <= outp_bw) | ||
368 | failsafe = cfg; | ||
369 | if (failsafe && cfg[1].rate < dataKBps) | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | if (WARN_ON(!failsafe)) | ||
374 | return ret; | ||
375 | |||
376 | /* Ensure sink is not in a low-power state. */ | ||
377 | if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) { | ||
378 | if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { | ||
379 | pwr &= ~DPCD_SC00_SET_POWER; | ||
380 | pwr |= DPCD_SC00_SET_POWER_D0; | ||
381 | nvkm_wraux(dp->aux, DPCD_SC00, &pwr, 1); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | /* Link training. */ | ||
386 | OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)", | ||
387 | failsafe->nr, failsafe->bw * 27); | ||
388 | nvkm_dp_train_init(dp); | ||
389 | for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) { | ||
390 | /* Skip configurations not supported by both OR and sink. */ | ||
391 | if ((cfg->nr > outp_nr || cfg->bw > outp_bw || | ||
392 | cfg->nr > sink_nr || cfg->bw > sink_bw)) { | ||
393 | if (cfg != failsafe) | ||
394 | continue; | ||
395 | OUTP_ERR(&dp->outp, "link rate unsupported by sink"); | ||
396 | } | ||
397 | ior->dp.mst = dp->lt.mst; | ||
398 | ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; | ||
399 | ior->dp.bw = cfg->bw; | ||
400 | ior->dp.nr = cfg->nr; | ||
401 | |||
402 | /* Program selected link configuration. */ | ||
403 | ret = nvkm_dp_train_links(dp); | ||
404 | } | ||
405 | nvkm_dp_train_fini(dp); | ||
406 | if (ret < 0) | ||
407 | OUTP_ERR(&dp->outp, "training failed"); | ||
408 | else | ||
409 | OUTP_DBG(&dp->outp, "training done"); | ||
410 | atomic_set(&dp->lt.done, 1); | ||
411 | return ret; | ||
412 | } | ||
413 | |||
414 | static void | ||
415 | nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior) | ||
416 | { | ||
417 | struct nvkm_dp *dp = nvkm_dp(outp); | ||
418 | |||
419 | /* Prevent link from being retrained if sink sends an IRQ. */ | ||
420 | atomic_set(&dp->lt.done, 0); | ||
421 | ior->dp.nr = 0; | ||
422 | |||
423 | /* Execute DisableLT script from DP Info Table. */ | ||
424 | nvbios_init(&ior->disp->engine.subdev, dp->info.script[4], | ||
425 | init.outp = &dp->outp.info; | ||
426 | init.or = ior->id; | ||
427 | init.link = ior->arm.link; | ||
428 | ); | ||
429 | } | ||
430 | |||
431 | static int | ||
432 | nvkm_dp_acquire(struct nvkm_outp *outp) | ||
433 | { | ||
434 | struct nvkm_dp *dp = nvkm_dp(outp); | ||
435 | struct nvkm_ior *ior = dp->outp.ior; | ||
436 | struct nvkm_head *head; | ||
437 | bool retrain = true; | ||
438 | u32 datakbps = 0; | ||
439 | u32 dataKBps; | ||
440 | u32 linkKBps; | ||
441 | u8 stat[3]; | ||
442 | int ret, i; | ||
443 | |||
444 | mutex_lock(&dp->mutex); | ||
445 | |||
446 | /* Check that link configuration meets current requirements. */ | ||
447 | list_for_each_entry(head, &outp->disp->head, head) { | ||
448 | if (ior->asy.head & (1 << head->id)) { | ||
449 | u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; | ||
450 | datakbps += khz * head->asy.or.depth; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | linkKBps = ior->dp.bw * 27000 * ior->dp.nr; | ||
455 | dataKBps = DIV_ROUND_UP(datakbps, 8); | ||
456 | OUTP_DBG(&dp->outp, "data %d KB/s link %d KB/s mst %d->%d", | ||
457 | dataKBps, linkKBps, ior->dp.mst, dp->lt.mst); | ||
458 | if (linkKBps < dataKBps || ior->dp.mst != dp->lt.mst) { | ||
459 | OUTP_DBG(&dp->outp, "link requirements changed"); | ||
460 | goto done; | ||
461 | } | ||
462 | |||
463 | /* Check that link is still trained. */ | ||
464 | ret = nvkm_rdaux(dp->aux, DPCD_LS02, stat, 3); | ||
465 | if (ret) { | ||
466 | OUTP_DBG(&dp->outp, | ||
467 | "failed to read link status, assuming no sink"); | ||
468 | goto done; | ||
469 | } | ||
470 | |||
471 | if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { | ||
472 | for (i = 0; i < ior->dp.nr; i++) { | ||
473 | u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; | ||
474 | if (!(lane & DPCD_LS02_LANE0_CR_DONE) || | ||
475 | !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | ||
476 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { | ||
477 | OUTP_DBG(&dp->outp, | ||
478 | "lane %d not equalised", lane); | ||
479 | goto done; | ||
480 | } | ||
481 | } | ||
482 | retrain = false; | ||
483 | } else { | ||
484 | OUTP_DBG(&dp->outp, "no inter-lane alignment"); | ||
485 | } | ||
486 | |||
487 | done: | ||
488 | if (retrain || !atomic_read(&dp->lt.done)) | ||
489 | ret = nvkm_dp_train(dp, dataKBps); | ||
490 | mutex_unlock(&dp->mutex); | ||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | static void | ||
495 | nvkm_dp_enable(struct nvkm_dp *dp, bool enable) | ||
496 | { | ||
497 | struct nvkm_i2c_aux *aux = dp->aux; | ||
498 | |||
499 | if (enable) { | ||
500 | if (!dp->present) { | ||
501 | OUTP_DBG(&dp->outp, "aux power -> always"); | ||
502 | nvkm_i2c_aux_monitor(aux, true); | ||
503 | dp->present = true; | ||
504 | } | ||
505 | |||
506 | if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, | ||
507 | sizeof(dp->dpcd))) | ||
508 | return; | ||
509 | } | ||
510 | |||
511 | if (dp->present) { | ||
512 | OUTP_DBG(&dp->outp, "aux power -> demand"); | ||
513 | nvkm_i2c_aux_monitor(aux, false); | ||
514 | dp->present = false; | ||
515 | } | ||
516 | |||
517 | atomic_set(&dp->lt.done, 0); | ||
518 | } | ||
519 | |||
520 | static int | ||
521 | nvkm_dp_hpd(struct nvkm_notify *notify) | ||
522 | { | ||
523 | const struct nvkm_i2c_ntfy_rep *line = notify->data; | ||
524 | struct nvkm_dp *dp = container_of(notify, typeof(*dp), hpd); | ||
525 | struct nvkm_conn *conn = dp->outp.conn; | ||
526 | struct nvkm_disp *disp = dp->outp.disp; | ||
527 | struct nvif_notify_conn_rep_v0 rep = {}; | ||
528 | |||
529 | OUTP_DBG(&dp->outp, "HPD: %d", line->mask); | ||
530 | if (line->mask & NVKM_I2C_IRQ) { | ||
531 | if (atomic_read(&dp->lt.done)) | ||
532 | dp->outp.func->acquire(&dp->outp); | ||
533 | rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ; | ||
534 | } else { | ||
535 | nvkm_dp_enable(dp, true); | ||
536 | } | ||
537 | |||
538 | if (line->mask & NVKM_I2C_UNPLUG) | ||
539 | rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; | ||
540 | if (line->mask & NVKM_I2C_PLUG) | ||
541 | rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; | ||
542 | |||
543 | nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); | ||
544 | return NVKM_NOTIFY_KEEP; | ||
545 | } | ||
546 | |||
547 | static void | ||
548 | nvkm_dp_fini(struct nvkm_outp *outp) | ||
549 | { | ||
550 | struct nvkm_dp *dp = nvkm_dp(outp); | ||
551 | nvkm_notify_put(&dp->hpd); | ||
552 | nvkm_dp_enable(dp, false); | ||
553 | } | ||
554 | |||
555 | static void | ||
556 | nvkm_dp_init(struct nvkm_outp *outp) | ||
557 | { | ||
558 | struct nvkm_dp *dp = nvkm_dp(outp); | ||
559 | nvkm_notify_put(&dp->outp.conn->hpd); | ||
560 | nvkm_dp_enable(dp, true); | ||
561 | nvkm_notify_get(&dp->hpd); | ||
562 | } | ||
563 | |||
564 | static void * | ||
565 | nvkm_dp_dtor(struct nvkm_outp *outp) | ||
566 | { | ||
567 | struct nvkm_dp *dp = nvkm_dp(outp); | ||
568 | nvkm_notify_fini(&dp->hpd); | ||
569 | return dp; | ||
570 | } | ||
571 | |||
572 | static const struct nvkm_outp_func | ||
573 | nvkm_dp_func = { | ||
574 | .dtor = nvkm_dp_dtor, | ||
575 | .init = nvkm_dp_init, | ||
576 | .fini = nvkm_dp_fini, | ||
577 | .acquire = nvkm_dp_acquire, | ||
578 | .release = nvkm_dp_release, | ||
579 | }; | ||
580 | |||
581 | static int | ||
582 | nvkm_dp_ctor(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | ||
583 | struct nvkm_i2c_aux *aux, struct nvkm_dp *dp) | ||
584 | { | ||
585 | struct nvkm_device *device = disp->engine.subdev.device; | ||
586 | struct nvkm_bios *bios = device->bios; | ||
587 | struct nvkm_i2c *i2c = device->i2c; | ||
588 | u8 hdr, cnt, len; | ||
589 | u32 data; | ||
590 | int ret; | ||
591 | |||
592 | ret = nvkm_outp_ctor(&nvkm_dp_func, disp, index, dcbE, &dp->outp); | ||
593 | if (ret) | ||
594 | return ret; | ||
595 | |||
596 | dp->aux = aux; | ||
597 | if (!dp->aux) { | ||
598 | OUTP_ERR(&dp->outp, "no aux"); | ||
599 | return -EINVAL; | ||
600 | } | ||
601 | |||
602 | /* bios data is not optional */ | ||
603 | data = nvbios_dpout_match(bios, dp->outp.info.hasht, | ||
604 | dp->outp.info.hashm, &dp->version, | ||
605 | &hdr, &cnt, &len, &dp->info); | ||
606 | if (!data) { | ||
607 | OUTP_ERR(&dp->outp, "no bios dp data"); | ||
608 | return -EINVAL; | ||
609 | } | ||
610 | |||
611 | OUTP_DBG(&dp->outp, "bios dp %02x %02x %02x %02x", | ||
612 | dp->version, hdr, cnt, len); | ||
613 | |||
614 | /* hotplug detect, replaces gpio-based mechanism with aux events */ | ||
615 | ret = nvkm_notify_init(NULL, &i2c->event, nvkm_dp_hpd, true, | ||
616 | &(struct nvkm_i2c_ntfy_req) { | ||
617 | .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG | | ||
618 | NVKM_I2C_IRQ, | ||
619 | .port = dp->aux->id, | ||
620 | }, | ||
621 | sizeof(struct nvkm_i2c_ntfy_req), | ||
622 | sizeof(struct nvkm_i2c_ntfy_rep), | ||
623 | &dp->hpd); | ||
624 | if (ret) { | ||
625 | OUTP_ERR(&dp->outp, "error monitoring aux hpd: %d", ret); | ||
626 | return ret; | ||
627 | } | ||
628 | |||
629 | mutex_init(&dp->mutex); | ||
630 | atomic_set(&dp->lt.done, 0); | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | int | ||
635 | nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | ||
636 | struct nvkm_outp **poutp) | ||
637 | { | ||
638 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; | ||
639 | struct nvkm_i2c_aux *aux; | ||
640 | struct nvkm_dp *dp; | ||
641 | |||
642 | if (dcbE->location == 0) | ||
643 | aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index)); | ||
644 | else | ||
645 | aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); | ||
646 | |||
647 | if (!(dp = kzalloc(sizeof(*dp), GFP_KERNEL))) | ||
648 | return -ENOMEM; | ||
649 | *poutp = &dp->outp; | ||
650 | |||
651 | return nvkm_dp_ctor(disp, index, dcbE, aux, dp); | ||
652 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h index baf1dd9ff975..59173c290525 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.h | |||
@@ -1,6 +1,36 @@ | |||
1 | #ifndef __NVKM_DISP_DPORT_H__ | 1 | #ifndef __NVKM_DISP_DP_H__ |
2 | #define __NVKM_DISP_DPORT_H__ | 2 | #define __NVKM_DISP_DP_H__ |
3 | struct nvkm_output_dp; | 3 | #define nvkm_dp(p) container_of((p), struct nvkm_dp, outp) |
4 | #include "outp.h" | ||
5 | |||
6 | #include <core/notify.h> | ||
7 | #include <subdev/bios.h> | ||
8 | #include <subdev/bios/dp.h> | ||
9 | |||
10 | struct nvkm_dp { | ||
11 | union { | ||
12 | struct nvkm_outp base; | ||
13 | struct nvkm_outp outp; | ||
14 | }; | ||
15 | |||
16 | struct nvbios_dpout info; | ||
17 | u8 version; | ||
18 | |||
19 | struct nvkm_i2c_aux *aux; | ||
20 | |||
21 | struct nvkm_notify hpd; | ||
22 | bool present; | ||
23 | u8 dpcd[16]; | ||
24 | |||
25 | struct mutex mutex; | ||
26 | struct { | ||
27 | atomic_t done; | ||
28 | bool mst; | ||
29 | } lt; | ||
30 | }; | ||
31 | |||
32 | int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *, | ||
33 | struct nvkm_outp **); | ||
4 | 34 | ||
5 | /* DPCD Receiver Capabilities */ | 35 | /* DPCD Receiver Capabilities */ |
6 | #define DPCD_RC00_DPCD_REV 0x00000 | 36 | #define DPCD_RC00_DPCD_REV 0x00000 |
@@ -76,6 +106,4 @@ struct nvkm_output_dp; | |||
76 | #define DPCD_SC00_SET_POWER 0x03 | 106 | #define DPCD_SC00_SET_POWER 0x03 |
77 | #define DPCD_SC00_SET_POWER_D0 0x01 | 107 | #define DPCD_SC00_SET_POWER_D0 0x01 |
78 | #define DPCD_SC00_SET_POWER_D3 0x03 | 108 | #define DPCD_SC00_SET_POWER_D3 0x03 |
79 | |||
80 | void nvkm_dp_train(struct nvkm_output_dp *); | ||
81 | #endif | 109 | #endif |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c deleted file mode 100644 index 4a93ceb850ac..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dport.c +++ /dev/null | |||
@@ -1,401 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright 2013 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 | */ | ||
24 | #include "dport.h" | ||
25 | #include "outpdp.h" | ||
26 | #include "nv50.h" | ||
27 | |||
28 | #include <subdev/bios.h> | ||
29 | #include <subdev/bios/init.h> | ||
30 | #include <subdev/i2c.h> | ||
31 | |||
32 | #include <nvif/class.h> | ||
33 | |||
34 | /****************************************************************************** | ||
35 | * link training | ||
36 | *****************************************************************************/ | ||
37 | struct dp_state { | ||
38 | struct nvkm_output_dp *outp; | ||
39 | int link_nr; | ||
40 | u32 link_bw; | ||
41 | u8 stat[6]; | ||
42 | u8 conf[4]; | ||
43 | bool pc2; | ||
44 | u8 pc2stat; | ||
45 | u8 pc2conf[2]; | ||
46 | }; | ||
47 | |||
48 | static int | ||
49 | dp_set_link_config(struct dp_state *dp) | ||
50 | { | ||
51 | struct nvkm_output_dp *outp = dp->outp; | ||
52 | struct nvkm_disp *disp = outp->base.disp; | ||
53 | struct nvkm_subdev *subdev = &disp->engine.subdev; | ||
54 | struct nvkm_bios *bios = subdev->device->bios; | ||
55 | struct nvbios_init init = { | ||
56 | .subdev = subdev, | ||
57 | .bios = bios, | ||
58 | .offset = 0x0000, | ||
59 | .outp = &outp->base.info, | ||
60 | .crtc = -1, | ||
61 | .execute = 1, | ||
62 | }; | ||
63 | u32 lnkcmp; | ||
64 | u8 sink[2]; | ||
65 | int ret; | ||
66 | |||
67 | OUTP_DBG(&outp->base, "%d lanes at %d KB/s", dp->link_nr, dp->link_bw); | ||
68 | |||
69 | /* set desired link configuration on the source */ | ||
70 | if ((lnkcmp = dp->outp->info.lnkcmp)) { | ||
71 | if (outp->version < 0x30) { | ||
72 | while ((dp->link_bw / 10) < nvbios_rd16(bios, lnkcmp)) | ||
73 | lnkcmp += 4; | ||
74 | init.offset = nvbios_rd16(bios, lnkcmp + 2); | ||
75 | } else { | ||
76 | while ((dp->link_bw / 27000) < nvbios_rd08(bios, lnkcmp)) | ||
77 | lnkcmp += 3; | ||
78 | init.offset = nvbios_rd16(bios, lnkcmp + 1); | ||
79 | } | ||
80 | |||
81 | nvbios_exec(&init); | ||
82 | } | ||
83 | |||
84 | ret = outp->func->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000, | ||
85 | outp->dpcd[DPCD_RC02] & | ||
86 | DPCD_RC02_ENHANCED_FRAME_CAP); | ||
87 | if (ret) { | ||
88 | if (ret < 0) | ||
89 | OUTP_ERR(&outp->base, "lnk_ctl failed with %d", ret); | ||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | outp->func->lnk_pwr(outp, dp->link_nr); | ||
94 | |||
95 | /* set desired link configuration on the sink */ | ||
96 | sink[0] = dp->link_bw / 27000; | ||
97 | sink[1] = dp->link_nr; | ||
98 | if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP) | ||
99 | sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; | ||
100 | |||
101 | return nvkm_wraux(outp->aux, DPCD_LC00_LINK_BW_SET, sink, 2); | ||
102 | } | ||
103 | |||
104 | static void | ||
105 | dp_set_training_pattern(struct dp_state *dp, u8 pattern) | ||
106 | { | ||
107 | struct nvkm_output_dp *outp = dp->outp; | ||
108 | u8 sink_tp; | ||
109 | |||
110 | OUTP_DBG(&outp->base, "training pattern %d", pattern); | ||
111 | outp->func->pattern(outp, pattern); | ||
112 | |||
113 | nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1); | ||
114 | sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET; | ||
115 | sink_tp |= pattern; | ||
116 | nvkm_wraux(outp->aux, DPCD_LC02, &sink_tp, 1); | ||
117 | } | ||
118 | |||
119 | static int | ||
120 | dp_link_train_commit(struct dp_state *dp, bool pc) | ||
121 | { | ||
122 | struct nvkm_output_dp *outp = dp->outp; | ||
123 | int ret, i; | ||
124 | |||
125 | for (i = 0; i < dp->link_nr; i++) { | ||
126 | u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; | ||
127 | u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3; | ||
128 | u8 lpre = (lane & 0x0c) >> 2; | ||
129 | u8 lvsw = (lane & 0x03) >> 0; | ||
130 | u8 hivs = 3 - lpre; | ||
131 | u8 hipe = 3; | ||
132 | u8 hipc = 3; | ||
133 | |||
134 | if (lpc2 >= hipc) | ||
135 | lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED; | ||
136 | if (lpre >= hipe) { | ||
137 | lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */ | ||
138 | lvsw = hivs = 3 - (lpre & 3); | ||
139 | } else | ||
140 | if (lvsw >= hivs) { | ||
141 | lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED; | ||
142 | } | ||
143 | |||
144 | dp->conf[i] = (lpre << 3) | lvsw; | ||
145 | dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4); | ||
146 | |||
147 | OUTP_DBG(&outp->base, "config lane %d %02x %02x", | ||
148 | i, dp->conf[i], lpc2); | ||
149 | outp->func->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3); | ||
150 | } | ||
151 | |||
152 | ret = nvkm_wraux(outp->aux, DPCD_LC03(0), dp->conf, 4); | ||
153 | if (ret) | ||
154 | return ret; | ||
155 | |||
156 | if (pc) { | ||
157 | ret = nvkm_wraux(outp->aux, DPCD_LC0F, dp->pc2conf, 2); | ||
158 | if (ret) | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static int | ||
166 | dp_link_train_update(struct dp_state *dp, bool pc, u32 delay) | ||
167 | { | ||
168 | struct nvkm_output_dp *outp = dp->outp; | ||
169 | int ret; | ||
170 | |||
171 | if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL]) | ||
172 | mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4); | ||
173 | else | ||
174 | udelay(delay); | ||
175 | |||
176 | ret = nvkm_rdaux(outp->aux, DPCD_LS02, dp->stat, 6); | ||
177 | if (ret) | ||
178 | return ret; | ||
179 | |||
180 | if (pc) { | ||
181 | ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &dp->pc2stat, 1); | ||
182 | if (ret) | ||
183 | dp->pc2stat = 0x00; | ||
184 | OUTP_DBG(&outp->base, "status %6ph pc2 %02x", | ||
185 | dp->stat, dp->pc2stat); | ||
186 | } else { | ||
187 | OUTP_DBG(&outp->base, "status %6ph", dp->stat); | ||
188 | } | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int | ||
194 | dp_link_train_cr(struct dp_state *dp) | ||
195 | { | ||
196 | bool cr_done = false, abort = false; | ||
197 | int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | ||
198 | int tries = 0, i; | ||
199 | |||
200 | dp_set_training_pattern(dp, 1); | ||
201 | |||
202 | do { | ||
203 | if (dp_link_train_commit(dp, false) || | ||
204 | dp_link_train_update(dp, false, 100)) | ||
205 | break; | ||
206 | |||
207 | cr_done = true; | ||
208 | for (i = 0; i < dp->link_nr; i++) { | ||
209 | u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
210 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) { | ||
211 | cr_done = false; | ||
212 | if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED) | ||
213 | abort = true; | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) { | ||
219 | voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET; | ||
220 | tries = 0; | ||
221 | } | ||
222 | } while (!cr_done && !abort && ++tries < 5); | ||
223 | |||
224 | return cr_done ? 0 : -1; | ||
225 | } | ||
226 | |||
227 | static int | ||
228 | dp_link_train_eq(struct dp_state *dp) | ||
229 | { | ||
230 | struct nvkm_output_dp *outp = dp->outp; | ||
231 | bool eq_done = false, cr_done = true; | ||
232 | int tries = 0, i; | ||
233 | |||
234 | if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED) | ||
235 | dp_set_training_pattern(dp, 3); | ||
236 | else | ||
237 | dp_set_training_pattern(dp, 2); | ||
238 | |||
239 | do { | ||
240 | if ((tries && | ||
241 | dp_link_train_commit(dp, dp->pc2)) || | ||
242 | dp_link_train_update(dp, dp->pc2, 400)) | ||
243 | break; | ||
244 | |||
245 | eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE); | ||
246 | for (i = 0; i < dp->link_nr && eq_done; i++) { | ||
247 | u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
248 | if (!(lane & DPCD_LS02_LANE0_CR_DONE)) | ||
249 | cr_done = false; | ||
250 | if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | ||
251 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) | ||
252 | eq_done = false; | ||
253 | } | ||
254 | } while (!eq_done && cr_done && ++tries <= 5); | ||
255 | |||
256 | return eq_done ? 0 : -1; | ||
257 | } | ||
258 | |||
259 | static void | ||
260 | dp_link_train_init(struct dp_state *dp, bool spread) | ||
261 | { | ||
262 | struct nvkm_output_dp *outp = dp->outp; | ||
263 | struct nvkm_disp *disp = outp->base.disp; | ||
264 | struct nvkm_subdev *subdev = &disp->engine.subdev; | ||
265 | struct nvbios_init init = { | ||
266 | .subdev = subdev, | ||
267 | .bios = subdev->device->bios, | ||
268 | .outp = &outp->base.info, | ||
269 | .crtc = -1, | ||
270 | .execute = 1, | ||
271 | }; | ||
272 | |||
273 | /* set desired spread */ | ||
274 | if (spread) | ||
275 | init.offset = outp->info.script[2]; | ||
276 | else | ||
277 | init.offset = outp->info.script[3]; | ||
278 | nvbios_exec(&init); | ||
279 | |||
280 | /* pre-train script */ | ||
281 | init.offset = outp->info.script[0]; | ||
282 | nvbios_exec(&init); | ||
283 | } | ||
284 | |||
285 | static void | ||
286 | dp_link_train_fini(struct dp_state *dp) | ||
287 | { | ||
288 | struct nvkm_output_dp *outp = dp->outp; | ||
289 | struct nvkm_disp *disp = outp->base.disp; | ||
290 | struct nvkm_subdev *subdev = &disp->engine.subdev; | ||
291 | struct nvbios_init init = { | ||
292 | .subdev = subdev, | ||
293 | .bios = subdev->device->bios, | ||
294 | .outp = &outp->base.info, | ||
295 | .crtc = -1, | ||
296 | .execute = 1, | ||
297 | }; | ||
298 | |||
299 | /* post-train script */ | ||
300 | init.offset = outp->info.script[1], | ||
301 | nvbios_exec(&init); | ||
302 | } | ||
303 | |||
304 | static const struct dp_rates { | ||
305 | u32 rate; | ||
306 | u8 bw; | ||
307 | u8 nr; | ||
308 | } nvkm_dp_rates[] = { | ||
309 | { 2160000, 0x14, 4 }, | ||
310 | { 1080000, 0x0a, 4 }, | ||
311 | { 1080000, 0x14, 2 }, | ||
312 | { 648000, 0x06, 4 }, | ||
313 | { 540000, 0x0a, 2 }, | ||
314 | { 540000, 0x14, 1 }, | ||
315 | { 324000, 0x06, 2 }, | ||
316 | { 270000, 0x0a, 1 }, | ||
317 | { 162000, 0x06, 1 }, | ||
318 | {} | ||
319 | }; | ||
320 | |||
321 | void | ||
322 | nvkm_dp_train(struct nvkm_output_dp *outp) | ||
323 | { | ||
324 | struct nv50_disp *disp = nv50_disp(outp->base.disp); | ||
325 | const struct dp_rates *cfg = nvkm_dp_rates; | ||
326 | struct dp_state _dp = { | ||
327 | .outp = outp, | ||
328 | }, *dp = &_dp; | ||
329 | u32 datarate = 0; | ||
330 | u8 pwr; | ||
331 | int ret; | ||
332 | |||
333 | if (!outp->base.info.location && disp->func->sor.magic) | ||
334 | disp->func->sor.magic(&outp->base); | ||
335 | |||
336 | /* bring capabilities within encoder limits */ | ||
337 | if (disp->base.engine.subdev.device->chipset < 0xd0) | ||
338 | outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED; | ||
339 | if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) { | ||
340 | outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT; | ||
341 | outp->dpcd[2] |= outp->base.info.dpconf.link_nr; | ||
342 | } | ||
343 | if (outp->dpcd[1] > outp->base.info.dpconf.link_bw) | ||
344 | outp->dpcd[1] = outp->base.info.dpconf.link_bw; | ||
345 | dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED; | ||
346 | |||
347 | /* restrict link config to the lowest required rate, if requested */ | ||
348 | if (datarate) { | ||
349 | datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */ | ||
350 | while (cfg[1].rate >= datarate) | ||
351 | cfg++; | ||
352 | } | ||
353 | cfg--; | ||
354 | |||
355 | /* ensure sink is not in a low-power state */ | ||
356 | if (!nvkm_rdaux(outp->aux, DPCD_SC00, &pwr, 1)) { | ||
357 | if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { | ||
358 | pwr &= ~DPCD_SC00_SET_POWER; | ||
359 | pwr |= DPCD_SC00_SET_POWER_D0; | ||
360 | nvkm_wraux(outp->aux, DPCD_SC00, &pwr, 1); | ||
361 | } | ||
362 | } | ||
363 | |||
364 | /* enable down-spreading and execute pre-train script from vbios */ | ||
365 | dp_link_train_init(dp, outp->dpcd[3] & 0x01); | ||
366 | |||
367 | while (ret = -EIO, (++cfg)->rate) { | ||
368 | /* select next configuration supported by encoder and sink */ | ||
369 | while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) || | ||
370 | cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE])) | ||
371 | cfg++; | ||
372 | dp->link_bw = cfg->bw * 27000; | ||
373 | dp->link_nr = cfg->nr; | ||
374 | |||
375 | /* program selected link configuration */ | ||
376 | ret = dp_set_link_config(dp); | ||
377 | if (ret == 0) { | ||
378 | /* attempt to train the link at this configuration */ | ||
379 | memset(dp->stat, 0x00, sizeof(dp->stat)); | ||
380 | if (!dp_link_train_cr(dp) && | ||
381 | !dp_link_train_eq(dp)) | ||
382 | break; | ||
383 | } else | ||
384 | if (ret) { | ||
385 | /* dp_set_link_config() handled training, or | ||
386 | * we failed to communicate with the sink. | ||
387 | */ | ||
388 | break; | ||
389 | } | ||
390 | } | ||
391 | |||
392 | /* finish link training and execute post-train script from vbios */ | ||
393 | dp_set_training_pattern(dp, 0); | ||
394 | if (ret < 0) | ||
395 | OUTP_ERR(&outp->base, "link training failed"); | ||
396 | |||
397 | dp_link_train_fini(dp); | ||
398 | |||
399 | OUTP_DBG(&outp->base, "training complete"); | ||
400 | atomic_set(&outp->lt.done, 1); | ||
401 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c index 3e3e592cd09f..842e1b72ee42 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c | |||
@@ -22,30 +22,20 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
28 | g84_disp = { | 30 | g84_disp = { |
29 | .intr = nv50_disp_intr, | 31 | .intr = nv50_disp_intr, |
30 | .uevent = &nv50_disp_chan_uevent, | 32 | .uevent = &nv50_disp_chan_uevent, |
31 | .super = nv50_disp_intr_supervisor, | 33 | .super = nv50_disp_super, |
32 | .root = &g84_disp_root_oclass, | 34 | .root = &g84_disp_root_oclass, |
33 | .head.vblank_init = nv50_disp_vblank_init, | 35 | .head.new = nv50_head_new, |
34 | .head.vblank_fini = nv50_disp_vblank_fini, | 36 | .dac = { .nr = 3, .new = nv50_dac_new }, |
35 | .head.scanoutpos = nv50_disp_root_scanoutpos, | 37 | .sor = { .nr = 2, .new = g84_sor_new }, |
36 | .outp.internal.crt = nv50_dac_output_new, | 38 | .pior = { .nr = 3, .new = nv50_pior_new }, |
37 | .outp.internal.tmds = nv50_sor_output_new, | ||
38 | .outp.internal.lvds = nv50_sor_output_new, | ||
39 | .outp.external.tmds = nv50_pior_output_new, | ||
40 | .outp.external.dp = nv50_pior_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 2, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hdmi = g84_hdmi_ctrl, | ||
47 | .pior.nr = 3, | ||
48 | .pior.power = nv50_pior_power, | ||
49 | }; | 39 | }; |
50 | 40 | ||
51 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c index 7a7af3b478f8..d184e6ab8918 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c | |||
@@ -22,31 +22,20 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
28 | g94_disp = { | 30 | g94_disp = { |
29 | .intr = nv50_disp_intr, | 31 | .intr = nv50_disp_intr, |
30 | .uevent = &nv50_disp_chan_uevent, | 32 | .uevent = &nv50_disp_chan_uevent, |
31 | .super = nv50_disp_intr_supervisor, | 33 | .super = nv50_disp_super, |
32 | .root = &g94_disp_root_oclass, | 34 | .root = &g94_disp_root_oclass, |
33 | .head.vblank_init = nv50_disp_vblank_init, | 35 | .head.new = nv50_head_new, |
34 | .head.vblank_fini = nv50_disp_vblank_fini, | 36 | .dac = { .nr = 3, .new = nv50_dac_new }, |
35 | .head.scanoutpos = nv50_disp_root_scanoutpos, | 37 | .sor = { .nr = 4, .new = g94_sor_new }, |
36 | .outp.internal.crt = nv50_dac_output_new, | 38 | .pior = { .nr = 3, .new = nv50_pior_new }, |
37 | .outp.internal.tmds = nv50_sor_output_new, | ||
38 | .outp.internal.lvds = nv50_sor_output_new, | ||
39 | .outp.internal.dp = g94_sor_dp_new, | ||
40 | .outp.external.tmds = nv50_pior_output_new, | ||
41 | .outp.external.dp = nv50_pior_dp_new, | ||
42 | .dac.nr = 3, | ||
43 | .dac.power = nv50_dac_power, | ||
44 | .dac.sense = nv50_dac_sense, | ||
45 | .sor.nr = 4, | ||
46 | .sor.power = nv50_sor_power, | ||
47 | .sor.hdmi = g84_hdmi_ctrl, | ||
48 | .pior.nr = 3, | ||
49 | .pior.power = nv50_pior_power, | ||
50 | }; | 39 | }; |
51 | 40 | ||
52 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c index 7b346ccc38b7..d8765b57180b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c | |||
@@ -22,397 +22,63 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | #include <subdev/bios.h> | ||
28 | #include <subdev/bios/disp.h> | ||
29 | #include <subdev/bios/init.h> | ||
30 | #include <subdev/bios/pll.h> | ||
31 | #include <subdev/devinit.h> | ||
32 | |||
33 | void | ||
34 | gf119_disp_vblank_init(struct nv50_disp *disp, int head) | ||
35 | { | ||
36 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
37 | nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001); | ||
38 | } | ||
39 | |||
40 | void | ||
41 | gf119_disp_vblank_fini(struct nv50_disp *disp, int head) | ||
42 | { | ||
43 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
44 | nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000); | ||
45 | } | ||
46 | |||
47 | static struct nvkm_output * | ||
48 | exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl, | ||
49 | u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, | ||
50 | struct nvbios_outp *info) | ||
51 | { | ||
52 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
53 | struct nvkm_bios *bios = subdev->device->bios; | ||
54 | struct nvkm_output *outp; | ||
55 | u16 mask, type; | ||
56 | |||
57 | if (or < 4) { | ||
58 | type = DCB_OUTPUT_ANALOG; | ||
59 | mask = 0; | ||
60 | } else { | ||
61 | or -= 4; | ||
62 | switch (ctrl & 0x00000f00) { | ||
63 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; | ||
64 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; | ||
65 | case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; | ||
66 | case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; | ||
67 | case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; | ||
68 | case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; | ||
69 | default: | ||
70 | nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl); | ||
71 | return NULL; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | mask = 0x00c0 & (mask << 6); | ||
76 | mask |= 0x0001 << or; | ||
77 | mask |= 0x0100 << head; | ||
78 | |||
79 | list_for_each_entry(outp, &disp->base.outp, head) { | ||
80 | if ((outp->info.hasht & 0xff) == type && | ||
81 | (outp->info.hashm & mask) == mask) { | ||
82 | *data = nvbios_outp_match(bios, outp->info.hasht, mask, | ||
83 | ver, hdr, cnt, len, info); | ||
84 | if (!*data) | ||
85 | return NULL; | ||
86 | return outp; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | return NULL; | ||
91 | } | ||
92 | |||
93 | static struct nvkm_output * | ||
94 | exec_script(struct nv50_disp *disp, int head, int id) | ||
95 | { | ||
96 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
97 | struct nvkm_device *device = subdev->device; | ||
98 | struct nvkm_bios *bios = device->bios; | ||
99 | struct nvkm_output *outp; | ||
100 | struct nvbios_outp info; | ||
101 | u8 ver, hdr, cnt, len; | ||
102 | u32 data, ctrl = 0; | ||
103 | int or; | ||
104 | |||
105 | for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { | ||
106 | ctrl = nvkm_rd32(device, 0x640180 + (or * 0x20)); | ||
107 | if (ctrl & (1 << head)) | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | if (or == 8) | ||
112 | return NULL; | ||
113 | |||
114 | outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info); | ||
115 | if (outp) { | ||
116 | struct nvbios_init init = { | ||
117 | .subdev = subdev, | ||
118 | .bios = bios, | ||
119 | .offset = info.script[id], | ||
120 | .outp = &outp->info, | ||
121 | .crtc = head, | ||
122 | .execute = 1, | ||
123 | }; | ||
124 | |||
125 | nvbios_exec(&init); | ||
126 | } | ||
127 | |||
128 | return outp; | ||
129 | } | ||
130 | |||
131 | static struct nvkm_output * | ||
132 | exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) | ||
133 | { | ||
134 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
135 | struct nvkm_device *device = subdev->device; | ||
136 | struct nvkm_bios *bios = device->bios; | ||
137 | struct nvkm_output *outp; | ||
138 | struct nvbios_outp info1; | ||
139 | struct nvbios_ocfg info2; | ||
140 | u8 ver, hdr, cnt, len; | ||
141 | u32 data, ctrl = 0; | ||
142 | int or; | ||
143 | |||
144 | for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) { | ||
145 | ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20)); | ||
146 | if (ctrl & (1 << head)) | ||
147 | break; | ||
148 | } | ||
149 | |||
150 | if (or == 8) | ||
151 | return NULL; | ||
152 | |||
153 | outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); | ||
154 | if (!outp) | ||
155 | return NULL; | ||
156 | |||
157 | *conf = (ctrl & 0x00000f00) >> 8; | ||
158 | switch (outp->info.type) { | ||
159 | case DCB_OUTPUT_TMDS: | ||
160 | if (*conf == 5) | ||
161 | *conf |= 0x0100; | ||
162 | break; | ||
163 | case DCB_OUTPUT_LVDS: | ||
164 | *conf |= disp->sor.lvdsconf; | ||
165 | break; | ||
166 | default: | ||
167 | break; | ||
168 | } | ||
169 | |||
170 | data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8, | ||
171 | &ver, &hdr, &cnt, &len, &info2); | ||
172 | if (data && id < 0xff) { | ||
173 | data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); | ||
174 | if (data) { | ||
175 | struct nvbios_init init = { | ||
176 | .subdev = subdev, | ||
177 | .bios = bios, | ||
178 | .offset = data, | ||
179 | .outp = &outp->info, | ||
180 | .crtc = head, | ||
181 | .execute = 1, | ||
182 | }; | ||
183 | |||
184 | nvbios_exec(&init); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | return outp; | ||
189 | } | ||
190 | |||
191 | static void | ||
192 | gf119_disp_intr_unk1_0(struct nv50_disp *disp, int head) | ||
193 | { | ||
194 | exec_script(disp, head, 1); | ||
195 | } | ||
196 | |||
197 | static void | ||
198 | gf119_disp_intr_unk2_0(struct nv50_disp *disp, int head) | ||
199 | { | ||
200 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
201 | struct nvkm_output *outp = exec_script(disp, head, 2); | ||
202 | |||
203 | /* see note in nv50_disp_intr_unk20_0() */ | ||
204 | if (outp && outp->info.type == DCB_OUTPUT_DP) { | ||
205 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | ||
206 | if (!outpdp->lt.mst) { | ||
207 | struct nvbios_init init = { | ||
208 | .subdev = subdev, | ||
209 | .bios = subdev->device->bios, | ||
210 | .outp = &outp->info, | ||
211 | .crtc = head, | ||
212 | .offset = outpdp->info.script[4], | ||
213 | .execute = 1, | ||
214 | }; | ||
215 | |||
216 | nvkm_notify_put(&outpdp->irq); | ||
217 | nvbios_exec(&init); | ||
218 | atomic_set(&outpdp->lt.done, 0); | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static void | ||
224 | gf119_disp_intr_unk2_1(struct nv50_disp *disp, int head) | ||
225 | { | ||
226 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
227 | struct nvkm_devinit *devinit = device->devinit; | ||
228 | u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | ||
229 | if (pclk) | ||
230 | nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk); | ||
231 | nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000); | ||
232 | } | ||
233 | |||
234 | static void | ||
235 | gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head, | ||
236 | struct dcb_output *outp) | ||
237 | { | ||
238 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
239 | const int or = ffs(outp->or) - 1; | ||
240 | const u32 ctrl = nvkm_rd32(device, 0x660200 + (or * 0x020)); | ||
241 | const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300)); | ||
242 | const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff; | ||
243 | const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff; | ||
244 | const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff; | ||
245 | const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | ||
246 | const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; | ||
247 | const u32 hoff = (head * 0x800); | ||
248 | const u32 soff = ( or * 0x800); | ||
249 | const u32 loff = (link * 0x080) + soff; | ||
250 | const u32 symbol = 100000; | ||
251 | const u32 TU = 64; | ||
252 | u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff); | ||
253 | u32 clksor = nvkm_rd32(device, 0x612300 + soff); | ||
254 | u32 datarate, link_nr, link_bw, bits; | ||
255 | u64 ratio, value; | ||
256 | |||
257 | link_nr = hweight32(dpctrl & 0x000f0000); | ||
258 | link_bw = (clksor & 0x007c0000) >> 18; | ||
259 | link_bw *= 27000; | ||
260 | |||
261 | /* symbols/hblank - algorithm taken from comments in tegra driver */ | ||
262 | value = vblanke + vactive - vblanks - 7; | ||
263 | value = value * link_bw; | ||
264 | do_div(value, pclk); | ||
265 | value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); | ||
266 | nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value); | ||
267 | |||
268 | /* symbols/vblank - algorithm taken from comments in tegra driver */ | ||
269 | value = vblanks - vblanke - 25; | ||
270 | value = value * link_bw; | ||
271 | do_div(value, pclk); | ||
272 | value = value - ((36 / link_nr) + 3) - 1; | ||
273 | nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value); | ||
274 | |||
275 | /* watermark */ | ||
276 | if ((conf & 0x3c0) == 0x180) bits = 30; | ||
277 | else if ((conf & 0x3c0) == 0x140) bits = 24; | ||
278 | else bits = 18; | ||
279 | datarate = (pclk * bits) / 8; | ||
280 | |||
281 | ratio = datarate; | ||
282 | ratio *= symbol; | ||
283 | do_div(ratio, link_nr * link_bw); | ||
284 | |||
285 | value = (symbol - ratio) * TU; | ||
286 | value *= ratio; | ||
287 | do_div(value, symbol); | ||
288 | do_div(value, symbol); | ||
289 | |||
290 | value += 5; | ||
291 | value |= 0x08000000; | ||
292 | |||
293 | nvkm_wr32(device, 0x616610 + hoff, value); | ||
294 | } | ||
295 | |||
296 | static void | ||
297 | gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head) | ||
298 | { | ||
299 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
300 | struct nvkm_output *outp; | ||
301 | u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | ||
302 | u32 conf, addr, data; | ||
303 | |||
304 | outp = exec_clkcmp(disp, head, 0xff, pclk, &conf); | ||
305 | if (!outp) | ||
306 | return; | ||
307 | |||
308 | /* see note in nv50_disp_intr_unk20_2() */ | ||
309 | if (outp->info.type == DCB_OUTPUT_DP) { | ||
310 | u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300)); | ||
311 | switch ((sync & 0x000003c0) >> 6) { | ||
312 | case 6: pclk = pclk * 30; break; | ||
313 | case 5: pclk = pclk * 24; break; | ||
314 | case 2: | ||
315 | default: | ||
316 | pclk = pclk * 18; | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | if (nvkm_output_dp_train(outp, pclk)) | ||
321 | OUTP_ERR(outp, "link not trained before attach"); | ||
322 | } else { | ||
323 | if (disp->func->sor.magic) | ||
324 | disp->func->sor.magic(outp); | ||
325 | } | ||
326 | |||
327 | exec_clkcmp(disp, head, 0, pclk, &conf); | ||
328 | |||
329 | if (outp->info.type == DCB_OUTPUT_ANALOG) { | ||
330 | addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800; | ||
331 | data = 0x00000000; | ||
332 | } else { | ||
333 | addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800; | ||
334 | data = (conf & 0x0100) ? 0x00000101 : 0x00000000; | ||
335 | switch (outp->info.type) { | ||
336 | case DCB_OUTPUT_TMDS: | ||
337 | nvkm_mask(device, addr, 0x007c0000, 0x00280000); | ||
338 | break; | ||
339 | case DCB_OUTPUT_DP: | ||
340 | gf119_disp_intr_unk2_2_tu(disp, head, &outp->info); | ||
341 | break; | ||
342 | default: | ||
343 | break; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | nvkm_mask(device, addr, 0x00000707, data); | ||
348 | } | ||
349 | |||
350 | static void | ||
351 | gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head) | ||
352 | { | ||
353 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
354 | u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000; | ||
355 | u32 conf; | ||
356 | |||
357 | exec_clkcmp(disp, head, 1, pclk, &conf); | ||
358 | } | ||
359 | |||
360 | void | 29 | void |
361 | gf119_disp_intr_supervisor(struct work_struct *work) | 30 | gf119_disp_super(struct work_struct *work) |
362 | { | 31 | { |
363 | struct nv50_disp *disp = | 32 | struct nv50_disp *disp = |
364 | container_of(work, struct nv50_disp, supervisor); | 33 | container_of(work, struct nv50_disp, supervisor); |
365 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 34 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
366 | struct nvkm_device *device = subdev->device; | 35 | struct nvkm_device *device = subdev->device; |
36 | struct nvkm_head *head; | ||
367 | u32 mask[4]; | 37 | u32 mask[4]; |
368 | int head; | ||
369 | 38 | ||
370 | nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super)); | 39 | nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super)); |
371 | for (head = 0; head < disp->base.head.nr; head++) { | 40 | list_for_each_entry(head, &disp->base.head, head) { |
372 | mask[head] = nvkm_rd32(device, 0x6101d4 + (head * 0x800)); | 41 | mask[head->id] = nvkm_rd32(device, 0x6101d4 + (head->id * 0x800)); |
373 | nvkm_debug(subdev, "head %d: %08x\n", head, mask[head]); | 42 | HEAD_DBG(head, "%08x", mask[head->id]); |
374 | } | 43 | } |
375 | 44 | ||
376 | if (disp->super & 0x00000001) { | 45 | if (disp->super & 0x00000001) { |
377 | nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); | 46 | nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); |
378 | for (head = 0; head < disp->base.head.nr; head++) { | 47 | nv50_disp_super_1(disp); |
379 | if (!(mask[head] & 0x00001000)) | 48 | list_for_each_entry(head, &disp->base.head, head) { |
49 | if (!(mask[head->id] & 0x00001000)) | ||
380 | continue; | 50 | continue; |
381 | nvkm_debug(subdev, "supervisor 1.0 - head %d\n", head); | 51 | nv50_disp_super_1_0(disp, head); |
382 | gf119_disp_intr_unk1_0(disp, head); | ||
383 | } | 52 | } |
384 | } else | 53 | } else |
385 | if (disp->super & 0x00000002) { | 54 | if (disp->super & 0x00000002) { |
386 | for (head = 0; head < disp->base.head.nr; head++) { | 55 | list_for_each_entry(head, &disp->base.head, head) { |
387 | if (!(mask[head] & 0x00001000)) | 56 | if (!(mask[head->id] & 0x00001000)) |
388 | continue; | 57 | continue; |
389 | nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head); | 58 | nv50_disp_super_2_0(disp, head); |
390 | gf119_disp_intr_unk2_0(disp, head); | ||
391 | } | 59 | } |
392 | for (head = 0; head < disp->base.head.nr; head++) { | 60 | nvkm_outp_route(&disp->base); |
393 | if (!(mask[head] & 0x00010000)) | 61 | list_for_each_entry(head, &disp->base.head, head) { |
62 | if (!(mask[head->id] & 0x00010000)) | ||
394 | continue; | 63 | continue; |
395 | nvkm_debug(subdev, "supervisor 2.1 - head %d\n", head); | 64 | nv50_disp_super_2_1(disp, head); |
396 | gf119_disp_intr_unk2_1(disp, head); | ||
397 | } | 65 | } |
398 | for (head = 0; head < disp->base.head.nr; head++) { | 66 | list_for_each_entry(head, &disp->base.head, head) { |
399 | if (!(mask[head] & 0x00001000)) | 67 | if (!(mask[head->id] & 0x00001000)) |
400 | continue; | 68 | continue; |
401 | nvkm_debug(subdev, "supervisor 2.2 - head %d\n", head); | 69 | nv50_disp_super_2_2(disp, head); |
402 | gf119_disp_intr_unk2_2(disp, head); | ||
403 | } | 70 | } |
404 | } else | 71 | } else |
405 | if (disp->super & 0x00000004) { | 72 | if (disp->super & 0x00000004) { |
406 | for (head = 0; head < disp->base.head.nr; head++) { | 73 | list_for_each_entry(head, &disp->base.head, head) { |
407 | if (!(mask[head] & 0x00001000)) | 74 | if (!(mask[head->id] & 0x00001000)) |
408 | continue; | 75 | continue; |
409 | nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head); | 76 | nv50_disp_super_3_0(disp, head); |
410 | gf119_disp_intr_unk4_0(disp, head); | ||
411 | } | 77 | } |
412 | } | 78 | } |
413 | 79 | ||
414 | for (head = 0; head < disp->base.head.nr; head++) | 80 | list_for_each_entry(head, &disp->base.head, head) |
415 | nvkm_wr32(device, 0x6101d4 + (head * 0x800), 0x00000000); | 81 | nvkm_wr32(device, 0x6101d4 + (head->id * 0x800), 0x00000000); |
416 | nvkm_wr32(device, 0x6101d0, 0x80000000); | 82 | nvkm_wr32(device, 0x6101d0, 0x80000000); |
417 | } | 83 | } |
418 | 84 | ||
@@ -447,8 +113,8 @@ gf119_disp_intr(struct nv50_disp *disp) | |||
447 | { | 113 | { |
448 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 114 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
449 | struct nvkm_device *device = subdev->device; | 115 | struct nvkm_device *device = subdev->device; |
116 | struct nvkm_head *head; | ||
450 | u32 intr = nvkm_rd32(device, 0x610088); | 117 | u32 intr = nvkm_rd32(device, 0x610088); |
451 | int i; | ||
452 | 118 | ||
453 | if (intr & 0x00000001) { | 119 | if (intr & 0x00000001) { |
454 | u32 stat = nvkm_rd32(device, 0x61008c); | 120 | u32 stat = nvkm_rd32(device, 0x61008c); |
@@ -472,7 +138,7 @@ gf119_disp_intr(struct nv50_disp *disp) | |||
472 | u32 stat = nvkm_rd32(device, 0x6100ac); | 138 | u32 stat = nvkm_rd32(device, 0x6100ac); |
473 | if (stat & 0x00000007) { | 139 | if (stat & 0x00000007) { |
474 | disp->super = (stat & 0x00000007); | 140 | disp->super = (stat & 0x00000007); |
475 | schedule_work(&disp->supervisor); | 141 | queue_work(disp->wq, &disp->supervisor); |
476 | nvkm_wr32(device, 0x6100ac, disp->super); | 142 | nvkm_wr32(device, 0x6100ac, disp->super); |
477 | stat &= ~0x00000007; | 143 | stat &= ~0x00000007; |
478 | } | 144 | } |
@@ -485,14 +151,15 @@ gf119_disp_intr(struct nv50_disp *disp) | |||
485 | intr &= ~0x00100000; | 151 | intr &= ~0x00100000; |
486 | } | 152 | } |
487 | 153 | ||
488 | for (i = 0; i < disp->base.head.nr; i++) { | 154 | list_for_each_entry(head, &disp->base.head, head) { |
489 | u32 mask = 0x01000000 << i; | 155 | const u32 hoff = head->id * 0x800; |
156 | u32 mask = 0x01000000 << head->id; | ||
490 | if (mask & intr) { | 157 | if (mask & intr) { |
491 | u32 stat = nvkm_rd32(device, 0x6100bc + (i * 0x800)); | 158 | u32 stat = nvkm_rd32(device, 0x6100bc + hoff); |
492 | if (stat & 0x00000001) | 159 | if (stat & 0x00000001) |
493 | nvkm_disp_vblank(&disp->base, i); | 160 | nvkm_disp_vblank(&disp->base, head->id); |
494 | nvkm_mask(device, 0x6100bc + (i * 0x800), 0, 0); | 161 | nvkm_mask(device, 0x6100bc + hoff, 0, 0); |
495 | nvkm_rd32(device, 0x6100c0 + (i * 0x800)); | 162 | nvkm_rd32(device, 0x6100c0 + hoff); |
496 | } | 163 | } |
497 | } | 164 | } |
498 | } | 165 | } |
@@ -510,22 +177,11 @@ gf119_disp = { | |||
510 | .intr = gf119_disp_intr, | 177 | .intr = gf119_disp_intr, |
511 | .intr_error = gf119_disp_intr_error, | 178 | .intr_error = gf119_disp_intr_error, |
512 | .uevent = &gf119_disp_chan_uevent, | 179 | .uevent = &gf119_disp_chan_uevent, |
513 | .super = gf119_disp_intr_supervisor, | 180 | .super = gf119_disp_super, |
514 | .root = &gf119_disp_root_oclass, | 181 | .root = &gf119_disp_root_oclass, |
515 | .head.vblank_init = gf119_disp_vblank_init, | 182 | .head.new = gf119_head_new, |
516 | .head.vblank_fini = gf119_disp_vblank_fini, | 183 | .dac = { .nr = 3, .new = gf119_dac_new }, |
517 | .head.scanoutpos = gf119_disp_root_scanoutpos, | 184 | .sor = { .nr = 4, .new = gf119_sor_new }, |
518 | .outp.internal.crt = nv50_dac_output_new, | ||
519 | .outp.internal.tmds = nv50_sor_output_new, | ||
520 | .outp.internal.lvds = nv50_sor_output_new, | ||
521 | .outp.internal.dp = gf119_sor_dp_new, | ||
522 | .dac.nr = 3, | ||
523 | .dac.power = nv50_dac_power, | ||
524 | .dac.sense = nv50_dac_sense, | ||
525 | .sor.nr = 4, | ||
526 | .sor.power = nv50_sor_power, | ||
527 | .sor.hda_eld = gf119_hda_eld, | ||
528 | .sor.hdmi = gf119_hdmi_ctrl, | ||
529 | }; | 185 | }; |
530 | 186 | ||
531 | int | 187 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c index 37f145cf30d7..e8fe9f315d64 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
@@ -29,22 +31,11 @@ gk104_disp = { | |||
29 | .intr = gf119_disp_intr, | 31 | .intr = gf119_disp_intr, |
30 | .intr_error = gf119_disp_intr_error, | 32 | .intr_error = gf119_disp_intr_error, |
31 | .uevent = &gf119_disp_chan_uevent, | 33 | .uevent = &gf119_disp_chan_uevent, |
32 | .super = gf119_disp_intr_supervisor, | 34 | .super = gf119_disp_super, |
33 | .root = &gk104_disp_root_oclass, | 35 | .root = &gk104_disp_root_oclass, |
34 | .head.vblank_init = gf119_disp_vblank_init, | 36 | .head.new = gf119_head_new, |
35 | .head.vblank_fini = gf119_disp_vblank_fini, | 37 | .dac = { .nr = 3, .new = gf119_dac_new }, |
36 | .head.scanoutpos = gf119_disp_root_scanoutpos, | 38 | .sor = { .nr = 4, .new = gk104_sor_new }, |
37 | .outp.internal.crt = nv50_dac_output_new, | ||
38 | .outp.internal.tmds = nv50_sor_output_new, | ||
39 | .outp.internal.lvds = nv50_sor_output_new, | ||
40 | .outp.internal.dp = gf119_sor_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 4, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hda_eld = gf119_hda_eld, | ||
47 | .sor.hdmi = gk104_hdmi_ctrl, | ||
48 | }; | 39 | }; |
49 | 40 | ||
50 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c index e14ac946608c..769687502e7a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk110.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
@@ -29,22 +31,11 @@ gk110_disp = { | |||
29 | .intr = gf119_disp_intr, | 31 | .intr = gf119_disp_intr, |
30 | .intr_error = gf119_disp_intr_error, | 32 | .intr_error = gf119_disp_intr_error, |
31 | .uevent = &gf119_disp_chan_uevent, | 33 | .uevent = &gf119_disp_chan_uevent, |
32 | .super = gf119_disp_intr_supervisor, | 34 | .super = gf119_disp_super, |
33 | .root = &gk110_disp_root_oclass, | 35 | .root = &gk110_disp_root_oclass, |
34 | .head.vblank_init = gf119_disp_vblank_init, | 36 | .head.new = gf119_head_new, |
35 | .head.vblank_fini = gf119_disp_vblank_fini, | 37 | .dac = { .nr = 3, .new = gf119_dac_new }, |
36 | .head.scanoutpos = gf119_disp_root_scanoutpos, | 38 | .sor = { .nr = 4, .new = gk104_sor_new }, |
37 | .outp.internal.crt = nv50_dac_output_new, | ||
38 | .outp.internal.tmds = nv50_sor_output_new, | ||
39 | .outp.internal.lvds = nv50_sor_output_new, | ||
40 | .outp.internal.dp = gf119_sor_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 4, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hda_eld = gf119_hda_eld, | ||
47 | .sor.hdmi = gk104_hdmi_ctrl, | ||
48 | }; | 39 | }; |
49 | 40 | ||
50 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c index 2f2437cc5891..ede70e5d188e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
@@ -29,22 +31,11 @@ gm107_disp = { | |||
29 | .intr = gf119_disp_intr, | 31 | .intr = gf119_disp_intr, |
30 | .intr_error = gf119_disp_intr_error, | 32 | .intr_error = gf119_disp_intr_error, |
31 | .uevent = &gf119_disp_chan_uevent, | 33 | .uevent = &gf119_disp_chan_uevent, |
32 | .super = gf119_disp_intr_supervisor, | 34 | .super = gf119_disp_super, |
33 | .root = &gm107_disp_root_oclass, | 35 | .root = &gm107_disp_root_oclass, |
34 | .head.vblank_init = gf119_disp_vblank_init, | 36 | .head.new = gf119_head_new, |
35 | .head.vblank_fini = gf119_disp_vblank_fini, | 37 | .dac = { .nr = 3, .new = gf119_dac_new }, |
36 | .head.scanoutpos = gf119_disp_root_scanoutpos, | 38 | .sor = { .nr = 4, .new = gm107_sor_new }, |
37 | .outp.internal.crt = nv50_dac_output_new, | ||
38 | .outp.internal.tmds = nv50_sor_output_new, | ||
39 | .outp.internal.lvds = nv50_sor_output_new, | ||
40 | .outp.internal.dp = gm107_sor_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 4, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hda_eld = gf119_hda_eld, | ||
47 | .sor.hdmi = gk104_hdmi_ctrl, | ||
48 | }; | 39 | }; |
49 | 40 | ||
50 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c index 9f368d4ee61e..292d3b5f9704 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
@@ -29,23 +31,11 @@ gm200_disp = { | |||
29 | .intr = gf119_disp_intr, | 31 | .intr = gf119_disp_intr, |
30 | .intr_error = gf119_disp_intr_error, | 32 | .intr_error = gf119_disp_intr_error, |
31 | .uevent = &gf119_disp_chan_uevent, | 33 | .uevent = &gf119_disp_chan_uevent, |
32 | .super = gf119_disp_intr_supervisor, | 34 | .super = gf119_disp_super, |
33 | .root = &gm200_disp_root_oclass, | 35 | .root = &gm200_disp_root_oclass, |
34 | .head.vblank_init = gf119_disp_vblank_init, | 36 | .head.new = gf119_head_new, |
35 | .head.vblank_fini = gf119_disp_vblank_fini, | 37 | .dac = { .nr = 3, .new = gf119_dac_new }, |
36 | .head.scanoutpos = gf119_disp_root_scanoutpos, | 38 | .sor = { .nr = 4, .new = gm200_sor_new }, |
37 | .outp.internal.crt = nv50_dac_output_new, | ||
38 | .outp.internal.tmds = nv50_sor_output_new, | ||
39 | .outp.internal.lvds = nv50_sor_output_new, | ||
40 | .outp.internal.dp = gm200_sor_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 4, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hda_eld = gf119_hda_eld, | ||
47 | .sor.hdmi = gk104_hdmi_ctrl, | ||
48 | .sor.magic = gm200_sor_magic, | ||
49 | }; | 39 | }; |
50 | 40 | ||
51 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c index 4f81bf31435e..39eb98b2c3a2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs <bskeggs@redhat.com> | 22 | * Authors: Ben Skeggs <bskeggs@redhat.com> |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
@@ -29,23 +31,10 @@ gp100_disp = { | |||
29 | .intr = gf119_disp_intr, | 31 | .intr = gf119_disp_intr, |
30 | .intr_error = gf119_disp_intr_error, | 32 | .intr_error = gf119_disp_intr_error, |
31 | .uevent = &gf119_disp_chan_uevent, | 33 | .uevent = &gf119_disp_chan_uevent, |
32 | .super = gf119_disp_intr_supervisor, | 34 | .super = gf119_disp_super, |
33 | .root = &gp100_disp_root_oclass, | 35 | .root = &gp100_disp_root_oclass, |
34 | .head.vblank_init = gf119_disp_vblank_init, | 36 | .head.new = gf119_head_new, |
35 | .head.vblank_fini = gf119_disp_vblank_fini, | 37 | .sor = { .nr = 4, .new = gm200_sor_new }, |
36 | .head.scanoutpos = gf119_disp_root_scanoutpos, | ||
37 | .outp.internal.crt = nv50_dac_output_new, | ||
38 | .outp.internal.tmds = nv50_sor_output_new, | ||
39 | .outp.internal.lvds = nv50_sor_output_new, | ||
40 | .outp.internal.dp = gm200_sor_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 4, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hda_eld = gf119_hda_eld, | ||
47 | .sor.hdmi = gk104_hdmi_ctrl, | ||
48 | .sor.magic = gm200_sor_magic, | ||
49 | }; | 38 | }; |
50 | 39 | ||
51 | int | 40 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c index f5d613f82709..91d70fe18275 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp102.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs <bskeggs@redhat.com> | 22 | * Authors: Ben Skeggs <bskeggs@redhat.com> |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static void | 29 | static void |
@@ -55,23 +57,10 @@ gp102_disp = { | |||
55 | .intr = gf119_disp_intr, | 57 | .intr = gf119_disp_intr, |
56 | .intr_error = gp102_disp_intr_error, | 58 | .intr_error = gp102_disp_intr_error, |
57 | .uevent = &gf119_disp_chan_uevent, | 59 | .uevent = &gf119_disp_chan_uevent, |
58 | .super = gf119_disp_intr_supervisor, | 60 | .super = gf119_disp_super, |
59 | .root = &gp102_disp_root_oclass, | 61 | .root = &gp102_disp_root_oclass, |
60 | .head.vblank_init = gf119_disp_vblank_init, | 62 | .head.new = gf119_head_new, |
61 | .head.vblank_fini = gf119_disp_vblank_fini, | 63 | .sor = { .nr = 4, .new = gm200_sor_new }, |
62 | .head.scanoutpos = gf119_disp_root_scanoutpos, | ||
63 | .outp.internal.crt = nv50_dac_output_new, | ||
64 | .outp.internal.tmds = nv50_sor_output_new, | ||
65 | .outp.internal.lvds = nv50_sor_output_new, | ||
66 | .outp.internal.dp = gm200_sor_dp_new, | ||
67 | .dac.nr = 3, | ||
68 | .dac.power = nv50_dac_power, | ||
69 | .dac.sense = nv50_dac_sense, | ||
70 | .sor.nr = 4, | ||
71 | .sor.power = nv50_sor_power, | ||
72 | .sor.hda_eld = gf119_hda_eld, | ||
73 | .sor.hdmi = gk104_hdmi_ctrl, | ||
74 | .sor.magic = gm200_sor_magic, | ||
75 | }; | 64 | }; |
76 | 65 | ||
77 | int | 66 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c index 6bc3bf096001..bf00c4e3be3a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt200.c | |||
@@ -22,30 +22,20 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
28 | gt200_disp = { | 30 | gt200_disp = { |
29 | .intr = nv50_disp_intr, | 31 | .intr = nv50_disp_intr, |
30 | .uevent = &nv50_disp_chan_uevent, | 32 | .uevent = &nv50_disp_chan_uevent, |
31 | .super = nv50_disp_intr_supervisor, | 33 | .super = nv50_disp_super, |
32 | .root = >200_disp_root_oclass, | 34 | .root = >200_disp_root_oclass, |
33 | .head.vblank_init = nv50_disp_vblank_init, | 35 | .head.new = nv50_head_new, |
34 | .head.vblank_fini = nv50_disp_vblank_fini, | 36 | .dac = { .nr = 3, .new = nv50_dac_new }, |
35 | .head.scanoutpos = nv50_disp_root_scanoutpos, | 37 | .sor = { .nr = 2, .new = g84_sor_new }, |
36 | .outp.internal.crt = nv50_dac_output_new, | 38 | .pior = { .nr = 3, .new = nv50_pior_new }, |
37 | .outp.internal.tmds = nv50_sor_output_new, | ||
38 | .outp.internal.lvds = nv50_sor_output_new, | ||
39 | .outp.external.tmds = nv50_pior_output_new, | ||
40 | .outp.external.dp = nv50_pior_dp_new, | ||
41 | .dac.nr = 3, | ||
42 | .dac.power = nv50_dac_power, | ||
43 | .dac.sense = nv50_dac_sense, | ||
44 | .sor.nr = 2, | ||
45 | .sor.power = nv50_sor_power, | ||
46 | .sor.hdmi = g84_hdmi_ctrl, | ||
47 | .pior.nr = 3, | ||
48 | .pior.power = nv50_pior_power, | ||
49 | }; | 39 | }; |
50 | 40 | ||
51 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c index 94026288ab4d..2cdd4d7a98d3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c | |||
@@ -22,32 +22,20 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | static const struct nv50_disp_func | 29 | static const struct nv50_disp_func |
28 | gt215_disp = { | 30 | gt215_disp = { |
29 | .intr = nv50_disp_intr, | 31 | .intr = nv50_disp_intr, |
30 | .uevent = &nv50_disp_chan_uevent, | 32 | .uevent = &nv50_disp_chan_uevent, |
31 | .super = nv50_disp_intr_supervisor, | 33 | .super = nv50_disp_super, |
32 | .root = >215_disp_root_oclass, | 34 | .root = >215_disp_root_oclass, |
33 | .head.vblank_init = nv50_disp_vblank_init, | 35 | .head.new = nv50_head_new, |
34 | .head.vblank_fini = nv50_disp_vblank_fini, | 36 | .dac = { .nr = 3, .new = nv50_dac_new }, |
35 | .head.scanoutpos = nv50_disp_root_scanoutpos, | 37 | .sor = { .nr = 4, .new = gt215_sor_new }, |
36 | .outp.internal.crt = nv50_dac_output_new, | 38 | .pior = { .nr = 3, .new = nv50_pior_new }, |
37 | .outp.internal.tmds = nv50_sor_output_new, | ||
38 | .outp.internal.lvds = nv50_sor_output_new, | ||
39 | .outp.internal.dp = g94_sor_dp_new, | ||
40 | .outp.external.tmds = nv50_pior_output_new, | ||
41 | .outp.external.dp = nv50_pior_dp_new, | ||
42 | .dac.nr = 3, | ||
43 | .dac.power = nv50_dac_power, | ||
44 | .dac.sense = nv50_dac_sense, | ||
45 | .sor.nr = 4, | ||
46 | .sor.power = nv50_sor_power, | ||
47 | .sor.hda_eld = gt215_hda_eld, | ||
48 | .sor.hdmi = gt215_hdmi_ctrl, | ||
49 | .pior.nr = 3, | ||
50 | .pior.power = nv50_pior_power, | ||
51 | }; | 39 | }; |
52 | 40 | ||
53 | int | 41 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c index da6129b2b78f..0fa0ec0a1de0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagf119.c | |||
@@ -21,63 +21,34 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outp.h" | ||
26 | 25 | ||
27 | #include <core/client.h> | 26 | void |
28 | #include <subdev/bios.h> | 27 | gf119_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size) |
29 | #include <subdev/bios/dcb.h> | ||
30 | #include <subdev/timer.h> | ||
31 | |||
32 | #include <nvif/cl5070.h> | ||
33 | #include <nvif/unpack.h> | ||
34 | |||
35 | int | ||
36 | gf119_hda_eld(NV50_DISP_MTHD_V1) | ||
37 | { | 28 | { |
38 | struct nvkm_device *device = disp->base.engine.subdev.device; | 29 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
39 | union { | 30 | const u32 soff = 0x030 * ior->id; |
40 | struct nv50_disp_sor_hda_eld_v0 v0; | 31 | int i; |
41 | } *args = data; | ||
42 | const u32 soff = outp->or * 0x030; | ||
43 | const u32 hoff = head * 0x800; | ||
44 | int ret = -ENOSYS, i; | ||
45 | 32 | ||
46 | nvif_ioctl(object, "disp sor hda eld size %d\n", size); | 33 | for (i = 0; i < size; i++) |
47 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { | 34 | nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | data[i]); |
48 | nvif_ioctl(object, "disp sor hda eld vers %d\n", | 35 | for (; i < 0x60; i++) |
49 | args->v0.version); | 36 | nvkm_wr32(device, 0x10ec00 + soff, (i << 8)); |
50 | if (size > 0x60) | 37 | nvkm_mask(device, 0x10ec10 + soff, 0x80000002, 0x80000002); |
51 | return -E2BIG; | 38 | } |
52 | } else | ||
53 | return ret; | ||
54 | 39 | ||
55 | if (size && args->v0.data[0]) { | 40 | void |
56 | if (outp->info.type == DCB_OUTPUT_DP) { | 41 | gf119_hda_hpd(struct nvkm_ior *ior, int head, bool present) |
57 | nvkm_mask(device, 0x616618 + hoff, 0x8000000c, 0x80000001); | 42 | { |
58 | nvkm_msec(device, 2000, | 43 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
59 | u32 tmp = nvkm_rd32(device, 0x616618 + hoff); | 44 | const u32 hoff = 0x800 * head; |
60 | if (!(tmp & 0x80000000)) | 45 | u32 data = 0x80000000; |
61 | break; | 46 | u32 mask = 0x80000001; |
62 | ); | 47 | if (present) { |
63 | } | ||
64 | nvkm_mask(device, 0x616548 + hoff, 0x00000070, 0x00000000); | 48 | nvkm_mask(device, 0x616548 + hoff, 0x00000070, 0x00000000); |
65 | for (i = 0; i < size; i++) | 49 | data |= 0x00000001; |
66 | nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | args->v0.data[i]); | ||
67 | for (; i < 0x60; i++) | ||
68 | nvkm_wr32(device, 0x10ec00 + soff, (i << 8)); | ||
69 | nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000003); | ||
70 | } else { | 50 | } else { |
71 | if (outp->info.type == DCB_OUTPUT_DP) { | 51 | mask |= 0x00000002; |
72 | nvkm_mask(device, 0x616618 + hoff, 0x80000001, 0x80000000); | ||
73 | nvkm_msec(device, 2000, | ||
74 | u32 tmp = nvkm_rd32(device, 0x616618 + hoff); | ||
75 | if (!(tmp & 0x80000000)) | ||
76 | break; | ||
77 | ); | ||
78 | } | ||
79 | nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size); | ||
80 | } | 52 | } |
81 | 53 | nvkm_mask(device, 0x10ec10 + ior->id * 0x030, mask, data); | |
82 | return 0; | ||
83 | } | 54 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c index f8f2f16c22a2..4509d2ba880e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c | |||
@@ -21,59 +21,31 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outp.h" | ||
26 | 25 | ||
27 | #include <core/client.h> | 26 | void |
28 | #include <subdev/timer.h> | 27 | gt215_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size) |
29 | |||
30 | #include <nvif/cl5070.h> | ||
31 | #include <nvif/unpack.h> | ||
32 | |||
33 | int | ||
34 | gt215_hda_eld(NV50_DISP_MTHD_V1) | ||
35 | { | 28 | { |
36 | struct nvkm_device *device = disp->base.engine.subdev.device; | 29 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
37 | union { | 30 | const u32 soff = ior->id * 0x800; |
38 | struct nv50_disp_sor_hda_eld_v0 v0; | 31 | int i; |
39 | } *args = data; | ||
40 | const u32 soff = outp->or * 0x800; | ||
41 | int ret = -ENOSYS, i; | ||
42 | |||
43 | nvif_ioctl(object, "disp sor hda eld size %d\n", size); | ||
44 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { | ||
45 | nvif_ioctl(object, "disp sor hda eld vers %d\n", | ||
46 | args->v0.version); | ||
47 | if (size > 0x60) | ||
48 | return -E2BIG; | ||
49 | } else | ||
50 | return ret; | ||
51 | 32 | ||
52 | if (size && args->v0.data[0]) { | 33 | for (i = 0; i < size; i++) |
53 | if (outp->info.type == DCB_OUTPUT_DP) { | 34 | nvkm_wr32(device, 0x61c440 + soff, (i << 8) | data[i]); |
54 | nvkm_mask(device, 0x61c1e0 + soff, 0x8000000d, 0x80000001); | 35 | for (; i < 0x60; i++) |
55 | nvkm_msec(device, 2000, | 36 | nvkm_wr32(device, 0x61c440 + soff, (i << 8)); |
56 | u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff); | 37 | nvkm_mask(device, 0x61c448 + soff, 0x80000002, 0x80000002); |
57 | if (!(tmp & 0x80000000)) | 38 | } |
58 | break; | ||
59 | ); | ||
60 | } | ||
61 | for (i = 0; i < size; i++) | ||
62 | nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]); | ||
63 | for (; i < 0x60; i++) | ||
64 | nvkm_wr32(device, 0x61c440 + soff, (i << 8)); | ||
65 | nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003); | ||
66 | } else { | ||
67 | if (outp->info.type == DCB_OUTPUT_DP) { | ||
68 | nvkm_mask(device, 0x61c1e0 + soff, 0x80000001, 0x80000000); | ||
69 | nvkm_msec(device, 2000, | ||
70 | u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff); | ||
71 | if (!(tmp & 0x80000000)) | ||
72 | break; | ||
73 | ); | ||
74 | } | ||
75 | nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size); | ||
76 | } | ||
77 | 39 | ||
78 | return 0; | 40 | void |
41 | gt215_hda_hpd(struct nvkm_ior *ior, int head, bool present) | ||
42 | { | ||
43 | struct nvkm_device *device = ior->disp->engine.subdev.device; | ||
44 | u32 data = 0x80000000; | ||
45 | u32 mask = 0x80000001; | ||
46 | if (present) | ||
47 | data |= 0x00000001; | ||
48 | else | ||
49 | mask |= 0x00000002; | ||
50 | nvkm_mask(device, 0x61c448 + ior->id * 0x800, mask, data); | ||
79 | } | 51 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c new file mode 100644 index 000000000000..e82c68f18444 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.c | |||
@@ -0,0 +1,66 @@ | |||
1 | #include "hdmi.h" | ||
2 | |||
3 | void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, | ||
4 | u8 *raw_frame, ssize_t len) | ||
5 | { | ||
6 | u32 header = 0; | ||
7 | u32 subpack0_low = 0; | ||
8 | u32 subpack0_high = 0; | ||
9 | u32 subpack1_low = 0; | ||
10 | u32 subpack1_high = 0; | ||
11 | |||
12 | switch (len) { | ||
13 | /* | ||
14 | * "When in doubt, use brute force." | ||
15 | * -- Ken Thompson. | ||
16 | */ | ||
17 | default: | ||
18 | /* | ||
19 | * We presume that no valid frame is longer than 17 | ||
20 | * octets, including header... And truncate to that | ||
21 | * if it's longer. | ||
22 | */ | ||
23 | case 17: | ||
24 | subpack1_high = (raw_frame[16] << 16); | ||
25 | case 16: | ||
26 | subpack1_high |= (raw_frame[15] << 8); | ||
27 | case 15: | ||
28 | subpack1_high |= raw_frame[14]; | ||
29 | case 14: | ||
30 | subpack1_low = (raw_frame[13] << 24); | ||
31 | case 13: | ||
32 | subpack1_low |= (raw_frame[12] << 16); | ||
33 | case 12: | ||
34 | subpack1_low |= (raw_frame[11] << 8); | ||
35 | case 11: | ||
36 | subpack1_low |= raw_frame[10]; | ||
37 | case 10: | ||
38 | subpack0_high = (raw_frame[9] << 16); | ||
39 | case 9: | ||
40 | subpack0_high |= (raw_frame[8] << 8); | ||
41 | case 8: | ||
42 | subpack0_high |= raw_frame[7]; | ||
43 | case 7: | ||
44 | subpack0_low = (raw_frame[6] << 24); | ||
45 | case 6: | ||
46 | subpack0_low |= (raw_frame[5] << 16); | ||
47 | case 5: | ||
48 | subpack0_low |= (raw_frame[4] << 8); | ||
49 | case 4: | ||
50 | subpack0_low |= raw_frame[3]; | ||
51 | case 3: | ||
52 | header = (raw_frame[2] << 16); | ||
53 | case 2: | ||
54 | header |= (raw_frame[1] << 8); | ||
55 | case 1: | ||
56 | header |= raw_frame[0]; | ||
57 | case 0: | ||
58 | break; | ||
59 | } | ||
60 | |||
61 | packed_frame->header = header; | ||
62 | packed_frame->subpack0_low = subpack0_low; | ||
63 | packed_frame->subpack0_high = subpack0_high; | ||
64 | packed_frame->subpack1_low = subpack1_low; | ||
65 | packed_frame->subpack1_high = subpack1_high; | ||
66 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h new file mode 100644 index 000000000000..528f5621a496 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmi.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef __NVKM_DISP_HDMI_H__ | ||
2 | #define __NVKM_DISP_HDMI_H__ | ||
3 | #include "ior.h" | ||
4 | |||
5 | struct packed_hdmi_infoframe { | ||
6 | u32 header; | ||
7 | u32 subpack0_low; | ||
8 | u32 subpack0_high; | ||
9 | u32 subpack1_low; | ||
10 | u32 subpack1_high; | ||
11 | }; | ||
12 | |||
13 | void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame, | ||
14 | u8 *raw_frame, ssize_t len); | ||
15 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c index 1c4256e8cbd6..661410f9b457 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmig84.c | |||
@@ -21,54 +21,42 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "hdmi.h" |
25 | 25 | ||
26 | #include <core/client.h> | 26 | void |
27 | 27 | g84_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, | |
28 | #include <nvif/cl5070.h> | 28 | u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) |
29 | #include <nvif/unpack.h> | ||
30 | |||
31 | int | ||
32 | g84_hdmi_ctrl(NV50_DISP_MTHD_V1) | ||
33 | { | 29 | { |
34 | struct nvkm_device *device = disp->base.engine.subdev.device; | 30 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
35 | const u32 hoff = (head * 0x800); | 31 | const u32 ctrl = 0x40000000 * enable | |
36 | union { | 32 | 0x1f000000 /* ??? */ | |
37 | struct nv50_disp_sor_hdmi_pwr_v0 v0; | 33 | max_ac_packet << 16 | |
38 | } *args = data; | 34 | rekey; |
39 | u32 ctrl; | 35 | const u32 hoff = head * 0x800; |
40 | int ret = -ENOSYS; | 36 | struct packed_hdmi_infoframe avi_infoframe; |
37 | struct packed_hdmi_infoframe vendor_infoframe; | ||
41 | 38 | ||
42 | nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); | 39 | pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); |
43 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 40 | pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); |
44 | nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " | ||
45 | "max_ac_packet %d rekey %d\n", | ||
46 | args->v0.version, args->v0.state, | ||
47 | args->v0.max_ac_packet, args->v0.rekey); | ||
48 | if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) | ||
49 | return -EINVAL; | ||
50 | ctrl = 0x40000000 * !!args->v0.state; | ||
51 | ctrl |= args->v0.max_ac_packet << 16; | ||
52 | ctrl |= args->v0.rekey; | ||
53 | ctrl |= 0x1f000000; /* ??? */ | ||
54 | } else | ||
55 | return ret; | ||
56 | 41 | ||
57 | if (!(ctrl & 0x40000000)) { | 42 | if (!(ctrl & 0x40000000)) { |
58 | nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); | 43 | nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000); |
44 | nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000); | ||
59 | nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); | 45 | nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); |
60 | nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); | 46 | nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); |
61 | return 0; | 47 | return; |
62 | } | 48 | } |
63 | 49 | ||
64 | /* AVI InfoFrame */ | 50 | /* AVI InfoFrame */ |
65 | nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); | 51 | nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000); |
66 | nvkm_wr32(device, 0x616528 + hoff, 0x000d0282); | 52 | if (avi_size) { |
67 | nvkm_wr32(device, 0x61652c + hoff, 0x0000006f); | 53 | nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header); |
68 | nvkm_wr32(device, 0x616530 + hoff, 0x00000000); | 54 | nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low); |
69 | nvkm_wr32(device, 0x616534 + hoff, 0x00000000); | 55 | nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high); |
70 | nvkm_wr32(device, 0x616538 + hoff, 0x00000000); | 56 | nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low); |
71 | nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); | 57 | nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high); |
58 | nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001); | ||
59 | } | ||
72 | 60 | ||
73 | /* Audio InfoFrame */ | 61 | /* Audio InfoFrame */ |
74 | nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); | 62 | nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000); |
@@ -77,6 +65,18 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) | |||
77 | nvkm_wr32(device, 0x616510 + hoff, 0x00000000); | 65 | nvkm_wr32(device, 0x616510 + hoff, 0x00000000); |
78 | nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001); | 66 | nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001); |
79 | 67 | ||
68 | /* Vendor InfoFrame */ | ||
69 | nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000); | ||
70 | if (vendor_size) { | ||
71 | nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header); | ||
72 | nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low); | ||
73 | nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high); | ||
74 | /* Is there a second (or up to fourth?) set of subpack registers here? */ | ||
75 | /* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */ | ||
76 | /* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */ | ||
77 | nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001); | ||
78 | } | ||
79 | |||
80 | nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ | 80 | nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ |
81 | nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ | 81 | nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ |
82 | nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ | 82 | nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ |
@@ -88,5 +88,4 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1) | |||
88 | 88 | ||
89 | /* HDMI_CTRL */ | 89 | /* HDMI_CTRL */ |
90 | nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl); | 90 | nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl); |
91 | return 0; | ||
92 | } | 91 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c index 632f02da1382..6cac0e72b4cc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigf119.c | |||
@@ -21,53 +21,56 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "hdmi.h" |
25 | 25 | ||
26 | #include <core/client.h> | 26 | void |
27 | 27 | gf119_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, | |
28 | #include <nvif/cl5070.h> | 28 | u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) |
29 | #include <nvif/unpack.h> | ||
30 | |||
31 | int | ||
32 | gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) | ||
33 | { | 29 | { |
34 | struct nvkm_device *device = disp->base.engine.subdev.device; | 30 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
35 | const u32 hoff = (head * 0x800); | 31 | const u32 ctrl = 0x40000000 * enable | |
36 | union { | 32 | max_ac_packet << 16 | |
37 | struct nv50_disp_sor_hdmi_pwr_v0 v0; | 33 | rekey; |
38 | } *args = data; | 34 | const u32 hoff = head * 0x800; |
39 | u32 ctrl; | 35 | struct packed_hdmi_infoframe avi_infoframe; |
40 | int ret = -ENOSYS; | 36 | struct packed_hdmi_infoframe vendor_infoframe; |
41 | 37 | ||
42 | nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); | 38 | pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); |
43 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 39 | pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); |
44 | nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " | ||
45 | "max_ac_packet %d rekey %d\n", | ||
46 | args->v0.version, args->v0.state, | ||
47 | args->v0.max_ac_packet, args->v0.rekey); | ||
48 | if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) | ||
49 | return -EINVAL; | ||
50 | ctrl = 0x40000000 * !!args->v0.state; | ||
51 | ctrl |= args->v0.max_ac_packet << 16; | ||
52 | ctrl |= args->v0.rekey; | ||
53 | } else | ||
54 | return ret; | ||
55 | 40 | ||
56 | if (!(ctrl & 0x40000000)) { | 41 | if (!(ctrl & 0x40000000)) { |
57 | nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); | 42 | nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); |
43 | nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000); | ||
58 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); | 44 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); |
59 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); | 45 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); |
60 | return 0; | 46 | return; |
61 | } | 47 | } |
62 | 48 | ||
63 | /* AVI InfoFrame */ | 49 | /* AVI InfoFrame */ |
64 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); | 50 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000); |
65 | nvkm_wr32(device, 0x61671c + hoff, 0x000d0282); | 51 | if (avi_size) { |
66 | nvkm_wr32(device, 0x616720 + hoff, 0x0000006f); | 52 | nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header); |
67 | nvkm_wr32(device, 0x616724 + hoff, 0x00000000); | 53 | nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low); |
68 | nvkm_wr32(device, 0x616728 + hoff, 0x00000000); | 54 | nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high); |
69 | nvkm_wr32(device, 0x61672c + hoff, 0x00000000); | 55 | nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low); |
70 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); | 56 | nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high); |
57 | nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001); | ||
58 | } | ||
59 | |||
60 | /* GENERIC(?) / Vendor InfoFrame? */ | ||
61 | nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000); | ||
62 | if (vendor_size) { | ||
63 | /* | ||
64 | * These appear to be the audio infoframe registers, | ||
65 | * but no other set of infoframe registers has yet | ||
66 | * been found. | ||
67 | */ | ||
68 | nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header); | ||
69 | nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low); | ||
70 | nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high); | ||
71 | /* Is there a second (or further?) set of subpack registers here? */ | ||
72 | nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001); | ||
73 | } | ||
71 | 74 | ||
72 | /* ??? InfoFrame? */ | 75 | /* ??? InfoFrame? */ |
73 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); | 76 | nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000); |
@@ -76,5 +79,4 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1) | |||
76 | 79 | ||
77 | /* HDMI_CTRL */ | 80 | /* HDMI_CTRL */ |
78 | nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); | 81 | nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); |
79 | return 0; | ||
80 | } | 82 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c index 4e8067d511d7..ed0a6100d76b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigk104.c | |||
@@ -21,54 +21,53 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "hdmi.h" |
25 | 25 | ||
26 | #include <core/client.h> | 26 | void |
27 | 27 | gk104_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, | |
28 | #include <nvif/cl5070.h> | 28 | u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) |
29 | #include <nvif/unpack.h> | ||
30 | |||
31 | int | ||
32 | gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) | ||
33 | { | 29 | { |
34 | struct nvkm_device *device = disp->base.engine.subdev.device; | 30 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
35 | const u32 hoff = (head * 0x800); | 31 | const u32 ctrl = 0x40000000 * enable | |
36 | const u32 hdmi = (head * 0x400); | 32 | max_ac_packet << 16 | |
37 | union { | 33 | rekey; |
38 | struct nv50_disp_sor_hdmi_pwr_v0 v0; | 34 | const u32 hoff = head * 0x800; |
39 | } *args = data; | 35 | const u32 hdmi = head * 0x400; |
40 | u32 ctrl; | 36 | struct packed_hdmi_infoframe avi_infoframe; |
41 | int ret = -ENOSYS; | 37 | struct packed_hdmi_infoframe vendor_infoframe; |
42 | 38 | ||
43 | nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); | 39 | pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); |
44 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 40 | pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); |
45 | nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " | ||
46 | "max_ac_packet %d rekey %d\n", | ||
47 | args->v0.version, args->v0.state, | ||
48 | args->v0.max_ac_packet, args->v0.rekey); | ||
49 | if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) | ||
50 | return -EINVAL; | ||
51 | ctrl = 0x40000000 * !!args->v0.state; | ||
52 | ctrl |= args->v0.max_ac_packet << 16; | ||
53 | ctrl |= args->v0.rekey; | ||
54 | } else | ||
55 | return ret; | ||
56 | 41 | ||
57 | if (!(ctrl & 0x40000000)) { | 42 | if (!(ctrl & 0x40000000)) { |
58 | nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); | 43 | nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000); |
44 | nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000); | ||
59 | nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); | 45 | nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); |
60 | nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); | 46 | nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); |
61 | return 0; | 47 | return; |
62 | } | 48 | } |
63 | 49 | ||
64 | /* AVI InfoFrame */ | 50 | /* AVI InfoFrame */ |
65 | nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); | 51 | nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000); |
66 | nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282); | 52 | if (avi_size) { |
67 | nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f); | 53 | nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header); |
68 | nvkm_wr32(device, 0x690010 + hdmi, 0x00000000); | 54 | nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low); |
69 | nvkm_wr32(device, 0x690014 + hdmi, 0x00000000); | 55 | nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high); |
70 | nvkm_wr32(device, 0x690018 + hdmi, 0x00000000); | 56 | nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low); |
71 | nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); | 57 | nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high); |
58 | nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001); | ||
59 | } | ||
60 | |||
61 | /* GENERIC(?) / Vendor InfoFrame? */ | ||
62 | nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000); | ||
63 | if (vendor_size) { | ||
64 | nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header); | ||
65 | nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low); | ||
66 | nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high); | ||
67 | /* Is there a second (or further?) set of subpack registers here? */ | ||
68 | nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001); | ||
69 | } | ||
70 | |||
72 | 71 | ||
73 | /* ??? InfoFrame? */ | 72 | /* ??? InfoFrame? */ |
74 | nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); | 73 | nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000); |
@@ -80,5 +79,4 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1) | |||
80 | 79 | ||
81 | /* HDMI_CTRL */ | 80 | /* HDMI_CTRL */ |
82 | nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); | 81 | nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl); |
83 | return 0; | ||
84 | } | 82 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c index f1afc16494b6..0993d223bb9c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigt215.c | |||
@@ -21,55 +21,42 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "hdmi.h" |
25 | #include "outp.h" | ||
26 | 25 | ||
27 | #include <core/client.h> | 26 | void |
28 | 27 | gt215_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet, | |
29 | #include <nvif/cl5070.h> | 28 | u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size) |
30 | #include <nvif/unpack.h> | ||
31 | |||
32 | int | ||
33 | gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) | ||
34 | { | 29 | { |
35 | struct nvkm_device *device = disp->base.engine.subdev.device; | 30 | struct nvkm_device *device = ior->disp->engine.subdev.device; |
36 | const u32 soff = outp->or * 0x800; | 31 | const u32 ctrl = 0x40000000 * enable | |
37 | union { | 32 | 0x1f000000 /* ??? */ | |
38 | struct nv50_disp_sor_hdmi_pwr_v0 v0; | 33 | max_ac_packet << 16 | |
39 | } *args = data; | 34 | rekey; |
40 | u32 ctrl; | 35 | const u32 soff = nv50_ior_base(ior); |
41 | int ret = -ENOSYS; | 36 | struct packed_hdmi_infoframe avi_infoframe; |
37 | struct packed_hdmi_infoframe vendor_infoframe; | ||
42 | 38 | ||
43 | nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); | 39 | pack_hdmi_infoframe(&avi_infoframe, avi, avi_size); |
44 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 40 | pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size); |
45 | nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " | ||
46 | "max_ac_packet %d rekey %d\n", | ||
47 | args->v0.version, args->v0.state, | ||
48 | args->v0.max_ac_packet, args->v0.rekey); | ||
49 | if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) | ||
50 | return -EINVAL; | ||
51 | ctrl = 0x40000000 * !!args->v0.state; | ||
52 | ctrl |= args->v0.max_ac_packet << 16; | ||
53 | ctrl |= args->v0.rekey; | ||
54 | ctrl |= 0x1f000000; /* ??? */ | ||
55 | } else | ||
56 | return ret; | ||
57 | 41 | ||
58 | if (!(ctrl & 0x40000000)) { | 42 | if (!(ctrl & 0x40000000)) { |
59 | nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); | 43 | nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000); |
44 | nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000); | ||
60 | nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); | 45 | nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); |
61 | nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); | 46 | nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); |
62 | return 0; | 47 | return; |
63 | } | 48 | } |
64 | 49 | ||
65 | /* AVI InfoFrame */ | 50 | /* AVI InfoFrame */ |
66 | nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); | 51 | nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000); |
67 | nvkm_wr32(device, 0x61c528 + soff, 0x000d0282); | 52 | if (avi_size) { |
68 | nvkm_wr32(device, 0x61c52c + soff, 0x0000006f); | 53 | nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header); |
69 | nvkm_wr32(device, 0x61c530 + soff, 0x00000000); | 54 | nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low); |
70 | nvkm_wr32(device, 0x61c534 + soff, 0x00000000); | 55 | nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high); |
71 | nvkm_wr32(device, 0x61c538 + soff, 0x00000000); | 56 | nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low); |
72 | nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); | 57 | nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high); |
58 | nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001); | ||
59 | } | ||
73 | 60 | ||
74 | /* Audio InfoFrame */ | 61 | /* Audio InfoFrame */ |
75 | nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); | 62 | nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000); |
@@ -78,6 +65,18 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) | |||
78 | nvkm_wr32(device, 0x61c510 + soff, 0x00000000); | 65 | nvkm_wr32(device, 0x61c510 + soff, 0x00000000); |
79 | nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); | 66 | nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001); |
80 | 67 | ||
68 | /* Vendor InfoFrame */ | ||
69 | nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000); | ||
70 | if (vendor_size) { | ||
71 | nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header); | ||
72 | nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low); | ||
73 | nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high); | ||
74 | /* Is there a second (or up to fourth?) set of subpack registers here? */ | ||
75 | /* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */ | ||
76 | /* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */ | ||
77 | nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001); | ||
78 | } | ||
79 | |||
81 | nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ | 80 | nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ |
82 | nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ | 81 | nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ |
83 | nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ | 82 | nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ |
@@ -89,5 +88,4 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1) | |||
89 | 88 | ||
90 | /* HDMI_CTRL */ | 89 | /* HDMI_CTRL */ |
91 | nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl); | 90 | nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl); |
92 | return 0; | ||
93 | } | 91 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c new file mode 100644 index 000000000000..5c557f3e6c2c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 <bskeggs@redhat.com> | ||
23 | */ | ||
24 | #include "head.h" | ||
25 | |||
26 | #include <core/client.h> | ||
27 | |||
28 | #include <nvif/cl0046.h> | ||
29 | #include <nvif/unpack.h> | ||
30 | |||
31 | struct nvkm_head * | ||
32 | nvkm_head_find(struct nvkm_disp *disp, int id) | ||
33 | { | ||
34 | struct nvkm_head *head; | ||
35 | list_for_each_entry(head, &disp->head, head) { | ||
36 | if (head->id == id) | ||
37 | return head; | ||
38 | } | ||
39 | return NULL; | ||
40 | } | ||
41 | |||
42 | int | ||
43 | nvkm_head_mthd_scanoutpos(struct nvkm_object *object, | ||
44 | struct nvkm_head *head, void *data, u32 size) | ||
45 | { | ||
46 | union { | ||
47 | struct nv04_disp_scanoutpos_v0 v0; | ||
48 | } *args = data; | ||
49 | int ret = -ENOSYS; | ||
50 | |||
51 | nvif_ioctl(object, "head scanoutpos size %d\n", size); | ||
52 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
53 | nvif_ioctl(object, "head scanoutpos vers %d\n", | ||
54 | args->v0.version); | ||
55 | |||
56 | head->func->state(head, &head->arm); | ||
57 | args->v0.vtotal = head->arm.vtotal; | ||
58 | args->v0.vblanks = head->arm.vblanks; | ||
59 | args->v0.vblanke = head->arm.vblanke; | ||
60 | args->v0.htotal = head->arm.htotal; | ||
61 | args->v0.hblanks = head->arm.hblanks; | ||
62 | args->v0.hblanke = head->arm.hblanke; | ||
63 | |||
64 | /* We don't support reading htotal/vtotal on pre-NV50 VGA, | ||
65 | * so we have to give up and trigger the timestamping | ||
66 | * fallback in the drm core. | ||
67 | */ | ||
68 | if (!args->v0.vtotal || !args->v0.htotal) | ||
69 | return -ENOTSUPP; | ||
70 | |||
71 | args->v0.time[0] = ktime_to_ns(ktime_get()); | ||
72 | head->func->rgpos(head, &args->v0.hline, &args->v0.vline); | ||
73 | args->v0.time[1] = ktime_to_ns(ktime_get()); | ||
74 | } else | ||
75 | return ret; | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | void | ||
81 | nvkm_head_del(struct nvkm_head **phead) | ||
82 | { | ||
83 | struct nvkm_head *head = *phead; | ||
84 | if (head) { | ||
85 | HEAD_DBG(head, "dtor"); | ||
86 | list_del(&head->head); | ||
87 | kfree(*phead); | ||
88 | *phead = NULL; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | int | ||
93 | nvkm_head_new_(const struct nvkm_head_func *func, | ||
94 | struct nvkm_disp *disp, int id) | ||
95 | { | ||
96 | struct nvkm_head *head; | ||
97 | if (!(head = kzalloc(sizeof(*head), GFP_KERNEL))) | ||
98 | return -ENOMEM; | ||
99 | head->func = func; | ||
100 | head->disp = disp; | ||
101 | head->id = id; | ||
102 | list_add_tail(&head->head, &disp->head); | ||
103 | HEAD_DBG(head, "ctor"); | ||
104 | return 0; | ||
105 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h new file mode 100644 index 000000000000..b04c49d2eeeb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h | |||
@@ -0,0 +1,56 @@ | |||
1 | #ifndef __NVKM_DISP_HEAD_H__ | ||
2 | #define __NVKM_DISP_HEAD_H__ | ||
3 | #include "priv.h" | ||
4 | |||
5 | struct nvkm_head { | ||
6 | const struct nvkm_head_func *func; | ||
7 | struct nvkm_disp *disp; | ||
8 | int id; | ||
9 | |||
10 | struct list_head head; | ||
11 | |||
12 | struct nvkm_head_state { | ||
13 | u16 htotal; | ||
14 | u16 hsynce; | ||
15 | u16 hblanke; | ||
16 | u16 hblanks; | ||
17 | u16 vtotal; | ||
18 | u16 vsynce; | ||
19 | u16 vblanke; | ||
20 | u16 vblanks; | ||
21 | u32 hz; | ||
22 | |||
23 | /* Prior to GF119, these are set by the OR. */ | ||
24 | struct { | ||
25 | u8 depth; | ||
26 | } or; | ||
27 | } arm, asy; | ||
28 | }; | ||
29 | |||
30 | int nvkm_head_new_(const struct nvkm_head_func *, struct nvkm_disp *, int id); | ||
31 | void nvkm_head_del(struct nvkm_head **); | ||
32 | int nvkm_head_mthd_scanoutpos(struct nvkm_object *, | ||
33 | struct nvkm_head *, void *, u32); | ||
34 | struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id); | ||
35 | |||
36 | struct nvkm_head_func { | ||
37 | void (*state)(struct nvkm_head *, struct nvkm_head_state *); | ||
38 | void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline); | ||
39 | void (*rgclk)(struct nvkm_head *, int div); | ||
40 | void (*vblank_get)(struct nvkm_head *); | ||
41 | void (*vblank_put)(struct nvkm_head *); | ||
42 | }; | ||
43 | |||
44 | void nv50_head_rgpos(struct nvkm_head *, u16 *, u16 *); | ||
45 | |||
46 | #define HEAD_MSG(h,l,f,a...) do { \ | ||
47 | struct nvkm_head *_h = (h); \ | ||
48 | nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \ | ||
49 | } while(0) | ||
50 | #define HEAD_WARN(h,f,a...) HEAD_MSG((h), warn, f, ##a) | ||
51 | #define HEAD_DBG(h,f,a...) HEAD_MSG((h), debug, f, ##a) | ||
52 | |||
53 | int nv04_head_new(struct nvkm_disp *, int id); | ||
54 | int nv50_head_new(struct nvkm_disp *, int id); | ||
55 | int gf119_head_new(struct nvkm_disp *, int id); | ||
56 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c new file mode 100644 index 000000000000..b33552757647 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headgf119.c | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 <bskeggs@redhat.com> | ||
23 | */ | ||
24 | #include "head.h" | ||
25 | |||
26 | static void | ||
27 | gf119_head_vblank_put(struct nvkm_head *head) | ||
28 | { | ||
29 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
30 | const u32 hoff = head->id * 0x800; | ||
31 | nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000000); | ||
32 | } | ||
33 | |||
34 | static void | ||
35 | gf119_head_vblank_get(struct nvkm_head *head) | ||
36 | { | ||
37 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
38 | const u32 hoff = head->id * 0x800; | ||
39 | nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001); | ||
40 | } | ||
41 | |||
42 | static void | ||
43 | gf119_head_rgclk(struct nvkm_head *head, int div) | ||
44 | { | ||
45 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
46 | nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div); | ||
47 | } | ||
48 | |||
49 | static void | ||
50 | gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state) | ||
51 | { | ||
52 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
53 | const u32 hoff = (state == &head->asy) * 0x20000 + head->id * 0x300; | ||
54 | u32 data; | ||
55 | |||
56 | data = nvkm_rd32(device, 0x640414 + hoff); | ||
57 | state->vtotal = (data & 0xffff0000) >> 16; | ||
58 | state->htotal = (data & 0x0000ffff); | ||
59 | data = nvkm_rd32(device, 0x640418 + hoff); | ||
60 | state->vsynce = (data & 0xffff0000) >> 16; | ||
61 | state->hsynce = (data & 0x0000ffff); | ||
62 | data = nvkm_rd32(device, 0x64041c + hoff); | ||
63 | state->vblanke = (data & 0xffff0000) >> 16; | ||
64 | state->hblanke = (data & 0x0000ffff); | ||
65 | data = nvkm_rd32(device, 0x640420 + hoff); | ||
66 | state->vblanks = (data & 0xffff0000) >> 16; | ||
67 | state->hblanks = (data & 0x0000ffff); | ||
68 | state->hz = nvkm_rd32(device, 0x640450 + hoff); | ||
69 | |||
70 | data = nvkm_rd32(device, 0x640404 + hoff); | ||
71 | switch ((data & 0x000003c0) >> 6) { | ||
72 | case 6: state->or.depth = 30; break; | ||
73 | case 5: state->or.depth = 24; break; | ||
74 | case 2: state->or.depth = 18; break; | ||
75 | case 0: state->or.depth = 18; break; /*XXX: "default" */ | ||
76 | default: | ||
77 | state->or.depth = 18; | ||
78 | WARN_ON(1); | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | static const struct nvkm_head_func | ||
84 | gf119_head = { | ||
85 | .state = gf119_head_state, | ||
86 | .rgpos = nv50_head_rgpos, | ||
87 | .rgclk = gf119_head_rgclk, | ||
88 | .vblank_get = gf119_head_vblank_get, | ||
89 | .vblank_put = gf119_head_vblank_put, | ||
90 | }; | ||
91 | |||
92 | int | ||
93 | gf119_head_new(struct nvkm_disp *disp, int id) | ||
94 | { | ||
95 | return nvkm_head_new_(&gf119_head, disp, id); | ||
96 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c new file mode 100644 index 000000000000..dcf459282aa1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv04.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 <bskeggs@redhat.com> | ||
23 | */ | ||
24 | #include "head.h" | ||
25 | |||
26 | static void | ||
27 | nv04_head_vblank_put(struct nvkm_head *head) | ||
28 | { | ||
29 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
30 | nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000000); | ||
31 | } | ||
32 | |||
33 | static void | ||
34 | nv04_head_vblank_get(struct nvkm_head *head) | ||
35 | { | ||
36 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
37 | nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000001); | ||
38 | } | ||
39 | |||
40 | static void | ||
41 | nv04_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) | ||
42 | { | ||
43 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
44 | u32 data = nvkm_rd32(device, 0x600868 + (head->id * 0x2000)); | ||
45 | *hline = (data & 0xffff0000) >> 16; | ||
46 | *vline = (data & 0x0000ffff); | ||
47 | } | ||
48 | |||
49 | static void | ||
50 | nv04_head_state(struct nvkm_head *head, struct nvkm_head_state *state) | ||
51 | { | ||
52 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
53 | const u32 hoff = head->id * 0x0200; | ||
54 | state->vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0x0000ffff; | ||
55 | state->vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0x0000ffff; | ||
56 | state->vblanke = state->vtotal - 1; | ||
57 | state->hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0x0000ffff; | ||
58 | state->htotal = nvkm_rd32(device, 0x680824 + hoff) & 0x0000ffff; | ||
59 | state->hblanke = state->htotal - 1; | ||
60 | } | ||
61 | |||
62 | static const struct nvkm_head_func | ||
63 | nv04_head = { | ||
64 | .state = nv04_head_state, | ||
65 | .rgpos = nv04_head_rgpos, | ||
66 | .vblank_get = nv04_head_vblank_get, | ||
67 | .vblank_put = nv04_head_vblank_put, | ||
68 | }; | ||
69 | |||
70 | int | ||
71 | nv04_head_new(struct nvkm_disp *disp, int id) | ||
72 | { | ||
73 | return nvkm_head_new_(&nv04_head, disp, id); | ||
74 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c new file mode 100644 index 000000000000..c80d06d5168f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/headnv50.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 <bskeggs@redhat.com> | ||
23 | */ | ||
24 | #include "head.h" | ||
25 | |||
26 | static void | ||
27 | nv50_head_vblank_put(struct nvkm_head *head) | ||
28 | { | ||
29 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
30 | nvkm_mask(device, 0x61002c, (4 << head->id), 0); | ||
31 | } | ||
32 | |||
33 | static void | ||
34 | nv50_head_vblank_get(struct nvkm_head *head) | ||
35 | { | ||
36 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
37 | nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id)); | ||
38 | } | ||
39 | |||
40 | static void | ||
41 | nv50_head_rgclk(struct nvkm_head *head, int div) | ||
42 | { | ||
43 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
44 | nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div); | ||
45 | } | ||
46 | |||
47 | void | ||
48 | nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) | ||
49 | { | ||
50 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
51 | const u32 hoff = head->id * 0x800; | ||
52 | /* vline read locks hline. */ | ||
53 | *vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff; | ||
54 | *hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff; | ||
55 | } | ||
56 | |||
57 | static void | ||
58 | nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state) | ||
59 | { | ||
60 | struct nvkm_device *device = head->disp->engine.subdev.device; | ||
61 | const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4; | ||
62 | u32 data; | ||
63 | |||
64 | data = nvkm_rd32(device, 0x610ae8 + hoff); | ||
65 | state->vblanke = (data & 0xffff0000) >> 16; | ||
66 | state->hblanke = (data & 0x0000ffff); | ||
67 | data = nvkm_rd32(device, 0x610af0 + hoff); | ||
68 | state->vblanks = (data & 0xffff0000) >> 16; | ||
69 | state->hblanks = (data & 0x0000ffff); | ||
70 | data = nvkm_rd32(device, 0x610af8 + hoff); | ||
71 | state->vtotal = (data & 0xffff0000) >> 16; | ||
72 | state->htotal = (data & 0x0000ffff); | ||
73 | data = nvkm_rd32(device, 0x610b00 + hoff); | ||
74 | state->vsynce = (data & 0xffff0000) >> 16; | ||
75 | state->hsynce = (data & 0x0000ffff); | ||
76 | state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000; | ||
77 | } | ||
78 | |||
79 | static const struct nvkm_head_func | ||
80 | nv50_head = { | ||
81 | .state = nv50_head_state, | ||
82 | .rgpos = nv50_head_rgpos, | ||
83 | .rgclk = nv50_head_rgclk, | ||
84 | .vblank_get = nv50_head_vblank_get, | ||
85 | .vblank_put = nv50_head_vblank_put, | ||
86 | }; | ||
87 | |||
88 | int | ||
89 | nv50_head_new(struct nvkm_disp *disp, int id) | ||
90 | { | ||
91 | return nvkm_head_new_(&nv50_head, disp, id); | ||
92 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c new file mode 100644 index 000000000000..a475ea56795c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 <bskeggs@redhat.com> | ||
23 | */ | ||
24 | #include "ior.h" | ||
25 | |||
26 | static const char * | ||
27 | nvkm_ior_name[] = { | ||
28 | [DAC] = "DAC", | ||
29 | [SOR] = "SOR", | ||
30 | [PIOR] = "PIOR", | ||
31 | }; | ||
32 | |||
33 | struct nvkm_ior * | ||
34 | nvkm_ior_find(struct nvkm_disp *disp, enum nvkm_ior_type type, int id) | ||
35 | { | ||
36 | struct nvkm_ior *ior; | ||
37 | list_for_each_entry(ior, &disp->ior, head) { | ||
38 | if (ior->type == type && (id < 0 || ior->id == id)) | ||
39 | return ior; | ||
40 | } | ||
41 | return NULL; | ||
42 | } | ||
43 | |||
44 | void | ||
45 | nvkm_ior_del(struct nvkm_ior **pior) | ||
46 | { | ||
47 | struct nvkm_ior *ior = *pior; | ||
48 | if (ior) { | ||
49 | IOR_DBG(ior, "dtor"); | ||
50 | list_del(&ior->head); | ||
51 | kfree(*pior); | ||
52 | *pior = NULL; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | int | ||
57 | nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, | ||
58 | enum nvkm_ior_type type, int id) | ||
59 | { | ||
60 | struct nvkm_ior *ior; | ||
61 | if (!(ior = kzalloc(sizeof(*ior), GFP_KERNEL))) | ||
62 | return -ENOMEM; | ||
63 | ior->func = func; | ||
64 | ior->disp = disp; | ||
65 | ior->type = type; | ||
66 | ior->id = id; | ||
67 | snprintf(ior->name, sizeof(ior->name), "%s-%d", | ||
68 | nvkm_ior_name[ior->type], ior->id); | ||
69 | list_add_tail(&ior->head, &disp->ior); | ||
70 | IOR_DBG(ior, "ctor"); | ||
71 | return 0; | ||
72 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h new file mode 100644 index 000000000000..a24312fb0228 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h | |||
@@ -0,0 +1,169 @@ | |||
1 | #ifndef __NVKM_DISP_IOR_H__ | ||
2 | #define __NVKM_DISP_IOR_H__ | ||
3 | #include "priv.h" | ||
4 | struct nvkm_i2c_aux; | ||
5 | |||
6 | struct nvkm_ior { | ||
7 | const struct nvkm_ior_func *func; | ||
8 | struct nvkm_disp *disp; | ||
9 | enum nvkm_ior_type { | ||
10 | DAC, | ||
11 | SOR, | ||
12 | PIOR, | ||
13 | } type; | ||
14 | int id; | ||
15 | char name[8]; | ||
16 | |||
17 | struct list_head head; | ||
18 | |||
19 | struct nvkm_ior_state { | ||
20 | struct nvkm_outp *outp; | ||
21 | unsigned rgdiv; | ||
22 | unsigned proto_evo:4; | ||
23 | enum nvkm_ior_proto { | ||
24 | CRT, | ||
25 | TMDS, | ||
26 | LVDS, | ||
27 | DP, | ||
28 | UNKNOWN | ||
29 | } proto:3; | ||
30 | unsigned link:2; | ||
31 | unsigned head:4; | ||
32 | } arm, asy; | ||
33 | |||
34 | /* Armed DP state. */ | ||
35 | struct { | ||
36 | bool mst; | ||
37 | bool ef; | ||
38 | u8 nr; | ||
39 | u8 bw; | ||
40 | } dp; | ||
41 | }; | ||
42 | |||
43 | struct nvkm_ior_func { | ||
44 | struct { | ||
45 | int (*get)(struct nvkm_outp *, int *link); | ||
46 | void (*set)(struct nvkm_outp *, struct nvkm_ior *); | ||
47 | } route; | ||
48 | |||
49 | void (*state)(struct nvkm_ior *, struct nvkm_ior_state *); | ||
50 | void (*power)(struct nvkm_ior *, bool normal, bool pu, | ||
51 | bool data, bool vsync, bool hsync); | ||
52 | int (*sense)(struct nvkm_ior *, u32 loadval); | ||
53 | void (*clock)(struct nvkm_ior *); | ||
54 | void (*war_2)(struct nvkm_ior *); | ||
55 | void (*war_3)(struct nvkm_ior *); | ||
56 | |||
57 | struct { | ||
58 | void (*ctrl)(struct nvkm_ior *, int head, bool enable, | ||
59 | u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size, | ||
60 | u8 *vendor, u8 vendor_size); | ||
61 | } hdmi; | ||
62 | |||
63 | struct { | ||
64 | u8 lanes[4]; | ||
65 | int (*links)(struct nvkm_ior *, struct nvkm_i2c_aux *); | ||
66 | void (*power)(struct nvkm_ior *, int nr); | ||
67 | void (*pattern)(struct nvkm_ior *, int pattern); | ||
68 | void (*drive)(struct nvkm_ior *, int ln, int pc, | ||
69 | int dc, int pe, int tx_pu); | ||
70 | void (*vcpi)(struct nvkm_ior *, int head, u8 slot, | ||
71 | u8 slot_nr, u16 pbn, u16 aligned); | ||
72 | void (*audio)(struct nvkm_ior *, int head, bool enable); | ||
73 | void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v); | ||
74 | void (*activesym)(struct nvkm_ior *, int head, | ||
75 | u8 TU, u8 VTUa, u8 VTUf, u8 VTUi); | ||
76 | void (*watermark)(struct nvkm_ior *, int head, u8 watermark); | ||
77 | } dp; | ||
78 | |||
79 | struct { | ||
80 | void (*hpd)(struct nvkm_ior *, int head, bool present); | ||
81 | void (*eld)(struct nvkm_ior *, u8 *data, u8 size); | ||
82 | } hda; | ||
83 | }; | ||
84 | |||
85 | int nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *, | ||
86 | enum nvkm_ior_type type, int id); | ||
87 | void nvkm_ior_del(struct nvkm_ior **); | ||
88 | struct nvkm_ior *nvkm_ior_find(struct nvkm_disp *, enum nvkm_ior_type, int id); | ||
89 | |||
90 | static inline u32 | ||
91 | nv50_ior_base(struct nvkm_ior *ior) | ||
92 | { | ||
93 | return ior->id * 0x800; | ||
94 | } | ||
95 | |||
96 | void nv50_dac_power(struct nvkm_ior *, bool, bool, bool, bool, bool); | ||
97 | int nv50_dac_sense(struct nvkm_ior *, u32); | ||
98 | |||
99 | void nv50_pior_depth(struct nvkm_ior *, struct nvkm_ior_state *, u32 ctrl); | ||
100 | |||
101 | static inline u32 | ||
102 | nv50_sor_link(struct nvkm_ior *ior) | ||
103 | { | ||
104 | return nv50_ior_base(ior) + ((ior->asy.link == 2) * 0x80); | ||
105 | } | ||
106 | |||
107 | int nv50_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id); | ||
108 | void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); | ||
109 | void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool); | ||
110 | void nv50_sor_clock(struct nvkm_ior *); | ||
111 | |||
112 | void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); | ||
113 | int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *); | ||
114 | void g94_sor_dp_power(struct nvkm_ior *, int); | ||
115 | void g94_sor_dp_pattern(struct nvkm_ior *, int); | ||
116 | void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); | ||
117 | void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); | ||
118 | void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8); | ||
119 | void g94_sor_dp_watermark(struct nvkm_ior *, int, u8); | ||
120 | |||
121 | void gt215_sor_dp_audio(struct nvkm_ior *, int, bool); | ||
122 | |||
123 | int gf119_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id); | ||
124 | void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); | ||
125 | void gf119_sor_clock(struct nvkm_ior *); | ||
126 | int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *); | ||
127 | void gf119_sor_dp_pattern(struct nvkm_ior *, int); | ||
128 | void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); | ||
129 | void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16); | ||
130 | void gf119_sor_dp_audio(struct nvkm_ior *, int, bool); | ||
131 | void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); | ||
132 | void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8); | ||
133 | |||
134 | void gm107_sor_dp_pattern(struct nvkm_ior *, int); | ||
135 | |||
136 | void g84_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); | ||
137 | void gt215_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); | ||
138 | void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); | ||
139 | void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8); | ||
140 | |||
141 | void gt215_hda_hpd(struct nvkm_ior *, int, bool); | ||
142 | void gt215_hda_eld(struct nvkm_ior *, u8 *, u8); | ||
143 | |||
144 | void gf119_hda_hpd(struct nvkm_ior *, int, bool); | ||
145 | void gf119_hda_eld(struct nvkm_ior *, u8 *, u8); | ||
146 | |||
147 | #define IOR_MSG(i,l,f,a...) do { \ | ||
148 | struct nvkm_ior *_ior = (i); \ | ||
149 | nvkm_##l(&_ior->disp->engine.subdev, "%s: "f, _ior->name, ##a); \ | ||
150 | } while(0) | ||
151 | #define IOR_WARN(i,f,a...) IOR_MSG((i), warn, f, ##a) | ||
152 | #define IOR_DBG(i,f,a...) IOR_MSG((i), debug, f, ##a) | ||
153 | |||
154 | int nv50_dac_new(struct nvkm_disp *, int); | ||
155 | int gf119_dac_new(struct nvkm_disp *, int); | ||
156 | |||
157 | int nv50_pior_new(struct nvkm_disp *, int); | ||
158 | |||
159 | int nv50_sor_new(struct nvkm_disp *, int); | ||
160 | int g84_sor_new(struct nvkm_disp *, int); | ||
161 | int g94_sor_new(struct nvkm_disp *, int); | ||
162 | int mcp77_sor_new(struct nvkm_disp *, int); | ||
163 | int gt215_sor_new(struct nvkm_disp *, int); | ||
164 | int mcp89_sor_new(struct nvkm_disp *, int); | ||
165 | int gf119_sor_new(struct nvkm_disp *, int); | ||
166 | int gk104_sor_new(struct nvkm_disp *, int); | ||
167 | int gm107_sor_new(struct nvkm_disp *, int); | ||
168 | int gm200_sor_new(struct nvkm_disp *, int); | ||
169 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c new file mode 100644 index 000000000000..d7e0fbb12bf1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp77.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "nv50.h" | ||
23 | #include "head.h" | ||
24 | #include "ior.h" | ||
25 | #include "rootnv50.h" | ||
26 | |||
27 | static const struct nv50_disp_func | ||
28 | mcp77_disp = { | ||
29 | .intr = nv50_disp_intr, | ||
30 | .uevent = &nv50_disp_chan_uevent, | ||
31 | .super = nv50_disp_super, | ||
32 | .root = &g94_disp_root_oclass, | ||
33 | .head.new = nv50_head_new, | ||
34 | .dac = { .nr = 3, .new = nv50_dac_new }, | ||
35 | .sor = { .nr = 4, .new = mcp77_sor_new }, | ||
36 | .pior = { .nr = 3, .new = nv50_pior_new }, | ||
37 | }; | ||
38 | |||
39 | int | ||
40 | mcp77_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) | ||
41 | { | ||
42 | return nv50_disp_new_(&mcp77_disp, device, index, 2, pdisp); | ||
43 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c new file mode 100644 index 000000000000..7b75c57c12ed --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "nv50.h" | ||
23 | #include "head.h" | ||
24 | #include "ior.h" | ||
25 | #include "rootnv50.h" | ||
26 | |||
27 | static const struct nv50_disp_func | ||
28 | mcp89_disp = { | ||
29 | .intr = nv50_disp_intr, | ||
30 | .uevent = &nv50_disp_chan_uevent, | ||
31 | .super = nv50_disp_super, | ||
32 | .root = >215_disp_root_oclass, | ||
33 | .head.new = nv50_head_new, | ||
34 | .dac = { .nr = 3, .new = nv50_dac_new }, | ||
35 | .sor = { .nr = 4, .new = mcp89_sor_new }, | ||
36 | .pior = { .nr = 3, .new = nv50_pior_new }, | ||
37 | }; | ||
38 | |||
39 | int | ||
40 | mcp89_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) | ||
41 | { | ||
42 | return nv50_disp_new_(&mcp89_disp, device, index, 2, pdisp); | ||
43 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c index 67254ce6f83f..b780ba1a3bc7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv04.c | |||
@@ -22,6 +22,7 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "priv.h" | 24 | #include "priv.h" |
25 | #include "head.h" | ||
25 | 26 | ||
26 | static const struct nvkm_disp_oclass * | 27 | static const struct nvkm_disp_oclass * |
27 | nv04_disp_root(struct nvkm_disp *disp) | 28 | nv04_disp_root(struct nvkm_disp *disp) |
@@ -30,20 +31,6 @@ nv04_disp_root(struct nvkm_disp *disp) | |||
30 | } | 31 | } |
31 | 32 | ||
32 | static void | 33 | static void |
33 | nv04_disp_vblank_init(struct nvkm_disp *disp, int head) | ||
34 | { | ||
35 | struct nvkm_device *device = disp->engine.subdev.device; | ||
36 | nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000001); | ||
37 | } | ||
38 | |||
39 | static void | ||
40 | nv04_disp_vblank_fini(struct nvkm_disp *disp, int head) | ||
41 | { | ||
42 | struct nvkm_device *device = disp->engine.subdev.device; | ||
43 | nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000000); | ||
44 | } | ||
45 | |||
46 | static void | ||
47 | nv04_disp_intr(struct nvkm_disp *disp) | 34 | nv04_disp_intr(struct nvkm_disp *disp) |
48 | { | 35 | { |
49 | struct nvkm_subdev *subdev = &disp->engine.subdev; | 36 | struct nvkm_subdev *subdev = &disp->engine.subdev; |
@@ -74,12 +61,22 @@ static const struct nvkm_disp_func | |||
74 | nv04_disp = { | 61 | nv04_disp = { |
75 | .intr = nv04_disp_intr, | 62 | .intr = nv04_disp_intr, |
76 | .root = nv04_disp_root, | 63 | .root = nv04_disp_root, |
77 | .head.vblank_init = nv04_disp_vblank_init, | ||
78 | .head.vblank_fini = nv04_disp_vblank_fini, | ||
79 | }; | 64 | }; |
80 | 65 | ||
81 | int | 66 | int |
82 | nv04_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) | 67 | nv04_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp) |
83 | { | 68 | { |
84 | return nvkm_disp_new_(&nv04_disp, device, index, 2, pdisp); | 69 | int ret, i; |
70 | |||
71 | ret = nvkm_disp_new_(&nv04_disp, device, index, pdisp); | ||
72 | if (ret) | ||
73 | return ret; | ||
74 | |||
75 | for (i = 0; i < 2; i++) { | ||
76 | ret = nv04_head_new(*pdisp, i); | ||
77 | if (ret) | ||
78 | return ret; | ||
79 | } | ||
80 | |||
81 | return 0; | ||
85 | } | 82 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index 0db8efbf1c2e..0c570dbd3021 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "nv50.h" |
25 | #include "head.h" | ||
26 | #include "ior.h" | ||
25 | #include "rootnv50.h" | 27 | #include "rootnv50.h" |
26 | 28 | ||
27 | #include <core/client.h> | 29 | #include <core/client.h> |
@@ -40,77 +42,6 @@ nv50_disp_root_(struct nvkm_disp *base) | |||
40 | return nv50_disp(base)->func->root; | 42 | return nv50_disp(base)->func->root; |
41 | } | 43 | } |
42 | 44 | ||
43 | static int | ||
44 | nv50_disp_outp_internal_crt_(struct nvkm_disp *base, int index, | ||
45 | struct dcb_output *dcb, struct nvkm_output **poutp) | ||
46 | { | ||
47 | struct nv50_disp *disp = nv50_disp(base); | ||
48 | return disp->func->outp.internal.crt(base, index, dcb, poutp); | ||
49 | } | ||
50 | |||
51 | static int | ||
52 | nv50_disp_outp_internal_tmds_(struct nvkm_disp *base, int index, | ||
53 | struct dcb_output *dcb, | ||
54 | struct nvkm_output **poutp) | ||
55 | { | ||
56 | struct nv50_disp *disp = nv50_disp(base); | ||
57 | return disp->func->outp.internal.tmds(base, index, dcb, poutp); | ||
58 | } | ||
59 | |||
60 | static int | ||
61 | nv50_disp_outp_internal_lvds_(struct nvkm_disp *base, int index, | ||
62 | struct dcb_output *dcb, | ||
63 | struct nvkm_output **poutp) | ||
64 | { | ||
65 | struct nv50_disp *disp = nv50_disp(base); | ||
66 | return disp->func->outp.internal.lvds(base, index, dcb, poutp); | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | nv50_disp_outp_internal_dp_(struct nvkm_disp *base, int index, | ||
71 | struct dcb_output *dcb, struct nvkm_output **poutp) | ||
72 | { | ||
73 | struct nv50_disp *disp = nv50_disp(base); | ||
74 | if (disp->func->outp.internal.dp) | ||
75 | return disp->func->outp.internal.dp(base, index, dcb, poutp); | ||
76 | return -ENODEV; | ||
77 | } | ||
78 | |||
79 | static int | ||
80 | nv50_disp_outp_external_tmds_(struct nvkm_disp *base, int index, | ||
81 | struct dcb_output *dcb, | ||
82 | struct nvkm_output **poutp) | ||
83 | { | ||
84 | struct nv50_disp *disp = nv50_disp(base); | ||
85 | if (disp->func->outp.external.tmds) | ||
86 | return disp->func->outp.external.tmds(base, index, dcb, poutp); | ||
87 | return -ENODEV; | ||
88 | } | ||
89 | |||
90 | static int | ||
91 | nv50_disp_outp_external_dp_(struct nvkm_disp *base, int index, | ||
92 | struct dcb_output *dcb, struct nvkm_output **poutp) | ||
93 | { | ||
94 | struct nv50_disp *disp = nv50_disp(base); | ||
95 | if (disp->func->outp.external.dp) | ||
96 | return disp->func->outp.external.dp(base, index, dcb, poutp); | ||
97 | return -ENODEV; | ||
98 | } | ||
99 | |||
100 | static void | ||
101 | nv50_disp_vblank_fini_(struct nvkm_disp *base, int head) | ||
102 | { | ||
103 | struct nv50_disp *disp = nv50_disp(base); | ||
104 | disp->func->head.vblank_fini(disp, head); | ||
105 | } | ||
106 | |||
107 | static void | ||
108 | nv50_disp_vblank_init_(struct nvkm_disp *base, int head) | ||
109 | { | ||
110 | struct nv50_disp *disp = nv50_disp(base); | ||
111 | disp->func->head.vblank_init(disp, head); | ||
112 | } | ||
113 | |||
114 | static void | 45 | static void |
115 | nv50_disp_intr_(struct nvkm_disp *base) | 46 | nv50_disp_intr_(struct nvkm_disp *base) |
116 | { | 47 | { |
@@ -123,6 +54,8 @@ nv50_disp_dtor_(struct nvkm_disp *base) | |||
123 | { | 54 | { |
124 | struct nv50_disp *disp = nv50_disp(base); | 55 | struct nv50_disp *disp = nv50_disp(base); |
125 | nvkm_event_fini(&disp->uevent); | 56 | nvkm_event_fini(&disp->uevent); |
57 | if (disp->wq) | ||
58 | destroy_workqueue(disp->wq); | ||
126 | return disp; | 59 | return disp; |
127 | } | 60 | } |
128 | 61 | ||
@@ -131,14 +64,6 @@ nv50_disp_ = { | |||
131 | .dtor = nv50_disp_dtor_, | 64 | .dtor = nv50_disp_dtor_, |
132 | .intr = nv50_disp_intr_, | 65 | .intr = nv50_disp_intr_, |
133 | .root = nv50_disp_root_, | 66 | .root = nv50_disp_root_, |
134 | .outp.internal.crt = nv50_disp_outp_internal_crt_, | ||
135 | .outp.internal.tmds = nv50_disp_outp_internal_tmds_, | ||
136 | .outp.internal.lvds = nv50_disp_outp_internal_lvds_, | ||
137 | .outp.internal.dp = nv50_disp_outp_internal_dp_, | ||
138 | .outp.external.tmds = nv50_disp_outp_external_tmds_, | ||
139 | .outp.external.dp = nv50_disp_outp_external_dp_, | ||
140 | .head.vblank_init = nv50_disp_vblank_init_, | ||
141 | .head.vblank_fini = nv50_disp_vblank_fini_, | ||
142 | }; | 67 | }; |
143 | 68 | ||
144 | int | 69 | int |
@@ -146,517 +71,227 @@ nv50_disp_new_(const struct nv50_disp_func *func, struct nvkm_device *device, | |||
146 | int index, int heads, struct nvkm_disp **pdisp) | 71 | int index, int heads, struct nvkm_disp **pdisp) |
147 | { | 72 | { |
148 | struct nv50_disp *disp; | 73 | struct nv50_disp *disp; |
149 | int ret; | 74 | int ret, i; |
150 | 75 | ||
151 | if (!(disp = kzalloc(sizeof(*disp), GFP_KERNEL))) | 76 | if (!(disp = kzalloc(sizeof(*disp), GFP_KERNEL))) |
152 | return -ENOMEM; | 77 | return -ENOMEM; |
153 | INIT_WORK(&disp->supervisor, func->super); | ||
154 | disp->func = func; | 78 | disp->func = func; |
155 | *pdisp = &disp->base; | 79 | *pdisp = &disp->base; |
156 | 80 | ||
157 | ret = nvkm_disp_ctor(&nv50_disp_, device, index, heads, &disp->base); | 81 | ret = nvkm_disp_ctor(&nv50_disp_, device, index, &disp->base); |
158 | if (ret) | 82 | if (ret) |
159 | return ret; | 83 | return ret; |
160 | 84 | ||
161 | return nvkm_event_init(func->uevent, 1, 1 + (heads * 4), &disp->uevent); | 85 | disp->wq = create_singlethread_workqueue("nvkm-disp"); |
162 | } | 86 | if (!disp->wq) |
163 | 87 | return -ENOMEM; | |
164 | void | 88 | INIT_WORK(&disp->supervisor, func->super); |
165 | nv50_disp_vblank_fini(struct nv50_disp *disp, int head) | ||
166 | { | ||
167 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
168 | nvkm_mask(device, 0x61002c, (4 << head), 0); | ||
169 | } | ||
170 | |||
171 | void | ||
172 | nv50_disp_vblank_init(struct nv50_disp *disp, int head) | ||
173 | { | ||
174 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
175 | nvkm_mask(device, 0x61002c, (4 << head), (4 << head)); | ||
176 | } | ||
177 | |||
178 | static const struct nvkm_enum | ||
179 | nv50_disp_intr_error_type[] = { | ||
180 | { 3, "ILLEGAL_MTHD" }, | ||
181 | { 4, "INVALID_VALUE" }, | ||
182 | { 5, "INVALID_STATE" }, | ||
183 | { 7, "INVALID_HANDLE" }, | ||
184 | {} | ||
185 | }; | ||
186 | |||
187 | static const struct nvkm_enum | ||
188 | nv50_disp_intr_error_code[] = { | ||
189 | { 0x00, "" }, | ||
190 | {} | ||
191 | }; | ||
192 | |||
193 | static void | ||
194 | nv50_disp_intr_error(struct nv50_disp *disp, int chid) | ||
195 | { | ||
196 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
197 | struct nvkm_device *device = subdev->device; | ||
198 | u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); | ||
199 | u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); | ||
200 | u32 code = (addr & 0x00ff0000) >> 16; | ||
201 | u32 type = (addr & 0x00007000) >> 12; | ||
202 | u32 mthd = (addr & 0x00000ffc); | ||
203 | const struct nvkm_enum *ec, *et; | ||
204 | |||
205 | et = nvkm_enum_find(nv50_disp_intr_error_type, type); | ||
206 | ec = nvkm_enum_find(nv50_disp_intr_error_code, code); | ||
207 | |||
208 | nvkm_error(subdev, | ||
209 | "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", | ||
210 | type, et ? et->name : "", code, ec ? ec->name : "", | ||
211 | chid, mthd, data); | ||
212 | |||
213 | if (chid < ARRAY_SIZE(disp->chan)) { | ||
214 | switch (mthd) { | ||
215 | case 0x0080: | ||
216 | nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); | ||
217 | break; | ||
218 | default: | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | nvkm_wr32(device, 0x610020, 0x00010000 << chid); | ||
224 | nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); | ||
225 | } | ||
226 | |||
227 | static struct nvkm_output * | ||
228 | exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl, | ||
229 | u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, | ||
230 | struct nvbios_outp *info) | ||
231 | { | ||
232 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
233 | struct nvkm_bios *bios = subdev->device->bios; | ||
234 | struct nvkm_output *outp; | ||
235 | u16 mask, type; | ||
236 | |||
237 | if (or < 4) { | ||
238 | type = DCB_OUTPUT_ANALOG; | ||
239 | mask = 0; | ||
240 | } else | ||
241 | if (or < 8) { | ||
242 | switch (ctrl & 0x00000f00) { | ||
243 | case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; | ||
244 | case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; | ||
245 | case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; | ||
246 | case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; | ||
247 | case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; | ||
248 | case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; | ||
249 | default: | ||
250 | nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl); | ||
251 | return NULL; | ||
252 | } | ||
253 | or -= 4; | ||
254 | } else { | ||
255 | or = or - 8; | ||
256 | type = 0x0010; | ||
257 | mask = 0; | ||
258 | switch (ctrl & 0x00000f00) { | ||
259 | case 0x00000000: type |= disp->pior.type[or]; break; | ||
260 | default: | ||
261 | nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl); | ||
262 | return NULL; | ||
263 | } | ||
264 | } | ||
265 | 89 | ||
266 | mask = 0x00c0 & (mask << 6); | 90 | for (i = 0; func->head.new && i < heads; i++) { |
267 | mask |= 0x0001 << or; | 91 | ret = func->head.new(&disp->base, i); |
268 | mask |= 0x0100 << head; | 92 | if (ret) |
269 | 93 | return ret; | |
270 | list_for_each_entry(outp, &disp->base.outp, head) { | ||
271 | if ((outp->info.hasht & 0xff) == type && | ||
272 | (outp->info.hashm & mask) == mask) { | ||
273 | *data = nvbios_outp_match(bios, outp->info.hasht, mask, | ||
274 | ver, hdr, cnt, len, info); | ||
275 | if (!*data) | ||
276 | return NULL; | ||
277 | return outp; | ||
278 | } | ||
279 | } | 94 | } |
280 | 95 | ||
281 | return NULL; | 96 | for (i = 0; func->dac.new && i < func->dac.nr; i++) { |
282 | } | 97 | ret = func->dac.new(&disp->base, i); |
283 | 98 | if (ret) | |
284 | static struct nvkm_output * | 99 | return ret; |
285 | exec_script(struct nv50_disp *disp, int head, int id) | ||
286 | { | ||
287 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
288 | struct nvkm_device *device = subdev->device; | ||
289 | struct nvkm_bios *bios = device->bios; | ||
290 | struct nvkm_output *outp; | ||
291 | struct nvbios_outp info; | ||
292 | u8 ver, hdr, cnt, len; | ||
293 | u32 data, ctrl = 0; | ||
294 | u32 reg; | ||
295 | int i; | ||
296 | |||
297 | /* DAC */ | ||
298 | for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++) | ||
299 | ctrl = nvkm_rd32(device, 0x610b5c + (i * 8)); | ||
300 | |||
301 | /* SOR */ | ||
302 | if (!(ctrl & (1 << head))) { | ||
303 | if (device->chipset < 0x90 || | ||
304 | device->chipset == 0x92 || | ||
305 | device->chipset == 0xa0) { | ||
306 | reg = 0x610b74; | ||
307 | } else { | ||
308 | reg = 0x610798; | ||
309 | } | ||
310 | for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++) | ||
311 | ctrl = nvkm_rd32(device, reg + (i * 8)); | ||
312 | i += 4; | ||
313 | } | 100 | } |
314 | 101 | ||
315 | /* PIOR */ | 102 | for (i = 0; func->pior.new && i < func->pior.nr; i++) { |
316 | if (!(ctrl & (1 << head))) { | 103 | ret = func->pior.new(&disp->base, i); |
317 | for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++) | 104 | if (ret) |
318 | ctrl = nvkm_rd32(device, 0x610b84 + (i * 8)); | 105 | return ret; |
319 | i += 8; | ||
320 | } | 106 | } |
321 | 107 | ||
322 | if (!(ctrl & (1 << head))) | 108 | for (i = 0; func->sor.new && i < func->sor.nr; i++) { |
323 | return NULL; | 109 | ret = func->sor.new(&disp->base, i); |
324 | i--; | 110 | if (ret) |
325 | 111 | return ret; | |
326 | outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info); | ||
327 | if (outp) { | ||
328 | struct nvbios_init init = { | ||
329 | .subdev = subdev, | ||
330 | .bios = bios, | ||
331 | .offset = info.script[id], | ||
332 | .outp = &outp->info, | ||
333 | .crtc = head, | ||
334 | .execute = 1, | ||
335 | }; | ||
336 | |||
337 | nvbios_exec(&init); | ||
338 | } | 112 | } |
339 | 113 | ||
340 | return outp; | 114 | return nvkm_event_init(func->uevent, 1, 1 + (heads * 4), &disp->uevent); |
341 | } | 115 | } |
342 | 116 | ||
343 | static struct nvkm_output * | 117 | static u32 |
344 | exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf) | 118 | nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp, |
119 | u8 *ver, u8 *hdr, u8 *cnt, u8 *len, | ||
120 | struct nvbios_outp *iedt) | ||
345 | { | 121 | { |
346 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 122 | struct nvkm_bios *bios = head->disp->engine.subdev.device->bios; |
347 | struct nvkm_device *device = subdev->device; | 123 | const u8 l = ffs(outp->info.link); |
348 | struct nvkm_bios *bios = device->bios; | 124 | const u16 t = outp->info.hasht; |
349 | struct nvkm_output *outp; | 125 | const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or; |
350 | struct nvbios_outp info1; | 126 | u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt); |
351 | struct nvbios_ocfg info2; | 127 | if (!data) |
352 | u8 ver, hdr, cnt, len; | 128 | OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m); |
353 | u32 data, ctrl = 0; | 129 | return data; |
354 | u32 reg; | ||
355 | int i; | ||
356 | |||
357 | /* DAC */ | ||
358 | for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++) | ||
359 | ctrl = nvkm_rd32(device, 0x610b58 + (i * 8)); | ||
360 | |||
361 | /* SOR */ | ||
362 | if (!(ctrl & (1 << head))) { | ||
363 | if (device->chipset < 0x90 || | ||
364 | device->chipset == 0x92 || | ||
365 | device->chipset == 0xa0) { | ||
366 | reg = 0x610b70; | ||
367 | } else { | ||
368 | reg = 0x610794; | ||
369 | } | ||
370 | for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++) | ||
371 | ctrl = nvkm_rd32(device, reg + (i * 8)); | ||
372 | i += 4; | ||
373 | } | ||
374 | |||
375 | /* PIOR */ | ||
376 | if (!(ctrl & (1 << head))) { | ||
377 | for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++) | ||
378 | ctrl = nvkm_rd32(device, 0x610b80 + (i * 8)); | ||
379 | i += 8; | ||
380 | } | ||
381 | |||
382 | if (!(ctrl & (1 << head))) | ||
383 | return NULL; | ||
384 | i--; | ||
385 | |||
386 | outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1); | ||
387 | if (!outp) | ||
388 | return NULL; | ||
389 | |||
390 | *conf = (ctrl & 0x00000f00) >> 8; | ||
391 | if (outp->info.location == 0) { | ||
392 | switch (outp->info.type) { | ||
393 | case DCB_OUTPUT_TMDS: | ||
394 | if (*conf == 5) | ||
395 | *conf |= 0x0100; | ||
396 | break; | ||
397 | case DCB_OUTPUT_LVDS: | ||
398 | *conf |= disp->sor.lvdsconf; | ||
399 | break; | ||
400 | default: | ||
401 | break; | ||
402 | } | ||
403 | } else { | ||
404 | *conf = (ctrl & 0x00000f00) >> 8; | ||
405 | pclk = pclk / 2; | ||
406 | } | ||
407 | |||
408 | data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8, | ||
409 | &ver, &hdr, &cnt, &len, &info2); | ||
410 | if (data && id < 0xff) { | ||
411 | data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); | ||
412 | if (data) { | ||
413 | struct nvbios_init init = { | ||
414 | .subdev = subdev, | ||
415 | .bios = bios, | ||
416 | .offset = data, | ||
417 | .outp = &outp->info, | ||
418 | .crtc = head, | ||
419 | .execute = 1, | ||
420 | }; | ||
421 | |||
422 | nvbios_exec(&init); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | return outp; | ||
427 | } | 130 | } |
428 | 131 | ||
429 | static bool | 132 | static void |
430 | nv50_disp_dptmds_war(struct nvkm_device *device) | 133 | nv50_disp_super_ied_on(struct nvkm_head *head, |
134 | struct nvkm_ior *ior, int id, u32 khz) | ||
431 | { | 135 | { |
432 | switch (device->chipset) { | 136 | struct nvkm_subdev *subdev = &head->disp->engine.subdev; |
433 | case 0x94: | 137 | struct nvkm_bios *bios = subdev->device->bios; |
434 | case 0x96: | 138 | struct nvkm_outp *outp = ior->asy.outp; |
435 | case 0x98: | 139 | struct nvbios_ocfg iedtrs; |
436 | return true; | 140 | struct nvbios_outp iedt; |
437 | default: | 141 | u8 ver, hdr, cnt, len, flags = 0x00; |
438 | break; | 142 | u32 data; |
143 | |||
144 | if (!outp) { | ||
145 | IOR_DBG(ior, "nothing to attach"); | ||
146 | return; | ||
439 | } | 147 | } |
440 | return false; | ||
441 | } | ||
442 | 148 | ||
443 | static bool | 149 | /* Lookup IED table for the device. */ |
444 | nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp) | 150 | data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); |
445 | { | 151 | if (!data) |
446 | struct nvkm_device *device = disp->base.engine.subdev.device; | 152 | return; |
447 | const u32 soff = __ffs(outp->or) * 0x800; | 153 | |
448 | if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) { | 154 | /* Lookup IEDT runtime settings for the current configuration. */ |
449 | switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) { | 155 | if (ior->type == SOR) { |
450 | case 0x00000000: | 156 | if (ior->asy.proto == LVDS) { |
451 | case 0x00030000: | 157 | if (head->asy.or.depth == 24) |
452 | return true; | 158 | flags |= 0x02; |
453 | default: | ||
454 | break; | ||
455 | } | 159 | } |
160 | if (ior->asy.link == 3) | ||
161 | flags |= 0x01; | ||
456 | } | 162 | } |
457 | return false; | ||
458 | |||
459 | } | ||
460 | |||
461 | static void | ||
462 | nv50_disp_dptmds_war_2(struct nv50_disp *disp, struct dcb_output *outp) | ||
463 | { | ||
464 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
465 | const u32 soff = __ffs(outp->or) * 0x800; | ||
466 | 163 | ||
467 | if (!nv50_disp_dptmds_war_needed(disp, outp)) | 164 | data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags, |
165 | &ver, &hdr, &cnt, &len, &iedtrs); | ||
166 | if (!data) { | ||
167 | OUTP_DBG(outp, "missing IEDT RS for %02x:%02x", | ||
168 | ior->asy.proto_evo, flags); | ||
468 | return; | 169 | return; |
469 | |||
470 | nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000); | ||
471 | nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000); | ||
472 | nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001); | ||
473 | |||
474 | nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000); | ||
475 | nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000); | ||
476 | nvkm_usec(device, 400, NVKM_DELAY); | ||
477 | nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000); | ||
478 | nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000); | ||
479 | |||
480 | if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) { | ||
481 | u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); | ||
482 | u32 pu_pc = seqctl & 0x0000000f; | ||
483 | nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000); | ||
484 | } | 170 | } |
485 | } | ||
486 | 171 | ||
487 | static void | 172 | /* Execute the OnInt[23] script for the current frequency. */ |
488 | nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp) | 173 | data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz); |
489 | { | 174 | if (!data) { |
490 | struct nvkm_device *device = disp->base.engine.subdev.device; | 175 | OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz", |
491 | const u32 soff = __ffs(outp->or) * 0x800; | 176 | id, ior->asy.proto_evo, flags, khz); |
492 | u32 sorpwr; | ||
493 | |||
494 | if (!nv50_disp_dptmds_war_needed(disp, outp)) | ||
495 | return; | 177 | return; |
496 | |||
497 | sorpwr = nvkm_rd32(device, 0x61c004 + soff); | ||
498 | if (sorpwr & 0x00000001) { | ||
499 | u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); | ||
500 | u32 pd_pc = (seqctl & 0x00000f00) >> 8; | ||
501 | u32 pu_pc = seqctl & 0x0000000f; | ||
502 | |||
503 | nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000); | ||
504 | |||
505 | nvkm_msec(device, 2000, | ||
506 | if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) | ||
507 | break; | ||
508 | ); | ||
509 | nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000); | ||
510 | nvkm_msec(device, 2000, | ||
511 | if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) | ||
512 | break; | ||
513 | ); | ||
514 | |||
515 | nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000); | ||
516 | nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000); | ||
517 | } | 178 | } |
518 | 179 | ||
519 | nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000); | 180 | nvbios_init(subdev, data, |
520 | nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000); | 181 | init.outp = &outp->info; |
521 | 182 | init.or = ior->id; | |
522 | if (sorpwr & 0x00000001) { | 183 | init.link = ior->asy.link; |
523 | nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001); | 184 | init.head = head->id; |
524 | } | 185 | ); |
525 | } | 186 | } |
526 | 187 | ||
527 | static void | 188 | static void |
528 | nv50_disp_update_sppll1(struct nv50_disp *disp) | 189 | nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id) |
529 | { | 190 | { |
530 | struct nvkm_device *device = disp->base.engine.subdev.device; | 191 | struct nvkm_outp *outp = ior->arm.outp; |
531 | bool used = false; | 192 | struct nvbios_outp iedt; |
532 | int sor; | 193 | u8 ver, hdr, cnt, len; |
194 | u32 data; | ||
533 | 195 | ||
534 | if (!nv50_disp_dptmds_war(device)) | 196 | if (!outp) { |
197 | IOR_DBG(ior, "nothing attached"); | ||
535 | return; | 198 | return; |
536 | |||
537 | for (sor = 0; sor < disp->func->sor.nr; sor++) { | ||
538 | u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800)); | ||
539 | switch (clksor & 0x03000000) { | ||
540 | case 0x02000000: | ||
541 | case 0x03000000: | ||
542 | used = true; | ||
543 | break; | ||
544 | default: | ||
545 | break; | ||
546 | } | ||
547 | } | 199 | } |
548 | 200 | ||
549 | if (used) | 201 | data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt); |
202 | if (!data) | ||
550 | return; | 203 | return; |
551 | 204 | ||
552 | nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000); | 205 | nvbios_init(&head->disp->engine.subdev, iedt.script[id], |
206 | init.outp = &outp->info; | ||
207 | init.or = ior->id; | ||
208 | init.link = ior->arm.link; | ||
209 | init.head = head->id; | ||
210 | ); | ||
553 | } | 211 | } |
554 | 212 | ||
555 | static void | 213 | static struct nvkm_ior * |
556 | nv50_disp_intr_unk10_0(struct nv50_disp *disp, int head) | 214 | nv50_disp_super_ior_asy(struct nvkm_head *head) |
557 | { | 215 | { |
558 | exec_script(disp, head, 1); | 216 | struct nvkm_ior *ior; |
217 | list_for_each_entry(ior, &head->disp->ior, head) { | ||
218 | if (ior->asy.head & (1 << head->id)) { | ||
219 | HEAD_DBG(head, "to %s", ior->name); | ||
220 | return ior; | ||
221 | } | ||
222 | } | ||
223 | HEAD_DBG(head, "nothing to attach"); | ||
224 | return NULL; | ||
559 | } | 225 | } |
560 | 226 | ||
561 | static void | 227 | static struct nvkm_ior * |
562 | nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head) | 228 | nv50_disp_super_ior_arm(struct nvkm_head *head) |
563 | { | 229 | { |
564 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 230 | struct nvkm_ior *ior; |
565 | struct nvkm_output *outp = exec_script(disp, head, 2); | 231 | list_for_each_entry(ior, &head->disp->ior, head) { |
566 | 232 | if (ior->arm.head & (1 << head->id)) { | |
567 | /* the binary driver does this outside of the supervisor handling | 233 | HEAD_DBG(head, "on %s", ior->name); |
568 | * (after the third supervisor from a detach). we (currently?) | 234 | return ior; |
569 | * allow both detach/attach to happen in the same set of | 235 | } |
570 | * supervisor interrupts, so it would make sense to execute this | ||
571 | * (full power down?) script after all the detach phases of the | ||
572 | * supervisor handling. like with training if needed from the | ||
573 | * second supervisor, nvidia doesn't do this, so who knows if it's | ||
574 | * entirely safe, but it does appear to work.. | ||
575 | * | ||
576 | * without this script being run, on some configurations i've | ||
577 | * seen, switching from DP to TMDS on a DP connector may result | ||
578 | * in a blank screen (SOR_PWR off/on can restore it) | ||
579 | */ | ||
580 | if (outp && outp->info.type == DCB_OUTPUT_DP) { | ||
581 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | ||
582 | struct nvbios_init init = { | ||
583 | .subdev = subdev, | ||
584 | .bios = subdev->device->bios, | ||
585 | .outp = &outp->info, | ||
586 | .crtc = head, | ||
587 | .offset = outpdp->info.script[4], | ||
588 | .execute = 1, | ||
589 | }; | ||
590 | |||
591 | nvkm_notify_put(&outpdp->irq); | ||
592 | nvbios_exec(&init); | ||
593 | atomic_set(&outpdp->lt.done, 0); | ||
594 | } | 236 | } |
237 | HEAD_DBG(head, "nothing attached"); | ||
238 | return NULL; | ||
595 | } | 239 | } |
596 | 240 | ||
597 | static void | 241 | void |
598 | nv50_disp_intr_unk20_1(struct nv50_disp *disp, int head) | 242 | nv50_disp_super_3_0(struct nv50_disp *disp, struct nvkm_head *head) |
599 | { | 243 | { |
600 | struct nvkm_device *device = disp->base.engine.subdev.device; | 244 | struct nvkm_ior *ior; |
601 | struct nvkm_devinit *devinit = device->devinit; | 245 | |
602 | u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; | 246 | /* Determine which OR, if any, we're attaching to the head. */ |
603 | if (pclk) | 247 | HEAD_DBG(head, "supervisor 3.0"); |
604 | nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk); | 248 | ior = nv50_disp_super_ior_asy(head); |
249 | if (!ior) | ||
250 | return; | ||
251 | |||
252 | /* Execute OnInt3 IED script. */ | ||
253 | nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000); | ||
254 | |||
255 | /* OR-specific handling. */ | ||
256 | if (ior->func->war_3) | ||
257 | ior->func->war_3(ior); | ||
605 | } | 258 | } |
606 | 259 | ||
607 | static void | 260 | static void |
608 | nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, | 261 | nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior) |
609 | struct dcb_output *outp, u32 pclk) | ||
610 | { | 262 | { |
611 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 263 | struct nvkm_subdev *subdev = &head->disp->engine.subdev; |
612 | struct nvkm_device *device = subdev->device; | 264 | const u32 khz = head->asy.hz / 1000; |
613 | const int link = !(outp->sorconf.link & 1); | 265 | const u32 linkKBps = ior->dp.bw * 27000; |
614 | const int or = ffs(outp->or) - 1; | 266 | const u32 symbol = 100000; |
615 | const u32 soff = ( or * 0x800); | ||
616 | const u32 loff = (link * 0x080) + soff; | ||
617 | const u32 ctrl = nvkm_rd32(device, 0x610794 + (or * 8)); | ||
618 | const u32 symbol = 100000; | ||
619 | const s32 vactive = nvkm_rd32(device, 0x610af8 + (head * 0x540)) & 0xffff; | ||
620 | const s32 vblanke = nvkm_rd32(device, 0x610ae8 + (head * 0x540)) & 0xffff; | ||
621 | const s32 vblanks = nvkm_rd32(device, 0x610af0 + (head * 0x540)) & 0xffff; | ||
622 | u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff); | ||
623 | u32 clksor = nvkm_rd32(device, 0x614300 + soff); | ||
624 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; | 267 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; |
625 | int TU, VTUi, VTUf, VTUa; | 268 | int TU, VTUi, VTUf, VTUa; |
626 | u64 link_data_rate, link_ratio, unk; | 269 | u64 link_data_rate, link_ratio, unk; |
627 | u32 best_diff = 64 * symbol; | 270 | u32 best_diff = 64 * symbol; |
628 | u32 link_nr, link_bw, bits; | 271 | u64 h, v; |
629 | u64 value; | ||
630 | |||
631 | link_bw = (clksor & 0x000c0000) ? 270000 : 162000; | ||
632 | link_nr = hweight32(dpctrl & 0x000f0000); | ||
633 | 272 | ||
634 | /* symbols/hblank - algorithm taken from comments in tegra driver */ | 273 | /* symbols/hblank - algorithm taken from comments in tegra driver */ |
635 | value = vblanke + vactive - vblanks - 7; | 274 | h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7; |
636 | value = value * link_bw; | 275 | h = h * linkKBps; |
637 | do_div(value, pclk); | 276 | do_div(h, khz); |
638 | value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr); | 277 | h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr); |
639 | nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, value); | ||
640 | 278 | ||
641 | /* symbols/vblank - algorithm taken from comments in tegra driver */ | 279 | /* symbols/vblank - algorithm taken from comments in tegra driver */ |
642 | value = vblanks - vblanke - 25; | 280 | v = head->asy.vblanks - head->asy.vblanke - 25; |
643 | value = value * link_bw; | 281 | v = v * linkKBps; |
644 | do_div(value, pclk); | 282 | do_div(v, khz); |
645 | value = value - ((36 / link_nr) + 3) - 1; | 283 | v = v - ((36 / ior->dp.nr) + 3) - 1; |
646 | nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, value); | ||
647 | 284 | ||
648 | /* watermark / activesym */ | 285 | ior->func->dp.audio_sym(ior, head->id, h, v); |
649 | if ((ctrl & 0xf0000) == 0x60000) bits = 30; | ||
650 | else if ((ctrl & 0xf0000) == 0x50000) bits = 24; | ||
651 | else bits = 18; | ||
652 | 286 | ||
653 | link_data_rate = (pclk * bits / 8) / link_nr; | 287 | /* watermark / activesym */ |
288 | link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr; | ||
654 | 289 | ||
655 | /* calculate ratio of packed data rate to link symbol rate */ | 290 | /* calculate ratio of packed data rate to link symbol rate */ |
656 | link_ratio = link_data_rate * symbol; | 291 | link_ratio = link_data_rate * symbol; |
657 | do_div(link_ratio, link_bw); | 292 | do_div(link_ratio, linkKBps); |
658 | 293 | ||
659 | for (TU = 64; TU >= 32; TU--) { | 294 | for (TU = 64; ior->func->dp.activesym && TU >= 32; TU--) { |
660 | /* calculate average number of valid symbols in each TU */ | 295 | /* calculate average number of valid symbols in each TU */ |
661 | u32 tu_valid = link_ratio * TU; | 296 | u32 tu_valid = link_ratio * TU; |
662 | u32 calc, diff; | 297 | u32 calc, diff; |
@@ -707,9 +342,15 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, | |||
707 | } | 342 | } |
708 | } | 343 | } |
709 | 344 | ||
710 | if (!bestTU) { | 345 | if (ior->func->dp.activesym) { |
711 | nvkm_error(subdev, "unable to find suitable dp config\n"); | 346 | if (!bestTU) { |
712 | return; | 347 | nvkm_error(subdev, "unable to determine dp config\n"); |
348 | return; | ||
349 | } | ||
350 | ior->func->dp.activesym(ior, head->id, bestTU, | ||
351 | bestVTUa, bestVTUf, bestVTUi); | ||
352 | } else { | ||
353 | bestTU = 64; | ||
713 | } | 354 | } |
714 | 355 | ||
715 | /* XXX close to vbios numbers, but not right */ | 356 | /* XXX close to vbios numbers, but not right */ |
@@ -719,190 +360,223 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp *disp, int head, | |||
719 | do_div(unk, symbol); | 360 | do_div(unk, symbol); |
720 | unk += 6; | 361 | unk += 6; |
721 | 362 | ||
722 | nvkm_mask(device, 0x61c10c + loff, 0x000001fc, bestTU << 2); | 363 | ior->func->dp.watermark(ior, head->id, unk); |
723 | nvkm_mask(device, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 | | ||
724 | bestVTUf << 16 | | ||
725 | bestVTUi << 8 | unk); | ||
726 | } | 364 | } |
727 | 365 | ||
728 | static void | 366 | void |
729 | nv50_disp_intr_unk20_2(struct nv50_disp *disp, int head) | 367 | nv50_disp_super_2_2(struct nv50_disp *disp, struct nvkm_head *head) |
730 | { | 368 | { |
731 | struct nvkm_device *device = disp->base.engine.subdev.device; | 369 | const u32 khz = head->asy.hz / 1000; |
732 | struct nvkm_output *outp; | 370 | struct nvkm_outp *outp; |
733 | u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; | 371 | struct nvkm_ior *ior; |
734 | u32 hval, hreg = 0x614200 + (head * 0x800); | 372 | |
735 | u32 oval, oreg; | 373 | /* Determine which OR, if any, we're attaching from the head. */ |
736 | u32 mask, conf; | 374 | HEAD_DBG(head, "supervisor 2.2"); |
737 | 375 | ior = nv50_disp_super_ior_asy(head); | |
738 | outp = exec_clkcmp(disp, head, 0xff, pclk, &conf); | 376 | if (!ior) |
739 | if (!outp) | ||
740 | return; | 377 | return; |
741 | 378 | ||
742 | /* we allow both encoder attach and detach operations to occur | 379 | /* For some reason, NVIDIA decided not to: |
743 | * within a single supervisor (ie. modeset) sequence. the | ||
744 | * encoder detach scripts quite often switch off power to the | ||
745 | * lanes, which requires the link to be re-trained. | ||
746 | * | ||
747 | * this is not generally an issue as the sink "must" (heh) | ||
748 | * signal an irq when it's lost sync so the driver can | ||
749 | * re-train. | ||
750 | * | 380 | * |
751 | * however, on some boards, if one does not configure at least | 381 | * A) Give dual-link LVDS a separate EVO protocol, like for TMDS. |
752 | * the gpu side of the link *before* attaching, then various | 382 | * and |
753 | * things can go horribly wrong (PDISP disappearing from mmio, | 383 | * B) Use SetControlOutputResource.PixelDepth on LVDS. |
754 | * third supervisor never happens, etc). | ||
755 | * | 384 | * |
756 | * the solution is simply to retrain here, if necessary. last | 385 | * Override the values we usually read from HW with the same |
757 | * i checked, the binary driver userspace does not appear to | 386 | * data we pass though an ioctl instead. |
758 | * trigger this situation (it forces an UPDATE between steps). | ||
759 | */ | 387 | */ |
760 | if (outp->info.type == DCB_OUTPUT_DP) { | 388 | if (ior->type == SOR && ior->asy.proto == LVDS) { |
761 | u32 soff = (ffs(outp->info.or) - 1) * 0x08; | 389 | head->asy.or.depth = (disp->sor.lvdsconf & 0x0200) ? 24 : 18; |
762 | u32 ctrl, datarate; | 390 | ior->asy.link = (disp->sor.lvdsconf & 0x0100) ? 3 : 1; |
763 | 391 | } | |
764 | if (outp->info.location == 0) { | ||
765 | ctrl = nvkm_rd32(device, 0x610794 + soff); | ||
766 | soff = 1; | ||
767 | } else { | ||
768 | ctrl = nvkm_rd32(device, 0x610b80 + soff); | ||
769 | soff = 2; | ||
770 | } | ||
771 | 392 | ||
772 | switch ((ctrl & 0x000f0000) >> 16) { | 393 | /* Handle any link training, etc. */ |
773 | case 6: datarate = pclk * 30; break; | 394 | if ((outp = ior->asy.outp) && outp->func->acquire) |
774 | case 5: datarate = pclk * 24; break; | 395 | outp->func->acquire(outp); |
775 | case 2: | ||
776 | default: | ||
777 | datarate = pclk * 18; | ||
778 | break; | ||
779 | } | ||
780 | 396 | ||
781 | if (nvkm_output_dp_train(outp, datarate / soff)) | 397 | /* Execute OnInt2 IED script. */ |
782 | OUTP_ERR(outp, "link not trained before attach"); | 398 | nv50_disp_super_ied_on(head, ior, 0, khz); |
783 | } | ||
784 | 399 | ||
785 | exec_clkcmp(disp, head, 0, pclk, &conf); | 400 | /* Program RG clock divider. */ |
401 | head->func->rgclk(head, ior->asy.rgdiv); | ||
786 | 402 | ||
787 | if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) { | 403 | /* Mode-specific internal DP configuration. */ |
788 | oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800; | 404 | if (ior->type == SOR && ior->asy.proto == DP) |
789 | oval = 0x00000000; | 405 | nv50_disp_super_2_2_dp(head, ior); |
790 | hval = 0x00000000; | ||
791 | mask = 0xffffffff; | ||
792 | } else | ||
793 | if (!outp->info.location) { | ||
794 | if (outp->info.type == DCB_OUTPUT_DP) | ||
795 | nv50_disp_intr_unk20_2_dp(disp, head, &outp->info, pclk); | ||
796 | oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800; | ||
797 | oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; | ||
798 | hval = 0x00000000; | ||
799 | mask = 0x00000707; | ||
800 | } else { | ||
801 | oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800; | ||
802 | oval = 0x00000001; | ||
803 | hval = 0x00000001; | ||
804 | mask = 0x00000707; | ||
805 | } | ||
806 | 406 | ||
807 | nvkm_mask(device, hreg, 0x0000000f, hval); | 407 | /* OR-specific handling. */ |
808 | nvkm_mask(device, oreg, mask, oval); | 408 | ior->func->clock(ior); |
409 | if (ior->func->war_2) | ||
410 | ior->func->war_2(ior); | ||
411 | } | ||
809 | 412 | ||
810 | nv50_disp_dptmds_war_2(disp, &outp->info); | 413 | void |
414 | nv50_disp_super_2_1(struct nv50_disp *disp, struct nvkm_head *head) | ||
415 | { | ||
416 | struct nvkm_devinit *devinit = disp->base.engine.subdev.device->devinit; | ||
417 | const u32 khz = head->asy.hz / 1000; | ||
418 | HEAD_DBG(head, "supervisor 2.1 - %d khz", khz); | ||
419 | if (khz) | ||
420 | nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz); | ||
811 | } | 421 | } |
812 | 422 | ||
813 | /* If programming a TMDS output on a SOR that can also be configured for | 423 | void |
814 | * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. | 424 | nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head) |
815 | * | ||
816 | * It looks like the VBIOS TMDS scripts make an attempt at this, however, | ||
817 | * the VBIOS scripts on at least one board I have only switch it off on | ||
818 | * link 0, causing a blank display if the output has previously been | ||
819 | * programmed for DisplayPort. | ||
820 | */ | ||
821 | static void | ||
822 | nv50_disp_intr_unk40_0_tmds(struct nv50_disp *disp, | ||
823 | struct dcb_output *outp) | ||
824 | { | 425 | { |
825 | struct nvkm_device *device = disp->base.engine.subdev.device; | 426 | struct nvkm_outp *outp; |
826 | struct nvkm_bios *bios = device->bios; | 427 | struct nvkm_ior *ior; |
827 | const int link = !(outp->sorconf.link & 1); | 428 | |
828 | const int or = ffs(outp->or) - 1; | 429 | /* Determine which OR, if any, we're detaching from the head. */ |
829 | const u32 loff = (or * 0x800) + (link * 0x80); | 430 | HEAD_DBG(head, "supervisor 2.0"); |
830 | const u16 mask = (outp->sorconf.link << 6) | outp->or; | 431 | ior = nv50_disp_super_ior_arm(head); |
831 | struct dcb_output match; | 432 | if (!ior) |
832 | u8 ver, hdr; | 433 | return; |
833 | 434 | ||
834 | if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match)) | 435 | /* Execute OffInt2 IED script. */ |
835 | nvkm_mask(device, 0x61c10c + loff, 0x00000001, 0x00000000); | 436 | nv50_disp_super_ied_off(head, ior, 2); |
437 | |||
438 | /* If we're shutting down the OR's only active head, execute | ||
439 | * the output path's release function. | ||
440 | */ | ||
441 | if (ior->arm.head == (1 << head->id)) { | ||
442 | if ((outp = ior->arm.outp) && outp->func->release) | ||
443 | outp->func->release(outp, ior); | ||
444 | } | ||
836 | } | 445 | } |
837 | 446 | ||
838 | static void | 447 | void |
839 | nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head) | 448 | nv50_disp_super_1_0(struct nv50_disp *disp, struct nvkm_head *head) |
840 | { | 449 | { |
841 | struct nvkm_device *device = disp->base.engine.subdev.device; | 450 | struct nvkm_ior *ior; |
842 | struct nvkm_output *outp; | ||
843 | u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff; | ||
844 | u32 conf; | ||
845 | 451 | ||
846 | outp = exec_clkcmp(disp, head, 1, pclk, &conf); | 452 | /* Determine which OR, if any, we're detaching from the head. */ |
847 | if (!outp) | 453 | HEAD_DBG(head, "supervisor 1.0"); |
454 | ior = nv50_disp_super_ior_arm(head); | ||
455 | if (!ior) | ||
848 | return; | 456 | return; |
849 | 457 | ||
850 | if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS) | 458 | /* Execute OffInt1 IED script. */ |
851 | nv50_disp_intr_unk40_0_tmds(disp, &outp->info); | 459 | nv50_disp_super_ied_off(head, ior, 1); |
852 | nv50_disp_dptmds_war_3(disp, &outp->info); | ||
853 | } | 460 | } |
854 | 461 | ||
855 | void | 462 | void |
856 | nv50_disp_intr_supervisor(struct work_struct *work) | 463 | nv50_disp_super_1(struct nv50_disp *disp) |
464 | { | ||
465 | struct nvkm_head *head; | ||
466 | struct nvkm_ior *ior; | ||
467 | |||
468 | list_for_each_entry(head, &disp->base.head, head) { | ||
469 | head->func->state(head, &head->arm); | ||
470 | head->func->state(head, &head->asy); | ||
471 | } | ||
472 | |||
473 | list_for_each_entry(ior, &disp->base.ior, head) { | ||
474 | ior->func->state(ior, &ior->arm); | ||
475 | ior->func->state(ior, &ior->asy); | ||
476 | } | ||
477 | } | ||
478 | |||
479 | void | ||
480 | nv50_disp_super(struct work_struct *work) | ||
857 | { | 481 | { |
858 | struct nv50_disp *disp = | 482 | struct nv50_disp *disp = |
859 | container_of(work, struct nv50_disp, supervisor); | 483 | container_of(work, struct nv50_disp, supervisor); |
860 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 484 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
861 | struct nvkm_device *device = subdev->device; | 485 | struct nvkm_device *device = subdev->device; |
486 | struct nvkm_head *head; | ||
862 | u32 super = nvkm_rd32(device, 0x610030); | 487 | u32 super = nvkm_rd32(device, 0x610030); |
863 | int head; | ||
864 | 488 | ||
865 | nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super); | 489 | nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super, super); |
866 | 490 | ||
867 | if (disp->super & 0x00000010) { | 491 | if (disp->super & 0x00000010) { |
868 | nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); | 492 | nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG); |
869 | for (head = 0; head < disp->base.head.nr; head++) { | 493 | nv50_disp_super_1(disp); |
870 | if (!(super & (0x00000020 << head))) | 494 | list_for_each_entry(head, &disp->base.head, head) { |
495 | if (!(super & (0x00000020 << head->id))) | ||
871 | continue; | 496 | continue; |
872 | if (!(super & (0x00000080 << head))) | 497 | if (!(super & (0x00000080 << head->id))) |
873 | continue; | 498 | continue; |
874 | nv50_disp_intr_unk10_0(disp, head); | 499 | nv50_disp_super_1_0(disp, head); |
875 | } | 500 | } |
876 | } else | 501 | } else |
877 | if (disp->super & 0x00000020) { | 502 | if (disp->super & 0x00000020) { |
878 | for (head = 0; head < disp->base.head.nr; head++) { | 503 | list_for_each_entry(head, &disp->base.head, head) { |
879 | if (!(super & (0x00000080 << head))) | 504 | if (!(super & (0x00000080 << head->id))) |
880 | continue; | 505 | continue; |
881 | nv50_disp_intr_unk20_0(disp, head); | 506 | nv50_disp_super_2_0(disp, head); |
882 | } | 507 | } |
883 | for (head = 0; head < disp->base.head.nr; head++) { | 508 | nvkm_outp_route(&disp->base); |
884 | if (!(super & (0x00000200 << head))) | 509 | list_for_each_entry(head, &disp->base.head, head) { |
510 | if (!(super & (0x00000200 << head->id))) | ||
885 | continue; | 511 | continue; |
886 | nv50_disp_intr_unk20_1(disp, head); | 512 | nv50_disp_super_2_1(disp, head); |
887 | } | 513 | } |
888 | for (head = 0; head < disp->base.head.nr; head++) { | 514 | list_for_each_entry(head, &disp->base.head, head) { |
889 | if (!(super & (0x00000080 << head))) | 515 | if (!(super & (0x00000080 << head->id))) |
890 | continue; | 516 | continue; |
891 | nv50_disp_intr_unk20_2(disp, head); | 517 | nv50_disp_super_2_2(disp, head); |
892 | } | 518 | } |
893 | } else | 519 | } else |
894 | if (disp->super & 0x00000040) { | 520 | if (disp->super & 0x00000040) { |
895 | for (head = 0; head < disp->base.head.nr; head++) { | 521 | list_for_each_entry(head, &disp->base.head, head) { |
896 | if (!(super & (0x00000080 << head))) | 522 | if (!(super & (0x00000080 << head->id))) |
897 | continue; | 523 | continue; |
898 | nv50_disp_intr_unk40_0(disp, head); | 524 | nv50_disp_super_3_0(disp, head); |
899 | } | 525 | } |
900 | nv50_disp_update_sppll1(disp); | ||
901 | } | 526 | } |
902 | 527 | ||
903 | nvkm_wr32(device, 0x610030, 0x80000000); | 528 | nvkm_wr32(device, 0x610030, 0x80000000); |
904 | } | 529 | } |
905 | 530 | ||
531 | static const struct nvkm_enum | ||
532 | nv50_disp_intr_error_type[] = { | ||
533 | { 3, "ILLEGAL_MTHD" }, | ||
534 | { 4, "INVALID_VALUE" }, | ||
535 | { 5, "INVALID_STATE" }, | ||
536 | { 7, "INVALID_HANDLE" }, | ||
537 | {} | ||
538 | }; | ||
539 | |||
540 | static const struct nvkm_enum | ||
541 | nv50_disp_intr_error_code[] = { | ||
542 | { 0x00, "" }, | ||
543 | {} | ||
544 | }; | ||
545 | |||
546 | static void | ||
547 | nv50_disp_intr_error(struct nv50_disp *disp, int chid) | ||
548 | { | ||
549 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | ||
550 | struct nvkm_device *device = subdev->device; | ||
551 | u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08)); | ||
552 | u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08)); | ||
553 | u32 code = (addr & 0x00ff0000) >> 16; | ||
554 | u32 type = (addr & 0x00007000) >> 12; | ||
555 | u32 mthd = (addr & 0x00000ffc); | ||
556 | const struct nvkm_enum *ec, *et; | ||
557 | |||
558 | et = nvkm_enum_find(nv50_disp_intr_error_type, type); | ||
559 | ec = nvkm_enum_find(nv50_disp_intr_error_code, code); | ||
560 | |||
561 | nvkm_error(subdev, | ||
562 | "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n", | ||
563 | type, et ? et->name : "", code, ec ? ec->name : "", | ||
564 | chid, mthd, data); | ||
565 | |||
566 | if (chid < ARRAY_SIZE(disp->chan)) { | ||
567 | switch (mthd) { | ||
568 | case 0x0080: | ||
569 | nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR); | ||
570 | break; | ||
571 | default: | ||
572 | break; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | nvkm_wr32(device, 0x610020, 0x00010000 << chid); | ||
577 | nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000); | ||
578 | } | ||
579 | |||
906 | void | 580 | void |
907 | nv50_disp_intr(struct nv50_disp *disp) | 581 | nv50_disp_intr(struct nv50_disp *disp) |
908 | { | 582 | { |
@@ -934,7 +608,7 @@ nv50_disp_intr(struct nv50_disp *disp) | |||
934 | 608 | ||
935 | if (intr1 & 0x00000070) { | 609 | if (intr1 & 0x00000070) { |
936 | disp->super = (intr1 & 0x00000070); | 610 | disp->super = (intr1 & 0x00000070); |
937 | schedule_work(&disp->supervisor); | 611 | queue_work(disp->wq, &disp->supervisor); |
938 | nvkm_wr32(device, 0x610024, disp->super); | 612 | nvkm_wr32(device, 0x610024, disp->super); |
939 | } | 613 | } |
940 | } | 614 | } |
@@ -943,23 +617,12 @@ static const struct nv50_disp_func | |||
943 | nv50_disp = { | 617 | nv50_disp = { |
944 | .intr = nv50_disp_intr, | 618 | .intr = nv50_disp_intr, |
945 | .uevent = &nv50_disp_chan_uevent, | 619 | .uevent = &nv50_disp_chan_uevent, |
946 | .super = nv50_disp_intr_supervisor, | 620 | .super = nv50_disp_super, |
947 | .root = &nv50_disp_root_oclass, | 621 | .root = &nv50_disp_root_oclass, |
948 | .head.vblank_init = nv50_disp_vblank_init, | 622 | .head.new = nv50_head_new, |
949 | .head.vblank_fini = nv50_disp_vblank_fini, | 623 | .dac = { .nr = 3, .new = nv50_dac_new }, |
950 | .head.scanoutpos = nv50_disp_root_scanoutpos, | 624 | .sor = { .nr = 2, .new = nv50_sor_new }, |
951 | .outp.internal.crt = nv50_dac_output_new, | 625 | .pior = { .nr = 3, .new = nv50_pior_new }, |
952 | .outp.internal.tmds = nv50_sor_output_new, | ||
953 | .outp.internal.lvds = nv50_sor_output_new, | ||
954 | .outp.external.tmds = nv50_pior_output_new, | ||
955 | .outp.external.dp = nv50_pior_dp_new, | ||
956 | .dac.nr = 3, | ||
957 | .dac.power = nv50_dac_power, | ||
958 | .dac.sense = nv50_dac_sense, | ||
959 | .sor.nr = 2, | ||
960 | .sor.power = nv50_sor_power, | ||
961 | .pior.nr = 3, | ||
962 | .pior.power = nv50_pior_power, | ||
963 | }; | 626 | }; |
964 | 627 | ||
965 | int | 628 | int |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index 1e1de6bfe85a..19c635663399 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h | |||
@@ -2,18 +2,13 @@ | |||
2 | #define __NV50_DISP_H__ | 2 | #define __NV50_DISP_H__ |
3 | #define nv50_disp(p) container_of((p), struct nv50_disp, base) | 3 | #define nv50_disp(p) container_of((p), struct nv50_disp, base) |
4 | #include "priv.h" | 4 | #include "priv.h" |
5 | struct nvkm_output; | 5 | struct nvkm_head; |
6 | struct nvkm_output_dp; | ||
7 | |||
8 | #define NV50_DISP_MTHD_ struct nvkm_object *object, \ | ||
9 | struct nv50_disp *disp, void *data, u32 size | ||
10 | #define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head | ||
11 | #define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp | ||
12 | 6 | ||
13 | struct nv50_disp { | 7 | struct nv50_disp { |
14 | const struct nv50_disp_func *func; | 8 | const struct nv50_disp_func *func; |
15 | struct nvkm_disp base; | 9 | struct nvkm_disp base; |
16 | 10 | ||
11 | struct workqueue_struct *wq; | ||
17 | struct work_struct supervisor; | 12 | struct work_struct supervisor; |
18 | u32 super; | 13 | u32 super; |
19 | 14 | ||
@@ -30,42 +25,18 @@ struct nv50_disp { | |||
30 | struct nv50_disp_chan *chan[17]; | 25 | struct nv50_disp_chan *chan[17]; |
31 | }; | 26 | }; |
32 | 27 | ||
33 | int nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0); | 28 | void nv50_disp_super_1(struct nv50_disp *); |
34 | 29 | void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *); | |
35 | int gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0); | 30 | void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *); |
36 | 31 | void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *); | |
37 | int nv50_dac_power(NV50_DISP_MTHD_V1); | 32 | void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *); |
38 | int nv50_dac_sense(NV50_DISP_MTHD_V1); | 33 | void nv50_disp_super_3_0(struct nv50_disp *, struct nvkm_head *); |
39 | |||
40 | int gt215_hda_eld(NV50_DISP_MTHD_V1); | ||
41 | int gf119_hda_eld(NV50_DISP_MTHD_V1); | ||
42 | |||
43 | int g84_hdmi_ctrl(NV50_DISP_MTHD_V1); | ||
44 | int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1); | ||
45 | int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1); | ||
46 | int gk104_hdmi_ctrl(NV50_DISP_MTHD_V1); | ||
47 | |||
48 | int nv50_sor_power(NV50_DISP_MTHD_V1); | ||
49 | int nv50_pior_power(NV50_DISP_MTHD_V1); | ||
50 | 34 | ||
51 | int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *, | 35 | int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *, |
52 | int index, int heads, struct nvkm_disp **); | 36 | int index, int heads, struct nvkm_disp **); |
53 | int gf119_disp_new_(const struct nv50_disp_func *, struct nvkm_device *, | 37 | int gf119_disp_new_(const struct nv50_disp_func *, struct nvkm_device *, |
54 | int index, struct nvkm_disp **); | 38 | int index, struct nvkm_disp **); |
55 | 39 | ||
56 | struct nv50_disp_func_outp { | ||
57 | int (* crt)(struct nvkm_disp *, int index, struct dcb_output *, | ||
58 | struct nvkm_output **); | ||
59 | int (* tv)(struct nvkm_disp *, int index, struct dcb_output *, | ||
60 | struct nvkm_output **); | ||
61 | int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *, | ||
62 | struct nvkm_output **); | ||
63 | int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *, | ||
64 | struct nvkm_output **); | ||
65 | int (* dp)(struct nvkm_disp *, int index, struct dcb_output *, | ||
66 | struct nvkm_output **); | ||
67 | }; | ||
68 | |||
69 | struct nv50_disp_func { | 40 | struct nv50_disp_func { |
70 | void (*intr)(struct nv50_disp *); | 41 | void (*intr)(struct nv50_disp *); |
71 | void (*intr_error)(struct nv50_disp *, int chid); | 42 | void (*intr_error)(struct nv50_disp *, int chid); |
@@ -76,44 +47,33 @@ struct nv50_disp_func { | |||
76 | const struct nvkm_disp_oclass *root; | 47 | const struct nvkm_disp_oclass *root; |
77 | 48 | ||
78 | struct { | 49 | struct { |
79 | void (*vblank_init)(struct nv50_disp *, int head); | 50 | int (*new)(struct nvkm_disp *, int id); |
80 | void (*vblank_fini)(struct nv50_disp *, int head); | ||
81 | int (*scanoutpos)(NV50_DISP_MTHD_V0); | ||
82 | } head; | 51 | } head; |
83 | 52 | ||
84 | struct { | 53 | struct { |
85 | const struct nv50_disp_func_outp internal; | ||
86 | const struct nv50_disp_func_outp external; | ||
87 | } outp; | ||
88 | |||
89 | struct { | ||
90 | int nr; | 54 | int nr; |
91 | int (*power)(NV50_DISP_MTHD_V1); | 55 | int (*new)(struct nvkm_disp *, int id); |
92 | int (*sense)(NV50_DISP_MTHD_V1); | ||
93 | } dac; | 56 | } dac; |
94 | 57 | ||
95 | struct { | 58 | struct { |
96 | int nr; | 59 | int nr; |
97 | int (*power)(NV50_DISP_MTHD_V1); | 60 | int (*new)(struct nvkm_disp *, int id); |
98 | int (*hda_eld)(NV50_DISP_MTHD_V1); | ||
99 | int (*hdmi)(NV50_DISP_MTHD_V1); | ||
100 | void (*magic)(struct nvkm_output *); | ||
101 | } sor; | 61 | } sor; |
102 | 62 | ||
103 | struct { | 63 | struct { |
104 | int nr; | 64 | int nr; |
105 | int (*power)(NV50_DISP_MTHD_V1); | 65 | int (*new)(struct nvkm_disp *, int id); |
106 | } pior; | 66 | } pior; |
107 | }; | 67 | }; |
108 | 68 | ||
109 | void nv50_disp_vblank_init(struct nv50_disp *, int); | ||
110 | void nv50_disp_vblank_fini(struct nv50_disp *, int); | ||
111 | void nv50_disp_intr(struct nv50_disp *); | 69 | void nv50_disp_intr(struct nv50_disp *); |
112 | void nv50_disp_intr_supervisor(struct work_struct *); | 70 | void nv50_disp_super(struct work_struct *); |
113 | 71 | ||
114 | void gf119_disp_vblank_init(struct nv50_disp *, int); | ||
115 | void gf119_disp_vblank_fini(struct nv50_disp *, int); | ||
116 | void gf119_disp_intr(struct nv50_disp *); | 72 | void gf119_disp_intr(struct nv50_disp *); |
117 | void gf119_disp_intr_supervisor(struct work_struct *); | 73 | void gf119_disp_super(struct work_struct *); |
118 | void gf119_disp_intr_error(struct nv50_disp *, int); | 74 | void gf119_disp_intr_error(struct nv50_disp *, int); |
75 | |||
76 | void nv50_disp_dptmds_war_2(struct nv50_disp *, struct dcb_output *); | ||
77 | void nv50_disp_dptmds_war_3(struct nv50_disp *, struct dcb_output *); | ||
78 | void nv50_disp_update_sppll1(struct nv50_disp *); | ||
119 | #endif | 79 | #endif |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c index 07540f3d32dc..f3b0fa2c5924 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/oimmnv50.c | |||
@@ -22,6 +22,7 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "channv50.h" | 24 | #include "channv50.h" |
25 | #include "head.h" | ||
25 | #include "rootnv50.h" | 26 | #include "rootnv50.h" |
26 | 27 | ||
27 | #include <core/client.h> | 28 | #include <core/client.h> |
@@ -48,7 +49,7 @@ nv50_disp_oimm_new(const struct nv50_disp_chan_func *func, | |||
48 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 49 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { |
49 | nvif_ioctl(parent, "create disp overlay vers %d head %d\n", | 50 | nvif_ioctl(parent, "create disp overlay vers %d head %d\n", |
50 | args->v0.version, args->v0.head); | 51 | args->v0.version, args->v0.head); |
51 | if (args->v0.head > disp->base.head.nr) | 52 | if (!nvkm_head_find(&disp->base, args->v0.head)) |
52 | return -EINVAL; | 53 | return -EINVAL; |
53 | head = args->v0.head; | 54 | head = args->v0.head; |
54 | } else | 55 | } else |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index bbe5ec0dedb2..85aff85394ac 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c | |||
@@ -22,29 +22,207 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "outp.h" | 24 | #include "outp.h" |
25 | #include "ior.h" | ||
25 | 26 | ||
26 | #include <subdev/bios.h> | 27 | #include <subdev/bios.h> |
27 | #include <subdev/bios/dcb.h> | 28 | #include <subdev/bios/dcb.h> |
28 | #include <subdev/i2c.h> | 29 | #include <subdev/i2c.h> |
29 | 30 | ||
30 | void | 31 | void |
31 | nvkm_output_fini(struct nvkm_output *outp) | 32 | nvkm_outp_route(struct nvkm_disp *disp) |
33 | { | ||
34 | struct nvkm_outp *outp; | ||
35 | struct nvkm_ior *ior; | ||
36 | |||
37 | list_for_each_entry(ior, &disp->ior, head) { | ||
38 | if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) { | ||
39 | OUTP_DBG(outp, "release %s", ior->name); | ||
40 | if (ior->func->route.set) | ||
41 | ior->func->route.set(outp, NULL); | ||
42 | ior->arm.outp = NULL; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | list_for_each_entry(ior, &disp->ior, head) { | ||
47 | if ((outp = ior->asy.outp)) { | ||
48 | OUTP_DBG(outp, "acquire %s", ior->name); | ||
49 | if (ior->asy.outp != ior->arm.outp) { | ||
50 | if (ior->func->route.set) | ||
51 | ior->func->route.set(outp, ior); | ||
52 | ior->arm.outp = ior->asy.outp; | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | static enum nvkm_ior_proto | ||
59 | nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) | ||
60 | { | ||
61 | switch (outp->info.location) { | ||
62 | case 0: | ||
63 | switch (outp->info.type) { | ||
64 | case DCB_OUTPUT_ANALOG: *type = DAC; return CRT; | ||
65 | case DCB_OUTPUT_TMDS : *type = SOR; return TMDS; | ||
66 | case DCB_OUTPUT_LVDS : *type = SOR; return LVDS; | ||
67 | case DCB_OUTPUT_DP : *type = SOR; return DP; | ||
68 | default: | ||
69 | break; | ||
70 | } | ||
71 | break; | ||
72 | case 1: | ||
73 | switch (outp->info.type) { | ||
74 | case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS; | ||
75 | case DCB_OUTPUT_DP : *type = PIOR; return TMDS; /* not a bug */ | ||
76 | default: | ||
77 | break; | ||
78 | } | ||
79 | break; | ||
80 | default: | ||
81 | break; | ||
82 | } | ||
83 | WARN_ON(1); | ||
84 | return UNKNOWN; | ||
85 | } | ||
86 | |||
87 | void | ||
88 | nvkm_outp_release(struct nvkm_outp *outp, u8 user) | ||
89 | { | ||
90 | struct nvkm_ior *ior = outp->ior; | ||
91 | OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); | ||
92 | if (ior) { | ||
93 | outp->acquired &= ~user; | ||
94 | if (!outp->acquired) { | ||
95 | outp->ior->asy.outp = NULL; | ||
96 | outp->ior = NULL; | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
101 | static inline int | ||
102 | nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) | ||
103 | { | ||
104 | outp->ior = ior; | ||
105 | outp->ior->asy.outp = outp; | ||
106 | outp->ior->asy.link = outp->info.sorconf.link; | ||
107 | outp->acquired |= user; | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | int | ||
112 | nvkm_outp_acquire(struct nvkm_outp *outp, u8 user) | ||
113 | { | ||
114 | struct nvkm_ior *ior = outp->ior; | ||
115 | enum nvkm_ior_proto proto; | ||
116 | enum nvkm_ior_type type; | ||
117 | |||
118 | OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior); | ||
119 | if (ior) { | ||
120 | outp->acquired |= user; | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* Lookup a compatible, and unused, OR to assign to the device. */ | ||
125 | proto = nvkm_outp_xlat(outp, &type); | ||
126 | if (proto == UNKNOWN) | ||
127 | return -ENOSYS; | ||
128 | |||
129 | /* First preference is to reuse the OR that is currently armed | ||
130 | * on HW, if any, in order to prevent unnecessary switching. | ||
131 | */ | ||
132 | list_for_each_entry(ior, &outp->disp->ior, head) { | ||
133 | if (!ior->asy.outp && ior->arm.outp == outp) | ||
134 | return nvkm_outp_acquire_ior(outp, user, ior); | ||
135 | } | ||
136 | |||
137 | /* Failing that, a completely unused OR is the next best thing. */ | ||
138 | list_for_each_entry(ior, &outp->disp->ior, head) { | ||
139 | if (!ior->asy.outp && ior->type == type && !ior->arm.outp && | ||
140 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) | ||
141 | return nvkm_outp_acquire_ior(outp, user, ior); | ||
142 | } | ||
143 | |||
144 | /* Last resort is to assign an OR that's already active on HW, | ||
145 | * but will be released during the next modeset. | ||
146 | */ | ||
147 | list_for_each_entry(ior, &outp->disp->ior, head) { | ||
148 | if (!ior->asy.outp && ior->type == type && | ||
149 | (ior->func->route.set || ior->id == __ffs(outp->info.or))) | ||
150 | return nvkm_outp_acquire_ior(outp, user, ior); | ||
151 | } | ||
152 | |||
153 | return -ENOSPC; | ||
154 | } | ||
155 | |||
156 | void | ||
157 | nvkm_outp_fini(struct nvkm_outp *outp) | ||
32 | { | 158 | { |
33 | if (outp->func->fini) | 159 | if (outp->func->fini) |
34 | outp->func->fini(outp); | 160 | outp->func->fini(outp); |
35 | } | 161 | } |
36 | 162 | ||
163 | static void | ||
164 | nvkm_outp_init_route(struct nvkm_outp *outp) | ||
165 | { | ||
166 | struct nvkm_disp *disp = outp->disp; | ||
167 | enum nvkm_ior_proto proto; | ||
168 | enum nvkm_ior_type type; | ||
169 | struct nvkm_ior *ior; | ||
170 | int id, link; | ||
171 | |||
172 | /* Find any OR from the class that is able to support this device. */ | ||
173 | proto = nvkm_outp_xlat(outp, &type); | ||
174 | if (proto == UNKNOWN) | ||
175 | return; | ||
176 | |||
177 | ior = nvkm_ior_find(disp, type, -1); | ||
178 | if (!ior) { | ||
179 | WARN_ON(1); | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | /* Determine the specific OR, if any, this device is attached to. */ | ||
184 | if (ior->func->route.get) { | ||
185 | id = ior->func->route.get(outp, &link); | ||
186 | if (id < 0) { | ||
187 | OUTP_DBG(outp, "no route"); | ||
188 | return; | ||
189 | } | ||
190 | } else { | ||
191 | /* Prior to DCB 4.1, this is hardwired like so. */ | ||
192 | id = ffs(outp->info.or) - 1; | ||
193 | link = (ior->type == SOR) ? outp->info.sorconf.link : 0; | ||
194 | } | ||
195 | |||
196 | ior = nvkm_ior_find(disp, type, id); | ||
197 | if (!ior) { | ||
198 | WARN_ON(1); | ||
199 | return; | ||
200 | } | ||
201 | |||
202 | /* Determine if the OR is already configured for this device. */ | ||
203 | ior->func->state(ior, &ior->arm); | ||
204 | if (!ior->arm.head || ior->arm.proto != proto) { | ||
205 | OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head, | ||
206 | ior->arm.proto, proto); | ||
207 | return; | ||
208 | } | ||
209 | |||
210 | OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link); | ||
211 | ior->arm.outp = outp; | ||
212 | } | ||
213 | |||
37 | void | 214 | void |
38 | nvkm_output_init(struct nvkm_output *outp) | 215 | nvkm_outp_init(struct nvkm_outp *outp) |
39 | { | 216 | { |
217 | nvkm_outp_init_route(outp); | ||
40 | if (outp->func->init) | 218 | if (outp->func->init) |
41 | outp->func->init(outp); | 219 | outp->func->init(outp); |
42 | } | 220 | } |
43 | 221 | ||
44 | void | 222 | void |
45 | nvkm_output_del(struct nvkm_output **poutp) | 223 | nvkm_outp_del(struct nvkm_outp **poutp) |
46 | { | 224 | { |
47 | struct nvkm_output *outp = *poutp; | 225 | struct nvkm_outp *outp = *poutp; |
48 | if (outp && !WARN_ON(!outp->func)) { | 226 | if (outp && !WARN_ON(!outp->func)) { |
49 | if (outp->func->dtor) | 227 | if (outp->func->dtor) |
50 | *poutp = outp->func->dtor(outp); | 228 | *poutp = outp->func->dtor(outp); |
@@ -53,11 +231,13 @@ nvkm_output_del(struct nvkm_output **poutp) | |||
53 | } | 231 | } |
54 | } | 232 | } |
55 | 233 | ||
56 | void | 234 | int |
57 | nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp, | 235 | nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp, |
58 | int index, struct dcb_output *dcbE, struct nvkm_output *outp) | 236 | int index, struct dcb_output *dcbE, struct nvkm_outp *outp) |
59 | { | 237 | { |
60 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; | 238 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; |
239 | enum nvkm_ior_proto proto; | ||
240 | enum nvkm_ior_type type; | ||
61 | 241 | ||
62 | outp->func = func; | 242 | outp->func = func; |
63 | outp->disp = disp; | 243 | outp->disp = disp; |
@@ -72,16 +252,24 @@ nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp, | |||
72 | outp->info.type >= 2 ? outp->info.sorconf.link : 0, | 252 | outp->info.type >= 2 ? outp->info.sorconf.link : 0, |
73 | outp->info.connector, outp->info.i2c_index, | 253 | outp->info.connector, outp->info.i2c_index, |
74 | outp->info.bus, outp->info.heads); | 254 | outp->info.bus, outp->info.heads); |
255 | |||
256 | /* Cull output paths we can't map to an output resource. */ | ||
257 | proto = nvkm_outp_xlat(outp, &type); | ||
258 | if (proto == UNKNOWN) | ||
259 | return -ENODEV; | ||
260 | |||
261 | return 0; | ||
75 | } | 262 | } |
76 | 263 | ||
264 | static const struct nvkm_outp_func | ||
265 | nvkm_outp = { | ||
266 | }; | ||
267 | |||
77 | int | 268 | int |
78 | nvkm_output_new_(const struct nvkm_output_func *func, | 269 | nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, |
79 | struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | 270 | struct nvkm_outp **poutp) |
80 | struct nvkm_output **poutp) | ||
81 | { | 271 | { |
82 | if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL))) | 272 | if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL))) |
83 | return -ENOMEM; | 273 | return -ENOMEM; |
84 | 274 | return nvkm_outp_ctor(&nvkm_outp, disp, index, dcbE, *poutp); | |
85 | nvkm_output_ctor(func, disp, index, dcbE, *poutp); | ||
86 | return 0; | ||
87 | } | 275 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index 07727198d7ce..146d101d4891 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h | |||
@@ -5,47 +5,46 @@ | |||
5 | #include <subdev/bios.h> | 5 | #include <subdev/bios.h> |
6 | #include <subdev/bios/dcb.h> | 6 | #include <subdev/bios/dcb.h> |
7 | 7 | ||
8 | struct nvkm_output { | 8 | struct nvkm_outp { |
9 | const struct nvkm_output_func *func; | 9 | const struct nvkm_outp_func *func; |
10 | struct nvkm_disp *disp; | 10 | struct nvkm_disp *disp; |
11 | int index; | 11 | int index; |
12 | struct dcb_output info; | 12 | struct dcb_output info; |
13 | 13 | ||
14 | // whatever (if anything) is pointed at by the dcb device entry | ||
15 | struct nvkm_i2c_bus *i2c; | 14 | struct nvkm_i2c_bus *i2c; |
16 | int or; | 15 | int or; |
17 | 16 | ||
18 | struct list_head head; | 17 | struct list_head head; |
19 | struct nvkm_connector *conn; | 18 | struct nvkm_conn *conn; |
20 | }; | ||
21 | 19 | ||
22 | struct nvkm_output_func { | 20 | /* Assembly state. */ |
23 | void *(*dtor)(struct nvkm_output *); | 21 | #define NVKM_OUTP_PRIV 1 |
24 | void (*init)(struct nvkm_output *); | 22 | #define NVKM_OUTP_USER 2 |
25 | void (*fini)(struct nvkm_output *); | 23 | u8 acquired:2; |
24 | struct nvkm_ior *ior; | ||
26 | }; | 25 | }; |
27 | 26 | ||
28 | void nvkm_output_ctor(const struct nvkm_output_func *, struct nvkm_disp *, | 27 | int nvkm_outp_ctor(const struct nvkm_outp_func *, struct nvkm_disp *, |
29 | int index, struct dcb_output *, struct nvkm_output *); | 28 | int index, struct dcb_output *, struct nvkm_outp *); |
30 | int nvkm_output_new_(const struct nvkm_output_func *, struct nvkm_disp *, | 29 | int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, |
31 | int index, struct dcb_output *, struct nvkm_output **); | 30 | struct nvkm_outp **); |
32 | void nvkm_output_del(struct nvkm_output **); | 31 | void nvkm_outp_del(struct nvkm_outp **); |
33 | void nvkm_output_init(struct nvkm_output *); | 32 | void nvkm_outp_init(struct nvkm_outp *); |
34 | void nvkm_output_fini(struct nvkm_output *); | 33 | void nvkm_outp_fini(struct nvkm_outp *); |
35 | 34 | int nvkm_outp_acquire(struct nvkm_outp *, u8 user); | |
36 | int nv50_dac_output_new(struct nvkm_disp *, int, struct dcb_output *, | 35 | void nvkm_outp_release(struct nvkm_outp *, u8 user); |
37 | struct nvkm_output **); | 36 | void nvkm_outp_route(struct nvkm_disp *); |
38 | int nv50_sor_output_new(struct nvkm_disp *, int, struct dcb_output *, | 37 | |
39 | struct nvkm_output **); | 38 | struct nvkm_outp_func { |
40 | int nv50_pior_output_new(struct nvkm_disp *, int, struct dcb_output *, | 39 | void *(*dtor)(struct nvkm_outp *); |
41 | struct nvkm_output **); | 40 | void (*init)(struct nvkm_outp *); |
42 | 41 | void (*fini)(struct nvkm_outp *); | |
43 | u32 g94_sor_dp_lane_map(struct nvkm_device *, u8 lane); | 42 | int (*acquire)(struct nvkm_outp *); |
44 | 43 | void (*release)(struct nvkm_outp *, struct nvkm_ior *); | |
45 | void gm200_sor_magic(struct nvkm_output *outp); | 44 | }; |
46 | 45 | ||
47 | #define OUTP_MSG(o,l,f,a...) do { \ | 46 | #define OUTP_MSG(o,l,f,a...) do { \ |
48 | struct nvkm_output *_outp = (o); \ | 47 | struct nvkm_outp *_outp = (o); \ |
49 | nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \ | 48 | nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \ |
50 | _outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \ | 49 | _outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \ |
51 | } while(0) | 50 | } while(0) |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c deleted file mode 100644 index de36f73b14dc..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.c +++ /dev/null | |||
@@ -1,282 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright 2014 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 | */ | ||
24 | #include "outpdp.h" | ||
25 | #include "conn.h" | ||
26 | #include "dport.h" | ||
27 | #include "priv.h" | ||
28 | |||
29 | #include <subdev/i2c.h> | ||
30 | |||
31 | #include <nvif/event.h> | ||
32 | |||
33 | int | ||
34 | nvkm_output_dp_train(struct nvkm_output *base, u32 datarate) | ||
35 | { | ||
36 | struct nvkm_output_dp *outp = nvkm_output_dp(base); | ||
37 | bool retrain = true; | ||
38 | u8 link[2], stat[3]; | ||
39 | u32 linkrate; | ||
40 | int ret, i; | ||
41 | |||
42 | mutex_lock(&outp->mutex); | ||
43 | |||
44 | /* check that the link is trained at a high enough rate */ | ||
45 | ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2); | ||
46 | if (ret) { | ||
47 | OUTP_DBG(&outp->base, | ||
48 | "failed to read link config, assuming no sink"); | ||
49 | goto done; | ||
50 | } | ||
51 | |||
52 | linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET); | ||
53 | linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */ | ||
54 | datarate = (datarate + 9) / 10; /* -> decakilobits */ | ||
55 | if (linkrate < datarate) { | ||
56 | OUTP_DBG(&outp->base, "link not trained at sufficient rate"); | ||
57 | goto done; | ||
58 | } | ||
59 | |||
60 | /* check that link is still trained */ | ||
61 | ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3); | ||
62 | if (ret) { | ||
63 | OUTP_DBG(&outp->base, | ||
64 | "failed to read link status, assuming no sink"); | ||
65 | goto done; | ||
66 | } | ||
67 | |||
68 | if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { | ||
69 | for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) { | ||
70 | u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; | ||
71 | if (!(lane & DPCD_LS02_LANE0_CR_DONE) || | ||
72 | !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || | ||
73 | !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { | ||
74 | OUTP_DBG(&outp->base, | ||
75 | "lane %d not equalised", lane); | ||
76 | goto done; | ||
77 | } | ||
78 | } | ||
79 | retrain = false; | ||
80 | } else { | ||
81 | OUTP_DBG(&outp->base, "no inter-lane alignment"); | ||
82 | } | ||
83 | |||
84 | done: | ||
85 | if (retrain || !atomic_read(&outp->lt.done)) { | ||
86 | /* no sink, but still need to configure source */ | ||
87 | if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { | ||
88 | outp->dpcd[DPCD_RC01_MAX_LINK_RATE] = | ||
89 | outp->base.info.dpconf.link_bw; | ||
90 | outp->dpcd[DPCD_RC02] = | ||
91 | outp->base.info.dpconf.link_nr; | ||
92 | } | ||
93 | nvkm_dp_train(outp); | ||
94 | } | ||
95 | |||
96 | mutex_unlock(&outp->mutex); | ||
97 | return ret; | ||
98 | } | ||
99 | |||
100 | static void | ||
101 | nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable) | ||
102 | { | ||
103 | struct nvkm_i2c_aux *aux = outp->aux; | ||
104 | |||
105 | if (enable) { | ||
106 | if (!outp->present) { | ||
107 | OUTP_DBG(&outp->base, "aux power -> always"); | ||
108 | nvkm_i2c_aux_monitor(aux, true); | ||
109 | outp->present = true; | ||
110 | } | ||
111 | |||
112 | if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dpcd, | ||
113 | sizeof(outp->dpcd))) { | ||
114 | nvkm_output_dp_train(&outp->base, 0); | ||
115 | return; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (outp->present) { | ||
120 | OUTP_DBG(&outp->base, "aux power -> demand"); | ||
121 | nvkm_i2c_aux_monitor(aux, false); | ||
122 | outp->present = false; | ||
123 | } | ||
124 | |||
125 | atomic_set(&outp->lt.done, 0); | ||
126 | } | ||
127 | |||
128 | static int | ||
129 | nvkm_output_dp_hpd(struct nvkm_notify *notify) | ||
130 | { | ||
131 | const struct nvkm_i2c_ntfy_rep *line = notify->data; | ||
132 | struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), hpd); | ||
133 | struct nvkm_connector *conn = outp->base.conn; | ||
134 | struct nvkm_disp *disp = outp->base.disp; | ||
135 | struct nvif_notify_conn_rep_v0 rep = {}; | ||
136 | |||
137 | OUTP_DBG(&outp->base, "HPD: %d", line->mask); | ||
138 | nvkm_output_dp_enable(outp, true); | ||
139 | |||
140 | if (line->mask & NVKM_I2C_UNPLUG) | ||
141 | rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG; | ||
142 | if (line->mask & NVKM_I2C_PLUG) | ||
143 | rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG; | ||
144 | |||
145 | nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); | ||
146 | return NVKM_NOTIFY_KEEP; | ||
147 | } | ||
148 | |||
149 | static int | ||
150 | nvkm_output_dp_irq(struct nvkm_notify *notify) | ||
151 | { | ||
152 | const struct nvkm_i2c_ntfy_rep *line = notify->data; | ||
153 | struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq); | ||
154 | struct nvkm_connector *conn = outp->base.conn; | ||
155 | struct nvkm_disp *disp = outp->base.disp; | ||
156 | struct nvif_notify_conn_rep_v0 rep = { | ||
157 | .mask = NVIF_NOTIFY_CONN_V0_IRQ, | ||
158 | }; | ||
159 | |||
160 | OUTP_DBG(&outp->base, "IRQ: %d", line->mask); | ||
161 | nvkm_output_dp_train(&outp->base, 0); | ||
162 | |||
163 | nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep)); | ||
164 | return NVKM_NOTIFY_KEEP; | ||
165 | } | ||
166 | |||
167 | static void | ||
168 | nvkm_output_dp_fini(struct nvkm_output *base) | ||
169 | { | ||
170 | struct nvkm_output_dp *outp = nvkm_output_dp(base); | ||
171 | nvkm_notify_put(&outp->hpd); | ||
172 | nvkm_notify_put(&outp->irq); | ||
173 | nvkm_output_dp_enable(outp, false); | ||
174 | } | ||
175 | |||
176 | static void | ||
177 | nvkm_output_dp_init(struct nvkm_output *base) | ||
178 | { | ||
179 | struct nvkm_output_dp *outp = nvkm_output_dp(base); | ||
180 | nvkm_notify_put(&outp->base.conn->hpd); | ||
181 | nvkm_output_dp_enable(outp, true); | ||
182 | nvkm_notify_get(&outp->irq); | ||
183 | nvkm_notify_get(&outp->hpd); | ||
184 | } | ||
185 | |||
186 | static void * | ||
187 | nvkm_output_dp_dtor(struct nvkm_output *base) | ||
188 | { | ||
189 | struct nvkm_output_dp *outp = nvkm_output_dp(base); | ||
190 | nvkm_notify_fini(&outp->hpd); | ||
191 | nvkm_notify_fini(&outp->irq); | ||
192 | return outp; | ||
193 | } | ||
194 | |||
195 | static const struct nvkm_output_func | ||
196 | nvkm_output_dp_func = { | ||
197 | .dtor = nvkm_output_dp_dtor, | ||
198 | .init = nvkm_output_dp_init, | ||
199 | .fini = nvkm_output_dp_fini, | ||
200 | }; | ||
201 | |||
202 | int | ||
203 | nvkm_output_dp_ctor(const struct nvkm_output_dp_func *func, | ||
204 | struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | ||
205 | struct nvkm_i2c_aux *aux, struct nvkm_output_dp *outp) | ||
206 | { | ||
207 | struct nvkm_device *device = disp->engine.subdev.device; | ||
208 | struct nvkm_bios *bios = device->bios; | ||
209 | struct nvkm_i2c *i2c = device->i2c; | ||
210 | u8 hdr, cnt, len; | ||
211 | u32 data; | ||
212 | int ret; | ||
213 | |||
214 | nvkm_output_ctor(&nvkm_output_dp_func, disp, index, dcbE, &outp->base); | ||
215 | outp->func = func; | ||
216 | outp->aux = aux; | ||
217 | if (!outp->aux) { | ||
218 | OUTP_ERR(&outp->base, "no aux"); | ||
219 | return -ENODEV; | ||
220 | } | ||
221 | |||
222 | /* bios data is not optional */ | ||
223 | data = nvbios_dpout_match(bios, outp->base.info.hasht, | ||
224 | outp->base.info.hashm, &outp->version, | ||
225 | &hdr, &cnt, &len, &outp->info); | ||
226 | if (!data) { | ||
227 | OUTP_ERR(&outp->base, "no bios dp data"); | ||
228 | return -ENODEV; | ||
229 | } | ||
230 | |||
231 | OUTP_DBG(&outp->base, "bios dp %02x %02x %02x %02x", | ||
232 | outp->version, hdr, cnt, len); | ||
233 | |||
234 | /* link maintenance */ | ||
235 | ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true, | ||
236 | &(struct nvkm_i2c_ntfy_req) { | ||
237 | .mask = NVKM_I2C_IRQ, | ||
238 | .port = outp->aux->id, | ||
239 | }, | ||
240 | sizeof(struct nvkm_i2c_ntfy_req), | ||
241 | sizeof(struct nvkm_i2c_ntfy_rep), | ||
242 | &outp->irq); | ||
243 | if (ret) { | ||
244 | OUTP_ERR(&outp->base, "error monitoring aux irq: %d", ret); | ||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | mutex_init(&outp->mutex); | ||
249 | atomic_set(&outp->lt.done, 0); | ||
250 | |||
251 | /* hotplug detect, replaces gpio-based mechanism with aux events */ | ||
252 | ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true, | ||
253 | &(struct nvkm_i2c_ntfy_req) { | ||
254 | .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG, | ||
255 | .port = outp->aux->id, | ||
256 | }, | ||
257 | sizeof(struct nvkm_i2c_ntfy_req), | ||
258 | sizeof(struct nvkm_i2c_ntfy_rep), | ||
259 | &outp->hpd); | ||
260 | if (ret) { | ||
261 | OUTP_ERR(&outp->base, "error monitoring aux hpd: %d", ret); | ||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | int | ||
269 | nvkm_output_dp_new_(const struct nvkm_output_dp_func *func, | ||
270 | struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | ||
271 | struct nvkm_output **poutp) | ||
272 | { | ||
273 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; | ||
274 | struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbE->i2c_index); | ||
275 | struct nvkm_output_dp *outp; | ||
276 | |||
277 | if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL))) | ||
278 | return -ENOMEM; | ||
279 | *poutp = &outp->base; | ||
280 | |||
281 | return nvkm_output_dp_ctor(func, disp, index, dcbE, aux, outp); | ||
282 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h deleted file mode 100644 index 3c83a561cd88..000000000000 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outpdp.h +++ /dev/null | |||
@@ -1,76 +0,0 @@ | |||
1 | #ifndef __NVKM_DISP_OUTP_DP_H__ | ||
2 | #define __NVKM_DISP_OUTP_DP_H__ | ||
3 | #define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base) | ||
4 | #ifndef MSG | ||
5 | #define MSG(l,f,a...) \ | ||
6 | nvkm_##l(&outp->base.disp->engine.subdev, "%02x:%04x:%04x: "f, \ | ||
7 | outp->base.index, outp->base.info.hasht, \ | ||
8 | outp->base.info.hashm, ##a) | ||
9 | #define DBG(f,a...) MSG(debug, f, ##a) | ||
10 | #define ERR(f,a...) MSG(error, f, ##a) | ||
11 | #endif | ||
12 | #include "outp.h" | ||
13 | |||
14 | #include <core/notify.h> | ||
15 | #include <subdev/bios.h> | ||
16 | #include <subdev/bios/dp.h> | ||
17 | |||
18 | struct nvkm_output_dp { | ||
19 | const struct nvkm_output_dp_func *func; | ||
20 | struct nvkm_output base; | ||
21 | |||
22 | struct nvbios_dpout info; | ||
23 | u8 version; | ||
24 | |||
25 | struct nvkm_i2c_aux *aux; | ||
26 | |||
27 | struct nvkm_notify irq; | ||
28 | struct nvkm_notify hpd; | ||
29 | bool present; | ||
30 | u8 dpcd[16]; | ||
31 | |||
32 | struct mutex mutex; | ||
33 | struct { | ||
34 | atomic_t done; | ||
35 | bool mst; | ||
36 | } lt; | ||
37 | }; | ||
38 | |||
39 | struct nvkm_output_dp_func { | ||
40 | int (*pattern)(struct nvkm_output_dp *, int); | ||
41 | int (*lnk_pwr)(struct nvkm_output_dp *, int nr); | ||
42 | int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef); | ||
43 | int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc); | ||
44 | void (*vcpi)(struct nvkm_output_dp *, int head, u8 start_slot, | ||
45 | u8 num_slots, u16 pbn, u16 aligned_pbn); | ||
46 | }; | ||
47 | |||
48 | int nvkm_output_dp_train(struct nvkm_output *, u32 rate); | ||
49 | |||
50 | int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *, | ||
51 | int index, struct dcb_output *, struct nvkm_i2c_aux *, | ||
52 | struct nvkm_output_dp *); | ||
53 | int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *, | ||
54 | int index, struct dcb_output *, | ||
55 | struct nvkm_output **); | ||
56 | |||
57 | int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *, | ||
58 | struct nvkm_output **); | ||
59 | |||
60 | int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, | ||
61 | struct nvkm_output **); | ||
62 | int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int); | ||
63 | |||
64 | int gf119_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, | ||
65 | struct nvkm_output **); | ||
66 | int gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool); | ||
67 | int gf119_sor_dp_drv_ctl(struct nvkm_output_dp *, int, int, int, int); | ||
68 | void gf119_sor_dp_vcpi(struct nvkm_output_dp *, int, u8, u8, u16, u16); | ||
69 | |||
70 | int gm107_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, | ||
71 | struct nvkm_output **); | ||
72 | int gm107_sor_dp_pattern(struct nvkm_output_dp *, int); | ||
73 | |||
74 | int gm200_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *, | ||
75 | struct nvkm_output **); | ||
76 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c index 2a49c46425cd..9ebaaa6e9e33 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ovlynv50.c | |||
@@ -22,6 +22,7 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "dmacnv50.h" | 24 | #include "dmacnv50.h" |
25 | #include "head.h" | ||
25 | #include "rootnv50.h" | 26 | #include "rootnv50.h" |
26 | 27 | ||
27 | #include <core/client.h> | 28 | #include <core/client.h> |
@@ -50,7 +51,7 @@ nv50_disp_ovly_new(const struct nv50_disp_dmac_func *func, | |||
50 | nvif_ioctl(parent, "create disp overlay channel dma vers %d " | 51 | nvif_ioctl(parent, "create disp overlay channel dma vers %d " |
51 | "pushbuf %016llx head %d\n", | 52 | "pushbuf %016llx head %d\n", |
52 | args->v0.version, args->v0.pushbuf, args->v0.head); | 53 | args->v0.version, args->v0.pushbuf, args->v0.head); |
53 | if (args->v0.head > disp->base.head.nr) | 54 | if (!nvkm_head_find(&disp->base, args->v0.head)) |
54 | return -EINVAL; | 55 | return -EINVAL; |
55 | push = args->v0.pushbuf; | 56 | push = args->v0.pushbuf; |
56 | head = args->v0.head; | 57 | head = args->v0.head; |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c index 6c532eadba17..99b3b9050635 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/piornv50.c | |||
@@ -21,111 +21,114 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "outpdp.h" | 24 | #include "ior.h" |
25 | #include "nv50.h" | 25 | #include "head.h" |
26 | 26 | ||
27 | #include <core/client.h> | ||
28 | #include <subdev/i2c.h> | 27 | #include <subdev/i2c.h> |
29 | #include <subdev/timer.h> | 28 | #include <subdev/timer.h> |
30 | 29 | ||
31 | #include <nvif/cl5070.h> | 30 | static void |
32 | #include <nvif/unpack.h> | 31 | nv50_pior_clock(struct nvkm_ior *pior) |
33 | |||
34 | int | ||
35 | nv50_pior_power(NV50_DISP_MTHD_V1) | ||
36 | { | 32 | { |
37 | struct nvkm_device *device = disp->base.engine.subdev.device; | 33 | struct nvkm_device *device = pior->disp->engine.subdev.device; |
38 | const u32 soff = outp->or * 0x800; | 34 | const u32 poff = nv50_ior_base(pior); |
39 | union { | 35 | nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001); |
40 | struct nv50_disp_pior_pwr_v0 v0; | 36 | } |
41 | } *args = data; | ||
42 | u32 ctrl, type; | ||
43 | int ret = -ENOSYS; | ||
44 | 37 | ||
45 | nvif_ioctl(object, "disp pior pwr size %d\n", size); | 38 | static int |
46 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 39 | nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux) |
47 | nvif_ioctl(object, "disp pior pwr vers %d state %d type %x\n", | 40 | { |
48 | args->v0.version, args->v0.state, args->v0.type); | 41 | int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw, |
49 | if (args->v0.type > 0x0f) | 42 | pior->dp.ef); |
50 | return -EINVAL; | 43 | if (ret) |
51 | ctrl = !!args->v0.state; | ||
52 | type = args->v0.type; | ||
53 | } else | ||
54 | return ret; | 44 | return ret; |
45 | return 1; | ||
46 | } | ||
55 | 47 | ||
48 | static void | ||
49 | nv50_pior_power_wait(struct nvkm_device *device, u32 poff) | ||
50 | { | ||
56 | nvkm_msec(device, 2000, | 51 | nvkm_msec(device, 2000, |
57 | if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000)) | 52 | if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000)) |
58 | break; | ||
59 | ); | ||
60 | nvkm_mask(device, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl); | ||
61 | nvkm_msec(device, 2000, | ||
62 | if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000)) | ||
63 | break; | 53 | break; |
64 | ); | 54 | ); |
65 | disp->pior.type[outp->or] = type; | ||
66 | return 0; | ||
67 | } | 55 | } |
68 | 56 | ||
69 | /****************************************************************************** | 57 | static void |
70 | * TMDS | 58 | nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu, |
71 | *****************************************************************************/ | 59 | bool data, bool vsync, bool hsync) |
72 | static const struct nvkm_output_func | ||
73 | nv50_pior_output_func = { | ||
74 | }; | ||
75 | |||
76 | int | ||
77 | nv50_pior_output_new(struct nvkm_disp *disp, int index, | ||
78 | struct dcb_output *dcbE, struct nvkm_output **poutp) | ||
79 | { | 60 | { |
80 | return nvkm_output_new_(&nv50_pior_output_func, disp, | 61 | struct nvkm_device *device = pior->disp->engine.subdev.device; |
81 | index, dcbE, poutp); | 62 | const u32 poff = nv50_ior_base(pior); |
82 | } | 63 | const u32 shift = normal ? 0 : 16; |
64 | const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift; | ||
65 | const u32 field = 0x80000000 | (0x00000101 << shift); | ||
83 | 66 | ||
84 | /****************************************************************************** | 67 | nv50_pior_power_wait(device, poff); |
85 | * DisplayPort | 68 | nvkm_mask(device, 0x61e004 + poff, field, state); |
86 | *****************************************************************************/ | 69 | nv50_pior_power_wait(device, poff); |
87 | static int | ||
88 | nv50_pior_output_dp_pattern(struct nvkm_output_dp *outp, int pattern) | ||
89 | { | ||
90 | return 0; | ||
91 | } | 70 | } |
92 | 71 | ||
93 | static int | 72 | void |
94 | nv50_pior_output_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) | 73 | nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl) |
95 | { | 74 | { |
96 | return 0; | 75 | /* GF119 moves this information to per-head methods, which is |
76 | * a lot more convenient, and where our shared code expect it. | ||
77 | */ | ||
78 | if (state->head && state == &ior->asy) { | ||
79 | struct nvkm_head *head = | ||
80 | nvkm_head_find(ior->disp, __ffs(state->head)); | ||
81 | if (!WARN_ON(!head)) { | ||
82 | struct nvkm_head_state *state = &head->asy; | ||
83 | switch ((ctrl & 0x000f0000) >> 16) { | ||
84 | case 6: state->or.depth = 30; break; | ||
85 | case 5: state->or.depth = 24; break; | ||
86 | case 2: state->or.depth = 18; break; | ||
87 | case 0: state->or.depth = 18; break; /*XXX*/ | ||
88 | default: | ||
89 | state->or.depth = 18; | ||
90 | WARN_ON(1); | ||
91 | break; | ||
92 | } | ||
93 | } | ||
94 | } | ||
97 | } | 95 | } |
98 | 96 | ||
99 | static int | 97 | static void |
100 | nv50_pior_output_dp_lnk_ctl(struct nvkm_output_dp *outp, | 98 | nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state) |
101 | int nr, int bw, bool ef) | ||
102 | { | 99 | { |
103 | int ret = nvkm_i2c_aux_lnk_ctl(outp->aux, nr, bw, ef); | 100 | struct nvkm_device *device = pior->disp->engine.subdev.device; |
104 | if (ret) | 101 | const u32 coff = pior->id * 8 + (state == &pior->arm) * 4; |
105 | return ret; | 102 | u32 ctrl = nvkm_rd32(device, 0x610b80 + coff); |
106 | return 1; | 103 | |
104 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | ||
105 | state->rgdiv = 1; | ||
106 | switch (state->proto_evo) { | ||
107 | case 0: state->proto = TMDS; break; | ||
108 | default: | ||
109 | state->proto = UNKNOWN; | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | state->head = ctrl & 0x00000003; | ||
114 | nv50_pior_depth(pior, state, ctrl); | ||
107 | } | 115 | } |
108 | 116 | ||
109 | static const struct nvkm_output_dp_func | 117 | static const struct nvkm_ior_func |
110 | nv50_pior_output_dp_func = { | 118 | nv50_pior = { |
111 | .pattern = nv50_pior_output_dp_pattern, | 119 | .state = nv50_pior_state, |
112 | .lnk_pwr = nv50_pior_output_dp_lnk_pwr, | 120 | .power = nv50_pior_power, |
113 | .lnk_ctl = nv50_pior_output_dp_lnk_ctl, | 121 | .clock = nv50_pior_clock, |
122 | .dp = { | ||
123 | .links = nv50_pior_dp_links, | ||
124 | }, | ||
114 | }; | 125 | }; |
115 | 126 | ||
116 | int | 127 | int |
117 | nv50_pior_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | 128 | nv50_pior_new(struct nvkm_disp *disp, int id) |
118 | struct nvkm_output **poutp) | ||
119 | { | 129 | { |
120 | struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c; | 130 | struct nvkm_device *device = disp->engine.subdev.device; |
121 | struct nvkm_i2c_aux *aux = | 131 | if (!(nvkm_rd32(device, 0x610184) & (0x10000000 << id))) |
122 | nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev)); | 132 | return 0; |
123 | struct nvkm_output_dp *outp; | 133 | return nvkm_ior_new_(&nv50_pior, disp, PIOR, id); |
124 | |||
125 | if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL))) | ||
126 | return -ENOMEM; | ||
127 | *poutp = &outp->base; | ||
128 | |||
129 | return nvkm_output_dp_ctor(&nv50_pior_output_dp_func, disp, | ||
130 | index, dcbE, aux, outp); | ||
131 | } | 134 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h index c2452957fc57..5772f0094129 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h | |||
@@ -2,42 +2,18 @@ | |||
2 | #define __NVKM_DISP_PRIV_H__ | 2 | #define __NVKM_DISP_PRIV_H__ |
3 | #include <engine/disp.h> | 3 | #include <engine/disp.h> |
4 | #include "outp.h" | 4 | #include "outp.h" |
5 | #include "outpdp.h" | ||
6 | 5 | ||
7 | int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *, | 6 | int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *, |
8 | int index, int heads, struct nvkm_disp *); | 7 | int index, struct nvkm_disp *); |
9 | int nvkm_disp_new_(const struct nvkm_disp_func *, struct nvkm_device *, | 8 | int nvkm_disp_new_(const struct nvkm_disp_func *, struct nvkm_device *, |
10 | int index, int heads, struct nvkm_disp **); | 9 | int index, struct nvkm_disp **); |
11 | void nvkm_disp_vblank(struct nvkm_disp *, int head); | 10 | void nvkm_disp_vblank(struct nvkm_disp *, int head); |
12 | 11 | ||
13 | struct nvkm_disp_func_outp { | ||
14 | int (* crt)(struct nvkm_disp *, int index, struct dcb_output *, | ||
15 | struct nvkm_output **); | ||
16 | int (* tv)(struct nvkm_disp *, int index, struct dcb_output *, | ||
17 | struct nvkm_output **); | ||
18 | int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *, | ||
19 | struct nvkm_output **); | ||
20 | int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *, | ||
21 | struct nvkm_output **); | ||
22 | int (* dp)(struct nvkm_disp *, int index, struct dcb_output *, | ||
23 | struct nvkm_output **); | ||
24 | }; | ||
25 | |||
26 | struct nvkm_disp_func { | 12 | struct nvkm_disp_func { |
27 | void *(*dtor)(struct nvkm_disp *); | 13 | void *(*dtor)(struct nvkm_disp *); |
28 | void (*intr)(struct nvkm_disp *); | 14 | void (*intr)(struct nvkm_disp *); |
29 | 15 | ||
30 | const struct nvkm_disp_oclass *(*root)(struct nvkm_disp *); | 16 | const struct nvkm_disp_oclass *(*root)(struct nvkm_disp *); |
31 | |||
32 | struct { | ||
33 | void (*vblank_init)(struct nvkm_disp *, int head); | ||
34 | void (*vblank_fini)(struct nvkm_disp *, int head); | ||
35 | } head; | ||
36 | |||
37 | struct { | ||
38 | const struct nvkm_disp_func_outp internal; | ||
39 | const struct nvkm_disp_func_outp external; | ||
40 | } outp; | ||
41 | }; | 17 | }; |
42 | 18 | ||
43 | int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **); | 19 | int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **); |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c index 335d88823c22..333c8424b413 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootgf119.c | |||
@@ -22,49 +22,13 @@ | |||
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "rootnv50.h" | 24 | #include "rootnv50.h" |
25 | #include "head.h" | ||
25 | #include "dmacnv50.h" | 26 | #include "dmacnv50.h" |
26 | 27 | ||
27 | #include <core/client.h> | ||
28 | #include <core/ramht.h> | 28 | #include <core/ramht.h> |
29 | #include <subdev/timer.h> | 29 | #include <subdev/timer.h> |
30 | 30 | ||
31 | #include <nvif/class.h> | 31 | #include <nvif/class.h> |
32 | #include <nvif/cl5070.h> | ||
33 | #include <nvif/unpack.h> | ||
34 | |||
35 | int | ||
36 | gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0) | ||
37 | { | ||
38 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
39 | const u32 total = nvkm_rd32(device, 0x640414 + (head * 0x300)); | ||
40 | const u32 blanke = nvkm_rd32(device, 0x64041c + (head * 0x300)); | ||
41 | const u32 blanks = nvkm_rd32(device, 0x640420 + (head * 0x300)); | ||
42 | union { | ||
43 | struct nv50_disp_scanoutpos_v0 v0; | ||
44 | } *args = data; | ||
45 | int ret = -ENOSYS; | ||
46 | |||
47 | nvif_ioctl(object, "disp scanoutpos size %d\n", size); | ||
48 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
49 | nvif_ioctl(object, "disp scanoutpos vers %d\n", | ||
50 | args->v0.version); | ||
51 | args->v0.vblanke = (blanke & 0xffff0000) >> 16; | ||
52 | args->v0.hblanke = (blanke & 0x0000ffff); | ||
53 | args->v0.vblanks = (blanks & 0xffff0000) >> 16; | ||
54 | args->v0.hblanks = (blanks & 0x0000ffff); | ||
55 | args->v0.vtotal = ( total & 0xffff0000) >> 16; | ||
56 | args->v0.htotal = ( total & 0x0000ffff); | ||
57 | args->v0.time[0] = ktime_to_ns(ktime_get()); | ||
58 | args->v0.vline = /* vline read locks hline */ | ||
59 | nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff; | ||
60 | args->v0.time[1] = ktime_to_ns(ktime_get()); | ||
61 | args->v0.hline = | ||
62 | nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff; | ||
63 | } else | ||
64 | return ret; | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | 32 | ||
69 | void | 33 | void |
70 | gf119_disp_root_fini(struct nv50_disp_root *root) | 34 | gf119_disp_root_fini(struct nv50_disp_root *root) |
@@ -78,6 +42,7 @@ int | |||
78 | gf119_disp_root_init(struct nv50_disp_root *root) | 42 | gf119_disp_root_init(struct nv50_disp_root *root) |
79 | { | 43 | { |
80 | struct nv50_disp *disp = root->disp; | 44 | struct nv50_disp *disp = root->disp; |
45 | struct nvkm_head *head; | ||
81 | struct nvkm_device *device = disp->base.engine.subdev.device; | 46 | struct nvkm_device *device = disp->base.engine.subdev.device; |
82 | u32 tmp; | 47 | u32 tmp; |
83 | int i; | 48 | int i; |
@@ -88,13 +53,14 @@ gf119_disp_root_init(struct nv50_disp_root *root) | |||
88 | */ | 53 | */ |
89 | 54 | ||
90 | /* ... CRTC caps */ | 55 | /* ... CRTC caps */ |
91 | for (i = 0; i < disp->base.head.nr; i++) { | 56 | list_for_each_entry(head, &disp->base.head, head) { |
92 | tmp = nvkm_rd32(device, 0x616104 + (i * 0x800)); | 57 | const u32 hoff = head->id * 0x800; |
93 | nvkm_wr32(device, 0x6101b4 + (i * 0x800), tmp); | 58 | tmp = nvkm_rd32(device, 0x616104 + hoff); |
94 | tmp = nvkm_rd32(device, 0x616108 + (i * 0x800)); | 59 | nvkm_wr32(device, 0x6101b4 + hoff, tmp); |
95 | nvkm_wr32(device, 0x6101b8 + (i * 0x800), tmp); | 60 | tmp = nvkm_rd32(device, 0x616108 + hoff); |
96 | tmp = nvkm_rd32(device, 0x61610c + (i * 0x800)); | 61 | nvkm_wr32(device, 0x6101b8 + hoff, tmp); |
97 | nvkm_wr32(device, 0x6101bc + (i * 0x800), tmp); | 62 | tmp = nvkm_rd32(device, 0x61610c + hoff); |
63 | nvkm_wr32(device, 0x6101bc + hoff, tmp); | ||
98 | } | 64 | } |
99 | 65 | ||
100 | /* ... DAC caps */ | 66 | /* ... DAC caps */ |
@@ -134,8 +100,10 @@ gf119_disp_root_init(struct nv50_disp_root *root) | |||
134 | * | 100 | * |
135 | * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt | 101 | * ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt |
136 | */ | 102 | */ |
137 | for (i = 0; i < disp->base.head.nr; i++) | 103 | list_for_each_entry(head, &disp->base.head, head) { |
138 | nvkm_mask(device, 0x616308 + (i * 0x800), 0x00000111, 0x00000010); | 104 | const u32 hoff = head->id * 0x800; |
105 | nvkm_mask(device, 0x616308 + hoff, 0x00000111, 0x00000010); | ||
106 | } | ||
139 | 107 | ||
140 | return 0; | 108 | return 0; |
141 | } | 109 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c index f535f43231e2..7f3e2554a83d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv04.c | |||
@@ -23,6 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | #define nv04_disp_root(p) container_of((p), struct nv04_disp_root, object) | 24 | #define nv04_disp_root(p) container_of((p), struct nv04_disp_root, object) |
25 | #include "priv.h" | 25 | #include "priv.h" |
26 | #include "head.h" | ||
26 | 27 | ||
27 | #include <core/client.h> | 28 | #include <core/client.h> |
28 | 29 | ||
@@ -36,73 +37,30 @@ struct nv04_disp_root { | |||
36 | }; | 37 | }; |
37 | 38 | ||
38 | static int | 39 | static int |
39 | nv04_disp_scanoutpos(struct nv04_disp_root *root, | ||
40 | void *data, u32 size, int head) | ||
41 | { | ||
42 | struct nvkm_device *device = root->disp->engine.subdev.device; | ||
43 | struct nvkm_object *object = &root->object; | ||
44 | const u32 hoff = head * 0x2000; | ||
45 | union { | ||
46 | struct nv04_disp_scanoutpos_v0 v0; | ||
47 | } *args = data; | ||
48 | u32 line; | ||
49 | int ret = -ENOSYS; | ||
50 | |||
51 | nvif_ioctl(object, "disp scanoutpos size %d\n", size); | ||
52 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
53 | nvif_ioctl(object, "disp scanoutpos vers %d\n", | ||
54 | args->v0.version); | ||
55 | args->v0.vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0xffff; | ||
56 | args->v0.vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0xffff; | ||
57 | args->v0.vblanke = args->v0.vtotal - 1; | ||
58 | |||
59 | args->v0.hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0xffff; | ||
60 | args->v0.htotal = nvkm_rd32(device, 0x680824 + hoff) & 0xffff; | ||
61 | args->v0.hblanke = args->v0.htotal - 1; | ||
62 | |||
63 | /* | ||
64 | * If output is vga instead of digital then vtotal/htotal is | ||
65 | * invalid so we have to give up and trigger the timestamping | ||
66 | * fallback in the drm core. | ||
67 | */ | ||
68 | if (!args->v0.vtotal || !args->v0.htotal) | ||
69 | return -ENOTSUPP; | ||
70 | |||
71 | args->v0.time[0] = ktime_to_ns(ktime_get()); | ||
72 | line = nvkm_rd32(device, 0x600868 + hoff); | ||
73 | args->v0.time[1] = ktime_to_ns(ktime_get()); | ||
74 | args->v0.hline = (line & 0xffff0000) >> 16; | ||
75 | args->v0.vline = (line & 0x0000ffff); | ||
76 | } else | ||
77 | return ret; | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int | ||
83 | nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) | 40 | nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) |
84 | { | 41 | { |
85 | struct nv04_disp_root *root = nv04_disp_root(object); | 42 | struct nv04_disp_root *root = nv04_disp_root(object); |
86 | union { | 43 | union { |
87 | struct nv04_disp_mthd_v0 v0; | 44 | struct nv04_disp_mthd_v0 v0; |
88 | } *args = data; | 45 | } *args = data; |
89 | int head, ret = -ENOSYS; | 46 | struct nvkm_head *head; |
47 | int id, ret = -ENOSYS; | ||
90 | 48 | ||
91 | nvif_ioctl(object, "disp mthd size %d\n", size); | 49 | nvif_ioctl(object, "disp mthd size %d\n", size); |
92 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { | 50 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { |
93 | nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", | 51 | nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", |
94 | args->v0.version, args->v0.method, args->v0.head); | 52 | args->v0.version, args->v0.method, args->v0.head); |
95 | mthd = args->v0.method; | 53 | mthd = args->v0.method; |
96 | head = args->v0.head; | 54 | id = args->v0.head; |
97 | } else | 55 | } else |
98 | return ret; | 56 | return ret; |
99 | 57 | ||
100 | if (head < 0 || head >= 2) | 58 | if (!(head = nvkm_head_find(root->disp, id))) |
101 | return -ENXIO; | 59 | return -ENXIO; |
102 | 60 | ||
103 | switch (mthd) { | 61 | switch (mthd) { |
104 | case NV04_DISP_SCANOUTPOS: | 62 | case NV04_DISP_SCANOUTPOS: |
105 | return nv04_disp_scanoutpos(root, data, size, head); | 63 | return nvkm_head_mthd_scanoutpos(object, head, data, size); |
106 | default: | 64 | default: |
107 | break; | 65 | break; |
108 | } | 66 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c index e70dc6a9ff7d..1208524aae14 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c | |||
@@ -23,6 +23,9 @@ | |||
23 | */ | 23 | */ |
24 | #include "rootnv50.h" | 24 | #include "rootnv50.h" |
25 | #include "dmacnv50.h" | 25 | #include "dmacnv50.h" |
26 | #include "dp.h" | ||
27 | #include "head.h" | ||
28 | #include "ior.h" | ||
26 | 29 | ||
27 | #include <core/client.h> | 30 | #include <core/client.h> |
28 | #include <core/ramht.h> | 31 | #include <core/ramht.h> |
@@ -32,40 +35,6 @@ | |||
32 | #include <nvif/cl5070.h> | 35 | #include <nvif/cl5070.h> |
33 | #include <nvif/unpack.h> | 36 | #include <nvif/unpack.h> |
34 | 37 | ||
35 | int | ||
36 | nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0) | ||
37 | { | ||
38 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
39 | const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540)); | ||
40 | const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540)); | ||
41 | const u32 total = nvkm_rd32(device, 0x610afc + (head * 0x540)); | ||
42 | union { | ||
43 | struct nv50_disp_scanoutpos_v0 v0; | ||
44 | } *args = data; | ||
45 | int ret = -ENOSYS; | ||
46 | |||
47 | nvif_ioctl(object, "disp scanoutpos size %d\n", size); | ||
48 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
49 | nvif_ioctl(object, "disp scanoutpos vers %d\n", | ||
50 | args->v0.version); | ||
51 | args->v0.vblanke = (blanke & 0xffff0000) >> 16; | ||
52 | args->v0.hblanke = (blanke & 0x0000ffff); | ||
53 | args->v0.vblanks = (blanks & 0xffff0000) >> 16; | ||
54 | args->v0.hblanks = (blanks & 0x0000ffff); | ||
55 | args->v0.vtotal = ( total & 0xffff0000) >> 16; | ||
56 | args->v0.htotal = ( total & 0x0000ffff); | ||
57 | args->v0.time[0] = ktime_to_ns(ktime_get()); | ||
58 | args->v0.vline = /* vline read locks hline */ | ||
59 | nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff; | ||
60 | args->v0.time[1] = ktime_to_ns(ktime_get()); | ||
61 | args->v0.hline = | ||
62 | nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff; | ||
63 | } else | ||
64 | return ret; | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | static int | 38 | static int |
70 | nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | 39 | nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) |
71 | { | 40 | { |
@@ -75,11 +44,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
75 | } *args = data; | 44 | } *args = data; |
76 | struct nv50_disp_root *root = nv50_disp_root(object); | 45 | struct nv50_disp_root *root = nv50_disp_root(object); |
77 | struct nv50_disp *disp = root->disp; | 46 | struct nv50_disp *disp = root->disp; |
78 | const struct nv50_disp_func *func = disp->func; | 47 | struct nvkm_outp *temp, *outp = NULL; |
79 | struct nvkm_output *outp = NULL; | 48 | struct nvkm_head *head; |
80 | struct nvkm_output *temp; | ||
81 | u16 type, mask = 0; | 49 | u16 type, mask = 0; |
82 | int head, ret = -ENOSYS; | 50 | int hidx, ret = -ENOSYS; |
83 | 51 | ||
84 | if (mthd != NV50_DISP_MTHD) | 52 | if (mthd != NV50_DISP_MTHD) |
85 | return -EINVAL; | 53 | return -EINVAL; |
@@ -89,7 +57,7 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
89 | nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", | 57 | nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", |
90 | args->v0.version, args->v0.method, args->v0.head); | 58 | args->v0.version, args->v0.method, args->v0.head); |
91 | mthd = args->v0.method; | 59 | mthd = args->v0.method; |
92 | head = args->v0.head; | 60 | hidx = args->v0.head; |
93 | } else | 61 | } else |
94 | if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) { | 62 | if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) { |
95 | nvif_ioctl(object, "disp mthd vers %d mthd %02x " | 63 | nvif_ioctl(object, "disp mthd vers %d mthd %02x " |
@@ -99,11 +67,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
99 | mthd = args->v1.method; | 67 | mthd = args->v1.method; |
100 | type = args->v1.hasht; | 68 | type = args->v1.hasht; |
101 | mask = args->v1.hashm; | 69 | mask = args->v1.hashm; |
102 | head = ffs((mask >> 8) & 0x0f) - 1; | 70 | hidx = ffs((mask >> 8) & 0x0f) - 1; |
103 | } else | 71 | } else |
104 | return ret; | 72 | return ret; |
105 | 73 | ||
106 | if (head < 0 || head >= disp->base.head.nr) | 74 | if (!(head = nvkm_head_find(&disp->base, hidx))) |
107 | return -ENXIO; | 75 | return -ENXIO; |
108 | 76 | ||
109 | if (mask) { | 77 | if (mask) { |
@@ -119,27 +87,126 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
119 | } | 87 | } |
120 | 88 | ||
121 | switch (mthd) { | 89 | switch (mthd) { |
122 | case NV50_DISP_SCANOUTPOS: | 90 | case NV50_DISP_SCANOUTPOS: { |
123 | return func->head.scanoutpos(object, disp, data, size, head); | 91 | return nvkm_head_mthd_scanoutpos(object, head, data, size); |
92 | } | ||
124 | default: | 93 | default: |
125 | break; | 94 | break; |
126 | } | 95 | } |
127 | 96 | ||
128 | switch (mthd * !!outp) { | 97 | switch (mthd * !!outp) { |
129 | case NV50_DISP_MTHD_V1_DAC_PWR: | 98 | case NV50_DISP_MTHD_V1_ACQUIRE: { |
130 | return func->dac.power(object, disp, data, size, head, outp); | 99 | union { |
131 | case NV50_DISP_MTHD_V1_DAC_LOAD: | 100 | struct nv50_disp_acquire_v0 v0; |
132 | return func->dac.sense(object, disp, data, size, head, outp); | 101 | } *args = data; |
133 | case NV50_DISP_MTHD_V1_SOR_PWR: | 102 | int ret = -ENOSYS; |
134 | return func->sor.power(object, disp, data, size, head, outp); | 103 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { |
135 | case NV50_DISP_MTHD_V1_SOR_HDA_ELD: | 104 | ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER); |
136 | if (!func->sor.hda_eld) | 105 | if (ret == 0) { |
106 | args->v0.or = outp->ior->id; | ||
107 | args->v0.link = outp->ior->asy.link; | ||
108 | } | ||
109 | } | ||
110 | return ret; | ||
111 | } | ||
112 | break; | ||
113 | case NV50_DISP_MTHD_V1_RELEASE: | ||
114 | nvkm_outp_release(outp, NVKM_OUTP_USER); | ||
115 | return 0; | ||
116 | case NV50_DISP_MTHD_V1_DAC_LOAD: { | ||
117 | union { | ||
118 | struct nv50_disp_dac_load_v0 v0; | ||
119 | } *args = data; | ||
120 | int ret = -ENOSYS; | ||
121 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
122 | if (args->v0.data & 0xfff00000) | ||
123 | return -EINVAL; | ||
124 | ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV); | ||
125 | if (ret) | ||
126 | return ret; | ||
127 | ret = outp->ior->func->sense(outp->ior, args->v0.data); | ||
128 | nvkm_outp_release(outp, NVKM_OUTP_PRIV); | ||
129 | if (ret < 0) | ||
130 | return ret; | ||
131 | args->v0.load = ret; | ||
132 | return 0; | ||
133 | } else | ||
134 | return ret; | ||
135 | } | ||
136 | break; | ||
137 | case NV50_DISP_MTHD_V1_SOR_HDA_ELD: { | ||
138 | union { | ||
139 | struct nv50_disp_sor_hda_eld_v0 v0; | ||
140 | } *args = data; | ||
141 | struct nvkm_ior *ior = outp->ior; | ||
142 | int ret = -ENOSYS; | ||
143 | |||
144 | nvif_ioctl(object, "disp sor hda eld size %d\n", size); | ||
145 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { | ||
146 | nvif_ioctl(object, "disp sor hda eld vers %d\n", | ||
147 | args->v0.version); | ||
148 | if (size > 0x60) | ||
149 | return -E2BIG; | ||
150 | } else | ||
151 | return ret; | ||
152 | |||
153 | if (!ior->func->hda.hpd) | ||
137 | return -ENODEV; | 154 | return -ENODEV; |
138 | return func->sor.hda_eld(object, disp, data, size, head, outp); | 155 | |
139 | case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: | 156 | if (size && args->v0.data[0]) { |
140 | if (!func->sor.hdmi) | 157 | if (outp->info.type == DCB_OUTPUT_DP) |
158 | ior->func->dp.audio(ior, hidx, true); | ||
159 | ior->func->hda.hpd(ior, hidx, true); | ||
160 | ior->func->hda.eld(ior, data, size); | ||
161 | } else { | ||
162 | if (outp->info.type == DCB_OUTPUT_DP) | ||
163 | ior->func->dp.audio(ior, hidx, false); | ||
164 | ior->func->hda.hpd(ior, hidx, false); | ||
165 | } | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | break; | ||
170 | case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: { | ||
171 | union { | ||
172 | struct nv50_disp_sor_hdmi_pwr_v0 v0; | ||
173 | } *args = data; | ||
174 | u8 *vendor, vendor_size; | ||
175 | u8 *avi, avi_size; | ||
176 | int ret = -ENOSYS; | ||
177 | |||
178 | nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size); | ||
179 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { | ||
180 | nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d " | ||
181 | "max_ac_packet %d rekey %d\n", | ||
182 | args->v0.version, args->v0.state, | ||
183 | args->v0.max_ac_packet, args->v0.rekey); | ||
184 | if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f) | ||
185 | return -EINVAL; | ||
186 | if ((args->v0.avi_infoframe_length | ||
187 | + args->v0.vendor_infoframe_length) > size) | ||
188 | return -EINVAL; | ||
189 | else | ||
190 | if ((args->v0.avi_infoframe_length | ||
191 | + args->v0.vendor_infoframe_length) < size) | ||
192 | return -E2BIG; | ||
193 | avi = data; | ||
194 | avi_size = args->v0.avi_infoframe_length; | ||
195 | vendor = avi + avi_size; | ||
196 | vendor_size = args->v0.vendor_infoframe_length; | ||
197 | } else | ||
198 | return ret; | ||
199 | |||
200 | if (!outp->ior->func->hdmi.ctrl) | ||
141 | return -ENODEV; | 201 | return -ENODEV; |
142 | return func->sor.hdmi(object, disp, data, size, head, outp); | 202 | |
203 | outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state, | ||
204 | args->v0.max_ac_packet, | ||
205 | args->v0.rekey, avi, avi_size, | ||
206 | vendor, vendor_size); | ||
207 | return 0; | ||
208 | } | ||
209 | break; | ||
143 | case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { | 210 | case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: { |
144 | union { | 211 | union { |
145 | struct nv50_disp_sor_lvds_script_v0 v0; | 212 | struct nv50_disp_sor_lvds_script_v0 v0; |
@@ -156,32 +223,8 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
156 | return ret; | 223 | return ret; |
157 | } | 224 | } |
158 | break; | 225 | break; |
159 | case NV50_DISP_MTHD_V1_SOR_DP_PWR: { | ||
160 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | ||
161 | union { | ||
162 | struct nv50_disp_sor_dp_pwr_v0 v0; | ||
163 | } *args = data; | ||
164 | int ret = -ENOSYS; | ||
165 | nvif_ioctl(object, "disp sor dp pwr size %d\n", size); | ||
166 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
167 | nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n", | ||
168 | args->v0.version, args->v0.state); | ||
169 | if (args->v0.state == 0) { | ||
170 | nvkm_notify_put(&outpdp->irq); | ||
171 | outpdp->func->lnk_pwr(outpdp, 0); | ||
172 | atomic_set(&outpdp->lt.done, 0); | ||
173 | return 0; | ||
174 | } else | ||
175 | if (args->v0.state != 0) { | ||
176 | nvkm_output_dp_train(&outpdp->base, 0); | ||
177 | return 0; | ||
178 | } | ||
179 | } else | ||
180 | return ret; | ||
181 | } | ||
182 | break; | ||
183 | case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: { | 226 | case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: { |
184 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | 227 | struct nvkm_dp *dp = nvkm_dp(outp); |
185 | union { | 228 | union { |
186 | struct nv50_disp_sor_dp_mst_link_v0 v0; | 229 | struct nv50_disp_sor_dp_mst_link_v0 v0; |
187 | } *args = data; | 230 | } *args = data; |
@@ -190,18 +233,13 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
190 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | 233 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { |
191 | nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n", | 234 | nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n", |
192 | args->v0.version, args->v0.state); | 235 | args->v0.version, args->v0.state); |
193 | if (outpdp->lt.mst != !!args->v0.state) { | 236 | dp->lt.mst = !!args->v0.state; |
194 | outpdp->lt.mst = !!args->v0.state; | ||
195 | atomic_set(&outpdp->lt.done, 0); | ||
196 | nvkm_output_dp_train(&outpdp->base, 0); | ||
197 | } | ||
198 | return 0; | 237 | return 0; |
199 | } else | 238 | } else |
200 | return ret; | 239 | return ret; |
201 | } | 240 | } |
202 | break; | 241 | break; |
203 | case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: { | 242 | case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: { |
204 | struct nvkm_output_dp *outpdp = nvkm_output_dp(outp); | ||
205 | union { | 243 | union { |
206 | struct nv50_disp_sor_dp_mst_vcpi_v0 v0; | 244 | struct nv50_disp_sor_dp_mst_vcpi_v0 v0; |
207 | } *args = data; | 245 | } *args = data; |
@@ -213,20 +251,18 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |||
213 | args->v0.version, args->v0.start_slot, | 251 | args->v0.version, args->v0.start_slot, |
214 | args->v0.num_slots, args->v0.pbn, | 252 | args->v0.num_slots, args->v0.pbn, |
215 | args->v0.aligned_pbn); | 253 | args->v0.aligned_pbn); |
216 | if (!outpdp->func->vcpi) | 254 | if (!outp->ior->func->dp.vcpi) |
217 | return -ENODEV; | 255 | return -ENODEV; |
218 | outpdp->func->vcpi(outpdp, head, args->v0.start_slot, | 256 | outp->ior->func->dp.vcpi(outp->ior, hidx, |
219 | args->v0.num_slots, args->v0.pbn, | 257 | args->v0.start_slot, |
220 | args->v0.aligned_pbn); | 258 | args->v0.num_slots, |
259 | args->v0.pbn, | ||
260 | args->v0.aligned_pbn); | ||
221 | return 0; | 261 | return 0; |
222 | } else | 262 | } else |
223 | return ret; | 263 | return ret; |
224 | } | 264 | } |
225 | break; | 265 | break; |
226 | case NV50_DISP_MTHD_V1_PIOR_PWR: | ||
227 | if (!func->pior.power) | ||
228 | return -ENODEV; | ||
229 | return func->pior.power(object, disp, data, size, head, outp); | ||
230 | default: | 266 | default: |
231 | break; | 267 | break; |
232 | } | 268 | } |
@@ -291,7 +327,21 @@ static int | |||
291 | nv50_disp_root_init_(struct nvkm_object *object) | 327 | nv50_disp_root_init_(struct nvkm_object *object) |
292 | { | 328 | { |
293 | struct nv50_disp_root *root = nv50_disp_root(object); | 329 | struct nv50_disp_root *root = nv50_disp_root(object); |
294 | return root->func->init(root); | 330 | struct nvkm_ior *ior; |
331 | int ret; | ||
332 | |||
333 | ret = root->func->init(root); | ||
334 | if (ret) | ||
335 | return ret; | ||
336 | |||
337 | /* Set 'normal' (ie. when it's attached to a head) state for | ||
338 | * each output resource to 'fully enabled'. | ||
339 | */ | ||
340 | list_for_each_entry(ior, &root->disp->base.ior, head) { | ||
341 | ior->func->power(ior, true, true, true, true, true); | ||
342 | } | ||
343 | |||
344 | return 0; | ||
295 | } | 345 | } |
296 | 346 | ||
297 | static void * | 347 | static void * |
@@ -352,6 +402,7 @@ int | |||
352 | nv50_disp_root_init(struct nv50_disp_root *root) | 402 | nv50_disp_root_init(struct nv50_disp_root *root) |
353 | { | 403 | { |
354 | struct nv50_disp *disp = root->disp; | 404 | struct nv50_disp *disp = root->disp; |
405 | struct nvkm_head *head; | ||
355 | struct nvkm_device *device = disp->base.engine.subdev.device; | 406 | struct nvkm_device *device = disp->base.engine.subdev.device; |
356 | u32 tmp; | 407 | u32 tmp; |
357 | int i; | 408 | int i; |
@@ -364,15 +415,15 @@ nv50_disp_root_init(struct nv50_disp_root *root) | |||
364 | nvkm_wr32(device, 0x610184, tmp); | 415 | nvkm_wr32(device, 0x610184, tmp); |
365 | 416 | ||
366 | /* ... CRTC caps */ | 417 | /* ... CRTC caps */ |
367 | for (i = 0; i < disp->base.head.nr; i++) { | 418 | list_for_each_entry(head, &disp->base.head, head) { |
368 | tmp = nvkm_rd32(device, 0x616100 + (i * 0x800)); | 419 | tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800)); |
369 | nvkm_wr32(device, 0x610190 + (i * 0x10), tmp); | 420 | nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp); |
370 | tmp = nvkm_rd32(device, 0x616104 + (i * 0x800)); | 421 | tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800)); |
371 | nvkm_wr32(device, 0x610194 + (i * 0x10), tmp); | 422 | nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp); |
372 | tmp = nvkm_rd32(device, 0x616108 + (i * 0x800)); | 423 | tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800)); |
373 | nvkm_wr32(device, 0x610198 + (i * 0x10), tmp); | 424 | nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp); |
374 | tmp = nvkm_rd32(device, 0x61610c + (i * 0x800)); | 425 | tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800)); |
375 | nvkm_wr32(device, 0x61019c + (i * 0x10), tmp); | 426 | nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp); |
376 | } | 427 | } |
377 | 428 | ||
378 | /* ... DAC caps */ | 429 | /* ... DAC caps */ |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c new file mode 100644 index 000000000000..f40b909b4ca2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg84.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "ior.h" | ||
23 | |||
24 | static const struct nvkm_ior_func | ||
25 | g84_sor = { | ||
26 | .state = nv50_sor_state, | ||
27 | .power = nv50_sor_power, | ||
28 | .clock = nv50_sor_clock, | ||
29 | .hdmi = { | ||
30 | .ctrl = g84_hdmi_ctrl, | ||
31 | }, | ||
32 | }; | ||
33 | |||
34 | int | ||
35 | g84_sor_new(struct nvkm_disp *disp, int id) | ||
36 | { | ||
37 | return nv50_sor_new_(&g84_sor, disp, id); | ||
38 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c index 627b9ee1ddd2..49aeafde0031 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c | |||
@@ -21,58 +21,75 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outpdp.h" | ||
26 | 25 | ||
27 | #include <subdev/timer.h> | 26 | #include <subdev/timer.h> |
28 | 27 | ||
29 | static inline u32 | 28 | void |
30 | g94_sor_soff(struct nvkm_output_dp *outp) | 29 | g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) |
31 | { | 30 | { |
32 | return (ffs(outp->base.info.or) - 1) * 0x800; | 31 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
32 | const u32 loff = nv50_sor_link(sor); | ||
33 | nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark); | ||
33 | } | 34 | } |
34 | 35 | ||
35 | static inline u32 | 36 | void |
36 | g94_sor_loff(struct nvkm_output_dp *outp) | 37 | g94_sor_dp_activesym(struct nvkm_ior *sor, int head, |
38 | u8 TU, u8 VTUa, u8 VTUf, u8 VTUi) | ||
37 | { | 39 | { |
38 | return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; | 40 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
41 | const u32 loff = nv50_sor_link(sor); | ||
42 | nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2); | ||
43 | nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 | | ||
44 | VTUf << 16 | | ||
45 | VTUi << 8); | ||
39 | } | 46 | } |
40 | 47 | ||
41 | /******************************************************************************* | 48 | void |
42 | * DisplayPort | 49 | g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) |
43 | ******************************************************************************/ | ||
44 | u32 | ||
45 | g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane) | ||
46 | { | 50 | { |
47 | static const u8 gm100[] = { 0, 8, 16, 24 }; | 51 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
48 | static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ | 52 | const u32 soff = nv50_ior_base(sor); |
49 | static const u8 g94[] = { 16, 8, 0, 24 }; | 53 | nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h); |
50 | if (device->chipset >= 0x110) | 54 | nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v); |
51 | return gm100[lane]; | ||
52 | if (device->chipset == 0xaf) | ||
53 | return mcp89[lane]; | ||
54 | return g94[lane]; | ||
55 | } | 55 | } |
56 | 56 | ||
57 | static int | 57 | void |
58 | g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) | 58 | g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) |
59 | { | 59 | { |
60 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 60 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
61 | const u32 loff = g94_sor_loff(outp); | 61 | const u32 loff = nv50_sor_link(sor); |
62 | const u32 shift = sor->func->dp.lanes[ln] * 8; | ||
63 | u32 data[3]; | ||
64 | |||
65 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); | ||
66 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); | ||
67 | data[2] = nvkm_rd32(device, 0x61c130 + loff); | ||
68 | if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) | ||
69 | data[2] = (data[2] & ~0x0000ff00) | (pu << 8); | ||
70 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); | ||
71 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); | ||
72 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | ||
73 | } | ||
74 | |||
75 | void | ||
76 | g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern) | ||
77 | { | ||
78 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
79 | const u32 loff = nv50_sor_link(sor); | ||
62 | nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24); | 80 | nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24); |
63 | return 0; | ||
64 | } | 81 | } |
65 | 82 | ||
66 | int | 83 | void |
67 | g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) | 84 | g94_sor_dp_power(struct nvkm_ior *sor, int nr) |
68 | { | 85 | { |
69 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 86 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
70 | const u32 soff = g94_sor_soff(outp); | 87 | const u32 soff = nv50_ior_base(sor); |
71 | const u32 loff = g94_sor_loff(outp); | 88 | const u32 loff = nv50_sor_link(sor); |
72 | u32 mask = 0, i; | 89 | u32 mask = 0, i; |
73 | 90 | ||
74 | for (i = 0; i < nr; i++) | 91 | for (i = 0; i < nr; i++) |
75 | mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3); | 92 | mask |= 1 << sor->func->dp.lanes[i]; |
76 | 93 | ||
77 | nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask); | 94 | nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask); |
78 | nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000); | 95 | nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000); |
@@ -80,22 +97,21 @@ g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) | |||
80 | if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000)) | 97 | if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000)) |
81 | break; | 98 | break; |
82 | ); | 99 | ); |
83 | return 0; | ||
84 | } | 100 | } |
85 | 101 | ||
86 | static int | 102 | int |
87 | g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) | 103 | g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) |
88 | { | 104 | { |
89 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 105 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
90 | const u32 soff = g94_sor_soff(outp); | 106 | const u32 soff = nv50_ior_base(sor); |
91 | const u32 loff = g94_sor_loff(outp); | 107 | const u32 loff = nv50_sor_link(sor); |
92 | u32 dpctrl = 0x00000000; | 108 | u32 dpctrl = 0x00000000; |
93 | u32 clksor = 0x00000000; | 109 | u32 clksor = 0x00000000; |
94 | 110 | ||
95 | dpctrl |= ((1 << nr) - 1) << 16; | 111 | dpctrl |= ((1 << sor->dp.nr) - 1) << 16; |
96 | if (ef) | 112 | if (sor->dp.ef) |
97 | dpctrl |= 0x00004000; | 113 | dpctrl |= 0x00004000; |
98 | if (bw > 0x06) | 114 | if (sor->dp.bw > 0x06) |
99 | clksor |= 0x00040000; | 115 | clksor |= 0x00040000; |
100 | 116 | ||
101 | nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor); | 117 | nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor); |
@@ -103,51 +119,165 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) | |||
103 | return 0; | 119 | return 0; |
104 | } | 120 | } |
105 | 121 | ||
106 | static int | 122 | static bool |
107 | g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc) | 123 | g94_sor_war_needed(struct nvkm_ior *sor) |
108 | { | 124 | { |
109 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 125 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
110 | struct nvkm_bios *bios = device->bios; | 126 | const u32 soff = nv50_ior_base(sor); |
111 | const u32 shift = g94_sor_dp_lane_map(device, ln); | 127 | if (sor->asy.proto == TMDS) { |
112 | const u32 loff = g94_sor_loff(outp); | 128 | switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) { |
113 | u32 addr, data[3]; | 129 | case 0x00000000: |
114 | u8 ver, hdr, cnt, len; | 130 | case 0x00030000: |
115 | struct nvbios_dpout info; | 131 | return true; |
116 | struct nvbios_dpcfg ocfg; | 132 | default: |
117 | 133 | break; | |
118 | addr = nvbios_dpout_match(bios, outp->base.info.hasht, | 134 | } |
119 | outp->base.info.hashm, | 135 | } |
120 | &ver, &hdr, &cnt, &len, &info); | 136 | return false; |
121 | if (!addr) | 137 | } |
122 | return -ENODEV; | ||
123 | |||
124 | addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe, | ||
125 | &ver, &hdr, &cnt, &len, &ocfg); | ||
126 | if (!addr) | ||
127 | return -EINVAL; | ||
128 | 138 | ||
129 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); | 139 | static void |
130 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); | 140 | g94_sor_war_update_sppll1(struct nvkm_disp *disp) |
131 | data[2] = nvkm_rd32(device, 0x61c130 + loff); | 141 | { |
132 | if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) | 142 | struct nvkm_device *device = disp->engine.subdev.device; |
133 | data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); | 143 | struct nvkm_ior *ior; |
134 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); | 144 | bool used = false; |
135 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); | 145 | u32 clksor; |
136 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | 146 | |
137 | return 0; | 147 | list_for_each_entry(ior, &disp->ior, head) { |
148 | if (ior->type != SOR) | ||
149 | continue; | ||
150 | |||
151 | clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior)); | ||
152 | switch (clksor & 0x03000000) { | ||
153 | case 0x02000000: | ||
154 | case 0x03000000: | ||
155 | used = true; | ||
156 | break; | ||
157 | default: | ||
158 | break; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | if (used) | ||
163 | return; | ||
164 | |||
165 | nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000); | ||
166 | } | ||
167 | |||
168 | static void | ||
169 | g94_sor_war_3(struct nvkm_ior *sor) | ||
170 | { | ||
171 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
172 | const u32 soff = nv50_ior_base(sor); | ||
173 | u32 sorpwr; | ||
174 | |||
175 | if (!g94_sor_war_needed(sor)) | ||
176 | return; | ||
177 | |||
178 | sorpwr = nvkm_rd32(device, 0x61c004 + soff); | ||
179 | if (sorpwr & 0x00000001) { | ||
180 | u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); | ||
181 | u32 pd_pc = (seqctl & 0x00000f00) >> 8; | ||
182 | u32 pu_pc = seqctl & 0x0000000f; | ||
183 | |||
184 | nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000); | ||
185 | |||
186 | nvkm_msec(device, 2000, | ||
187 | if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) | ||
188 | break; | ||
189 | ); | ||
190 | nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000); | ||
191 | nvkm_msec(device, 2000, | ||
192 | if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) | ||
193 | break; | ||
194 | ); | ||
195 | |||
196 | nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000); | ||
197 | nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000); | ||
198 | } | ||
199 | |||
200 | nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000); | ||
201 | nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000); | ||
202 | |||
203 | if (sorpwr & 0x00000001) { | ||
204 | nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001); | ||
205 | } | ||
206 | |||
207 | g94_sor_war_update_sppll1(sor->disp); | ||
208 | } | ||
209 | |||
210 | static void | ||
211 | g94_sor_war_2(struct nvkm_ior *sor) | ||
212 | { | ||
213 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
214 | const u32 soff = nv50_ior_base(sor); | ||
215 | |||
216 | if (!g94_sor_war_needed(sor)) | ||
217 | return; | ||
218 | |||
219 | nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000); | ||
220 | nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000); | ||
221 | nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001); | ||
222 | |||
223 | nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000); | ||
224 | nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000); | ||
225 | nvkm_usec(device, 400, NVKM_DELAY); | ||
226 | nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000); | ||
227 | nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000); | ||
228 | |||
229 | if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) { | ||
230 | u32 seqctl = nvkm_rd32(device, 0x61c030 + soff); | ||
231 | u32 pu_pc = seqctl & 0x0000000f; | ||
232 | nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | void | ||
237 | g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) | ||
238 | { | ||
239 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
240 | const u32 coff = sor->id * 8 + (state == &sor->arm) * 4; | ||
241 | u32 ctrl = nvkm_rd32(device, 0x610794 + coff); | ||
242 | |||
243 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | ||
244 | switch (state->proto_evo) { | ||
245 | case 0: state->proto = LVDS; state->link = 1; break; | ||
246 | case 1: state->proto = TMDS; state->link = 1; break; | ||
247 | case 2: state->proto = TMDS; state->link = 2; break; | ||
248 | case 5: state->proto = TMDS; state->link = 3; break; | ||
249 | case 8: state->proto = DP; state->link = 1; break; | ||
250 | case 9: state->proto = DP; state->link = 2; break; | ||
251 | default: | ||
252 | state->proto = UNKNOWN; | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | state->head = ctrl & 0x00000003; | ||
257 | nv50_pior_depth(sor, state, ctrl); | ||
138 | } | 258 | } |
139 | 259 | ||
140 | static const struct nvkm_output_dp_func | 260 | static const struct nvkm_ior_func |
141 | g94_sor_dp_func = { | 261 | g94_sor = { |
142 | .pattern = g94_sor_dp_pattern, | 262 | .state = g94_sor_state, |
143 | .lnk_pwr = g94_sor_dp_lnk_pwr, | 263 | .power = nv50_sor_power, |
144 | .lnk_ctl = g94_sor_dp_lnk_ctl, | 264 | .clock = nv50_sor_clock, |
145 | .drv_ctl = g94_sor_dp_drv_ctl, | 265 | .war_2 = g94_sor_war_2, |
266 | .war_3 = g94_sor_war_3, | ||
267 | .dp = { | ||
268 | .lanes = { 2, 1, 0, 3}, | ||
269 | .links = g94_sor_dp_links, | ||
270 | .power = g94_sor_dp_power, | ||
271 | .pattern = g94_sor_dp_pattern, | ||
272 | .drive = g94_sor_dp_drive, | ||
273 | .audio_sym = g94_sor_dp_audio_sym, | ||
274 | .activesym = g94_sor_dp_activesym, | ||
275 | .watermark = g94_sor_dp_watermark, | ||
276 | }, | ||
146 | }; | 277 | }; |
147 | 278 | ||
148 | int | 279 | int |
149 | g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | 280 | g94_sor_new(struct nvkm_disp *disp, int id) |
150 | struct nvkm_output **poutp) | ||
151 | { | 281 | { |
152 | return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp); | 282 | return nv50_sor_new_(&g94_sor, disp, id); |
153 | } | 283 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c index 6ffdaa65aa77..a2978a37b4f3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c | |||
@@ -21,44 +21,94 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outpdp.h" | ||
26 | 25 | ||
27 | static inline u32 | 26 | #include <subdev/timer.h> |
28 | gf119_sor_soff(struct nvkm_output_dp *outp) | 27 | |
28 | void | ||
29 | gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark) | ||
30 | { | ||
31 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
32 | const u32 hoff = head * 0x800; | ||
33 | nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark); | ||
34 | } | ||
35 | |||
36 | void | ||
37 | gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v) | ||
29 | { | 38 | { |
30 | return (ffs(outp->base.info.or) - 1) * 0x800; | 39 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
40 | const u32 hoff = head * 0x800; | ||
41 | nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h); | ||
42 | nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v); | ||
31 | } | 43 | } |
32 | 44 | ||
33 | static inline u32 | 45 | void |
34 | gf119_sor_loff(struct nvkm_output_dp *outp) | 46 | gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) |
35 | { | 47 | { |
36 | return gf119_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; | 48 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
49 | const u32 hoff = 0x800 * head; | ||
50 | const u32 data = 0x80000000 | (0x00000001 * enable); | ||
51 | const u32 mask = 0x8000000d; | ||
52 | nvkm_mask(device, 0x616618 + hoff, mask, data); | ||
53 | nvkm_msec(device, 2000, | ||
54 | if (!(nvkm_rd32(device, 0x616618 + hoff) & 0x80000000)) | ||
55 | break; | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | void | ||
60 | gf119_sor_dp_vcpi(struct nvkm_ior *sor, int head, | ||
61 | u8 slot, u8 slot_nr, u16 pbn, u16 aligned) | ||
62 | { | ||
63 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
64 | const u32 hoff = head * 0x800; | ||
65 | |||
66 | nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot); | ||
67 | nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn); | ||
68 | } | ||
69 | |||
70 | void | ||
71 | gf119_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) | ||
72 | { | ||
73 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
74 | const u32 loff = nv50_sor_link(sor); | ||
75 | const u32 shift = sor->func->dp.lanes[ln] * 8; | ||
76 | u32 data[4]; | ||
77 | |||
78 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); | ||
79 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); | ||
80 | data[2] = nvkm_rd32(device, 0x61c130 + loff); | ||
81 | if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0) | ||
82 | data[2] = (data[2] & ~0x0000ff00) | (pu << 8); | ||
83 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); | ||
84 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); | ||
85 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | ||
86 | data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); | ||
87 | nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); | ||
37 | } | 88 | } |
38 | 89 | ||
39 | static int | 90 | void |
40 | gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) | 91 | gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern) |
41 | { | 92 | { |
42 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 93 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
43 | const u32 soff = gf119_sor_soff(outp); | 94 | const u32 soff = nv50_ior_base(sor); |
44 | nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern); | 95 | nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern); |
45 | return 0; | ||
46 | } | 96 | } |
47 | 97 | ||
48 | int | 98 | int |
49 | gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) | 99 | gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux) |
50 | { | 100 | { |
51 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 101 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
52 | const u32 soff = gf119_sor_soff(outp); | 102 | const u32 soff = nv50_ior_base(sor); |
53 | const u32 loff = gf119_sor_loff(outp); | 103 | const u32 loff = nv50_sor_link(sor); |
54 | u32 dpctrl = 0x00000000; | 104 | u32 dpctrl = 0x00000000; |
55 | u32 clksor = 0x00000000; | 105 | u32 clksor = 0x00000000; |
56 | 106 | ||
57 | clksor |= bw << 18; | 107 | clksor |= sor->dp.bw << 18; |
58 | dpctrl |= ((1 << nr) - 1) << 16; | 108 | dpctrl |= ((1 << sor->dp.nr) - 1) << 16; |
59 | if (outp->lt.mst) | 109 | if (sor->dp.mst) |
60 | dpctrl |= 0x40000000; | 110 | dpctrl |= 0x40000000; |
61 | if (ef) | 111 | if (sor->dp.ef) |
62 | dpctrl |= 0x00004000; | 112 | dpctrl |= 0x00004000; |
63 | 113 | ||
64 | nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); | 114 | nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor); |
@@ -66,66 +116,77 @@ gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef) | |||
66 | return 0; | 116 | return 0; |
67 | } | 117 | } |
68 | 118 | ||
69 | int | 119 | void |
70 | gf119_sor_dp_drv_ctl(struct nvkm_output_dp *outp, | 120 | gf119_sor_clock(struct nvkm_ior *sor) |
71 | int ln, int vs, int pe, int pc) | ||
72 | { | 121 | { |
73 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 122 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
74 | struct nvkm_bios *bios = device->bios; | 123 | const int div = sor->asy.link == 3; |
75 | const u32 shift = g94_sor_dp_lane_map(device, ln); | 124 | const u32 soff = nv50_ior_base(sor); |
76 | const u32 loff = gf119_sor_loff(outp); | 125 | if (sor->asy.proto == TMDS) { |
77 | u32 addr, data[4]; | 126 | /* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */ |
78 | u8 ver, hdr, cnt, len; | 127 | nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18); |
79 | struct nvbios_dpout info; | 128 | } |
80 | struct nvbios_dpcfg ocfg; | 129 | nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div); |
81 | |||
82 | addr = nvbios_dpout_match(bios, outp->base.info.hasht, | ||
83 | outp->base.info.hashm, | ||
84 | &ver, &hdr, &cnt, &len, &info); | ||
85 | if (!addr) | ||
86 | return -ENODEV; | ||
87 | |||
88 | addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, | ||
89 | &ver, &hdr, &cnt, &len, &ocfg); | ||
90 | if (!addr) | ||
91 | return -EINVAL; | ||
92 | |||
93 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); | ||
94 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); | ||
95 | data[2] = nvkm_rd32(device, 0x61c130 + loff); | ||
96 | if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0) | ||
97 | data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8); | ||
98 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); | ||
99 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); | ||
100 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | ||
101 | data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); | ||
102 | nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); | ||
103 | return 0; | ||
104 | } | 130 | } |
105 | 131 | ||
106 | void | 132 | void |
107 | gf119_sor_dp_vcpi(struct nvkm_output_dp *outp, int head, u8 slot, | 133 | gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) |
108 | u8 slot_nr, u16 pbn, u16 aligned) | ||
109 | { | 134 | { |
110 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 135 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
111 | const u32 hoff = head * 0x800; | 136 | const u32 coff = (state == &sor->asy) * 0x20000 + sor->id * 0x20; |
137 | u32 ctrl = nvkm_rd32(device, 0x640200 + coff); | ||
112 | 138 | ||
113 | nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot); | 139 | state->proto_evo = (ctrl & 0x00000f00) >> 8; |
114 | nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn); | 140 | switch (state->proto_evo) { |
141 | case 0: state->proto = LVDS; state->link = 1; break; | ||
142 | case 1: state->proto = TMDS; state->link = 1; break; | ||
143 | case 2: state->proto = TMDS; state->link = 2; break; | ||
144 | case 5: state->proto = TMDS; state->link = 3; break; | ||
145 | case 8: state->proto = DP; state->link = 1; break; | ||
146 | case 9: state->proto = DP; state->link = 2; break; | ||
147 | default: | ||
148 | state->proto = UNKNOWN; | ||
149 | break; | ||
150 | } | ||
151 | |||
152 | state->head = ctrl & 0x0000000f; | ||
153 | } | ||
154 | |||
155 | int | ||
156 | gf119_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id) | ||
157 | { | ||
158 | struct nvkm_device *device = disp->engine.subdev.device; | ||
159 | if (!(nvkm_rd32(device, 0x612004) & (0x00000100 << id))) | ||
160 | return 0; | ||
161 | return nvkm_ior_new_(func, disp, SOR, id); | ||
115 | } | 162 | } |
116 | 163 | ||
117 | static const struct nvkm_output_dp_func | 164 | static const struct nvkm_ior_func |
118 | gf119_sor_dp_func = { | 165 | gf119_sor = { |
119 | .pattern = gf119_sor_dp_pattern, | 166 | .state = gf119_sor_state, |
120 | .lnk_pwr = g94_sor_dp_lnk_pwr, | 167 | .power = nv50_sor_power, |
121 | .lnk_ctl = gf119_sor_dp_lnk_ctl, | 168 | .clock = gf119_sor_clock, |
122 | .drv_ctl = gf119_sor_dp_drv_ctl, | 169 | .hdmi = { |
123 | .vcpi = gf119_sor_dp_vcpi, | 170 | .ctrl = gf119_hdmi_ctrl, |
171 | }, | ||
172 | .dp = { | ||
173 | .lanes = { 2, 1, 0, 3 }, | ||
174 | .links = gf119_sor_dp_links, | ||
175 | .power = g94_sor_dp_power, | ||
176 | .pattern = gf119_sor_dp_pattern, | ||
177 | .vcpi = gf119_sor_dp_vcpi, | ||
178 | .audio = gf119_sor_dp_audio, | ||
179 | .audio_sym = gf119_sor_dp_audio_sym, | ||
180 | .watermark = gf119_sor_dp_watermark, | ||
181 | }, | ||
182 | .hda = { | ||
183 | .hpd = gf119_hda_hpd, | ||
184 | .eld = gf119_hda_eld, | ||
185 | }, | ||
124 | }; | 186 | }; |
125 | 187 | ||
126 | int | 188 | int |
127 | gf119_sor_dp_new(struct nvkm_disp *disp, int index, | 189 | gf119_sor_new(struct nvkm_disp *disp, int id) |
128 | struct dcb_output *dcbE, struct nvkm_output **poutp) | ||
129 | { | 190 | { |
130 | return nvkm_output_dp_new_(&gf119_sor_dp_func, disp, index, dcbE, poutp); | 191 | return gf119_sor_new_(&gf119_sor, disp, id); |
131 | } | 192 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c new file mode 100644 index 000000000000..a1547bdf490b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgk104.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "ior.h" | ||
23 | |||
24 | static const struct nvkm_ior_func | ||
25 | gk104_sor = { | ||
26 | .state = gf119_sor_state, | ||
27 | .power = nv50_sor_power, | ||
28 | .clock = gf119_sor_clock, | ||
29 | .hdmi = { | ||
30 | .ctrl = gk104_hdmi_ctrl, | ||
31 | }, | ||
32 | .dp = { | ||
33 | .lanes = { 2, 1, 0, 3 }, | ||
34 | .links = gf119_sor_dp_links, | ||
35 | .power = g94_sor_dp_power, | ||
36 | .pattern = gf119_sor_dp_pattern, | ||
37 | .drive = gf119_sor_dp_drive, | ||
38 | .vcpi = gf119_sor_dp_vcpi, | ||
39 | .audio = gf119_sor_dp_audio, | ||
40 | .audio_sym = gf119_sor_dp_audio_sym, | ||
41 | .watermark = gf119_sor_dp_watermark, | ||
42 | }, | ||
43 | .hda = { | ||
44 | .hpd = gf119_hda_hpd, | ||
45 | .eld = gf119_hda_eld, | ||
46 | }, | ||
47 | }; | ||
48 | |||
49 | int | ||
50 | gk104_sor_new(struct nvkm_disp *disp, int id) | ||
51 | { | ||
52 | return gf119_sor_new_(&gk104_sor, disp, id); | ||
53 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c index 4cf8ad4d18ab..60230957d82b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm107.c | |||
@@ -21,34 +21,47 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs <bskeggs@redhat.com> | 22 | * Authors: Ben Skeggs <bskeggs@redhat.com> |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outpdp.h" | ||
26 | 25 | ||
27 | int | 26 | void |
28 | gm107_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern) | 27 | gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern) |
29 | { | 28 | { |
30 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 29 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
31 | const u32 soff = outp->base.or * 0x800; | 30 | const u32 soff = nv50_ior_base(sor); |
32 | const u32 data = 0x01010101 * pattern; | 31 | const u32 data = 0x01010101 * pattern; |
33 | if (outp->base.info.sorconf.link & 1) | 32 | if (sor->asy.link & 1) |
34 | nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data); | 33 | nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data); |
35 | else | 34 | else |
36 | nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data); | 35 | nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data); |
37 | return 0; | ||
38 | } | 36 | } |
39 | 37 | ||
40 | static const struct nvkm_output_dp_func | 38 | static const struct nvkm_ior_func |
41 | gm107_sor_dp_func = { | 39 | gm107_sor = { |
42 | .pattern = gm107_sor_dp_pattern, | 40 | .state = gf119_sor_state, |
43 | .lnk_pwr = g94_sor_dp_lnk_pwr, | 41 | .power = nv50_sor_power, |
44 | .lnk_ctl = gf119_sor_dp_lnk_ctl, | 42 | .clock = gf119_sor_clock, |
45 | .drv_ctl = gf119_sor_dp_drv_ctl, | 43 | .hdmi = { |
46 | .vcpi = gf119_sor_dp_vcpi, | 44 | .ctrl = gk104_hdmi_ctrl, |
45 | }, | ||
46 | .dp = { | ||
47 | .lanes = { 0, 1, 2, 3 }, | ||
48 | .links = gf119_sor_dp_links, | ||
49 | .power = g94_sor_dp_power, | ||
50 | .pattern = gm107_sor_dp_pattern, | ||
51 | .drive = gf119_sor_dp_drive, | ||
52 | .vcpi = gf119_sor_dp_vcpi, | ||
53 | .audio = gf119_sor_dp_audio, | ||
54 | .audio_sym = gf119_sor_dp_audio_sym, | ||
55 | .watermark = gf119_sor_dp_watermark, | ||
56 | }, | ||
57 | .hda = { | ||
58 | .hpd = gf119_hda_hpd, | ||
59 | .eld = gf119_hda_eld, | ||
60 | }, | ||
47 | }; | 61 | }; |
48 | 62 | ||
49 | int | 63 | int |
50 | gm107_sor_dp_new(struct nvkm_disp *disp, int index, | 64 | gm107_sor_new(struct nvkm_disp *disp, int id) |
51 | struct dcb_output *dcbE, struct nvkm_output **poutp) | ||
52 | { | 65 | { |
53 | return nvkm_output_dp_new_(&gm107_sor_dp_func, disp, index, dcbE, poutp); | 66 | return gf119_sor_new_(&gm107_sor, disp, id); |
54 | } | 67 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c index 81b788fa61be..f9b8107aa2a2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgm200.c | |||
@@ -21,111 +21,104 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outpdp.h" | ||
26 | 25 | ||
27 | #include <subdev/timer.h> | 26 | static void |
28 | 27 | gm200_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu) | |
29 | static inline u32 | ||
30 | gm200_sor_soff(struct nvkm_output_dp *outp) | ||
31 | { | ||
32 | return (ffs(outp->base.info.or) - 1) * 0x800; | ||
33 | } | ||
34 | |||
35 | static inline u32 | ||
36 | gm200_sor_loff(struct nvkm_output_dp *outp) | ||
37 | { | 28 | { |
38 | return gm200_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80; | 29 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
39 | } | 30 | const u32 loff = nv50_sor_link(sor); |
31 | const u32 shift = sor->func->dp.lanes[ln] * 8; | ||
32 | u32 data[4]; | ||
40 | 33 | ||
41 | void | 34 | pu &= 0x0f; |
42 | gm200_sor_magic(struct nvkm_output *outp) | ||
43 | { | ||
44 | struct nvkm_device *device = outp->disp->engine.subdev.device; | ||
45 | const u32 soff = outp->or * 0x100; | ||
46 | const u32 data = outp->or + 1; | ||
47 | if (outp->info.sorconf.link & 1) | ||
48 | nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data); | ||
49 | if (outp->info.sorconf.link & 2) | ||
50 | nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data); | ||
51 | } | ||
52 | 35 | ||
53 | static inline u32 | 36 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); |
54 | gm200_sor_dp_lane_map(struct nvkm_device *device, u8 lane) | 37 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); |
55 | { | 38 | data[2] = nvkm_rd32(device, 0x61c130 + loff); |
56 | return lane * 0x08; | 39 | if ((data[2] & 0x00000f00) < (pu << 8) || ln == 0) |
40 | data[2] = (data[2] & ~0x00000f00) | (pu << 8); | ||
41 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift)); | ||
42 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift)); | ||
43 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | ||
44 | data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); | ||
45 | nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift)); | ||
57 | } | 46 | } |
58 | 47 | ||
59 | static int | 48 | static void |
60 | gm200_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr) | 49 | gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior) |
61 | { | 50 | { |
62 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 51 | struct nvkm_device *device = outp->disp->engine.subdev.device; |
63 | const u32 soff = gm200_sor_soff(outp); | 52 | const u32 moff = __ffs(outp->info.or) * 0x100; |
64 | const u32 loff = gm200_sor_loff(outp); | 53 | const u32 sor = ior ? ior->id + 1 : 0; |
65 | u32 mask = 0, i; | 54 | u32 link = ior ? (ior->asy.link == 2) : 0; |
66 | 55 | ||
67 | for (i = 0; i < nr; i++) | 56 | if (outp->info.sorconf.link & 1) { |
68 | mask |= 1 << (gm200_sor_dp_lane_map(device, i) >> 3); | 57 | nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor); |
58 | link++; | ||
59 | } | ||
69 | 60 | ||
70 | nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask); | 61 | if (outp->info.sorconf.link & 2) |
71 | nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000); | 62 | nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor); |
72 | nvkm_msec(device, 2000, | ||
73 | if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000)) | ||
74 | break; | ||
75 | ); | ||
76 | return 0; | ||
77 | } | 63 | } |
78 | 64 | ||
79 | static int | 65 | static int |
80 | gm200_sor_dp_drv_ctl(struct nvkm_output_dp *outp, | 66 | gm200_sor_route_get(struct nvkm_outp *outp, int *link) |
81 | int ln, int vs, int pe, int pc) | ||
82 | { | 67 | { |
83 | struct nvkm_device *device = outp->base.disp->engine.subdev.device; | 68 | struct nvkm_device *device = outp->disp->engine.subdev.device; |
84 | struct nvkm_bios *bios = device->bios; | 69 | const int sublinks = outp->info.sorconf.link; |
85 | const u32 shift = gm200_sor_dp_lane_map(device, ln); | 70 | int lnk[2], sor[2], m, s; |
86 | const u32 loff = gm200_sor_loff(outp); | ||
87 | u32 addr, data[4]; | ||
88 | u8 ver, hdr, cnt, len; | ||
89 | struct nvbios_dpout info; | ||
90 | struct nvbios_dpcfg ocfg; | ||
91 | 71 | ||
92 | addr = nvbios_dpout_match(bios, outp->base.info.hasht, | 72 | for (*link = 0, m = __ffs(outp->info.or) * 2, s = 0; s < 2; m++, s++) { |
93 | outp->base.info.hashm, | 73 | if (sublinks & BIT(s)) { |
94 | &ver, &hdr, &cnt, &len, &info); | 74 | u32 data = nvkm_rd32(device, 0x612308 + (m * 0x80)); |
95 | if (!addr) | 75 | lnk[s] = (data & 0x00000010) >> 4; |
96 | return -ENODEV; | 76 | sor[s] = (data & 0x0000000f); |
77 | if (!sor[s]) | ||
78 | return -1; | ||
79 | *link |= lnk[s]; | ||
80 | } | ||
81 | } | ||
97 | 82 | ||
98 | addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe, | 83 | if (sublinks == 3) { |
99 | &ver, &hdr, &cnt, &len, &ocfg); | 84 | if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1])) |
100 | if (!addr) | 85 | return -1; |
101 | return -EINVAL; | 86 | } |
102 | ocfg.tx_pu &= 0x0f; | ||
103 | 87 | ||
104 | data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift); | 88 | return ((sublinks & 1) ? sor[0] : sor[1]) - 1; |
105 | data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift); | ||
106 | data[2] = nvkm_rd32(device, 0x61c130 + loff); | ||
107 | if ((data[2] & 0x00000f00) < (ocfg.tx_pu << 8) || ln == 0) | ||
108 | data[2] = (data[2] & ~0x00000f00) | (ocfg.tx_pu << 8); | ||
109 | nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift)); | ||
110 | nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift)); | ||
111 | nvkm_wr32(device, 0x61c130 + loff, data[2]); | ||
112 | data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift); | ||
113 | nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift)); | ||
114 | return 0; | ||
115 | } | 89 | } |
116 | 90 | ||
117 | static const struct nvkm_output_dp_func | 91 | static const struct nvkm_ior_func |
118 | gm200_sor_dp_func = { | 92 | gm200_sor = { |
119 | .pattern = gm107_sor_dp_pattern, | 93 | .route = { |
120 | .lnk_pwr = gm200_sor_dp_lnk_pwr, | 94 | .get = gm200_sor_route_get, |
121 | .lnk_ctl = gf119_sor_dp_lnk_ctl, | 95 | .set = gm200_sor_route_set, |
122 | .drv_ctl = gm200_sor_dp_drv_ctl, | 96 | }, |
123 | .vcpi = gf119_sor_dp_vcpi, | 97 | .state = gf119_sor_state, |
98 | .power = nv50_sor_power, | ||
99 | .clock = gf119_sor_clock, | ||
100 | .hdmi = { | ||
101 | .ctrl = gk104_hdmi_ctrl, | ||
102 | }, | ||
103 | .dp = { | ||
104 | .lanes = { 0, 1, 2, 3 }, | ||
105 | .links = gf119_sor_dp_links, | ||
106 | .power = g94_sor_dp_power, | ||
107 | .pattern = gm107_sor_dp_pattern, | ||
108 | .drive = gm200_sor_dp_drive, | ||
109 | .vcpi = gf119_sor_dp_vcpi, | ||
110 | .audio = gf119_sor_dp_audio, | ||
111 | .audio_sym = gf119_sor_dp_audio_sym, | ||
112 | .watermark = gf119_sor_dp_watermark, | ||
113 | }, | ||
114 | .hda = { | ||
115 | .hpd = gf119_hda_hpd, | ||
116 | .eld = gf119_hda_eld, | ||
117 | }, | ||
124 | }; | 118 | }; |
125 | 119 | ||
126 | int | 120 | int |
127 | gm200_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, | 121 | gm200_sor_new(struct nvkm_disp *disp, int id) |
128 | struct nvkm_output **poutp) | ||
129 | { | 122 | { |
130 | return nvkm_output_dp_new_(&gm200_sor_dp_func, disp, index, dcbE, poutp); | 123 | return gf119_sor_new_(&gm200_sor, disp, id); |
131 | } | 124 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c new file mode 100644 index 000000000000..da228b54b43e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgt215.c | |||
@@ -0,0 +1,69 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "ior.h" | ||
23 | |||
24 | #include <subdev/timer.h> | ||
25 | |||
26 | void | ||
27 | gt215_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable) | ||
28 | { | ||
29 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
30 | const u32 soff = nv50_ior_base(sor); | ||
31 | const u32 data = 0x80000000 | (0x00000001 * enable); | ||
32 | const u32 mask = 0x8000000d; | ||
33 | nvkm_mask(device, 0x61c1e0 + soff, mask, data); | ||
34 | nvkm_msec(device, 2000, | ||
35 | if (!(nvkm_rd32(device, 0x61c1e0 + soff) & 0x80000000)) | ||
36 | break; | ||
37 | ); | ||
38 | } | ||
39 | |||
40 | static const struct nvkm_ior_func | ||
41 | gt215_sor = { | ||
42 | .state = g94_sor_state, | ||
43 | .power = nv50_sor_power, | ||
44 | .clock = nv50_sor_clock, | ||
45 | .hdmi = { | ||
46 | .ctrl = gt215_hdmi_ctrl, | ||
47 | }, | ||
48 | .dp = { | ||
49 | .lanes = { 2, 1, 0, 3 }, | ||
50 | .links = g94_sor_dp_links, | ||
51 | .power = g94_sor_dp_power, | ||
52 | .pattern = g94_sor_dp_pattern, | ||
53 | .drive = g94_sor_dp_drive, | ||
54 | .audio = gt215_sor_dp_audio, | ||
55 | .audio_sym = g94_sor_dp_audio_sym, | ||
56 | .activesym = g94_sor_dp_activesym, | ||
57 | .watermark = g94_sor_dp_watermark, | ||
58 | }, | ||
59 | .hda = { | ||
60 | .hpd = gt215_hda_hpd, | ||
61 | .eld = gt215_hda_eld, | ||
62 | }, | ||
63 | }; | ||
64 | |||
65 | int | ||
66 | gt215_sor_new(struct nvkm_disp *disp, int id) | ||
67 | { | ||
68 | return nv50_sor_new_(>215_sor, disp, id); | ||
69 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c new file mode 100644 index 000000000000..c0179ccb956d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp77.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "ior.h" | ||
23 | |||
24 | static const struct nvkm_ior_func | ||
25 | mcp77_sor = { | ||
26 | .state = g94_sor_state, | ||
27 | .power = nv50_sor_power, | ||
28 | .clock = nv50_sor_clock, | ||
29 | .hdmi = { | ||
30 | .ctrl = g84_hdmi_ctrl, | ||
31 | }, | ||
32 | .dp = { | ||
33 | .lanes = { 2, 1, 0, 3}, | ||
34 | .links = g94_sor_dp_links, | ||
35 | .power = g94_sor_dp_power, | ||
36 | .pattern = g94_sor_dp_pattern, | ||
37 | .drive = g94_sor_dp_drive, | ||
38 | .audio_sym = g94_sor_dp_audio_sym, | ||
39 | .activesym = g94_sor_dp_activesym, | ||
40 | .watermark = g94_sor_dp_watermark, | ||
41 | }, | ||
42 | }; | ||
43 | |||
44 | int | ||
45 | mcp77_sor_new(struct nvkm_disp *disp, int id) | ||
46 | { | ||
47 | return nv50_sor_new_(&mcp77_sor, disp, id); | ||
48 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c new file mode 100644 index 000000000000..9bb01cd96697 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sormcp89.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * Copyright 2017 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 | #include "ior.h" | ||
23 | |||
24 | static const struct nvkm_ior_func | ||
25 | mcp89_sor = { | ||
26 | .state = g94_sor_state, | ||
27 | .power = nv50_sor_power, | ||
28 | .clock = nv50_sor_clock, | ||
29 | .hdmi = { | ||
30 | .ctrl = gt215_hdmi_ctrl, | ||
31 | }, | ||
32 | .dp = { | ||
33 | .lanes = { 3, 2, 1, 0 }, | ||
34 | .links = g94_sor_dp_links, | ||
35 | .power = g94_sor_dp_power, | ||
36 | .pattern = g94_sor_dp_pattern, | ||
37 | .drive = g94_sor_dp_drive, | ||
38 | .audio = gt215_sor_dp_audio, | ||
39 | .audio_sym = g94_sor_dp_audio_sym, | ||
40 | .activesym = g94_sor_dp_activesym, | ||
41 | .watermark = g94_sor_dp_watermark, | ||
42 | }, | ||
43 | .hda = { | ||
44 | .hpd = gt215_hda_hpd, | ||
45 | .eld = gt215_hda_eld, | ||
46 | }, | ||
47 | }; | ||
48 | |||
49 | int | ||
50 | mcp89_sor_new(struct nvkm_disp *disp, int id) | ||
51 | { | ||
52 | return nv50_sor_new_(&mcp89_sor, disp, id); | ||
53 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c index 53596bed3c36..f3ebd0c22e7d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sornv50.c | |||
@@ -21,59 +21,87 @@ | |||
21 | * | 21 | * |
22 | * Authors: Ben Skeggs | 22 | * Authors: Ben Skeggs |
23 | */ | 23 | */ |
24 | #include "nv50.h" | 24 | #include "ior.h" |
25 | #include "outp.h" | ||
26 | 25 | ||
27 | #include <core/client.h> | ||
28 | #include <subdev/timer.h> | 26 | #include <subdev/timer.h> |
29 | 27 | ||
30 | #include <nvif/cl5070.h> | 28 | void |
31 | #include <nvif/unpack.h> | 29 | nv50_sor_clock(struct nvkm_ior *sor) |
32 | |||
33 | int | ||
34 | nv50_sor_power(NV50_DISP_MTHD_V1) | ||
35 | { | 30 | { |
36 | struct nvkm_device *device = disp->base.engine.subdev.device; | 31 | struct nvkm_device *device = sor->disp->engine.subdev.device; |
37 | union { | 32 | const int div = sor->asy.link == 3; |
38 | struct nv50_disp_sor_pwr_v0 v0; | 33 | const u32 soff = nv50_ior_base(sor); |
39 | } *args = data; | 34 | nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div); |
40 | const u32 soff = outp->or * 0x800; | 35 | } |
41 | u32 stat; | ||
42 | int ret = -ENOSYS; | ||
43 | |||
44 | nvif_ioctl(object, "disp sor pwr size %d\n", size); | ||
45 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { | ||
46 | nvif_ioctl(object, "disp sor pwr vers %d state %d\n", | ||
47 | args->v0.version, args->v0.state); | ||
48 | stat = !!args->v0.state; | ||
49 | } else | ||
50 | return ret; | ||
51 | |||
52 | 36 | ||
37 | static void | ||
38 | nv50_sor_power_wait(struct nvkm_device *device, u32 soff) | ||
39 | { | ||
53 | nvkm_msec(device, 2000, | 40 | nvkm_msec(device, 2000, |
54 | if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000)) | 41 | if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000)) |
55 | break; | 42 | break; |
56 | ); | 43 | ); |
57 | nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000 | stat); | 44 | } |
58 | nvkm_msec(device, 2000, | 45 | |
59 | if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000)) | 46 | void |
60 | break; | 47 | nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu, |
61 | ); | 48 | bool data, bool vsync, bool hsync) |
49 | { | ||
50 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
51 | const u32 soff = nv50_ior_base(sor); | ||
52 | const u32 shift = normal ? 0 : 16; | ||
53 | const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift; | ||
54 | const u32 field = 0x80000000 | (0x00000001 << shift); | ||
55 | |||
56 | nv50_sor_power_wait(device, soff); | ||
57 | nvkm_mask(device, 0x61c004 + soff, field, state); | ||
58 | nv50_sor_power_wait(device, soff); | ||
59 | |||
62 | nvkm_msec(device, 2000, | 60 | nvkm_msec(device, 2000, |
63 | if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) | 61 | if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000)) |
64 | break; | 62 | break; |
65 | ); | 63 | ); |
66 | return 0; | ||
67 | } | 64 | } |
68 | 65 | ||
69 | static const struct nvkm_output_func | 66 | void |
70 | nv50_sor_output_func = { | 67 | nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state) |
68 | { | ||
69 | struct nvkm_device *device = sor->disp->engine.subdev.device; | ||
70 | const u32 coff = sor->id * 8 + (state == &sor->arm) * 4; | ||
71 | u32 ctrl = nvkm_rd32(device, 0x610b70 + coff); | ||
72 | |||
73 | state->proto_evo = (ctrl & 0x00000f00) >> 8; | ||
74 | switch (state->proto_evo) { | ||
75 | case 0: state->proto = LVDS; state->link = 1; break; | ||
76 | case 1: state->proto = TMDS; state->link = 1; break; | ||
77 | case 2: state->proto = TMDS; state->link = 2; break; | ||
78 | case 5: state->proto = TMDS; state->link = 3; break; | ||
79 | default: | ||
80 | state->proto = UNKNOWN; | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | state->head = ctrl & 0x00000003; | ||
85 | } | ||
86 | |||
87 | int | ||
88 | nv50_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id) | ||
89 | { | ||
90 | struct nvkm_device *device = disp->engine.subdev.device; | ||
91 | if (!(nvkm_rd32(device, 0x610184) & (0x01000000 << id))) | ||
92 | return 0; | ||
93 | return nvkm_ior_new_(func, disp, SOR, id); | ||
94 | } | ||
95 | |||
96 | static const struct nvkm_ior_func | ||
97 | nv50_sor = { | ||
98 | .state = nv50_sor_state, | ||
99 | .power = nv50_sor_power, | ||
100 | .clock = nv50_sor_clock, | ||
71 | }; | 101 | }; |
72 | 102 | ||
73 | int | 103 | int |
74 | nv50_sor_output_new(struct nvkm_disp *disp, int index, | 104 | nv50_sor_new(struct nvkm_disp *disp, int id) |
75 | struct dcb_output *dcbE, struct nvkm_output **poutp) | ||
76 | { | 105 | { |
77 | return nvkm_output_new_(&nv50_sor_output_func, disp, | 106 | return nv50_sor_new_(&nv50_sor, disp, id); |
78 | index, dcbE, poutp); | ||
79 | } | 107 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c index 3953d11844ea..23caef8df17f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/iccsense.c | |||
@@ -87,7 +87,10 @@ nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense) | |||
87 | 87 | ||
88 | switch(ver) { | 88 | switch(ver) { |
89 | case 0x10: | 89 | case 0x10: |
90 | rail->mode = nvbios_rd08(bios, entry + 0x1); | 90 | if ((nvbios_rd08(bios, entry + 0x1) & 0xf8) == 0xf8) |
91 | rail->mode = 1; | ||
92 | else | ||
93 | rail->mode = 0; | ||
91 | rail->extdev_id = nvbios_rd08(bios, entry + 0x2); | 94 | rail->extdev_id = nvbios_rd08(bios, entry + 0x2); |
92 | res_start = 0x3; | 95 | res_start = 0x3; |
93 | break; | 96 | break; |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c index 38ed09fd3d2f..b58ee99f7bfc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c | |||
@@ -37,7 +37,7 @@ | |||
37 | #include <subdev/vga.h> | 37 | #include <subdev/vga.h> |
38 | 38 | ||
39 | #define bioslog(lvl, fmt, args...) do { \ | 39 | #define bioslog(lvl, fmt, args...) do { \ |
40 | nvkm_printk(init->subdev, lvl, info, "0x%04x[%c]: "fmt, \ | 40 | nvkm_printk(init->subdev, lvl, info, "0x%08x[%c]: "fmt, \ |
41 | init->offset, init_exec(init) ? \ | 41 | init->offset, init_exec(init) ? \ |
42 | '0' + (init->nested - 1) : ' ', ##args); \ | 42 | '0' + (init->nested - 1) : ' ', ##args); \ |
43 | } while(0) | 43 | } while(0) |
@@ -87,8 +87,8 @@ static inline int | |||
87 | init_or(struct nvbios_init *init) | 87 | init_or(struct nvbios_init *init) |
88 | { | 88 | { |
89 | if (init_exec(init)) { | 89 | if (init_exec(init)) { |
90 | if (init->outp) | 90 | if (init->or >= 0) |
91 | return ffs(init->outp->or) - 1; | 91 | return init->or; |
92 | error("script needs OR!!\n"); | 92 | error("script needs OR!!\n"); |
93 | } | 93 | } |
94 | return 0; | 94 | return 0; |
@@ -98,20 +98,20 @@ static inline int | |||
98 | init_link(struct nvbios_init *init) | 98 | init_link(struct nvbios_init *init) |
99 | { | 99 | { |
100 | if (init_exec(init)) { | 100 | if (init_exec(init)) { |
101 | if (init->outp) | 101 | if (init->link) |
102 | return !(init->outp->sorconf.link & 1); | 102 | return init->link == 2; |
103 | error("script needs OR link\n"); | 103 | error("script needs OR link\n"); |
104 | } | 104 | } |
105 | return 0; | 105 | return 0; |
106 | } | 106 | } |
107 | 107 | ||
108 | static inline int | 108 | static inline int |
109 | init_crtc(struct nvbios_init *init) | 109 | init_head(struct nvbios_init *init) |
110 | { | 110 | { |
111 | if (init_exec(init)) { | 111 | if (init_exec(init)) { |
112 | if (init->crtc >= 0) | 112 | if (init->head >= 0) |
113 | return init->crtc; | 113 | return init->head; |
114 | error("script needs crtc\n"); | 114 | error("script needs head\n"); |
115 | } | 115 | } |
116 | return 0; | 116 | return 0; |
117 | } | 117 | } |
@@ -119,7 +119,7 @@ init_crtc(struct nvbios_init *init) | |||
119 | static u8 | 119 | static u8 |
120 | init_conn(struct nvbios_init *init) | 120 | init_conn(struct nvbios_init *init) |
121 | { | 121 | { |
122 | struct nvkm_bios *bios = init->bios; | 122 | struct nvkm_bios *bios = init->subdev->device->bios; |
123 | struct nvbios_connE connE; | 123 | struct nvbios_connE connE; |
124 | u8 ver, hdr; | 124 | u8 ver, hdr; |
125 | u32 conn; | 125 | u32 conn; |
@@ -141,7 +141,7 @@ init_conn(struct nvbios_init *init) | |||
141 | static inline u32 | 141 | static inline u32 |
142 | init_nvreg(struct nvbios_init *init, u32 reg) | 142 | init_nvreg(struct nvbios_init *init, u32 reg) |
143 | { | 143 | { |
144 | struct nvkm_devinit *devinit = init->bios->subdev.device->devinit; | 144 | struct nvkm_devinit *devinit = init->subdev->device->devinit; |
145 | 145 | ||
146 | /* C51 (at least) sometimes has the lower bits set which the VBIOS | 146 | /* C51 (at least) sometimes has the lower bits set which the VBIOS |
147 | * interprets to mean that access needs to go through certain IO | 147 | * interprets to mean that access needs to go through certain IO |
@@ -154,9 +154,9 @@ init_nvreg(struct nvbios_init *init, u32 reg) | |||
154 | /* GF8+ display scripts need register addresses mangled a bit to | 154 | /* GF8+ display scripts need register addresses mangled a bit to |
155 | * select a specific CRTC/OR | 155 | * select a specific CRTC/OR |
156 | */ | 156 | */ |
157 | if (init->bios->subdev.device->card_type >= NV_50) { | 157 | if (init->subdev->device->card_type >= NV_50) { |
158 | if (reg & 0x80000000) { | 158 | if (reg & 0x80000000) { |
159 | reg += init_crtc(init) * 0x800; | 159 | reg += init_head(init) * 0x800; |
160 | reg &= ~0x80000000; | 160 | reg &= ~0x80000000; |
161 | } | 161 | } |
162 | 162 | ||
@@ -179,7 +179,7 @@ init_nvreg(struct nvbios_init *init, u32 reg) | |||
179 | static u32 | 179 | static u32 |
180 | init_rd32(struct nvbios_init *init, u32 reg) | 180 | init_rd32(struct nvbios_init *init, u32 reg) |
181 | { | 181 | { |
182 | struct nvkm_device *device = init->bios->subdev.device; | 182 | struct nvkm_device *device = init->subdev->device; |
183 | reg = init_nvreg(init, reg); | 183 | reg = init_nvreg(init, reg); |
184 | if (reg != ~0 && init_exec(init)) | 184 | if (reg != ~0 && init_exec(init)) |
185 | return nvkm_rd32(device, reg); | 185 | return nvkm_rd32(device, reg); |
@@ -189,7 +189,7 @@ init_rd32(struct nvbios_init *init, u32 reg) | |||
189 | static void | 189 | static void |
190 | init_wr32(struct nvbios_init *init, u32 reg, u32 val) | 190 | init_wr32(struct nvbios_init *init, u32 reg, u32 val) |
191 | { | 191 | { |
192 | struct nvkm_device *device = init->bios->subdev.device; | 192 | struct nvkm_device *device = init->subdev->device; |
193 | reg = init_nvreg(init, reg); | 193 | reg = init_nvreg(init, reg); |
194 | if (reg != ~0 && init_exec(init)) | 194 | if (reg != ~0 && init_exec(init)) |
195 | nvkm_wr32(device, reg, val); | 195 | nvkm_wr32(device, reg, val); |
@@ -198,7 +198,7 @@ init_wr32(struct nvbios_init *init, u32 reg, u32 val) | |||
198 | static u32 | 198 | static u32 |
199 | init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val) | 199 | init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val) |
200 | { | 200 | { |
201 | struct nvkm_device *device = init->bios->subdev.device; | 201 | struct nvkm_device *device = init->subdev->device; |
202 | reg = init_nvreg(init, reg); | 202 | reg = init_nvreg(init, reg); |
203 | if (reg != ~0 && init_exec(init)) { | 203 | if (reg != ~0 && init_exec(init)) { |
204 | u32 tmp = nvkm_rd32(device, reg); | 204 | u32 tmp = nvkm_rd32(device, reg); |
@@ -212,7 +212,7 @@ static u8 | |||
212 | init_rdport(struct nvbios_init *init, u16 port) | 212 | init_rdport(struct nvbios_init *init, u16 port) |
213 | { | 213 | { |
214 | if (init_exec(init)) | 214 | if (init_exec(init)) |
215 | return nvkm_rdport(init->subdev->device, init->crtc, port); | 215 | return nvkm_rdport(init->subdev->device, init->head, port); |
216 | return 0x00; | 216 | return 0x00; |
217 | } | 217 | } |
218 | 218 | ||
@@ -220,7 +220,7 @@ static void | |||
220 | init_wrport(struct nvbios_init *init, u16 port, u8 value) | 220 | init_wrport(struct nvbios_init *init, u16 port, u8 value) |
221 | { | 221 | { |
222 | if (init_exec(init)) | 222 | if (init_exec(init)) |
223 | nvkm_wrport(init->subdev->device, init->crtc, port, value); | 223 | nvkm_wrport(init->subdev->device, init->head, port, value); |
224 | } | 224 | } |
225 | 225 | ||
226 | static u8 | 226 | static u8 |
@@ -228,7 +228,7 @@ init_rdvgai(struct nvbios_init *init, u16 port, u8 index) | |||
228 | { | 228 | { |
229 | struct nvkm_subdev *subdev = init->subdev; | 229 | struct nvkm_subdev *subdev = init->subdev; |
230 | if (init_exec(init)) { | 230 | if (init_exec(init)) { |
231 | int head = init->crtc < 0 ? 0 : init->crtc; | 231 | int head = init->head < 0 ? 0 : init->head; |
232 | return nvkm_rdvgai(subdev->device, head, port, index); | 232 | return nvkm_rdvgai(subdev->device, head, port, index); |
233 | } | 233 | } |
234 | return 0x00; | 234 | return 0x00; |
@@ -242,25 +242,25 @@ init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value) | |||
242 | /* force head 0 for updates to cr44, it only exists on first head */ | 242 | /* force head 0 for updates to cr44, it only exists on first head */ |
243 | if (device->card_type < NV_50) { | 243 | if (device->card_type < NV_50) { |
244 | if (port == 0x03d4 && index == 0x44) | 244 | if (port == 0x03d4 && index == 0x44) |
245 | init->crtc = 0; | 245 | init->head = 0; |
246 | } | 246 | } |
247 | 247 | ||
248 | if (init_exec(init)) { | 248 | if (init_exec(init)) { |
249 | int head = init->crtc < 0 ? 0 : init->crtc; | 249 | int head = init->head < 0 ? 0 : init->head; |
250 | nvkm_wrvgai(device, head, port, index, value); | 250 | nvkm_wrvgai(device, head, port, index, value); |
251 | } | 251 | } |
252 | 252 | ||
253 | /* select head 1 if cr44 write selected it */ | 253 | /* select head 1 if cr44 write selected it */ |
254 | if (device->card_type < NV_50) { | 254 | if (device->card_type < NV_50) { |
255 | if (port == 0x03d4 && index == 0x44 && value == 3) | 255 | if (port == 0x03d4 && index == 0x44 && value == 3) |
256 | init->crtc = 1; | 256 | init->head = 1; |
257 | } | 257 | } |
258 | } | 258 | } |
259 | 259 | ||
260 | static struct i2c_adapter * | 260 | static struct i2c_adapter * |
261 | init_i2c(struct nvbios_init *init, int index) | 261 | init_i2c(struct nvbios_init *init, int index) |
262 | { | 262 | { |
263 | struct nvkm_i2c *i2c = init->bios->subdev.device->i2c; | 263 | struct nvkm_i2c *i2c = init->subdev->device->i2c; |
264 | struct nvkm_i2c_bus *bus; | 264 | struct nvkm_i2c_bus *bus; |
265 | 265 | ||
266 | if (index == 0xff) { | 266 | if (index == 0xff) { |
@@ -300,7 +300,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val) | |||
300 | static struct nvkm_i2c_aux * | 300 | static struct nvkm_i2c_aux * |
301 | init_aux(struct nvbios_init *init) | 301 | init_aux(struct nvbios_init *init) |
302 | { | 302 | { |
303 | struct nvkm_i2c *i2c = init->bios->subdev.device->i2c; | 303 | struct nvkm_i2c *i2c = init->subdev->device->i2c; |
304 | if (!init->outp) { | 304 | if (!init->outp) { |
305 | if (init_exec(init)) | 305 | if (init_exec(init)) |
306 | error("script needs output for aux\n"); | 306 | error("script needs output for aux\n"); |
@@ -341,7 +341,7 @@ init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) | |||
341 | static void | 341 | static void |
342 | init_prog_pll(struct nvbios_init *init, u32 id, u32 freq) | 342 | init_prog_pll(struct nvbios_init *init, u32 id, u32 freq) |
343 | { | 343 | { |
344 | struct nvkm_devinit *devinit = init->bios->subdev.device->devinit; | 344 | struct nvkm_devinit *devinit = init->subdev->device->devinit; |
345 | if (init_exec(init)) { | 345 | if (init_exec(init)) { |
346 | int ret = nvkm_devinit_pll_set(devinit, id, freq); | 346 | int ret = nvkm_devinit_pll_set(devinit, id, freq); |
347 | if (ret) | 347 | if (ret) |
@@ -374,7 +374,7 @@ init_table(struct nvkm_bios *bios, u16 *len) | |||
374 | static u16 | 374 | static u16 |
375 | init_table_(struct nvbios_init *init, u16 offset, const char *name) | 375 | init_table_(struct nvbios_init *init, u16 offset, const char *name) |
376 | { | 376 | { |
377 | struct nvkm_bios *bios = init->bios; | 377 | struct nvkm_bios *bios = init->subdev->device->bios; |
378 | u16 len, data = init_table(bios, &len); | 378 | u16 len, data = init_table(bios, &len); |
379 | if (data) { | 379 | if (data) { |
380 | if (len >= offset + 2) { | 380 | if (len >= offset + 2) { |
@@ -406,7 +406,7 @@ init_table_(struct nvbios_init *init, u16 offset, const char *name) | |||
406 | static u16 | 406 | static u16 |
407 | init_script(struct nvkm_bios *bios, int index) | 407 | init_script(struct nvkm_bios *bios, int index) |
408 | { | 408 | { |
409 | struct nvbios_init init = { .bios = bios }; | 409 | struct nvbios_init init = { .subdev = &bios->subdev }; |
410 | u16 bmp_ver = bmp_version(bios), data; | 410 | u16 bmp_ver = bmp_version(bios), data; |
411 | 411 | ||
412 | if (bmp_ver && bmp_ver < 0x0510) { | 412 | if (bmp_ver && bmp_ver < 0x0510) { |
@@ -436,7 +436,7 @@ init_unknown_script(struct nvkm_bios *bios) | |||
436 | static u8 | 436 | static u8 |
437 | init_ram_restrict_group_count(struct nvbios_init *init) | 437 | init_ram_restrict_group_count(struct nvbios_init *init) |
438 | { | 438 | { |
439 | return nvbios_ramcfg_count(init->bios); | 439 | return nvbios_ramcfg_count(init->subdev->device->bios); |
440 | } | 440 | } |
441 | 441 | ||
442 | static u8 | 442 | static u8 |
@@ -450,7 +450,7 @@ init_ram_restrict(struct nvbios_init *init) | |||
450 | * Preserving the non-caching behaviour on earlier chipsets just | 450 | * Preserving the non-caching behaviour on earlier chipsets just |
451 | * in case *not* re-reading the strap causes similar breakage. | 451 | * in case *not* re-reading the strap causes similar breakage. |
452 | */ | 452 | */ |
453 | if (!init->ramcfg || init->bios->version.major < 0x70) | 453 | if (!init->ramcfg || init->subdev->device->bios->version.major < 0x70) |
454 | init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev); | 454 | init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev); |
455 | return (init->ramcfg & 0x7fffffff); | 455 | return (init->ramcfg & 0x7fffffff); |
456 | } | 456 | } |
@@ -458,7 +458,7 @@ init_ram_restrict(struct nvbios_init *init) | |||
458 | static u8 | 458 | static u8 |
459 | init_xlat_(struct nvbios_init *init, u8 index, u8 offset) | 459 | init_xlat_(struct nvbios_init *init, u8 index, u8 offset) |
460 | { | 460 | { |
461 | struct nvkm_bios *bios = init->bios; | 461 | struct nvkm_bios *bios = init->subdev->device->bios; |
462 | u16 table = init_xlat_table(init); | 462 | u16 table = init_xlat_table(init); |
463 | if (table) { | 463 | if (table) { |
464 | u16 data = nvbios_rd16(bios, table + (index * 2)); | 464 | u16 data = nvbios_rd16(bios, table + (index * 2)); |
@@ -476,7 +476,7 @@ init_xlat_(struct nvbios_init *init, u8 index, u8 offset) | |||
476 | static bool | 476 | static bool |
477 | init_condition_met(struct nvbios_init *init, u8 cond) | 477 | init_condition_met(struct nvbios_init *init, u8 cond) |
478 | { | 478 | { |
479 | struct nvkm_bios *bios = init->bios; | 479 | struct nvkm_bios *bios = init->subdev->device->bios; |
480 | u16 table = init_condition_table(init); | 480 | u16 table = init_condition_table(init); |
481 | if (table) { | 481 | if (table) { |
482 | u32 reg = nvbios_rd32(bios, table + (cond * 12) + 0); | 482 | u32 reg = nvbios_rd32(bios, table + (cond * 12) + 0); |
@@ -492,7 +492,7 @@ init_condition_met(struct nvbios_init *init, u8 cond) | |||
492 | static bool | 492 | static bool |
493 | init_io_condition_met(struct nvbios_init *init, u8 cond) | 493 | init_io_condition_met(struct nvbios_init *init, u8 cond) |
494 | { | 494 | { |
495 | struct nvkm_bios *bios = init->bios; | 495 | struct nvkm_bios *bios = init->subdev->device->bios; |
496 | u16 table = init_io_condition_table(init); | 496 | u16 table = init_io_condition_table(init); |
497 | if (table) { | 497 | if (table) { |
498 | u16 port = nvbios_rd16(bios, table + (cond * 5) + 0); | 498 | u16 port = nvbios_rd16(bios, table + (cond * 5) + 0); |
@@ -509,7 +509,7 @@ init_io_condition_met(struct nvbios_init *init, u8 cond) | |||
509 | static bool | 509 | static bool |
510 | init_io_flag_condition_met(struct nvbios_init *init, u8 cond) | 510 | init_io_flag_condition_met(struct nvbios_init *init, u8 cond) |
511 | { | 511 | { |
512 | struct nvkm_bios *bios = init->bios; | 512 | struct nvkm_bios *bios = init->subdev->device->bios; |
513 | u16 table = init_io_flag_condition_table(init); | 513 | u16 table = init_io_flag_condition_table(init); |
514 | if (table) { | 514 | if (table) { |
515 | u16 port = nvbios_rd16(bios, table + (cond * 9) + 0); | 515 | u16 port = nvbios_rd16(bios, table + (cond * 9) + 0); |
@@ -580,7 +580,8 @@ init_tmds_reg(struct nvbios_init *init, u8 tmds) | |||
580 | static void | 580 | static void |
581 | init_reserved(struct nvbios_init *init) | 581 | init_reserved(struct nvbios_init *init) |
582 | { | 582 | { |
583 | u8 opcode = nvbios_rd08(init->bios, init->offset); | 583 | struct nvkm_bios *bios = init->subdev->device->bios; |
584 | u8 opcode = nvbios_rd08(bios, init->offset); | ||
584 | u8 length, i; | 585 | u8 length, i; |
585 | 586 | ||
586 | switch (opcode) { | 587 | switch (opcode) { |
@@ -594,7 +595,7 @@ init_reserved(struct nvbios_init *init) | |||
594 | 595 | ||
595 | trace("RESERVED 0x%02x\t", opcode); | 596 | trace("RESERVED 0x%02x\t", opcode); |
596 | for (i = 1; i < length; i++) | 597 | for (i = 1; i < length; i++) |
597 | cont(" 0x%02x", nvbios_rd08(init->bios, init->offset + i)); | 598 | cont(" 0x%02x", nvbios_rd08(bios, init->offset + i)); |
598 | cont("\n"); | 599 | cont("\n"); |
599 | init->offset += length; | 600 | init->offset += length; |
600 | } | 601 | } |
@@ -617,7 +618,7 @@ init_done(struct nvbios_init *init) | |||
617 | static void | 618 | static void |
618 | init_io_restrict_prog(struct nvbios_init *init) | 619 | init_io_restrict_prog(struct nvbios_init *init) |
619 | { | 620 | { |
620 | struct nvkm_bios *bios = init->bios; | 621 | struct nvkm_bios *bios = init->subdev->device->bios; |
621 | u16 port = nvbios_rd16(bios, init->offset + 1); | 622 | u16 port = nvbios_rd16(bios, init->offset + 1); |
622 | u8 index = nvbios_rd08(bios, init->offset + 3); | 623 | u8 index = nvbios_rd08(bios, init->offset + 3); |
623 | u8 mask = nvbios_rd08(bios, init->offset + 4); | 624 | u8 mask = nvbios_rd08(bios, init->offset + 4); |
@@ -654,7 +655,7 @@ init_io_restrict_prog(struct nvbios_init *init) | |||
654 | static void | 655 | static void |
655 | init_repeat(struct nvbios_init *init) | 656 | init_repeat(struct nvbios_init *init) |
656 | { | 657 | { |
657 | struct nvkm_bios *bios = init->bios; | 658 | struct nvkm_bios *bios = init->subdev->device->bios; |
658 | u8 count = nvbios_rd08(bios, init->offset + 1); | 659 | u8 count = nvbios_rd08(bios, init->offset + 1); |
659 | u16 repeat = init->repeat; | 660 | u16 repeat = init->repeat; |
660 | 661 | ||
@@ -680,7 +681,7 @@ init_repeat(struct nvbios_init *init) | |||
680 | static void | 681 | static void |
681 | init_io_restrict_pll(struct nvbios_init *init) | 682 | init_io_restrict_pll(struct nvbios_init *init) |
682 | { | 683 | { |
683 | struct nvkm_bios *bios = init->bios; | 684 | struct nvkm_bios *bios = init->subdev->device->bios; |
684 | u16 port = nvbios_rd16(bios, init->offset + 1); | 685 | u16 port = nvbios_rd16(bios, init->offset + 1); |
685 | u8 index = nvbios_rd08(bios, init->offset + 3); | 686 | u8 index = nvbios_rd08(bios, init->offset + 3); |
686 | u8 mask = nvbios_rd08(bios, init->offset + 4); | 687 | u8 mask = nvbios_rd08(bios, init->offset + 4); |
@@ -736,7 +737,7 @@ init_end_repeat(struct nvbios_init *init) | |||
736 | static void | 737 | static void |
737 | init_copy(struct nvbios_init *init) | 738 | init_copy(struct nvbios_init *init) |
738 | { | 739 | { |
739 | struct nvkm_bios *bios = init->bios; | 740 | struct nvkm_bios *bios = init->subdev->device->bios; |
740 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 741 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
741 | u8 shift = nvbios_rd08(bios, init->offset + 5); | 742 | u8 shift = nvbios_rd08(bios, init->offset + 5); |
742 | u8 smask = nvbios_rd08(bios, init->offset + 6); | 743 | u8 smask = nvbios_rd08(bios, init->offset + 6); |
@@ -775,7 +776,7 @@ init_not(struct nvbios_init *init) | |||
775 | static void | 776 | static void |
776 | init_io_flag_condition(struct nvbios_init *init) | 777 | init_io_flag_condition(struct nvbios_init *init) |
777 | { | 778 | { |
778 | struct nvkm_bios *bios = init->bios; | 779 | struct nvkm_bios *bios = init->subdev->device->bios; |
779 | u8 cond = nvbios_rd08(bios, init->offset + 1); | 780 | u8 cond = nvbios_rd08(bios, init->offset + 1); |
780 | 781 | ||
781 | trace("IO_FLAG_CONDITION\t0x%02x\n", cond); | 782 | trace("IO_FLAG_CONDITION\t0x%02x\n", cond); |
@@ -792,7 +793,7 @@ init_io_flag_condition(struct nvbios_init *init) | |||
792 | static void | 793 | static void |
793 | init_generic_condition(struct nvbios_init *init) | 794 | init_generic_condition(struct nvbios_init *init) |
794 | { | 795 | { |
795 | struct nvkm_bios *bios = init->bios; | 796 | struct nvkm_bios *bios = init->subdev->device->bios; |
796 | struct nvbios_dpout info; | 797 | struct nvbios_dpout info; |
797 | u8 cond = nvbios_rd08(bios, init->offset + 1); | 798 | u8 cond = nvbios_rd08(bios, init->offset + 1); |
798 | u8 size = nvbios_rd08(bios, init->offset + 2); | 799 | u8 size = nvbios_rd08(bios, init->offset + 2); |
@@ -841,7 +842,7 @@ init_generic_condition(struct nvbios_init *init) | |||
841 | static void | 842 | static void |
842 | init_io_mask_or(struct nvbios_init *init) | 843 | init_io_mask_or(struct nvbios_init *init) |
843 | { | 844 | { |
844 | struct nvkm_bios *bios = init->bios; | 845 | struct nvkm_bios *bios = init->subdev->device->bios; |
845 | u8 index = nvbios_rd08(bios, init->offset + 1); | 846 | u8 index = nvbios_rd08(bios, init->offset + 1); |
846 | u8 or = init_or(init); | 847 | u8 or = init_or(init); |
847 | u8 data; | 848 | u8 data; |
@@ -860,7 +861,7 @@ init_io_mask_or(struct nvbios_init *init) | |||
860 | static void | 861 | static void |
861 | init_io_or(struct nvbios_init *init) | 862 | init_io_or(struct nvbios_init *init) |
862 | { | 863 | { |
863 | struct nvkm_bios *bios = init->bios; | 864 | struct nvkm_bios *bios = init->subdev->device->bios; |
864 | u8 index = nvbios_rd08(bios, init->offset + 1); | 865 | u8 index = nvbios_rd08(bios, init->offset + 1); |
865 | u8 or = init_or(init); | 866 | u8 or = init_or(init); |
866 | u8 data; | 867 | u8 data; |
@@ -879,7 +880,7 @@ init_io_or(struct nvbios_init *init) | |||
879 | static void | 880 | static void |
880 | init_andn_reg(struct nvbios_init *init) | 881 | init_andn_reg(struct nvbios_init *init) |
881 | { | 882 | { |
882 | struct nvkm_bios *bios = init->bios; | 883 | struct nvkm_bios *bios = init->subdev->device->bios; |
883 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 884 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
884 | u32 mask = nvbios_rd32(bios, init->offset + 5); | 885 | u32 mask = nvbios_rd32(bios, init->offset + 5); |
885 | 886 | ||
@@ -896,7 +897,7 @@ init_andn_reg(struct nvbios_init *init) | |||
896 | static void | 897 | static void |
897 | init_or_reg(struct nvbios_init *init) | 898 | init_or_reg(struct nvbios_init *init) |
898 | { | 899 | { |
899 | struct nvkm_bios *bios = init->bios; | 900 | struct nvkm_bios *bios = init->subdev->device->bios; |
900 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 901 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
901 | u32 mask = nvbios_rd32(bios, init->offset + 5); | 902 | u32 mask = nvbios_rd32(bios, init->offset + 5); |
902 | 903 | ||
@@ -913,7 +914,7 @@ init_or_reg(struct nvbios_init *init) | |||
913 | static void | 914 | static void |
914 | init_idx_addr_latched(struct nvbios_init *init) | 915 | init_idx_addr_latched(struct nvbios_init *init) |
915 | { | 916 | { |
916 | struct nvkm_bios *bios = init->bios; | 917 | struct nvkm_bios *bios = init->subdev->device->bios; |
917 | u32 creg = nvbios_rd32(bios, init->offset + 1); | 918 | u32 creg = nvbios_rd32(bios, init->offset + 1); |
918 | u32 dreg = nvbios_rd32(bios, init->offset + 5); | 919 | u32 dreg = nvbios_rd32(bios, init->offset + 5); |
919 | u32 mask = nvbios_rd32(bios, init->offset + 9); | 920 | u32 mask = nvbios_rd32(bios, init->offset + 9); |
@@ -943,7 +944,7 @@ init_idx_addr_latched(struct nvbios_init *init) | |||
943 | static void | 944 | static void |
944 | init_io_restrict_pll2(struct nvbios_init *init) | 945 | init_io_restrict_pll2(struct nvbios_init *init) |
945 | { | 946 | { |
946 | struct nvkm_bios *bios = init->bios; | 947 | struct nvkm_bios *bios = init->subdev->device->bios; |
947 | u16 port = nvbios_rd16(bios, init->offset + 1); | 948 | u16 port = nvbios_rd16(bios, init->offset + 1); |
948 | u8 index = nvbios_rd08(bios, init->offset + 3); | 949 | u8 index = nvbios_rd08(bios, init->offset + 3); |
949 | u8 mask = nvbios_rd08(bios, init->offset + 4); | 950 | u8 mask = nvbios_rd08(bios, init->offset + 4); |
@@ -978,7 +979,7 @@ init_io_restrict_pll2(struct nvbios_init *init) | |||
978 | static void | 979 | static void |
979 | init_pll2(struct nvbios_init *init) | 980 | init_pll2(struct nvbios_init *init) |
980 | { | 981 | { |
981 | struct nvkm_bios *bios = init->bios; | 982 | struct nvkm_bios *bios = init->subdev->device->bios; |
982 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 983 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
983 | u32 freq = nvbios_rd32(bios, init->offset + 5); | 984 | u32 freq = nvbios_rd32(bios, init->offset + 5); |
984 | 985 | ||
@@ -995,7 +996,7 @@ init_pll2(struct nvbios_init *init) | |||
995 | static void | 996 | static void |
996 | init_i2c_byte(struct nvbios_init *init) | 997 | init_i2c_byte(struct nvbios_init *init) |
997 | { | 998 | { |
998 | struct nvkm_bios *bios = init->bios; | 999 | struct nvkm_bios *bios = init->subdev->device->bios; |
999 | u8 index = nvbios_rd08(bios, init->offset + 1); | 1000 | u8 index = nvbios_rd08(bios, init->offset + 1); |
1000 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; | 1001 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; |
1001 | u8 count = nvbios_rd08(bios, init->offset + 3); | 1002 | u8 count = nvbios_rd08(bios, init->offset + 3); |
@@ -1026,7 +1027,7 @@ init_i2c_byte(struct nvbios_init *init) | |||
1026 | static void | 1027 | static void |
1027 | init_zm_i2c_byte(struct nvbios_init *init) | 1028 | init_zm_i2c_byte(struct nvbios_init *init) |
1028 | { | 1029 | { |
1029 | struct nvkm_bios *bios = init->bios; | 1030 | struct nvkm_bios *bios = init->subdev->device->bios; |
1030 | u8 index = nvbios_rd08(bios, init->offset + 1); | 1031 | u8 index = nvbios_rd08(bios, init->offset + 1); |
1031 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; | 1032 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; |
1032 | u8 count = nvbios_rd08(bios, init->offset + 3); | 1033 | u8 count = nvbios_rd08(bios, init->offset + 3); |
@@ -1052,7 +1053,7 @@ init_zm_i2c_byte(struct nvbios_init *init) | |||
1052 | static void | 1053 | static void |
1053 | init_zm_i2c(struct nvbios_init *init) | 1054 | init_zm_i2c(struct nvbios_init *init) |
1054 | { | 1055 | { |
1055 | struct nvkm_bios *bios = init->bios; | 1056 | struct nvkm_bios *bios = init->subdev->device->bios; |
1056 | u8 index = nvbios_rd08(bios, init->offset + 1); | 1057 | u8 index = nvbios_rd08(bios, init->offset + 1); |
1057 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; | 1058 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; |
1058 | u8 count = nvbios_rd08(bios, init->offset + 3); | 1059 | u8 count = nvbios_rd08(bios, init->offset + 3); |
@@ -1086,7 +1087,7 @@ init_zm_i2c(struct nvbios_init *init) | |||
1086 | static void | 1087 | static void |
1087 | init_tmds(struct nvbios_init *init) | 1088 | init_tmds(struct nvbios_init *init) |
1088 | { | 1089 | { |
1089 | struct nvkm_bios *bios = init->bios; | 1090 | struct nvkm_bios *bios = init->subdev->device->bios; |
1090 | u8 tmds = nvbios_rd08(bios, init->offset + 1); | 1091 | u8 tmds = nvbios_rd08(bios, init->offset + 1); |
1091 | u8 addr = nvbios_rd08(bios, init->offset + 2); | 1092 | u8 addr = nvbios_rd08(bios, init->offset + 2); |
1092 | u8 mask = nvbios_rd08(bios, init->offset + 3); | 1093 | u8 mask = nvbios_rd08(bios, init->offset + 3); |
@@ -1112,7 +1113,7 @@ init_tmds(struct nvbios_init *init) | |||
1112 | static void | 1113 | static void |
1113 | init_zm_tmds_group(struct nvbios_init *init) | 1114 | init_zm_tmds_group(struct nvbios_init *init) |
1114 | { | 1115 | { |
1115 | struct nvkm_bios *bios = init->bios; | 1116 | struct nvkm_bios *bios = init->subdev->device->bios; |
1116 | u8 tmds = nvbios_rd08(bios, init->offset + 1); | 1117 | u8 tmds = nvbios_rd08(bios, init->offset + 1); |
1117 | u8 count = nvbios_rd08(bios, init->offset + 2); | 1118 | u8 count = nvbios_rd08(bios, init->offset + 2); |
1118 | u32 reg = init_tmds_reg(init, tmds); | 1119 | u32 reg = init_tmds_reg(init, tmds); |
@@ -1139,7 +1140,7 @@ init_zm_tmds_group(struct nvbios_init *init) | |||
1139 | static void | 1140 | static void |
1140 | init_cr_idx_adr_latch(struct nvbios_init *init) | 1141 | init_cr_idx_adr_latch(struct nvbios_init *init) |
1141 | { | 1142 | { |
1142 | struct nvkm_bios *bios = init->bios; | 1143 | struct nvkm_bios *bios = init->subdev->device->bios; |
1143 | u8 addr0 = nvbios_rd08(bios, init->offset + 1); | 1144 | u8 addr0 = nvbios_rd08(bios, init->offset + 1); |
1144 | u8 addr1 = nvbios_rd08(bios, init->offset + 2); | 1145 | u8 addr1 = nvbios_rd08(bios, init->offset + 2); |
1145 | u8 base = nvbios_rd08(bios, init->offset + 3); | 1146 | u8 base = nvbios_rd08(bios, init->offset + 3); |
@@ -1169,7 +1170,7 @@ init_cr_idx_adr_latch(struct nvbios_init *init) | |||
1169 | static void | 1170 | static void |
1170 | init_cr(struct nvbios_init *init) | 1171 | init_cr(struct nvbios_init *init) |
1171 | { | 1172 | { |
1172 | struct nvkm_bios *bios = init->bios; | 1173 | struct nvkm_bios *bios = init->subdev->device->bios; |
1173 | u8 addr = nvbios_rd08(bios, init->offset + 1); | 1174 | u8 addr = nvbios_rd08(bios, init->offset + 1); |
1174 | u8 mask = nvbios_rd08(bios, init->offset + 2); | 1175 | u8 mask = nvbios_rd08(bios, init->offset + 2); |
1175 | u8 data = nvbios_rd08(bios, init->offset + 3); | 1176 | u8 data = nvbios_rd08(bios, init->offset + 3); |
@@ -1189,7 +1190,7 @@ init_cr(struct nvbios_init *init) | |||
1189 | static void | 1190 | static void |
1190 | init_zm_cr(struct nvbios_init *init) | 1191 | init_zm_cr(struct nvbios_init *init) |
1191 | { | 1192 | { |
1192 | struct nvkm_bios *bios = init->bios; | 1193 | struct nvkm_bios *bios = init->subdev->device->bios; |
1193 | u8 addr = nvbios_rd08(bios, init->offset + 1); | 1194 | u8 addr = nvbios_rd08(bios, init->offset + 1); |
1194 | u8 data = nvbios_rd08(bios, init->offset + 2); | 1195 | u8 data = nvbios_rd08(bios, init->offset + 2); |
1195 | 1196 | ||
@@ -1206,7 +1207,7 @@ init_zm_cr(struct nvbios_init *init) | |||
1206 | static void | 1207 | static void |
1207 | init_zm_cr_group(struct nvbios_init *init) | 1208 | init_zm_cr_group(struct nvbios_init *init) |
1208 | { | 1209 | { |
1209 | struct nvkm_bios *bios = init->bios; | 1210 | struct nvkm_bios *bios = init->subdev->device->bios; |
1210 | u8 count = nvbios_rd08(bios, init->offset + 1); | 1211 | u8 count = nvbios_rd08(bios, init->offset + 1); |
1211 | 1212 | ||
1212 | trace("ZM_CR_GROUP\n"); | 1213 | trace("ZM_CR_GROUP\n"); |
@@ -1230,7 +1231,7 @@ init_zm_cr_group(struct nvbios_init *init) | |||
1230 | static void | 1231 | static void |
1231 | init_condition_time(struct nvbios_init *init) | 1232 | init_condition_time(struct nvbios_init *init) |
1232 | { | 1233 | { |
1233 | struct nvkm_bios *bios = init->bios; | 1234 | struct nvkm_bios *bios = init->subdev->device->bios; |
1234 | u8 cond = nvbios_rd08(bios, init->offset + 1); | 1235 | u8 cond = nvbios_rd08(bios, init->offset + 1); |
1235 | u8 retry = nvbios_rd08(bios, init->offset + 2); | 1236 | u8 retry = nvbios_rd08(bios, init->offset + 2); |
1236 | u8 wait = min((u16)retry * 50, 100); | 1237 | u8 wait = min((u16)retry * 50, 100); |
@@ -1257,7 +1258,7 @@ init_condition_time(struct nvbios_init *init) | |||
1257 | static void | 1258 | static void |
1258 | init_ltime(struct nvbios_init *init) | 1259 | init_ltime(struct nvbios_init *init) |
1259 | { | 1260 | { |
1260 | struct nvkm_bios *bios = init->bios; | 1261 | struct nvkm_bios *bios = init->subdev->device->bios; |
1261 | u16 msec = nvbios_rd16(bios, init->offset + 1); | 1262 | u16 msec = nvbios_rd16(bios, init->offset + 1); |
1262 | 1263 | ||
1263 | trace("LTIME\t0x%04x\n", msec); | 1264 | trace("LTIME\t0x%04x\n", msec); |
@@ -1274,7 +1275,7 @@ init_ltime(struct nvbios_init *init) | |||
1274 | static void | 1275 | static void |
1275 | init_zm_reg_sequence(struct nvbios_init *init) | 1276 | init_zm_reg_sequence(struct nvbios_init *init) |
1276 | { | 1277 | { |
1277 | struct nvkm_bios *bios = init->bios; | 1278 | struct nvkm_bios *bios = init->subdev->device->bios; |
1278 | u32 base = nvbios_rd32(bios, init->offset + 1); | 1279 | u32 base = nvbios_rd32(bios, init->offset + 1); |
1279 | u8 count = nvbios_rd08(bios, init->offset + 5); | 1280 | u8 count = nvbios_rd08(bios, init->offset + 5); |
1280 | 1281 | ||
@@ -1299,7 +1300,7 @@ init_zm_reg_sequence(struct nvbios_init *init) | |||
1299 | static void | 1300 | static void |
1300 | init_pll_indirect(struct nvbios_init *init) | 1301 | init_pll_indirect(struct nvbios_init *init) |
1301 | { | 1302 | { |
1302 | struct nvkm_bios *bios = init->bios; | 1303 | struct nvkm_bios *bios = init->subdev->device->bios; |
1303 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 1304 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
1304 | u16 addr = nvbios_rd16(bios, init->offset + 5); | 1305 | u16 addr = nvbios_rd16(bios, init->offset + 5); |
1305 | u32 freq = (u32)nvbios_rd16(bios, addr) * 1000; | 1306 | u32 freq = (u32)nvbios_rd16(bios, addr) * 1000; |
@@ -1318,7 +1319,7 @@ init_pll_indirect(struct nvbios_init *init) | |||
1318 | static void | 1319 | static void |
1319 | init_zm_reg_indirect(struct nvbios_init *init) | 1320 | init_zm_reg_indirect(struct nvbios_init *init) |
1320 | { | 1321 | { |
1321 | struct nvkm_bios *bios = init->bios; | 1322 | struct nvkm_bios *bios = init->subdev->device->bios; |
1322 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 1323 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
1323 | u16 addr = nvbios_rd16(bios, init->offset + 5); | 1324 | u16 addr = nvbios_rd16(bios, init->offset + 5); |
1324 | u32 data = nvbios_rd32(bios, addr); | 1325 | u32 data = nvbios_rd32(bios, addr); |
@@ -1337,7 +1338,7 @@ init_zm_reg_indirect(struct nvbios_init *init) | |||
1337 | static void | 1338 | static void |
1338 | init_sub_direct(struct nvbios_init *init) | 1339 | init_sub_direct(struct nvbios_init *init) |
1339 | { | 1340 | { |
1340 | struct nvkm_bios *bios = init->bios; | 1341 | struct nvkm_bios *bios = init->subdev->device->bios; |
1341 | u16 addr = nvbios_rd16(bios, init->offset + 1); | 1342 | u16 addr = nvbios_rd16(bios, init->offset + 1); |
1342 | u16 save; | 1343 | u16 save; |
1343 | 1344 | ||
@@ -1363,7 +1364,7 @@ init_sub_direct(struct nvbios_init *init) | |||
1363 | static void | 1364 | static void |
1364 | init_jump(struct nvbios_init *init) | 1365 | init_jump(struct nvbios_init *init) |
1365 | { | 1366 | { |
1366 | struct nvkm_bios *bios = init->bios; | 1367 | struct nvkm_bios *bios = init->subdev->device->bios; |
1367 | u16 offset = nvbios_rd16(bios, init->offset + 1); | 1368 | u16 offset = nvbios_rd16(bios, init->offset + 1); |
1368 | 1369 | ||
1369 | trace("JUMP\t0x%04x\n", offset); | 1370 | trace("JUMP\t0x%04x\n", offset); |
@@ -1381,7 +1382,7 @@ init_jump(struct nvbios_init *init) | |||
1381 | static void | 1382 | static void |
1382 | init_i2c_if(struct nvbios_init *init) | 1383 | init_i2c_if(struct nvbios_init *init) |
1383 | { | 1384 | { |
1384 | struct nvkm_bios *bios = init->bios; | 1385 | struct nvkm_bios *bios = init->subdev->device->bios; |
1385 | u8 index = nvbios_rd08(bios, init->offset + 1); | 1386 | u8 index = nvbios_rd08(bios, init->offset + 1); |
1386 | u8 addr = nvbios_rd08(bios, init->offset + 2); | 1387 | u8 addr = nvbios_rd08(bios, init->offset + 2); |
1387 | u8 reg = nvbios_rd08(bios, init->offset + 3); | 1388 | u8 reg = nvbios_rd08(bios, init->offset + 3); |
@@ -1408,7 +1409,7 @@ init_i2c_if(struct nvbios_init *init) | |||
1408 | static void | 1409 | static void |
1409 | init_copy_nv_reg(struct nvbios_init *init) | 1410 | init_copy_nv_reg(struct nvbios_init *init) |
1410 | { | 1411 | { |
1411 | struct nvkm_bios *bios = init->bios; | 1412 | struct nvkm_bios *bios = init->subdev->device->bios; |
1412 | u32 sreg = nvbios_rd32(bios, init->offset + 1); | 1413 | u32 sreg = nvbios_rd32(bios, init->offset + 1); |
1413 | u8 shift = nvbios_rd08(bios, init->offset + 5); | 1414 | u8 shift = nvbios_rd08(bios, init->offset + 5); |
1414 | u32 smask = nvbios_rd32(bios, init->offset + 6); | 1415 | u32 smask = nvbios_rd32(bios, init->offset + 6); |
@@ -1434,7 +1435,7 @@ init_copy_nv_reg(struct nvbios_init *init) | |||
1434 | static void | 1435 | static void |
1435 | init_zm_index_io(struct nvbios_init *init) | 1436 | init_zm_index_io(struct nvbios_init *init) |
1436 | { | 1437 | { |
1437 | struct nvkm_bios *bios = init->bios; | 1438 | struct nvkm_bios *bios = init->subdev->device->bios; |
1438 | u16 port = nvbios_rd16(bios, init->offset + 1); | 1439 | u16 port = nvbios_rd16(bios, init->offset + 1); |
1439 | u8 index = nvbios_rd08(bios, init->offset + 3); | 1440 | u8 index = nvbios_rd08(bios, init->offset + 3); |
1440 | u8 data = nvbios_rd08(bios, init->offset + 4); | 1441 | u8 data = nvbios_rd08(bios, init->offset + 4); |
@@ -1452,7 +1453,7 @@ init_zm_index_io(struct nvbios_init *init) | |||
1452 | static void | 1453 | static void |
1453 | init_compute_mem(struct nvbios_init *init) | 1454 | init_compute_mem(struct nvbios_init *init) |
1454 | { | 1455 | { |
1455 | struct nvkm_devinit *devinit = init->bios->subdev.device->devinit; | 1456 | struct nvkm_devinit *devinit = init->subdev->device->devinit; |
1456 | 1457 | ||
1457 | trace("COMPUTE_MEM\n"); | 1458 | trace("COMPUTE_MEM\n"); |
1458 | init->offset += 1; | 1459 | init->offset += 1; |
@@ -1470,7 +1471,7 @@ init_compute_mem(struct nvbios_init *init) | |||
1470 | static void | 1471 | static void |
1471 | init_reset(struct nvbios_init *init) | 1472 | init_reset(struct nvbios_init *init) |
1472 | { | 1473 | { |
1473 | struct nvkm_bios *bios = init->bios; | 1474 | struct nvkm_bios *bios = init->subdev->device->bios; |
1474 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 1475 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
1475 | u32 data1 = nvbios_rd32(bios, init->offset + 5); | 1476 | u32 data1 = nvbios_rd32(bios, init->offset + 5); |
1476 | u32 data2 = nvbios_rd32(bios, init->offset + 9); | 1477 | u32 data2 = nvbios_rd32(bios, init->offset + 9); |
@@ -1497,7 +1498,7 @@ init_reset(struct nvbios_init *init) | |||
1497 | static u16 | 1498 | static u16 |
1498 | init_configure_mem_clk(struct nvbios_init *init) | 1499 | init_configure_mem_clk(struct nvbios_init *init) |
1499 | { | 1500 | { |
1500 | u16 mdata = bmp_mem_init_table(init->bios); | 1501 | u16 mdata = bmp_mem_init_table(init->subdev->device->bios); |
1501 | if (mdata) | 1502 | if (mdata) |
1502 | mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66; | 1503 | mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66; |
1503 | return mdata; | 1504 | return mdata; |
@@ -1506,7 +1507,7 @@ init_configure_mem_clk(struct nvbios_init *init) | |||
1506 | static void | 1507 | static void |
1507 | init_configure_mem(struct nvbios_init *init) | 1508 | init_configure_mem(struct nvbios_init *init) |
1508 | { | 1509 | { |
1509 | struct nvkm_bios *bios = init->bios; | 1510 | struct nvkm_bios *bios = init->subdev->device->bios; |
1510 | u16 mdata, sdata; | 1511 | u16 mdata, sdata; |
1511 | u32 addr, data; | 1512 | u32 addr, data; |
1512 | 1513 | ||
@@ -1556,7 +1557,7 @@ init_configure_mem(struct nvbios_init *init) | |||
1556 | static void | 1557 | static void |
1557 | init_configure_clk(struct nvbios_init *init) | 1558 | init_configure_clk(struct nvbios_init *init) |
1558 | { | 1559 | { |
1559 | struct nvkm_bios *bios = init->bios; | 1560 | struct nvkm_bios *bios = init->subdev->device->bios; |
1560 | u16 mdata, clock; | 1561 | u16 mdata, clock; |
1561 | 1562 | ||
1562 | trace("CONFIGURE_CLK\n"); | 1563 | trace("CONFIGURE_CLK\n"); |
@@ -1590,7 +1591,7 @@ init_configure_clk(struct nvbios_init *init) | |||
1590 | static void | 1591 | static void |
1591 | init_configure_preinit(struct nvbios_init *init) | 1592 | init_configure_preinit(struct nvbios_init *init) |
1592 | { | 1593 | { |
1593 | struct nvkm_bios *bios = init->bios; | 1594 | struct nvkm_bios *bios = init->subdev->device->bios; |
1594 | u32 strap; | 1595 | u32 strap; |
1595 | 1596 | ||
1596 | trace("CONFIGURE_PREINIT\n"); | 1597 | trace("CONFIGURE_PREINIT\n"); |
@@ -1616,7 +1617,7 @@ init_configure_preinit(struct nvbios_init *init) | |||
1616 | static void | 1617 | static void |
1617 | init_io(struct nvbios_init *init) | 1618 | init_io(struct nvbios_init *init) |
1618 | { | 1619 | { |
1619 | struct nvkm_bios *bios = init->bios; | 1620 | struct nvkm_bios *bios = init->subdev->device->bios; |
1620 | u16 port = nvbios_rd16(bios, init->offset + 1); | 1621 | u16 port = nvbios_rd16(bios, init->offset + 1); |
1621 | u8 mask = nvbios_rd16(bios, init->offset + 3); | 1622 | u8 mask = nvbios_rd16(bios, init->offset + 3); |
1622 | u8 data = nvbios_rd16(bios, init->offset + 4); | 1623 | u8 data = nvbios_rd16(bios, init->offset + 4); |
@@ -1656,7 +1657,7 @@ init_io(struct nvbios_init *init) | |||
1656 | static void | 1657 | static void |
1657 | init_sub(struct nvbios_init *init) | 1658 | init_sub(struct nvbios_init *init) |
1658 | { | 1659 | { |
1659 | struct nvkm_bios *bios = init->bios; | 1660 | struct nvkm_bios *bios = init->subdev->device->bios; |
1660 | u8 index = nvbios_rd08(bios, init->offset + 1); | 1661 | u8 index = nvbios_rd08(bios, init->offset + 1); |
1661 | u16 addr, save; | 1662 | u16 addr, save; |
1662 | 1663 | ||
@@ -1683,7 +1684,7 @@ init_sub(struct nvbios_init *init) | |||
1683 | static void | 1684 | static void |
1684 | init_ram_condition(struct nvbios_init *init) | 1685 | init_ram_condition(struct nvbios_init *init) |
1685 | { | 1686 | { |
1686 | struct nvkm_bios *bios = init->bios; | 1687 | struct nvkm_bios *bios = init->subdev->device->bios; |
1687 | u8 mask = nvbios_rd08(bios, init->offset + 1); | 1688 | u8 mask = nvbios_rd08(bios, init->offset + 1); |
1688 | u8 value = nvbios_rd08(bios, init->offset + 2); | 1689 | u8 value = nvbios_rd08(bios, init->offset + 2); |
1689 | 1690 | ||
@@ -1702,7 +1703,7 @@ init_ram_condition(struct nvbios_init *init) | |||
1702 | static void | 1703 | static void |
1703 | init_nv_reg(struct nvbios_init *init) | 1704 | init_nv_reg(struct nvbios_init *init) |
1704 | { | 1705 | { |
1705 | struct nvkm_bios *bios = init->bios; | 1706 | struct nvkm_bios *bios = init->subdev->device->bios; |
1706 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 1707 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
1707 | u32 mask = nvbios_rd32(bios, init->offset + 5); | 1708 | u32 mask = nvbios_rd32(bios, init->offset + 5); |
1708 | u32 data = nvbios_rd32(bios, init->offset + 9); | 1709 | u32 data = nvbios_rd32(bios, init->offset + 9); |
@@ -1720,7 +1721,7 @@ init_nv_reg(struct nvbios_init *init) | |||
1720 | static void | 1721 | static void |
1721 | init_macro(struct nvbios_init *init) | 1722 | init_macro(struct nvbios_init *init) |
1722 | { | 1723 | { |
1723 | struct nvkm_bios *bios = init->bios; | 1724 | struct nvkm_bios *bios = init->subdev->device->bios; |
1724 | u8 macro = nvbios_rd08(bios, init->offset + 1); | 1725 | u8 macro = nvbios_rd08(bios, init->offset + 1); |
1725 | u16 table; | 1726 | u16 table; |
1726 | 1727 | ||
@@ -1756,7 +1757,7 @@ init_resume(struct nvbios_init *init) | |||
1756 | static void | 1757 | static void |
1757 | init_strap_condition(struct nvbios_init *init) | 1758 | init_strap_condition(struct nvbios_init *init) |
1758 | { | 1759 | { |
1759 | struct nvkm_bios *bios = init->bios; | 1760 | struct nvkm_bios *bios = init->subdev->device->bios; |
1760 | u32 mask = nvbios_rd32(bios, init->offset + 1); | 1761 | u32 mask = nvbios_rd32(bios, init->offset + 1); |
1761 | u32 value = nvbios_rd32(bios, init->offset + 5); | 1762 | u32 value = nvbios_rd32(bios, init->offset + 5); |
1762 | 1763 | ||
@@ -1774,7 +1775,7 @@ init_strap_condition(struct nvbios_init *init) | |||
1774 | static void | 1775 | static void |
1775 | init_time(struct nvbios_init *init) | 1776 | init_time(struct nvbios_init *init) |
1776 | { | 1777 | { |
1777 | struct nvkm_bios *bios = init->bios; | 1778 | struct nvkm_bios *bios = init->subdev->device->bios; |
1778 | u16 usec = nvbios_rd16(bios, init->offset + 1); | 1779 | u16 usec = nvbios_rd16(bios, init->offset + 1); |
1779 | 1780 | ||
1780 | trace("TIME\t0x%04x\n", usec); | 1781 | trace("TIME\t0x%04x\n", usec); |
@@ -1795,7 +1796,7 @@ init_time(struct nvbios_init *init) | |||
1795 | static void | 1796 | static void |
1796 | init_condition(struct nvbios_init *init) | 1797 | init_condition(struct nvbios_init *init) |
1797 | { | 1798 | { |
1798 | struct nvkm_bios *bios = init->bios; | 1799 | struct nvkm_bios *bios = init->subdev->device->bios; |
1799 | u8 cond = nvbios_rd08(bios, init->offset + 1); | 1800 | u8 cond = nvbios_rd08(bios, init->offset + 1); |
1800 | 1801 | ||
1801 | trace("CONDITION\t0x%02x\n", cond); | 1802 | trace("CONDITION\t0x%02x\n", cond); |
@@ -1812,7 +1813,7 @@ init_condition(struct nvbios_init *init) | |||
1812 | static void | 1813 | static void |
1813 | init_io_condition(struct nvbios_init *init) | 1814 | init_io_condition(struct nvbios_init *init) |
1814 | { | 1815 | { |
1815 | struct nvkm_bios *bios = init->bios; | 1816 | struct nvkm_bios *bios = init->subdev->device->bios; |
1816 | u8 cond = nvbios_rd08(bios, init->offset + 1); | 1817 | u8 cond = nvbios_rd08(bios, init->offset + 1); |
1817 | 1818 | ||
1818 | trace("IO_CONDITION\t0x%02x\n", cond); | 1819 | trace("IO_CONDITION\t0x%02x\n", cond); |
@@ -1829,7 +1830,7 @@ init_io_condition(struct nvbios_init *init) | |||
1829 | static void | 1830 | static void |
1830 | init_zm_reg16(struct nvbios_init *init) | 1831 | init_zm_reg16(struct nvbios_init *init) |
1831 | { | 1832 | { |
1832 | struct nvkm_bios *bios = init->bios; | 1833 | struct nvkm_bios *bios = init->subdev->device->bios; |
1833 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 1834 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
1834 | u16 data = nvbios_rd16(bios, init->offset + 5); | 1835 | u16 data = nvbios_rd16(bios, init->offset + 5); |
1835 | 1836 | ||
@@ -1846,7 +1847,7 @@ init_zm_reg16(struct nvbios_init *init) | |||
1846 | static void | 1847 | static void |
1847 | init_index_io(struct nvbios_init *init) | 1848 | init_index_io(struct nvbios_init *init) |
1848 | { | 1849 | { |
1849 | struct nvkm_bios *bios = init->bios; | 1850 | struct nvkm_bios *bios = init->subdev->device->bios; |
1850 | u16 port = nvbios_rd16(bios, init->offset + 1); | 1851 | u16 port = nvbios_rd16(bios, init->offset + 1); |
1851 | u8 index = nvbios_rd16(bios, init->offset + 3); | 1852 | u8 index = nvbios_rd16(bios, init->offset + 3); |
1852 | u8 mask = nvbios_rd08(bios, init->offset + 4); | 1853 | u8 mask = nvbios_rd08(bios, init->offset + 4); |
@@ -1868,7 +1869,7 @@ init_index_io(struct nvbios_init *init) | |||
1868 | static void | 1869 | static void |
1869 | init_pll(struct nvbios_init *init) | 1870 | init_pll(struct nvbios_init *init) |
1870 | { | 1871 | { |
1871 | struct nvkm_bios *bios = init->bios; | 1872 | struct nvkm_bios *bios = init->subdev->device->bios; |
1872 | u32 reg = nvbios_rd32(bios, init->offset + 1); | 1873 | u32 reg = nvbios_rd32(bios, init->offset + 1); |
1873 | u32 freq = nvbios_rd16(bios, init->offset + 5) * 10; | 1874 | u32 freq = nvbios_rd16(bios, init->offset + 5) * 10; |
1874 | 1875 | ||
@@ -1885,7 +1886,7 @@ init_pll(struct nvbios_init *init) | |||
1885 | static void | 1886 | static void |
1886 | init_zm_reg(struct nvbios_init *init) | 1887 | init_zm_reg(struct nvbios_init *init) |
1887 | { | 1888 | { |
1888 | struct nvkm_bios *bios = init->bios; | 1889 | struct nvkm_bios *bios = init->subdev->device->bios; |
1889 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 1890 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
1890 | u32 data = nvbios_rd32(bios, init->offset + 5); | 1891 | u32 data = nvbios_rd32(bios, init->offset + 5); |
1891 | 1892 | ||
@@ -1905,7 +1906,7 @@ init_zm_reg(struct nvbios_init *init) | |||
1905 | static void | 1906 | static void |
1906 | init_ram_restrict_pll(struct nvbios_init *init) | 1907 | init_ram_restrict_pll(struct nvbios_init *init) |
1907 | { | 1908 | { |
1908 | struct nvkm_bios *bios = init->bios; | 1909 | struct nvkm_bios *bios = init->subdev->device->bios; |
1909 | u8 type = nvbios_rd08(bios, init->offset + 1); | 1910 | u8 type = nvbios_rd08(bios, init->offset + 1); |
1910 | u8 count = init_ram_restrict_group_count(init); | 1911 | u8 count = init_ram_restrict_group_count(init); |
1911 | u8 strap = init_ram_restrict(init); | 1912 | u8 strap = init_ram_restrict(init); |
@@ -1935,7 +1936,7 @@ init_ram_restrict_pll(struct nvbios_init *init) | |||
1935 | static void | 1936 | static void |
1936 | init_gpio(struct nvbios_init *init) | 1937 | init_gpio(struct nvbios_init *init) |
1937 | { | 1938 | { |
1938 | struct nvkm_gpio *gpio = init->bios->subdev.device->gpio; | 1939 | struct nvkm_gpio *gpio = init->subdev->device->gpio; |
1939 | 1940 | ||
1940 | trace("GPIO\n"); | 1941 | trace("GPIO\n"); |
1941 | init->offset += 1; | 1942 | init->offset += 1; |
@@ -1951,7 +1952,7 @@ init_gpio(struct nvbios_init *init) | |||
1951 | static void | 1952 | static void |
1952 | init_ram_restrict_zm_reg_group(struct nvbios_init *init) | 1953 | init_ram_restrict_zm_reg_group(struct nvbios_init *init) |
1953 | { | 1954 | { |
1954 | struct nvkm_bios *bios = init->bios; | 1955 | struct nvkm_bios *bios = init->subdev->device->bios; |
1955 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 1956 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
1956 | u8 incr = nvbios_rd08(bios, init->offset + 5); | 1957 | u8 incr = nvbios_rd08(bios, init->offset + 5); |
1957 | u8 num = nvbios_rd08(bios, init->offset + 6); | 1958 | u8 num = nvbios_rd08(bios, init->offset + 6); |
@@ -1989,7 +1990,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init) | |||
1989 | static void | 1990 | static void |
1990 | init_copy_zm_reg(struct nvbios_init *init) | 1991 | init_copy_zm_reg(struct nvbios_init *init) |
1991 | { | 1992 | { |
1992 | struct nvkm_bios *bios = init->bios; | 1993 | struct nvkm_bios *bios = init->subdev->device->bios; |
1993 | u32 sreg = nvbios_rd32(bios, init->offset + 1); | 1994 | u32 sreg = nvbios_rd32(bios, init->offset + 1); |
1994 | u32 dreg = nvbios_rd32(bios, init->offset + 5); | 1995 | u32 dreg = nvbios_rd32(bios, init->offset + 5); |
1995 | 1996 | ||
@@ -2006,7 +2007,7 @@ init_copy_zm_reg(struct nvbios_init *init) | |||
2006 | static void | 2007 | static void |
2007 | init_zm_reg_group(struct nvbios_init *init) | 2008 | init_zm_reg_group(struct nvbios_init *init) |
2008 | { | 2009 | { |
2009 | struct nvkm_bios *bios = init->bios; | 2010 | struct nvkm_bios *bios = init->subdev->device->bios; |
2010 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 2011 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
2011 | u8 count = nvbios_rd08(bios, init->offset + 5); | 2012 | u8 count = nvbios_rd08(bios, init->offset + 5); |
2012 | 2013 | ||
@@ -2028,7 +2029,7 @@ init_zm_reg_group(struct nvbios_init *init) | |||
2028 | static void | 2029 | static void |
2029 | init_xlat(struct nvbios_init *init) | 2030 | init_xlat(struct nvbios_init *init) |
2030 | { | 2031 | { |
2031 | struct nvkm_bios *bios = init->bios; | 2032 | struct nvkm_bios *bios = init->subdev->device->bios; |
2032 | u32 saddr = nvbios_rd32(bios, init->offset + 1); | 2033 | u32 saddr = nvbios_rd32(bios, init->offset + 1); |
2033 | u8 sshift = nvbios_rd08(bios, init->offset + 5); | 2034 | u8 sshift = nvbios_rd08(bios, init->offset + 5); |
2034 | u8 smask = nvbios_rd08(bios, init->offset + 6); | 2035 | u8 smask = nvbios_rd08(bios, init->offset + 6); |
@@ -2056,7 +2057,7 @@ init_xlat(struct nvbios_init *init) | |||
2056 | static void | 2057 | static void |
2057 | init_zm_mask_add(struct nvbios_init *init) | 2058 | init_zm_mask_add(struct nvbios_init *init) |
2058 | { | 2059 | { |
2059 | struct nvkm_bios *bios = init->bios; | 2060 | struct nvkm_bios *bios = init->subdev->device->bios; |
2060 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 2061 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
2061 | u32 mask = nvbios_rd32(bios, init->offset + 5); | 2062 | u32 mask = nvbios_rd32(bios, init->offset + 5); |
2062 | u32 add = nvbios_rd32(bios, init->offset + 9); | 2063 | u32 add = nvbios_rd32(bios, init->offset + 9); |
@@ -2077,7 +2078,7 @@ init_zm_mask_add(struct nvbios_init *init) | |||
2077 | static void | 2078 | static void |
2078 | init_auxch(struct nvbios_init *init) | 2079 | init_auxch(struct nvbios_init *init) |
2079 | { | 2080 | { |
2080 | struct nvkm_bios *bios = init->bios; | 2081 | struct nvkm_bios *bios = init->subdev->device->bios; |
2081 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 2082 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
2082 | u8 count = nvbios_rd08(bios, init->offset + 5); | 2083 | u8 count = nvbios_rd08(bios, init->offset + 5); |
2083 | 2084 | ||
@@ -2101,7 +2102,7 @@ init_auxch(struct nvbios_init *init) | |||
2101 | static void | 2102 | static void |
2102 | init_zm_auxch(struct nvbios_init *init) | 2103 | init_zm_auxch(struct nvbios_init *init) |
2103 | { | 2104 | { |
2104 | struct nvkm_bios *bios = init->bios; | 2105 | struct nvkm_bios *bios = init->subdev->device->bios; |
2105 | u32 addr = nvbios_rd32(bios, init->offset + 1); | 2106 | u32 addr = nvbios_rd32(bios, init->offset + 1); |
2106 | u8 count = nvbios_rd08(bios, init->offset + 5); | 2107 | u8 count = nvbios_rd08(bios, init->offset + 5); |
2107 | 2108 | ||
@@ -2123,7 +2124,7 @@ init_zm_auxch(struct nvbios_init *init) | |||
2123 | static void | 2124 | static void |
2124 | init_i2c_long_if(struct nvbios_init *init) | 2125 | init_i2c_long_if(struct nvbios_init *init) |
2125 | { | 2126 | { |
2126 | struct nvkm_bios *bios = init->bios; | 2127 | struct nvkm_bios *bios = init->subdev->device->bios; |
2127 | u8 index = nvbios_rd08(bios, init->offset + 1); | 2128 | u8 index = nvbios_rd08(bios, init->offset + 1); |
2128 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; | 2129 | u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1; |
2129 | u8 reglo = nvbios_rd08(bios, init->offset + 3); | 2130 | u8 reglo = nvbios_rd08(bios, init->offset + 3); |
@@ -2162,7 +2163,7 @@ init_i2c_long_if(struct nvbios_init *init) | |||
2162 | static void | 2163 | static void |
2163 | init_gpio_ne(struct nvbios_init *init) | 2164 | init_gpio_ne(struct nvbios_init *init) |
2164 | { | 2165 | { |
2165 | struct nvkm_bios *bios = init->bios; | 2166 | struct nvkm_bios *bios = init->subdev->device->bios; |
2166 | struct nvkm_gpio *gpio = bios->subdev.device->gpio; | 2167 | struct nvkm_gpio *gpio = bios->subdev.device->gpio; |
2167 | struct dcb_gpio_func func; | 2168 | struct dcb_gpio_func func; |
2168 | u8 count = nvbios_rd08(bios, init->offset + 1); | 2169 | u8 count = nvbios_rd08(bios, init->offset + 1); |
@@ -2275,9 +2276,11 @@ static struct nvbios_init_opcode { | |||
2275 | int | 2276 | int |
2276 | nvbios_exec(struct nvbios_init *init) | 2277 | nvbios_exec(struct nvbios_init *init) |
2277 | { | 2278 | { |
2279 | struct nvkm_bios *bios = init->subdev->device->bios; | ||
2280 | |||
2278 | init->nested++; | 2281 | init->nested++; |
2279 | while (init->offset) { | 2282 | while (init->offset) { |
2280 | u8 opcode = nvbios_rd08(init->bios, init->offset); | 2283 | u8 opcode = nvbios_rd08(bios, init->offset); |
2281 | if (opcode >= init_opcode_nr || !init_opcode[opcode].exec) { | 2284 | if (opcode >= init_opcode_nr || !init_opcode[opcode].exec) { |
2282 | error("unknown opcode 0x%02x\n", opcode); | 2285 | error("unknown opcode 0x%02x\n", opcode); |
2283 | return -EINVAL; | 2286 | return -EINVAL; |
@@ -2290,7 +2293,7 @@ nvbios_exec(struct nvbios_init *init) | |||
2290 | } | 2293 | } |
2291 | 2294 | ||
2292 | int | 2295 | int |
2293 | nvbios_init(struct nvkm_subdev *subdev, bool execute) | 2296 | nvbios_post(struct nvkm_subdev *subdev, bool execute) |
2294 | { | 2297 | { |
2295 | struct nvkm_bios *bios = subdev->device->bios; | 2298 | struct nvkm_bios *bios = subdev->device->bios; |
2296 | int ret = 0; | 2299 | int ret = 0; |
@@ -2300,32 +2303,18 @@ nvbios_init(struct nvkm_subdev *subdev, bool execute) | |||
2300 | if (execute) | 2303 | if (execute) |
2301 | nvkm_debug(subdev, "running init tables\n"); | 2304 | nvkm_debug(subdev, "running init tables\n"); |
2302 | while (!ret && (data = (init_script(bios, ++i)))) { | 2305 | while (!ret && (data = (init_script(bios, ++i)))) { |
2303 | struct nvbios_init init = { | 2306 | ret = nvbios_init(subdev, data, |
2304 | .subdev = subdev, | 2307 | init.execute = execute ? 1 : 0; |
2305 | .bios = bios, | 2308 | ); |
2306 | .offset = data, | ||
2307 | .outp = NULL, | ||
2308 | .crtc = -1, | ||
2309 | .execute = execute ? 1 : 0, | ||
2310 | }; | ||
2311 | |||
2312 | ret = nvbios_exec(&init); | ||
2313 | } | 2309 | } |
2314 | 2310 | ||
2315 | /* the vbios parser will run this right after the normal init | 2311 | /* the vbios parser will run this right after the normal init |
2316 | * tables, whereas the binary driver appears to run it later. | 2312 | * tables, whereas the binary driver appears to run it later. |
2317 | */ | 2313 | */ |
2318 | if (!ret && (data = init_unknown_script(bios))) { | 2314 | if (!ret && (data = init_unknown_script(bios))) { |
2319 | struct nvbios_init init = { | 2315 | ret = nvbios_init(subdev, data, |
2320 | .subdev = subdev, | 2316 | init.execute = execute ? 1 : 0; |
2321 | .bios = bios, | 2317 | ); |
2322 | .offset = data, | ||
2323 | .outp = NULL, | ||
2324 | .crtc = -1, | ||
2325 | .execute = execute ? 1 : 0, | ||
2326 | }; | ||
2327 | |||
2328 | ret = nvbios_exec(&init); | ||
2329 | } | 2318 | } |
2330 | 2319 | ||
2331 | return ret; | 2320 | return ret; |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c index a7797a9e9cbc..7143ea4611aa 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/volt.c | |||
@@ -93,9 +93,9 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, | |||
93 | info->step = nvbios_rd16(bios, volt + 0x08); | 93 | info->step = nvbios_rd16(bios, volt + 0x08); |
94 | info->vidmask = nvbios_rd08(bios, volt + 0x0b); | 94 | info->vidmask = nvbios_rd08(bios, volt + 0x0b); |
95 | info->ranged = true; /* XXX: find the flag byte */ | 95 | info->ranged = true; /* XXX: find the flag byte */ |
96 | /*XXX*/ | 96 | info->min = min(info->base, |
97 | info->min = 0; | 97 | info->base + info->step * info->vidmask); |
98 | info->max = info->base; | 98 | info->max = nvbios_rd32(bios, volt + 0x0e); |
99 | break; | 99 | break; |
100 | case 0x50: | 100 | case 0x50: |
101 | info->min = nvbios_rd32(bios, volt + 0x0a); | 101 | info->min = nvbios_rd32(bios, volt + 0x0a); |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c index c8d455346fcd..158977f8a6e6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv04.c | |||
@@ -393,7 +393,7 @@ nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq) | |||
393 | int | 393 | int |
394 | nv04_devinit_post(struct nvkm_devinit *init, bool execute) | 394 | nv04_devinit_post(struct nvkm_devinit *init, bool execute) |
395 | { | 395 | { |
396 | return nvbios_init(&init->subdev, execute); | 396 | return nvbios_post(&init->subdev, execute); |
397 | } | 397 | } |
398 | 398 | ||
399 | void | 399 | void |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c index 59362f8dee22..d7947c4391dc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/nv50.c | |||
@@ -137,16 +137,11 @@ nv50_devinit_init(struct nvkm_devinit *base) | |||
137 | while (init->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { | 137 | while (init->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) { |
138 | if (nvbios_outp_match(bios, outp.hasht, outp.hashm, | 138 | if (nvbios_outp_match(bios, outp.hasht, outp.hashm, |
139 | &ver, &hdr, &cnt, &len, &info)) { | 139 | &ver, &hdr, &cnt, &len, &info)) { |
140 | struct nvbios_init exec = { | 140 | nvbios_init(subdev, info.script[0], |
141 | .subdev = subdev, | 141 | init.outp = &outp; |
142 | .bios = bios, | 142 | init.or = ffs(outp.or) - 1; |
143 | .offset = info.script[0], | 143 | init.link = outp.sorconf.link == 2; |
144 | .outp = &outp, | 144 | ); |
145 | .crtc = -1, | ||
146 | .execute = 1, | ||
147 | }; | ||
148 | |||
149 | nvbios_exec(&exec); | ||
150 | } | 145 | } |
151 | i++; | 146 | i++; |
152 | } | 147 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c index f6c00791722c..75814f15eb53 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c | |||
@@ -1424,12 +1424,7 @@ gk104_ram_init(struct nvkm_ram *ram) | |||
1424 | for (i = 0; i < cnt; i++, data += 4) { | 1424 | for (i = 0; i < cnt; i++, data += 4) { |
1425 | if (i != save >> 4) { | 1425 | if (i != save >> 4) { |
1426 | nvkm_mask(device, 0x10f65c, 0x000000f0, i << 4); | 1426 | nvkm_mask(device, 0x10f65c, 0x000000f0, i << 4); |
1427 | nvbios_exec(&(struct nvbios_init) { | 1427 | nvbios_init(subdev, nvbios_rd32(bios, data)); |
1428 | .subdev = subdev, | ||
1429 | .bios = bios, | ||
1430 | .offset = nvbios_rd32(bios, data), | ||
1431 | .execute = 1, | ||
1432 | }); | ||
1433 | } | 1428 | } |
1434 | } | 1429 | } |
1435 | nvkm_mask(device, 0x10f65c, 0x000000f0, save); | 1430 | nvkm_mask(device, 0x10f65c, 0x000000f0, save); |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c index cac70047ad5a..df8a87333b67 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c | |||
@@ -59,12 +59,7 @@ gp100_ram_init(struct nvkm_ram *ram) | |||
59 | for (i = 0; i < cnt; i++, data += 4) { | 59 | for (i = 0; i < cnt; i++, data += 4) { |
60 | if (i != save >> 4) { | 60 | if (i != save >> 4) { |
61 | nvkm_mask(device, 0x9a065c, 0x000000f0, i << 4); | 61 | nvkm_mask(device, 0x9a065c, 0x000000f0, i << 4); |
62 | nvbios_exec(&(struct nvbios_init) { | 62 | nvbios_init(subdev, nvbios_rd32(bios, data)); |
63 | .subdev = subdev, | ||
64 | .bios = bios, | ||
65 | .offset = nvbios_rd32(bios, data), | ||
66 | .execute = 1, | ||
67 | }); | ||
68 | } | 63 | } |
69 | } | 64 | } |
70 | nvkm_mask(device, 0x9a065c, 0x000000f0, save); | 65 | nvkm_mask(device, 0x9a065c, 0x000000f0, save); |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c index 56f8cffc2560..70c63535d56b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv40.c | |||
@@ -150,16 +150,8 @@ nv40_ram_prog(struct nvkm_ram *base) | |||
150 | udelay(100); | 150 | udelay(100); |
151 | 151 | ||
152 | /* execute memory reset script from vbios */ | 152 | /* execute memory reset script from vbios */ |
153 | if (!bit_entry(bios, 'M', &M)) { | 153 | if (!bit_entry(bios, 'M', &M)) |
154 | struct nvbios_init init = { | 154 | nvbios_init(subdev, nvbios_rd16(bios, M.offset + 0x00)); |
155 | .subdev = subdev, | ||
156 | .bios = bios, | ||
157 | .offset = nvbios_rd16(bios, M.offset + 0x00), | ||
158 | .execute = 1, | ||
159 | }; | ||
160 | |||
161 | nvbios_exec(&init); | ||
162 | } | ||
163 | 155 | ||
164 | /* make sure we're in vblank (hopefully the same one as before), and | 156 | /* make sure we're in vblank (hopefully the same one as before), and |
165 | * then re-enable crtc memory access | 157 | * then re-enable crtc memory access |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c index 9ca0db796cbe..978aae3c1001 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk20a.c | |||
@@ -158,7 +158,7 @@ static void | |||
158 | gk20a_pmu_fini(struct nvkm_pmu *pmu) | 158 | gk20a_pmu_fini(struct nvkm_pmu *pmu) |
159 | { | 159 | { |
160 | struct gk20a_pmu *gpmu = gk20a_pmu(pmu); | 160 | struct gk20a_pmu *gpmu = gk20a_pmu(pmu); |
161 | nvkm_timer_alarm_cancel(pmu->subdev.device->timer, &gpmu->alarm); | 161 | nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm); |
162 | 162 | ||
163 | nvkm_falcon_put(pmu->falcon, &pmu->subdev); | 163 | nvkm_falcon_put(pmu->falcon, &pmu->subdev); |
164 | } | 164 | } |
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c index be691a7b972f..952a7cb0a59a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c | |||
@@ -116,7 +116,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) | |||
116 | 116 | ||
117 | switch (mode) { | 117 | switch (mode) { |
118 | case NVKM_THERM_CTRL_MANUAL: | 118 | case NVKM_THERM_CTRL_MANUAL: |
119 | nvkm_timer_alarm_cancel(tmr, &therm->alarm); | 119 | nvkm_timer_alarm(tmr, 0, &therm->alarm); |
120 | duty = nvkm_therm_fan_get(therm); | 120 | duty = nvkm_therm_fan_get(therm); |
121 | if (duty < 0) | 121 | if (duty < 0) |
122 | duty = 100; | 122 | duty = 100; |
@@ -142,7 +142,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) | |||
142 | break; | 142 | break; |
143 | case NVKM_THERM_CTRL_NONE: | 143 | case NVKM_THERM_CTRL_NONE: |
144 | default: | 144 | default: |
145 | nvkm_timer_alarm_cancel(tmr, &therm->alarm); | 145 | nvkm_timer_alarm(tmr, 0, &therm->alarm); |
146 | poll = false; | 146 | poll = false; |
147 | } | 147 | } |
148 | 148 | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c index e2feccec25f5..f8fa43c8a7d2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c | |||
@@ -215,7 +215,7 @@ nvkm_therm_fan_fini(struct nvkm_therm *therm, bool suspend) | |||
215 | { | 215 | { |
216 | struct nvkm_timer *tmr = therm->subdev.device->timer; | 216 | struct nvkm_timer *tmr = therm->subdev.device->timer; |
217 | if (suspend) | 217 | if (suspend) |
218 | nvkm_timer_alarm_cancel(tmr, &therm->fan->alarm); | 218 | nvkm_timer_alarm(tmr, 0, &therm->fan->alarm); |
219 | return 0; | 219 | return 0; |
220 | } | 220 | } |
221 | 221 | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c index 9a79e91fdfdc..e93b2410c38b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c | |||
@@ -220,7 +220,7 @@ nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend) | |||
220 | { | 220 | { |
221 | struct nvkm_timer *tmr = therm->subdev.device->timer; | 221 | struct nvkm_timer *tmr = therm->subdev.device->timer; |
222 | if (suspend) | 222 | if (suspend) |
223 | nvkm_timer_alarm_cancel(tmr, &therm->sensor.therm_poll_alarm); | 223 | nvkm_timer_alarm(tmr, 0, &therm->sensor.therm_poll_alarm); |
224 | return 0; | 224 | return 0; |
225 | } | 225 | } |
226 | 226 | ||
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c index 2437f7d41ca2..36de23d12ae4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c | |||
@@ -105,15 +105,6 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm) | |||
105 | spin_unlock_irqrestore(&tmr->lock, flags); | 105 | spin_unlock_irqrestore(&tmr->lock, flags); |
106 | } | 106 | } |
107 | 107 | ||
108 | void | ||
109 | nvkm_timer_alarm_cancel(struct nvkm_timer *tmr, struct nvkm_alarm *alarm) | ||
110 | { | ||
111 | unsigned long flags; | ||
112 | spin_lock_irqsave(&tmr->lock, flags); | ||
113 | list_del_init(&alarm->head); | ||
114 | spin_unlock_irqrestore(&tmr->lock, flags); | ||
115 | } | ||
116 | |||
117 | static void | 108 | static void |
118 | nvkm_timer_intr(struct nvkm_subdev *subdev) | 109 | nvkm_timer_intr(struct nvkm_subdev *subdev) |
119 | { | 110 | { |