diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 198 |
1 files changed, 189 insertions, 9 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index b12fd2c8081..3cb52bc52b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include "nouveau_hw.h" | 32 | #include "nouveau_hw.h" |
33 | #include "nouveau_crtc.h" | 33 | #include "nouveau_crtc.h" |
34 | #include "nouveau_dma.h" | 34 | #include "nouveau_dma.h" |
35 | #include "nouveau_connector.h" | ||
36 | #include "nouveau_gpio.h" | ||
35 | #include "nv50_display.h" | 37 | #include "nv50_display.h" |
36 | 38 | ||
37 | static void | 39 | static void |
@@ -64,7 +66,7 @@ static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { | |||
64 | int | 66 | int |
65 | nouveau_framebuffer_init(struct drm_device *dev, | 67 | nouveau_framebuffer_init(struct drm_device *dev, |
66 | struct nouveau_framebuffer *nv_fb, | 68 | struct nouveau_framebuffer *nv_fb, |
67 | struct drm_mode_fb_cmd *mode_cmd, | 69 | struct drm_mode_fb_cmd2 *mode_cmd, |
68 | struct nouveau_bo *nvbo) | 70 | struct nouveau_bo *nvbo) |
69 | { | 71 | { |
70 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 72 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
@@ -107,14 +109,14 @@ nouveau_framebuffer_init(struct drm_device *dev, | |||
107 | 109 | ||
108 | if (!tile_flags) { | 110 | if (!tile_flags) { |
109 | if (dev_priv->card_type < NV_D0) | 111 | if (dev_priv->card_type < NV_D0) |
110 | nv_fb->r_pitch = 0x00100000 | fb->pitch; | 112 | nv_fb->r_pitch = 0x00100000 | fb->pitches[0]; |
111 | else | 113 | else |
112 | nv_fb->r_pitch = 0x01000000 | fb->pitch; | 114 | nv_fb->r_pitch = 0x01000000 | fb->pitches[0]; |
113 | } else { | 115 | } else { |
114 | u32 mode = nvbo->tile_mode; | 116 | u32 mode = nvbo->tile_mode; |
115 | if (dev_priv->card_type >= NV_C0) | 117 | if (dev_priv->card_type >= NV_C0) |
116 | mode >>= 4; | 118 | mode >>= 4; |
117 | nv_fb->r_pitch = ((fb->pitch / 4) << 4) | mode; | 119 | nv_fb->r_pitch = ((fb->pitches[0] / 4) << 4) | mode; |
118 | } | 120 | } |
119 | } | 121 | } |
120 | 122 | ||
@@ -124,13 +126,13 @@ nouveau_framebuffer_init(struct drm_device *dev, | |||
124 | static struct drm_framebuffer * | 126 | static struct drm_framebuffer * |
125 | nouveau_user_framebuffer_create(struct drm_device *dev, | 127 | nouveau_user_framebuffer_create(struct drm_device *dev, |
126 | struct drm_file *file_priv, | 128 | struct drm_file *file_priv, |
127 | struct drm_mode_fb_cmd *mode_cmd) | 129 | struct drm_mode_fb_cmd2 *mode_cmd) |
128 | { | 130 | { |
129 | struct nouveau_framebuffer *nouveau_fb; | 131 | struct nouveau_framebuffer *nouveau_fb; |
130 | struct drm_gem_object *gem; | 132 | struct drm_gem_object *gem; |
131 | int ret; | 133 | int ret; |
132 | 134 | ||
133 | gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); | 135 | gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]); |
134 | if (!gem) | 136 | if (!gem) |
135 | return ERR_PTR(-ENOENT); | 137 | return ERR_PTR(-ENOENT); |
136 | 138 | ||
@@ -147,11 +149,186 @@ nouveau_user_framebuffer_create(struct drm_device *dev, | |||
147 | return &nouveau_fb->base; | 149 | return &nouveau_fb->base; |
148 | } | 150 | } |
149 | 151 | ||
150 | const struct drm_mode_config_funcs nouveau_mode_config_funcs = { | 152 | static const struct drm_mode_config_funcs nouveau_mode_config_funcs = { |
151 | .fb_create = nouveau_user_framebuffer_create, | 153 | .fb_create = nouveau_user_framebuffer_create, |
152 | .output_poll_changed = nouveau_fbcon_output_poll_changed, | 154 | .output_poll_changed = nouveau_fbcon_output_poll_changed, |
153 | }; | 155 | }; |
154 | 156 | ||
157 | |||
158 | struct drm_prop_enum_list { | ||
159 | u8 gen_mask; | ||
160 | int type; | ||
161 | char *name; | ||
162 | }; | ||
163 | |||
164 | static struct drm_prop_enum_list underscan[] = { | ||
165 | { 6, UNDERSCAN_AUTO, "auto" }, | ||
166 | { 6, UNDERSCAN_OFF, "off" }, | ||
167 | { 6, UNDERSCAN_ON, "on" }, | ||
168 | {} | ||
169 | }; | ||
170 | |||
171 | static struct drm_prop_enum_list dither_mode[] = { | ||
172 | { 7, DITHERING_MODE_AUTO, "auto" }, | ||
173 | { 7, DITHERING_MODE_OFF, "off" }, | ||
174 | { 1, DITHERING_MODE_ON, "on" }, | ||
175 | { 6, DITHERING_MODE_STATIC2X2, "static 2x2" }, | ||
176 | { 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" }, | ||
177 | { 4, DITHERING_MODE_TEMPORAL, "temporal" }, | ||
178 | {} | ||
179 | }; | ||
180 | |||
181 | static struct drm_prop_enum_list dither_depth[] = { | ||
182 | { 6, DITHERING_DEPTH_AUTO, "auto" }, | ||
183 | { 6, DITHERING_DEPTH_6BPC, "6 bpc" }, | ||
184 | { 6, DITHERING_DEPTH_8BPC, "8 bpc" }, | ||
185 | {} | ||
186 | }; | ||
187 | |||
188 | #define PROP_ENUM(p,gen,n,list) do { \ | ||
189 | struct drm_prop_enum_list *l = (list); \ | ||
190 | int c = 0; \ | ||
191 | while (l->gen_mask) { \ | ||
192 | if (l->gen_mask & (1 << (gen))) \ | ||
193 | c++; \ | ||
194 | l++; \ | ||
195 | } \ | ||
196 | if (c) { \ | ||
197 | p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \ | ||
198 | l = (list); \ | ||
199 | c = 0; \ | ||
200 | while (p && l->gen_mask) { \ | ||
201 | if (l->gen_mask & (1 << (gen))) { \ | ||
202 | drm_property_add_enum(p, c, l->type, l->name); \ | ||
203 | c++; \ | ||
204 | } \ | ||
205 | l++; \ | ||
206 | } \ | ||
207 | } \ | ||
208 | } while(0) | ||
209 | |||
210 | int | ||
211 | nouveau_display_init(struct drm_device *dev) | ||
212 | { | ||
213 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
214 | struct nouveau_display_engine *disp = &dev_priv->engine.display; | ||
215 | struct drm_connector *connector; | ||
216 | int ret; | ||
217 | |||
218 | ret = disp->init(dev); | ||
219 | if (ret) | ||
220 | return ret; | ||
221 | |||
222 | drm_kms_helper_poll_enable(dev); | ||
223 | |||
224 | /* enable hotplug interrupts */ | ||
225 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
226 | struct nouveau_connector *conn = nouveau_connector(connector); | ||
227 | nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); | ||
228 | } | ||
229 | |||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | void | ||
234 | nouveau_display_fini(struct drm_device *dev) | ||
235 | { | ||
236 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
237 | struct nouveau_display_engine *disp = &dev_priv->engine.display; | ||
238 | struct drm_connector *connector; | ||
239 | |||
240 | /* disable hotplug interrupts */ | ||
241 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
242 | struct nouveau_connector *conn = nouveau_connector(connector); | ||
243 | nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, false); | ||
244 | } | ||
245 | |||
246 | drm_kms_helper_poll_disable(dev); | ||
247 | disp->fini(dev); | ||
248 | } | ||
249 | |||
250 | int | ||
251 | nouveau_display_create(struct drm_device *dev) | ||
252 | { | ||
253 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
254 | struct nouveau_display_engine *disp = &dev_priv->engine.display; | ||
255 | int ret, gen; | ||
256 | |||
257 | drm_mode_config_init(dev); | ||
258 | drm_mode_create_scaling_mode_property(dev); | ||
259 | drm_mode_create_dvi_i_properties(dev); | ||
260 | |||
261 | if (dev_priv->card_type < NV_50) | ||
262 | gen = 0; | ||
263 | else | ||
264 | if (dev_priv->card_type < NV_D0) | ||
265 | gen = 1; | ||
266 | else | ||
267 | gen = 2; | ||
268 | |||
269 | PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode); | ||
270 | PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth); | ||
271 | PROP_ENUM(disp->underscan_property, gen, "underscan", underscan); | ||
272 | |||
273 | disp->underscan_hborder_property = | ||
274 | drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
275 | "underscan hborder", 2); | ||
276 | disp->underscan_hborder_property->values[0] = 0; | ||
277 | disp->underscan_hborder_property->values[1] = 128; | ||
278 | |||
279 | disp->underscan_vborder_property = | ||
280 | drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
281 | "underscan vborder", 2); | ||
282 | disp->underscan_vborder_property->values[0] = 0; | ||
283 | disp->underscan_vborder_property->values[1] = 128; | ||
284 | |||
285 | dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; | ||
286 | dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1); | ||
287 | |||
288 | dev->mode_config.min_width = 0; | ||
289 | dev->mode_config.min_height = 0; | ||
290 | if (dev_priv->card_type < NV_10) { | ||
291 | dev->mode_config.max_width = 2048; | ||
292 | dev->mode_config.max_height = 2048; | ||
293 | } else | ||
294 | if (dev_priv->card_type < NV_50) { | ||
295 | dev->mode_config.max_width = 4096; | ||
296 | dev->mode_config.max_height = 4096; | ||
297 | } else { | ||
298 | dev->mode_config.max_width = 8192; | ||
299 | dev->mode_config.max_height = 8192; | ||
300 | } | ||
301 | |||
302 | drm_kms_helper_poll_init(dev); | ||
303 | drm_kms_helper_poll_disable(dev); | ||
304 | |||
305 | ret = disp->create(dev); | ||
306 | if (ret) | ||
307 | return ret; | ||
308 | |||
309 | if (dev->mode_config.num_crtc) { | ||
310 | ret = drm_vblank_init(dev, dev->mode_config.num_crtc); | ||
311 | if (ret) | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | void | ||
319 | nouveau_display_destroy(struct drm_device *dev) | ||
320 | { | ||
321 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
322 | struct nouveau_display_engine *disp = &dev_priv->engine.display; | ||
323 | |||
324 | drm_vblank_cleanup(dev); | ||
325 | |||
326 | disp->destroy(dev); | ||
327 | |||
328 | drm_kms_helper_poll_fini(dev); | ||
329 | drm_mode_config_cleanup(dev); | ||
330 | } | ||
331 | |||
155 | int | 332 | int |
156 | nouveau_vblank_enable(struct drm_device *dev, int crtc) | 333 | nouveau_vblank_enable(struct drm_device *dev, int crtc) |
157 | { | 334 | { |
@@ -294,7 +471,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
294 | /* Initialize a page flip struct */ | 471 | /* Initialize a page flip struct */ |
295 | *s = (struct nouveau_page_flip_state) | 472 | *s = (struct nouveau_page_flip_state) |
296 | { { }, event, nouveau_crtc(crtc)->index, | 473 | { { }, event, nouveau_crtc(crtc)->index, |
297 | fb->bits_per_pixel, fb->pitch, crtc->x, crtc->y, | 474 | fb->bits_per_pixel, fb->pitches[0], crtc->x, crtc->y, |
298 | new_bo->bo.offset }; | 475 | new_bo->bo.offset }; |
299 | 476 | ||
300 | /* Choose the channel the flip will be handled in */ | 477 | /* Choose the channel the flip will be handled in */ |
@@ -305,7 +482,10 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
305 | 482 | ||
306 | /* Emit a page flip */ | 483 | /* Emit a page flip */ |
307 | if (dev_priv->card_type >= NV_50) { | 484 | if (dev_priv->card_type >= NV_50) { |
308 | ret = nv50_display_flip_next(crtc, fb, chan); | 485 | if (dev_priv->card_type >= NV_D0) |
486 | ret = nvd0_display_flip_next(crtc, fb, chan, 0); | ||
487 | else | ||
488 | ret = nv50_display_flip_next(crtc, fb, chan); | ||
309 | if (ret) { | 489 | if (ret) { |
310 | nouveau_channel_put(&chan); | 490 | nouveau_channel_put(&chan); |
311 | goto fail_unreserve; | 491 | goto fail_unreserve; |