aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c295
1 files changed, 227 insertions, 68 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 7e16dc5e6467..86124b131f4f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -24,20 +24,23 @@
*
*/
-#include "drmP.h"
-#include "drm_crtc_helper.h"
-#include "nouveau_drv.h"
-#include "nouveau_fb.h"
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.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,66 @@ 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;
+ u32 pclass = dev->pdev->class >> 8;
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 +343,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,20 +361,31 @@ nouveau_display_create(struct drm_device *dev)
drm_kms_helper_poll_init(dev);
drm_kms_helper_poll_disable(dev);
- ret = disp->create(dev);
- if (ret)
- goto disp_create_err;
-
- if (dev->mode_config.num_crtc) {
- ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (nouveau_modeset == 1 ||
+ (nouveau_modeset < 0 && pclass == PCI_CLASS_DISPLAY_VGA)) {
+ 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 vblank_err;
+ goto disp_create_err;
+
+ if (dev->mode_config.num_crtc) {
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (ret)
+ 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 +395,110 @@ 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);
+ if (disp->dtor)
+ 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 +510,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);
@@ -400,9 +536,11 @@ nouveau_page_flip_reserve(struct nouveau_bo *old_bo,
if (ret)
goto fail;
- ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0);
- if (ret)
- goto fail_unreserve;
+ if (likely(old_bo != new_bo)) {
+ ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0);
+ if (ret)
+ goto fail_unreserve;
+ }
return 0;
@@ -421,8 +559,10 @@ nouveau_page_flip_unreserve(struct nouveau_bo *old_bo,
nouveau_bo_fence(new_bo, fence);
ttm_bo_unreserve(&new_bo->bo);
- nouveau_bo_fence(old_bo, fence);
- ttm_bo_unreserve(&old_bo->bo);
+ if (likely(old_bo != new_bo)) {
+ nouveau_bo_fence(old_bo, fence);
+ ttm_bo_unreserve(&old_bo->bo);
+ }
nouveau_bo_unpin(old_bo);
}
@@ -434,15 +574,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 +595,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 +623,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 +631,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 +652,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 +692,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 +729,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)
{