diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 2e11fd65b4dd..505c6bfb4d75 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
| @@ -29,6 +29,9 @@ | |||
| 29 | #include "nouveau_drv.h" | 29 | #include "nouveau_drv.h" |
| 30 | #include "nouveau_fb.h" | 30 | #include "nouveau_fb.h" |
| 31 | #include "nouveau_fbcon.h" | 31 | #include "nouveau_fbcon.h" |
| 32 | #include "nouveau_hw.h" | ||
| 33 | #include "nouveau_crtc.h" | ||
| 34 | #include "nouveau_dma.h" | ||
| 32 | 35 | ||
| 33 | static void | 36 | static void |
| 34 | nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) | 37 | nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) |
| @@ -104,3 +107,207 @@ const struct drm_mode_config_funcs nouveau_mode_config_funcs = { | |||
| 104 | .output_poll_changed = nouveau_fbcon_output_poll_changed, | 107 | .output_poll_changed = nouveau_fbcon_output_poll_changed, |
| 105 | }; | 108 | }; |
| 106 | 109 | ||
| 110 | int | ||
| 111 | nouveau_vblank_enable(struct drm_device *dev, int crtc) | ||
| 112 | { | ||
| 113 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
| 114 | |||
| 115 | if (dev_priv->card_type >= NV_50) | ||
| 116 | nv_mask(dev, NV50_PDISPLAY_INTR_EN_1, 0, | ||
| 117 | NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc)); | ||
| 118 | else | ||
| 119 | NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, | ||
| 120 | NV_PCRTC_INTR_0_VBLANK); | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | void | ||
| 126 | nouveau_vblank_disable(struct drm_device *dev, int crtc) | ||
| 127 | { | ||
| 128 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
| 129 | |||
| 130 | if (dev_priv->card_type >= NV_50) | ||
| 131 | nv_mask(dev, NV50_PDISPLAY_INTR_EN_1, | ||
| 132 | NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0); | ||
| 133 | else | ||
| 134 | NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0); | ||
| 135 | } | ||
| 136 | |||
| 137 | static int | ||
| 138 | nouveau_page_flip_reserve(struct nouveau_bo *old_bo, | ||
| 139 | struct nouveau_bo *new_bo) | ||
| 140 | { | ||
| 141 | int ret; | ||
| 142 | |||
| 143 | ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM); | ||
| 144 | if (ret) | ||
| 145 | return ret; | ||
| 146 | |||
| 147 | ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0); | ||
| 148 | if (ret) | ||
| 149 | goto fail; | ||
| 150 | |||
| 151 | ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0); | ||
| 152 | if (ret) | ||
| 153 | goto fail_unreserve; | ||
| 154 | |||
| 155 | return 0; | ||
| 156 | |||
| 157 | fail_unreserve: | ||
| 158 | ttm_bo_unreserve(&new_bo->bo); | ||
| 159 | fail: | ||
| 160 | nouveau_bo_unpin(new_bo); | ||
| 161 | return ret; | ||
| 162 | } | ||
| 163 | |||
| 164 | static void | ||
| 165 | nouveau_page_flip_unreserve(struct nouveau_bo *old_bo, | ||
| 166 | struct nouveau_bo *new_bo, | ||
| 167 | struct nouveau_fence *fence) | ||
| 168 | { | ||
| 169 | nouveau_bo_fence(new_bo, fence); | ||
| 170 | ttm_bo_unreserve(&new_bo->bo); | ||
| 171 | |||
| 172 | nouveau_bo_fence(old_bo, fence); | ||
| 173 | ttm_bo_unreserve(&old_bo->bo); | ||
| 174 | |||
| 175 | nouveau_bo_unpin(old_bo); | ||
| 176 | } | ||
| 177 | |||
| 178 | static int | ||
| 179 | nouveau_page_flip_emit(struct nouveau_channel *chan, | ||
| 180 | struct nouveau_bo *old_bo, | ||
| 181 | struct nouveau_bo *new_bo, | ||
| 182 | struct nouveau_page_flip_state *s, | ||
| 183 | struct nouveau_fence **pfence) | ||
| 184 | { | ||
| 185 | struct drm_device *dev = chan->dev; | ||
| 186 | unsigned long flags; | ||
| 187 | int ret; | ||
| 188 | |||
| 189 | /* Queue it to the pending list */ | ||
| 190 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 191 | list_add_tail(&s->head, &chan->nvsw.flip); | ||
| 192 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 193 | |||
| 194 | /* Synchronize with the old framebuffer */ | ||
| 195 | ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan); | ||
| 196 | if (ret) | ||
| 197 | goto fail; | ||
| 198 | |||
| 199 | /* Emit the pageflip */ | ||
| 200 | ret = RING_SPACE(chan, 2); | ||
| 201 | if (ret) | ||
| 202 | goto fail; | ||
| 203 | |||
| 204 | BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); | ||
| 205 | OUT_RING(chan, 0); | ||
| 206 | FIRE_RING(chan); | ||
| 207 | |||
| 208 | ret = nouveau_fence_new(chan, pfence, true); | ||
| 209 | if (ret) | ||
| 210 | goto fail; | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | fail: | ||
| 214 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 215 | list_del(&s->head); | ||
| 216 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 217 | return ret; | ||
| 218 | } | ||
| 219 | |||
| 220 | int | ||
| 221 | nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | ||
| 222 | struct drm_pending_vblank_event *event) | ||
| 223 | { | ||
| 224 | struct drm_device *dev = crtc->dev; | ||
| 225 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
| 226 | struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo; | ||
| 227 | struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo; | ||
| 228 | struct nouveau_page_flip_state *s; | ||
| 229 | struct nouveau_channel *chan; | ||
| 230 | struct nouveau_fence *fence; | ||
| 231 | int ret; | ||
| 232 | |||
| 233 | if (dev_priv->engine.graph.accel_blocked) | ||
| 234 | return -ENODEV; | ||
| 235 | |||
| 236 | s = kzalloc(sizeof(*s), GFP_KERNEL); | ||
| 237 | if (!s) | ||
| 238 | return -ENOMEM; | ||
| 239 | |||
| 240 | /* Don't let the buffers go away while we flip */ | ||
| 241 | ret = nouveau_page_flip_reserve(old_bo, new_bo); | ||
| 242 | if (ret) | ||
| 243 | goto fail_free; | ||
| 244 | |||
| 245 | /* Initialize a page flip struct */ | ||
| 246 | *s = (struct nouveau_page_flip_state) | ||
| 247 | { { }, s->event, nouveau_crtc(crtc)->index, | ||
| 248 | fb->bits_per_pixel, fb->pitch, crtc->x, crtc->y, | ||
| 249 | new_bo->bo.offset }; | ||
| 250 | |||
| 251 | /* Choose the channel the flip will be handled in */ | ||
| 252 | chan = nouveau_fence_channel(new_bo->bo.sync_obj); | ||
| 253 | if (!chan) | ||
| 254 | chan = nouveau_channel_get_unlocked(dev_priv->channel); | ||
| 255 | mutex_lock(&chan->mutex); | ||
| 256 | |||
| 257 | /* Emit a page flip */ | ||
| 258 | ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); | ||
| 259 | nouveau_channel_put(&chan); | ||
| 260 | if (ret) | ||
| 261 | goto fail_unreserve; | ||
| 262 | |||
| 263 | /* Update the crtc struct and cleanup */ | ||
| 264 | crtc->fb = fb; | ||
| 265 | |||
| 266 | nouveau_page_flip_unreserve(old_bo, new_bo, fence); | ||
| 267 | nouveau_fence_unref(&fence); | ||
| 268 | return 0; | ||
| 269 | |||
| 270 | fail_unreserve: | ||
| 271 | nouveau_page_flip_unreserve(old_bo, new_bo, NULL); | ||
| 272 | fail_free: | ||
| 273 | kfree(s); | ||
| 274 | return ret; | ||
| 275 | } | ||
| 276 | |||
| 277 | int | ||
| 278 | nouveau_finish_page_flip(struct nouveau_channel *chan, | ||
| 279 | struct nouveau_page_flip_state *ps) | ||
| 280 | { | ||
| 281 | struct drm_device *dev = chan->dev; | ||
| 282 | struct nouveau_page_flip_state *s; | ||
| 283 | unsigned long flags; | ||
| 284 | |||
| 285 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 286 | |||
| 287 | if (list_empty(&chan->nvsw.flip)) { | ||
| 288 | NV_ERROR(dev, "Unexpected pageflip in channel %d.\n", chan->id); | ||
| 289 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 290 | return -EINVAL; | ||
| 291 | } | ||
| 292 | |||
| 293 | s = list_first_entry(&chan->nvsw.flip, | ||
| 294 | struct nouveau_page_flip_state, head); | ||
| 295 | if (s->event) { | ||
| 296 | struct drm_pending_vblank_event *e = s->event; | ||
| 297 | struct timeval now; | ||
| 298 | |||
| 299 | do_gettimeofday(&now); | ||
| 300 | e->event.sequence = 0; | ||
| 301 | e->event.tv_sec = now.tv_sec; | ||
| 302 | e->event.tv_usec = now.tv_usec; | ||
| 303 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | ||
| 304 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
| 305 | } | ||
| 306 | |||
| 307 | list_del(&s->head); | ||
| 308 | *ps = *s; | ||
| 309 | kfree(s); | ||
| 310 | |||
| 311 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 312 | return 0; | ||
| 313 | } | ||
