summaryrefslogtreecommitdiff
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.c259
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)
{