Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[linux-3.10.git] / drivers / gpu / drm / nouveau / nouveau_display.c
index e4eeeaf..8f98e5a 100644 (file)
 
 #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)
 {