diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 259 |
1 files changed, 204 insertions, 55 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index e4eeeaf20fd..8f98e5a8c48 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -26,18 +26,21 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> -#include "nouveau_drv.h" -#include "nouveau_fb.h" + #include "nouveau_fbcon.h" #include "nouveau_hw.h" #include "nouveau_crtc.h" #include "nouveau_dma.h" +#include "nouveau_gem.h" #include "nouveau_connector.h" -#include "nouveau_software.h" -#include "nouveau_gpio.h" -#include "nouveau_fence.h" #include "nv50_display.h" +#include "nouveau_fence.h" + +#include <subdev/bios/gpio.h> +#include <subdev/gpio.h> +#include <engine/disp.h> + static void nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) { @@ -71,7 +74,7 @@ nouveau_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct nouveau_bo *nvbo) { - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_drm *drm = nouveau_drm(dev); struct drm_framebuffer *fb = &nv_fb->base; int ret; @@ -83,7 +86,7 @@ nouveau_framebuffer_init(struct drm_device *dev, drm_helper_mode_fill_fb_struct(fb, mode_cmd); nv_fb->nvbo = nvbo; - if (dev_priv->card_type >= NV_50) { + if (nv_device(drm->device)->card_type >= NV_50) { u32 tile_flags = nouveau_bo_tile_layout(nvbo); if (tile_flags == 0x7a00 || tile_flags == 0xfe00) @@ -102,21 +105,21 @@ nouveau_framebuffer_init(struct drm_device *dev, case 32: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_24; break; case 30: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_30; break; default: - NV_ERROR(dev, "unknown depth %d\n", fb->depth); + NV_ERROR(drm, "unknown depth %d\n", fb->depth); return -EINVAL; } - if (dev_priv->chipset == 0x50) + if (nv_device(drm->device)->chipset == 0x50) nv_fb->r_format |= (tile_flags << 8); if (!tile_flags) { - if (dev_priv->card_type < NV_D0) + if (nv_device(drm->device)->card_type < NV_D0) nv_fb->r_pitch = 0x00100000 | fb->pitches[0]; else nv_fb->r_pitch = 0x01000000 | fb->pitches[0]; } else { u32 mode = nvbo->tile_mode; - if (dev_priv->card_type >= NV_C0) + if (nv_device(drm->device)->card_type >= NV_C0) mode >>= 4; nv_fb->r_pitch = ((fb->pitches[0] / 4) << 4) | mode; } @@ -212,8 +215,9 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = { int nouveau_display_init(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_display *disp = nouveau_display(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct drm_connector *connector; int ret; @@ -225,8 +229,8 @@ nouveau_display_init(struct drm_device *dev) * some vbios default this to off for some reason, causing the * panel to not work after resume */ - if (nouveau_gpio_func_get(dev, DCB_GPIO_PANEL_POWER) == 0) { - nouveau_gpio_func_set(dev, DCB_GPIO_PANEL_POWER, true); + if (gpio && gpio->get(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff) == 0) { + gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, 1); msleep(300); } @@ -236,7 +240,8 @@ nouveau_display_init(struct drm_device *dev) /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true); + if (gpio) + gpio->irq(gpio, 0, conn->hpd, 0xff, true); } return ret; @@ -245,35 +250,65 @@ nouveau_display_init(struct drm_device *dev) void nouveau_display_fini(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_display *disp = nouveau_display(dev); + struct nouveau_gpio *gpio = nouveau_gpio(drm->device); struct drm_connector *connector; /* disable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { struct nouveau_connector *conn = nouveau_connector(connector); - nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, false); + if (gpio) + gpio->irq(gpio, 0, conn->hpd, 0xff, false); } drm_kms_helper_poll_disable(dev); disp->fini(dev); } +static void +nouveau_display_vblank_notify(void *data, int crtc) +{ + drm_handle_vblank(data, crtc); +} + +static void +nouveau_display_vblank_get(void *data, int crtc) +{ + drm_vblank_get(data, crtc); +} + +static void +nouveau_display_vblank_put(void *data, int crtc) +{ + drm_vblank_put(data, crtc); +} + int nouveau_display_create(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_disp *pdisp = nouveau_disp(drm->device); + struct nouveau_display *disp; int ret, gen; + disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; + + pdisp->vblank.data = dev; + pdisp->vblank.notify = nouveau_display_vblank_notify; + pdisp->vblank.get = nouveau_display_vblank_get; + pdisp->vblank.put = nouveau_display_vblank_put; + drm_mode_config_init(dev); drm_mode_create_scaling_mode_property(dev); drm_mode_create_dvi_i_properties(dev); - if (dev_priv->card_type < NV_50) + if (nv_device(drm->device)->card_type < NV_50) gen = 0; else - if (dev_priv->card_type < NV_D0) + if (nv_device(drm->device)->card_type < NV_D0) gen = 1; else gen = 2; @@ -307,11 +342,11 @@ nouveau_display_create(struct drm_device *dev) dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; - if (dev_priv->card_type < NV_10) { + if (nv_device(drm->device)->card_type < NV_10) { dev->mode_config.max_width = 2048; dev->mode_config.max_height = 2048; } else - if (dev_priv->card_type < NV_50) { + if (nv_device(drm->device)->card_type < NV_50) { dev->mode_config.max_width = 4096; dev->mode_config.max_height = 4096; } else { @@ -325,7 +360,13 @@ nouveau_display_create(struct drm_device *dev) drm_kms_helper_poll_init(dev); drm_kms_helper_poll_disable(dev); - ret = disp->create(dev); + if (nv_device(drm->device)->card_type < NV_50) + ret = nv04_display_create(dev); + else + if (nv_device(drm->device)->card_type < NV_D0) + ret = nv50_display_create(dev); + else + ret = nvd0_display_create(dev); if (ret) goto disp_create_err; @@ -335,10 +376,11 @@ nouveau_display_create(struct drm_device *dev) goto vblank_err; } + nouveau_backlight_init(dev); return 0; vblank_err: - disp->destroy(dev); + disp->dtor(dev); disp_create_err: drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -348,24 +390,109 @@ disp_create_err: void nouveau_display_destroy(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_display_engine *disp = &dev_priv->engine.display; + struct nouveau_display *disp = nouveau_display(dev); + nouveau_backlight_exit(dev); drm_vblank_cleanup(dev); - disp->destroy(dev); + disp->dtor(dev); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); + nouveau_drm(dev)->display = NULL; + kfree(disp); +} + +int +nouveau_display_suspend(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_crtc *crtc; + + nouveau_display_fini(dev); + + NV_INFO(drm, "unpinning framebuffer(s)...\n"); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_framebuffer *nouveau_fb; + + nouveau_fb = nouveau_framebuffer(crtc->fb); + if (!nouveau_fb || !nouveau_fb->nvbo) + continue; + + nouveau_bo_unpin(nouveau_fb->nvbo); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + } + + return 0; +} + +void +nouveau_display_resume(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct drm_crtc *crtc; + int ret; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_framebuffer *nouveau_fb; + + nouveau_fb = nouveau_framebuffer(crtc->fb); + if (!nouveau_fb || !nouveau_fb->nvbo) + continue; + + nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) + ret = nouveau_bo_map(nv_crtc->cursor.nvbo); + if (ret) + NV_ERROR(drm, "Could not pin/map cursor.\n"); + } + + nouveau_fbcon_set_suspend(dev, 0); + nouveau_fbcon_zfill_all(dev); + + nouveau_display_init(dev); + + /* Force CLUT to get re-loaded during modeset */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + + nv_crtc->lut.depth = 0; + } + + drm_helper_resume_force_mode(dev); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 offset = nv_crtc->cursor.nvbo->bo.offset; + + nv_crtc->cursor.set_offset(nv_crtc, offset); + nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, + nv_crtc->cursor_saved_y); + } } int nouveau_vblank_enable(struct drm_device *dev, int crtc) { - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_device *device = nouveau_dev(dev); - if (dev_priv->card_type >= NV_50) - nv_mask(dev, NV50_PDISPLAY_INTR_EN_1, 0, + if (device->card_type >= NV_D0) + nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 1); + else + if (device->card_type >= NV_50) + nv_mask(device, NV50_PDISPLAY_INTR_EN_1, 0, NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc)); else NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, @@ -377,10 +504,13 @@ nouveau_vblank_enable(struct drm_device *dev, int crtc) void nouveau_vblank_disable(struct drm_device *dev, int crtc) { - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_device *device = nouveau_dev(dev); - if (dev_priv->card_type >= NV_50) - nv_mask(dev, NV50_PDISPLAY_INTR_EN_1, + if (device->card_type >= NV_D0) + nv_mask(device, 0x6100c0 + (crtc * 0x800), 1, 0); + else + if (device->card_type >= NV_50) + nv_mask(device, NV50_PDISPLAY_INTR_EN_1, NV50_PDISPLAY_INTR_EN_1_VBLANK_CRTC_(crtc), 0); else NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0); @@ -434,15 +564,15 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, struct nouveau_page_flip_state *s, struct nouveau_fence **pfence) { - struct nouveau_software_chan *swch = chan->engctx[NVOBJ_ENGINE_SW]; - struct drm_nouveau_private *dev_priv = chan->dev->dev_private; - struct drm_device *dev = chan->dev; + struct nouveau_fence_chan *fctx = chan->fence; + struct nouveau_drm *drm = chan->drm; + struct drm_device *dev = drm->dev; unsigned long flags; int ret; /* Queue it to the pending list */ spin_lock_irqsave(&dev->event_lock, flags); - list_add_tail(&s->head, &swch->flip); + list_add_tail(&s->head, &fctx->flip); spin_unlock_irqrestore(&dev->event_lock, flags); /* Synchronize with the old framebuffer */ @@ -455,7 +585,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan, if (ret) goto fail; - if (dev_priv->card_type < NV_C0) { + if (nv_device(drm->device)->card_type < NV_C0) { BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); OUT_RING (chan, 0x00000000); OUT_RING (chan, 0x00000000); @@ -483,7 +613,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) { struct drm_device *dev = crtc->dev; - struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo; struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo; struct nouveau_page_flip_state *s; @@ -491,7 +621,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct nouveau_fence *fence; int ret; - if (!dev_priv->channel) + if (!drm->channel) return -ENODEV; s = kzalloc(sizeof(*s), GFP_KERNEL); @@ -512,25 +642,25 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Choose the channel the flip will be handled in */ fence = new_bo->bo.sync_obj; if (fence) - chan = nouveau_channel_get_unlocked(fence->channel); + chan = fence->channel; if (!chan) - chan = nouveau_channel_get_unlocked(dev_priv->channel); - mutex_lock(&chan->mutex); + chan = drm->channel; + mutex_lock(&chan->cli->mutex); /* Emit a page flip */ - if (dev_priv->card_type >= NV_50) { - if (dev_priv->card_type >= NV_D0) + if (nv_device(drm->device)->card_type >= NV_50) { + if (nv_device(drm->device)->card_type >= NV_D0) ret = nvd0_display_flip_next(crtc, fb, chan, 0); else ret = nv50_display_flip_next(crtc, fb, chan); if (ret) { - nouveau_channel_put(&chan); + mutex_unlock(&chan->cli->mutex); goto fail_unreserve; } } ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); - nouveau_channel_put(&chan); + mutex_unlock(&chan->cli->mutex); if (ret) goto fail_unreserve; @@ -552,20 +682,21 @@ int nouveau_finish_page_flip(struct nouveau_channel *chan, struct nouveau_page_flip_state *ps) { - struct nouveau_software_chan *swch = chan->engctx[NVOBJ_ENGINE_SW]; - struct drm_device *dev = chan->dev; + struct nouveau_fence_chan *fctx = chan->fence; + struct nouveau_drm *drm = chan->drm; + struct drm_device *dev = drm->dev; struct nouveau_page_flip_state *s; unsigned long flags; spin_lock_irqsave(&dev->event_lock, flags); - if (list_empty(&swch->flip)) { - NV_ERROR(dev, "Unexpected pageflip in channel %d.\n", chan->id); + if (list_empty(&fctx->flip)) { + NV_ERROR(drm, "unexpected pageflip\n"); spin_unlock_irqrestore(&dev->event_lock, flags); return -EINVAL; } - s = list_first_entry(&swch->flip, struct nouveau_page_flip_state, head); + s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head); if (s->event) { struct drm_pending_vblank_event *e = s->event; struct timeval now; @@ -588,6 +719,24 @@ nouveau_finish_page_flip(struct nouveau_channel *chan, } int +nouveau_flip_complete(void *data) +{ + struct nouveau_channel *chan = data; + struct nouveau_drm *drm = chan->drm; + struct nouveau_page_flip_state state; + + if (!nouveau_finish_page_flip(chan, &state)) { + if (nv_device(drm->device)->card_type < NV_50) { + nv_set_crtc_base(drm->dev, state.crtc, state.offset + + state.y * state.pitch + + state.x * state.bpp / 8); + } + } + + return 0; +} + +int nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args) { |