drm/nv50-nvc0: switch to common disp impl, removing previous version
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
f9887d0911
commit
4f6029da58
@ -192,7 +192,7 @@ nouveau-y += nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o
|
||||
|
||||
# drm/kms
|
||||
nouveau-y += nouveau_bios.o nouveau_fbcon.o nouveau_display.o
|
||||
nouveau-y += nouveau_connector.o nouveau_hdmi.o nouveau_dp.o
|
||||
nouveau-y += nouveau_connector.o nouveau_dp.o
|
||||
nouveau-y += nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o
|
||||
|
||||
# drm/kms/nv04:nv50
|
||||
@ -201,9 +201,7 @@ nouveau-y += nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o
|
||||
nouveau-y += nv04_crtc.o nv04_display.o nv04_cursor.o
|
||||
|
||||
# drm/kms/nv50-
|
||||
nouveau-y += nv50_display.o nvd0_display.o
|
||||
nouveau-y += nv50_crtc.o nv50_dac.o nv50_sor.o nv50_cursor.o
|
||||
nouveau-y += nv50_evo.o
|
||||
nouveau-y += nvd0_display.o
|
||||
|
||||
# drm/pm
|
||||
nouveau-y += nouveau_pm.o nouveau_volt.o nouveau_perf.o
|
||||
|
@ -28,6 +28,7 @@
|
||||
#define __NOUVEAU_CONNECTOR_H__
|
||||
|
||||
#include <drm/drm_edid.h>
|
||||
#include "nouveau_crtc.h"
|
||||
|
||||
struct nouveau_i2c_port;
|
||||
|
||||
@ -80,6 +81,21 @@ static inline struct nouveau_connector *nouveau_connector(
|
||||
return container_of(con, struct nouveau_connector, base);
|
||||
}
|
||||
|
||||
static inline struct nouveau_connector *
|
||||
nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
|
||||
{
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_crtc *crtc = to_drm_crtc(nv_crtc);
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->encoder && connector->encoder->crtc == crtc)
|
||||
return nouveau_connector(connector);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct drm_connector *
|
||||
nouveau_connector_create(struct drm_device *, int index);
|
||||
|
||||
|
@ -91,7 +91,4 @@ int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
|
||||
int nv04_cursor_init(struct nouveau_crtc *);
|
||||
int nv50_cursor_init(struct nouveau_crtc *);
|
||||
|
||||
struct nouveau_connector *
|
||||
nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
|
||||
|
||||
#endif /* __NOUVEAU_CRTC_H__ */
|
||||
|
@ -98,12 +98,12 @@ nouveau_framebuffer_init(struct drm_device *dev,
|
||||
nv_fb->r_dma = NvEvoVRAM_LP;
|
||||
|
||||
switch (fb->depth) {
|
||||
case 8: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_8; break;
|
||||
case 15: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_15; break;
|
||||
case 16: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_16; break;
|
||||
case 8: nv_fb->r_format = 0x1e00; break;
|
||||
case 15: nv_fb->r_format = 0xe900; break;
|
||||
case 16: nv_fb->r_format = 0xe800; break;
|
||||
case 24:
|
||||
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;
|
||||
case 32: nv_fb->r_format = 0xcf00; break;
|
||||
case 30: nv_fb->r_format = 0xd100; break;
|
||||
default:
|
||||
NV_ERROR(drm, "unknown depth %d\n", fb->depth);
|
||||
return -EINVAL;
|
||||
@ -365,9 +365,6 @@ nouveau_display_create(struct drm_device *dev)
|
||||
(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)
|
||||
@ -660,10 +657,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
|
||||
/* Emit a page flip */
|
||||
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);
|
||||
ret = nvd0_display_flip_next(crtc, fb, chan, 0);
|
||||
if (ret) {
|
||||
mutex_unlock(&chan->cli->mutex);
|
||||
goto fail_unreserve;
|
||||
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_crtc.h"
|
||||
|
||||
#include <core/class.h>
|
||||
|
||||
#include "nv50_display.h"
|
||||
|
||||
static bool
|
||||
hdmi_sor(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
if (nv_device(drm->device)->chipset < 0xa3 ||
|
||||
nv_device(drm->device)->chipset == 0xaa ||
|
||||
nv_device(drm->device)->chipset == 0xac)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_audio_disconnect(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
u32 or = nv_encoder->or;
|
||||
|
||||
if (hdmi_sor(encoder))
|
||||
nv_exec(priv->core, NVA3_DISP_SOR_HDA_ELD + or, NULL, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_audio_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_connector *nv_connector;
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (!drm_detect_monitor_audio(nv_connector->edid)) {
|
||||
nouveau_audio_disconnect(encoder);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdmi_sor(encoder)) {
|
||||
drm_edid_to_eld(&nv_connector->base, nv_connector->edid);
|
||||
nv_exec(priv->core, NVA3_DISP_SOR_HDA_ELD + nv_encoder->or,
|
||||
nv_connector->base.eld,
|
||||
nv_connector->base.eld[2] * 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_hdmi_disconnect(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
const u32 moff = (nv_crtc->index << 3) | nv_encoder->or;
|
||||
|
||||
nouveau_audio_disconnect(encoder);
|
||||
|
||||
nv_call(priv->core, NV84_DISP_SOR_HDMI_PWR + moff, 0x00000000);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_hdmi_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
const u32 moff = (nv_crtc->index << 3) | nv_encoder->or;
|
||||
struct nouveau_connector *nv_connector;
|
||||
u32 max_ac_packet, rekey;
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (!mode || !nv_connector || !nv_connector->edid ||
|
||||
!drm_detect_hdmi_monitor(nv_connector->edid)) {
|
||||
nouveau_hdmi_disconnect(encoder);
|
||||
return;
|
||||
}
|
||||
|
||||
/* value matches nvidia binary driver, and tegra constant */
|
||||
rekey = 56;
|
||||
|
||||
max_ac_packet = mode->htotal - mode->hdisplay;
|
||||
max_ac_packet -= rekey;
|
||||
max_ac_packet -= 18; /* constant from tegra */
|
||||
max_ac_packet /= 32;
|
||||
|
||||
nv_call(priv->core, NV84_DISP_SOR_HDMI_PWR + moff,
|
||||
NV84_DISP_SOR_HDMI_PWR_STATE_ON |
|
||||
(max_ac_packet << 16) | rekey);
|
||||
|
||||
nouveau_audio_mode_set(encoder, mode);
|
||||
}
|
@ -1,753 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "nouveau_reg.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_gem.h"
|
||||
#include "nouveau_hw.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
#include <subdev/clock.h>
|
||||
|
||||
static void
|
||||
nv50_crtc_lut_load(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0);
|
||||
writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2);
|
||||
writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4);
|
||||
}
|
||||
|
||||
if (nv_crtc->lut.depth == 30) {
|
||||
writew(nv_crtc->lut.r[i - 1] >> 2, lut + 8*i + 0);
|
||||
writew(nv_crtc->lut.g[i - 1] >> 2, lut + 8*i + 2);
|
||||
writew(nv_crtc->lut.b[i - 1] >> 2, lut + 8*i + 4);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
|
||||
{
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
int index = nv_crtc->index, ret;
|
||||
|
||||
NV_DEBUG(drm, "index %d\n", nv_crtc->index);
|
||||
NV_DEBUG(drm, "%s\n", blanked ? "blanked" : "unblanked");
|
||||
|
||||
if (blanked) {
|
||||
nv_crtc->cursor.hide(nv_crtc, false);
|
||||
|
||||
ret = RING_SPACE(evo, nv_device(drm->device)->chipset != 0x50 ? 7 : 5);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while blanking crtc\n");
|
||||
return ret;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
|
||||
OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK);
|
||||
OUT_RING(evo, 0);
|
||||
if (nv_device(drm->device)->chipset != 0x50) {
|
||||
BEGIN_NV04(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
|
||||
OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE);
|
||||
}
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
|
||||
OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
|
||||
} else {
|
||||
if (nv_crtc->cursor.visible)
|
||||
nv_crtc->cursor.show(nv_crtc, false);
|
||||
else
|
||||
nv_crtc->cursor.hide(nv_crtc, false);
|
||||
|
||||
ret = RING_SPACE(evo, nv_device(drm->device)->chipset != 0x50 ? 10 : 8);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while unblanking crtc\n");
|
||||
return ret;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
|
||||
OUT_RING(evo, nv_crtc->lut.depth == 8 ?
|
||||
NV50_EVO_CRTC_CLUT_MODE_OFF :
|
||||
NV50_EVO_CRTC_CLUT_MODE_ON);
|
||||
OUT_RING(evo, nv_crtc->lut.nvbo->bo.offset >> 8);
|
||||
if (nv_device(drm->device)->chipset != 0x50) {
|
||||
BEGIN_NV04(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
|
||||
OUT_RING(evo, NvEvoVRAM);
|
||||
}
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2);
|
||||
OUT_RING(evo, nv_crtc->fb.offset >> 8);
|
||||
OUT_RING(evo, 0);
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
|
||||
if (nv_device(drm->device)->chipset != 0x50)
|
||||
if (nv_crtc->fb.tile_flags == 0x7a00 ||
|
||||
nv_crtc->fb.tile_flags == 0xfe00)
|
||||
OUT_RING(evo, NvEvoFB32);
|
||||
else
|
||||
if (nv_crtc->fb.tile_flags == 0x7000)
|
||||
OUT_RING(evo, NvEvoFB16);
|
||||
else
|
||||
OUT_RING(evo, NvEvoVRAM_LP);
|
||||
else
|
||||
OUT_RING(evo, NvEvoVRAM_LP);
|
||||
}
|
||||
|
||||
nv_crtc->fb.blanked = blanked;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
|
||||
{
|
||||
struct nouveau_channel *evo = nv50_display(nv_crtc->base.dev)->master;
|
||||
struct nouveau_connector *nv_connector;
|
||||
struct drm_connector *connector;
|
||||
int head = nv_crtc->index, ret;
|
||||
u32 mode = 0x00;
|
||||
|
||||
nv_connector = nouveau_crtc_connector_get(nv_crtc);
|
||||
connector = &nv_connector->base;
|
||||
if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) {
|
||||
if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3)
|
||||
mode = DITHERING_MODE_DYNAMIC2X2;
|
||||
} else {
|
||||
mode = nv_connector->dithering_mode;
|
||||
}
|
||||
|
||||
if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) {
|
||||
if (connector->display_info.bpc >= 8)
|
||||
mode |= DITHERING_DEPTH_8BPC;
|
||||
} else {
|
||||
mode |= nv_connector->dithering_depth;
|
||||
}
|
||||
|
||||
ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1);
|
||||
OUT_RING (evo, mode);
|
||||
if (update) {
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING (evo, 0);
|
||||
FIRE_RING (evo);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update)
|
||||
{
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
int ret;
|
||||
int adj;
|
||||
u32 hue, vib;
|
||||
|
||||
NV_DEBUG(drm, "vibrance = %i, hue = %i\n",
|
||||
nv_crtc->color_vibrance, nv_crtc->vibrant_hue);
|
||||
|
||||
ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while setting color vibrance\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adj = (nv_crtc->color_vibrance > 0) ? 50 : 0;
|
||||
vib = ((nv_crtc->color_vibrance * 2047 + adj) / 100) & 0xfff;
|
||||
|
||||
hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff;
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
|
||||
OUT_RING (evo, (hue << 20) | (vib << 8));
|
||||
|
||||
if (update) {
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING (evo, 0);
|
||||
FIRE_RING (evo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nouveau_connector *
|
||||
nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
|
||||
{
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_crtc *crtc = to_drm_crtc(nv_crtc);
|
||||
|
||||
/* The safest approach is to find an encoder with the right crtc, that
|
||||
* is also linked to a connector. */
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->encoder)
|
||||
if (connector->encoder->crtc == crtc)
|
||||
return nouveau_connector(connector);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
|
||||
{
|
||||
struct nouveau_connector *nv_connector;
|
||||
struct drm_crtc *crtc = &nv_crtc->base;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
struct drm_display_mode *umode = &crtc->mode;
|
||||
struct drm_display_mode *omode;
|
||||
int scaling_mode, ret;
|
||||
u32 ctrl = 0, oX, oY;
|
||||
|
||||
nv_connector = nouveau_crtc_connector_get(nv_crtc);
|
||||
if (!nv_connector || !nv_connector->native_mode) {
|
||||
NV_ERROR(drm, "no native mode, forcing panel scaling\n");
|
||||
scaling_mode = DRM_MODE_SCALE_NONE;
|
||||
} else {
|
||||
scaling_mode = nv_connector->scaling_mode;
|
||||
}
|
||||
|
||||
/* start off at the resolution we programmed the crtc for, this
|
||||
* effectively handles NONE/FULL scaling
|
||||
*/
|
||||
if (scaling_mode != DRM_MODE_SCALE_NONE)
|
||||
omode = nv_connector->native_mode;
|
||||
else
|
||||
omode = umode;
|
||||
|
||||
oX = omode->hdisplay;
|
||||
oY = omode->vdisplay;
|
||||
if (omode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
oY *= 2;
|
||||
|
||||
/* add overscan compensation if necessary, will keep the aspect
|
||||
* ratio the same as the backend mode unless overridden by the
|
||||
* user setting both hborder and vborder properties.
|
||||
*/
|
||||
if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON ||
|
||||
(nv_connector->underscan == UNDERSCAN_AUTO &&
|
||||
nv_connector->edid &&
|
||||
drm_detect_hdmi_monitor(nv_connector->edid)))) {
|
||||
u32 bX = nv_connector->underscan_hborder;
|
||||
u32 bY = nv_connector->underscan_vborder;
|
||||
u32 aspect = (oY << 19) / oX;
|
||||
|
||||
if (bX) {
|
||||
oX -= (bX * 2);
|
||||
if (bY) oY -= (bY * 2);
|
||||
else oY = ((oX * aspect) + (aspect / 2)) >> 19;
|
||||
} else {
|
||||
oX -= (oX >> 4) + 32;
|
||||
if (bY) oY -= (bY * 2);
|
||||
else oY = ((oX * aspect) + (aspect / 2)) >> 19;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle CENTER/ASPECT scaling, taking into account the areas
|
||||
* removed already for overscan compensation
|
||||
*/
|
||||
switch (scaling_mode) {
|
||||
case DRM_MODE_SCALE_CENTER:
|
||||
oX = min((u32)umode->hdisplay, oX);
|
||||
oY = min((u32)umode->vdisplay, oY);
|
||||
/* fall-through */
|
||||
case DRM_MODE_SCALE_ASPECT:
|
||||
if (oY < oX) {
|
||||
u32 aspect = (umode->hdisplay << 19) / umode->vdisplay;
|
||||
oX = ((oY * aspect) + (aspect / 2)) >> 19;
|
||||
} else {
|
||||
u32 aspect = (umode->vdisplay << 19) / umode->hdisplay;
|
||||
oY = ((oX * aspect) + (aspect / 2)) >> 19;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (umode->hdisplay != oX || umode->vdisplay != oY ||
|
||||
umode->flags & DRM_MODE_FLAG_INTERLACE ||
|
||||
umode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE;
|
||||
|
||||
ret = RING_SPACE(evo, 5);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
|
||||
OUT_RING (evo, ctrl);
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
|
||||
OUT_RING (evo, oY << 16 | oX);
|
||||
OUT_RING (evo, oY << 16 | oX);
|
||||
|
||||
if (update) {
|
||||
nv50_display_flip_stop(crtc);
|
||||
nv50_display_sync(dev);
|
||||
nv50_display_flip_next(crtc, crtc->fb, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_clock *clk = nouveau_clock(device);
|
||||
|
||||
return clk->pll_set(clk, PLL_VPLL0 + head, pclk);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_destroy(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
nouveau_bo_unmap(nv_crtc->lut.nvbo);
|
||||
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
|
||||
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
|
||||
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
||||
drm_crtc_cleanup(&nv_crtc->base);
|
||||
kfree(nv_crtc);
|
||||
}
|
||||
|
||||
int
|
||||
nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
|
||||
uint32_t buffer_handle, uint32_t width, uint32_t height)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct nouveau_bo *cursor = NULL;
|
||||
struct drm_gem_object *gem;
|
||||
int ret = 0, i;
|
||||
|
||||
if (!buffer_handle) {
|
||||
nv_crtc->cursor.hide(nv_crtc, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (width != 64 || height != 64)
|
||||
return -EINVAL;
|
||||
|
||||
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
|
||||
if (!gem)
|
||||
return -ENOENT;
|
||||
cursor = nouveau_gem_object(gem);
|
||||
|
||||
ret = nouveau_bo_map(cursor);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* The simple will do for now. */
|
||||
for (i = 0; i < 64 * 64; i++)
|
||||
nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, nouveau_bo_rd32(cursor, i));
|
||||
|
||||
nouveau_bo_unmap(cursor);
|
||||
|
||||
nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset);
|
||||
nv_crtc->cursor.show(nv_crtc, true);
|
||||
|
||||
out:
|
||||
drm_gem_object_unreference_unlocked(gem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
||||
{
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
nv_crtc->cursor.set_pos(nv_crtc, x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
|
||||
uint32_t start, uint32_t size)
|
||||
{
|
||||
int end = (start + size > 256) ? 256 : start + size, i;
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
nv_crtc->lut.r[i] = r[i];
|
||||
nv_crtc->lut.g[i] = g[i];
|
||||
nv_crtc->lut.b[i] = b[i];
|
||||
}
|
||||
|
||||
/* We need to know the depth before we upload, but it's possible to
|
||||
* get called before a framebuffer is bound. If this is the case,
|
||||
* mark the lut values as dirty by setting depth==0, and it'll be
|
||||
* uploaded on the first mode_set_base()
|
||||
*/
|
||||
if (!nv_crtc->base.fb) {
|
||||
nv_crtc->lut.depth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
nv50_crtc_lut_load(crtc);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_save(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(crtc->dev);
|
||||
NV_ERROR(drm, "!!\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_restore(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(crtc->dev);
|
||||
NV_ERROR(drm, "!!\n");
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs nv50_crtc_funcs = {
|
||||
.save = nv50_crtc_save,
|
||||
.restore = nv50_crtc_restore,
|
||||
.cursor_set = nv50_crtc_cursor_set,
|
||||
.cursor_move = nv50_crtc_cursor_move,
|
||||
.gamma_set = nv50_crtc_gamma_set,
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.page_flip = nouveau_crtc_page_flip,
|
||||
.destroy = nv50_crtc_destroy,
|
||||
};
|
||||
|
||||
static void
|
||||
nv50_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
NV_DEBUG(drm, "index %d\n", nv_crtc->index);
|
||||
|
||||
nv50_display_flip_stop(crtc);
|
||||
drm_vblank_pre_modeset(dev, nv_crtc->index);
|
||||
nv50_crtc_blank(nv_crtc, true);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
|
||||
NV_DEBUG(drm, "index %d\n", nv_crtc->index);
|
||||
|
||||
nv50_crtc_blank(nv_crtc, false);
|
||||
drm_vblank_post_modeset(dev, nv_crtc->index);
|
||||
nv50_display_sync(dev);
|
||||
nv50_display_flip_next(crtc, crtc->fb, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *passed_fb,
|
||||
int x, int y, bool atomic)
|
||||
{
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
struct drm_framebuffer *drm_fb;
|
||||
struct nouveau_framebuffer *fb;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG(drm, "index %d\n", nv_crtc->index);
|
||||
|
||||
/* no fb bound */
|
||||
if (!atomic && !crtc->fb) {
|
||||
NV_DEBUG(drm, "No FB bound\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If atomic, we want to switch to the fb we were passed, so
|
||||
* now we update pointers to do that. (We don't pin; just
|
||||
* assume we're already pinned and update the base address.)
|
||||
*/
|
||||
if (atomic) {
|
||||
drm_fb = passed_fb;
|
||||
fb = nouveau_framebuffer(passed_fb);
|
||||
} else {
|
||||
drm_fb = crtc->fb;
|
||||
fb = nouveau_framebuffer(crtc->fb);
|
||||
/* If not atomic, we can go ahead and pin, and unpin the
|
||||
* old fb we were passed.
|
||||
*/
|
||||
ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (passed_fb) {
|
||||
struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb);
|
||||
nouveau_bo_unpin(ofb->nvbo);
|
||||
}
|
||||
}
|
||||
|
||||
nv_crtc->fb.offset = fb->nvbo->bo.offset;
|
||||
nv_crtc->fb.tile_flags = nouveau_bo_tile_layout(fb->nvbo);
|
||||
nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
|
||||
if (!nv_crtc->fb.blanked && nv_device(drm->device)->chipset != 0x50) {
|
||||
ret = RING_SPACE(evo, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1);
|
||||
OUT_RING (evo, fb->r_dma);
|
||||
}
|
||||
|
||||
ret = RING_SPACE(evo, 12);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5);
|
||||
OUT_RING (evo, nv_crtc->fb.offset >> 8);
|
||||
OUT_RING (evo, 0);
|
||||
OUT_RING (evo, (drm_fb->height << 16) | drm_fb->width);
|
||||
OUT_RING (evo, fb->r_pitch);
|
||||
OUT_RING (evo, fb->r_format);
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1);
|
||||
OUT_RING (evo, fb->base.depth == 8 ?
|
||||
NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON);
|
||||
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1);
|
||||
OUT_RING (evo, (y << 16) | x);
|
||||
|
||||
if (nv_crtc->lut.depth != fb->base.depth) {
|
||||
nv_crtc->lut.depth = fb->base.depth;
|
||||
nv50_crtc_lut_load(crtc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
|
||||
struct drm_display_mode *mode, int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
u32 head = nv_crtc->index * 0x400;
|
||||
u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1;
|
||||
u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1;
|
||||
u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks;
|
||||
u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks;
|
||||
u32 vblan2e = 0, vblan2s = 1;
|
||||
int ret;
|
||||
|
||||
/* hw timing description looks like this:
|
||||
*
|
||||
* <sync> <back porch> <---------display---------> <front porch>
|
||||
* ______
|
||||
* |____________|---------------------------|____________|
|
||||
*
|
||||
* ^ synce ^ blanke ^ blanks ^ active
|
||||
*
|
||||
* interlaced modes also have 2 additional values pointing at the end
|
||||
* and start of the next field's blanking period.
|
||||
*/
|
||||
|
||||
hactive = mode->htotal;
|
||||
hsynce = mode->hsync_end - mode->hsync_start - 1;
|
||||
hbackp = mode->htotal - mode->hsync_end;
|
||||
hblanke = hsynce + hbackp;
|
||||
hfrontp = mode->hsync_start - mode->hdisplay;
|
||||
hblanks = mode->htotal - hfrontp - 1;
|
||||
|
||||
vactive = mode->vtotal * vscan / ilace;
|
||||
vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1;
|
||||
vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace;
|
||||
vblanke = vsynce + vbackp;
|
||||
vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace;
|
||||
vblanks = vactive - vfrontp - 1;
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
vblan2e = vactive + vsynce + vbackp;
|
||||
vblan2s = vblan2e + (mode->vdisplay * vscan / ilace);
|
||||
vactive = (vactive * 2) + 1;
|
||||
}
|
||||
|
||||
ret = RING_SPACE(evo, 18);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(evo, 0, 0x0804 + head, 2);
|
||||
OUT_RING (evo, 0x00800000 | mode->clock);
|
||||
OUT_RING (evo, (ilace == 2) ? 2 : 0);
|
||||
BEGIN_NV04(evo, 0, 0x0810 + head, 6);
|
||||
OUT_RING (evo, 0x00000000); /* border colour */
|
||||
OUT_RING (evo, (vactive << 16) | hactive);
|
||||
OUT_RING (evo, ( vsynce << 16) | hsynce);
|
||||
OUT_RING (evo, (vblanke << 16) | hblanke);
|
||||
OUT_RING (evo, (vblanks << 16) | hblanks);
|
||||
OUT_RING (evo, (vblan2e << 16) | vblan2s);
|
||||
BEGIN_NV04(evo, 0, 0x082c + head, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
BEGIN_NV04(evo, 0, 0x0900 + head, 1);
|
||||
OUT_RING (evo, 0x00000311); /* makes sync channel work */
|
||||
BEGIN_NV04(evo, 0, 0x08c8 + head, 1);
|
||||
OUT_RING (evo, (umode->vdisplay << 16) | umode->hdisplay);
|
||||
BEGIN_NV04(evo, 0, 0x08d4 + head, 1);
|
||||
OUT_RING (evo, 0x00000000); /* screen position */
|
||||
}
|
||||
|
||||
nv_crtc->set_dither(nv_crtc, false);
|
||||
nv_crtc->set_scale(nv_crtc, false);
|
||||
nv_crtc->set_color_vibrance(nv_crtc, false);
|
||||
|
||||
return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
nv50_display_flip_stop(crtc);
|
||||
ret = nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nv50_display_sync(crtc->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nv50_display_flip_next(crtc, crtc->fb, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
int x, int y, enum mode_set_atomic state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
nv50_display_flip_stop(crtc);
|
||||
ret = nv50_crtc_do_mode_set_base(crtc, fb, x, y, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return nv50_display_sync(crtc->dev);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
|
||||
.dpms = nv50_crtc_dpms,
|
||||
.prepare = nv50_crtc_prepare,
|
||||
.commit = nv50_crtc_commit,
|
||||
.mode_fixup = nv50_crtc_mode_fixup,
|
||||
.mode_set = nv50_crtc_mode_set,
|
||||
.mode_set_base = nv50_crtc_mode_set_base,
|
||||
.mode_set_base_atomic = nv50_crtc_mode_set_base_atomic,
|
||||
.load_lut = nv50_crtc_lut_load,
|
||||
};
|
||||
|
||||
int
|
||||
nv50_crtc_create(struct drm_device *dev, int index)
|
||||
{
|
||||
struct nouveau_crtc *nv_crtc = NULL;
|
||||
int ret, i;
|
||||
|
||||
nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
|
||||
if (!nv_crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
nv_crtc->index = index;
|
||||
nv_crtc->set_dither = nv50_crtc_set_dither;
|
||||
nv_crtc->set_scale = nv50_crtc_set_scale;
|
||||
nv_crtc->set_color_vibrance = nv50_crtc_set_color_vibrance;
|
||||
nv_crtc->color_vibrance = 50;
|
||||
nv_crtc->vibrant_hue = 0;
|
||||
nv_crtc->lut.depth = 0;
|
||||
for (i = 0; i < 256; i++) {
|
||||
nv_crtc->lut.r[i] = i << 8;
|
||||
nv_crtc->lut.g[i] = i << 8;
|
||||
nv_crtc->lut.b[i] = i << 8;
|
||||
}
|
||||
|
||||
drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs);
|
||||
drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
|
||||
drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
|
||||
|
||||
ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM,
|
||||
0, 0x0000, NULL, &nv_crtc->lut.nvbo);
|
||||
if (!ret) {
|
||||
ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
|
||||
if (!ret)
|
||||
ret = nouveau_bo_map(nv_crtc->lut.nvbo);
|
||||
if (ret)
|
||||
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
||||
ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
|
||||
0, 0x0000, NULL, &nv_crtc->cursor.nvbo);
|
||||
if (!ret) {
|
||||
ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
|
||||
if (!ret)
|
||||
ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
|
||||
if (ret)
|
||||
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
nv50_cursor_init(nv_crtc);
|
||||
out:
|
||||
if (ret)
|
||||
nv50_crtc_destroy(&nv_crtc->base);
|
||||
return ret;
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
static void
|
||||
nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
|
||||
{
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
int ret;
|
||||
|
||||
if (update && nv_crtc->cursor.visible)
|
||||
return;
|
||||
|
||||
ret = RING_SPACE(evo, (nv_device(drm->device)->chipset != 0x50 ? 5 : 3) + update * 2);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while unhiding cursor\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nv_device(drm->device)->chipset != 0x50) {
|
||||
BEGIN_NV04(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
|
||||
OUT_RING(evo, NvEvoVRAM);
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
|
||||
OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_SHOW);
|
||||
OUT_RING(evo, nv_crtc->cursor.offset >> 8);
|
||||
|
||||
if (update) {
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING(evo, 0);
|
||||
FIRE_RING(evo);
|
||||
nv_crtc->cursor.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
|
||||
{
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
int ret;
|
||||
|
||||
if (update && !nv_crtc->cursor.visible)
|
||||
return;
|
||||
|
||||
ret = RING_SPACE(evo, (nv_device(drm->device)->chipset != 0x50 ? 5 : 3) + update * 2);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while hiding cursor\n");
|
||||
return;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
|
||||
OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_HIDE);
|
||||
OUT_RING(evo, 0);
|
||||
if (nv_device(drm->device)->chipset != 0x50) {
|
||||
BEGIN_NV04(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
|
||||
OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING(evo, 0);
|
||||
FIRE_RING(evo);
|
||||
nv_crtc->cursor.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(nv_crtc->base.dev);
|
||||
|
||||
nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y;
|
||||
nv_wr32(device, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index),
|
||||
((y & 0xFFFF) << 16) | (x & 0xFFFF));
|
||||
/* Needed to make the cursor move. */
|
||||
nv_wr32(device, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
|
||||
{
|
||||
if (offset == nv_crtc->cursor.offset)
|
||||
return;
|
||||
|
||||
nv_crtc->cursor.offset = offset;
|
||||
if (nv_crtc->cursor.visible) {
|
||||
nv_crtc->cursor.visible = false;
|
||||
nv_crtc->cursor.show(nv_crtc, true);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nv50_cursor_init(struct nouveau_crtc *nv_crtc)
|
||||
{
|
||||
nv_crtc->cursor.set_offset = nv50_cursor_set_offset;
|
||||
nv_crtc->cursor.set_pos = nv50_cursor_set_pos;
|
||||
nv_crtc->cursor.hide = nv50_cursor_hide;
|
||||
nv_crtc->cursor.show = nv50_cursor_show;
|
||||
return 0;
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
||||
#include "nouveau_reg.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
#include <core/class.h>
|
||||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void
|
||||
nv50_dac_disconnect(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
int ret;
|
||||
|
||||
if (!nv_encoder->crtc)
|
||||
return;
|
||||
nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true);
|
||||
|
||||
NV_DEBUG(drm, "Disconnecting DAC %d\n", nv_encoder->or);
|
||||
|
||||
ret = RING_SPACE(evo, 4);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while disconnecting DAC\n");
|
||||
return;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1);
|
||||
OUT_RING (evo, 0);
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING (evo, 0);
|
||||
|
||||
nv_encoder->crtc = NULL;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
|
||||
{
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
int or = nv_encoder->or, ret;
|
||||
u32 load;
|
||||
|
||||
if (drm->vbios.dactestval)
|
||||
load = drm->vbios.dactestval;
|
||||
else
|
||||
load = 340;
|
||||
|
||||
ret = nv_exec(priv->core, NV50_DISP_DAC_LOAD + or, &load, sizeof(load));
|
||||
if (ret || load != 7)
|
||||
return connector_status_disconnected;
|
||||
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
uint32_t val;
|
||||
int or = nv_encoder->or;
|
||||
|
||||
NV_DEBUG(drm, "or %d mode %d\n", or, mode);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
val = NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
|
||||
break;
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
|
||||
break;
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
val |= NV50_PDISPLAY_DAC_DPMS_CTRL_OFF;
|
||||
val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
|
||||
val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
nv_call(priv->core, NV50_DISP_DAC_PWR + or, val);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_save(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
NV_ERROR(drm, "!!\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_restore(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
NV_ERROR(drm, "!!\n");
|
||||
}
|
||||
|
||||
static bool
|
||||
nv50_dac_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_connector *connector;
|
||||
|
||||
NV_DEBUG(drm, "or %d\n", nv_encoder->or);
|
||||
|
||||
connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (!connector) {
|
||||
NV_ERROR(drm, "Encoder has no connector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
|
||||
connector->native_mode)
|
||||
drm_mode_copy(adjusted_mode, connector->native_mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
|
||||
uint32_t mode_ctl = 0, mode_ctl2 = 0;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG(drm, "or %d type %d crtc %d\n",
|
||||
nv_encoder->or, nv_encoder->dcb->type, crtc->index);
|
||||
|
||||
nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
if (crtc->index == 1)
|
||||
mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC1;
|
||||
else
|
||||
mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0;
|
||||
|
||||
/* Lacking a working tv-out, this is not a 100% sure. */
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG)
|
||||
mode_ctl |= 0x40;
|
||||
else
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_TV)
|
||||
mode_ctl |= 0x100;
|
||||
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NHSYNC;
|
||||
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NVSYNC;
|
||||
|
||||
ret = RING_SPACE(evo, 3);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while connecting DAC\n");
|
||||
return;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2);
|
||||
OUT_RING(evo, mode_ctl);
|
||||
OUT_RING(evo, mode_ctl2);
|
||||
|
||||
nv_encoder->crtc = encoder->crtc;
|
||||
}
|
||||
|
||||
static struct drm_crtc *
|
||||
nv50_dac_crtc_get(struct drm_encoder *encoder)
|
||||
{
|
||||
return nouveau_encoder(encoder)->crtc;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
|
||||
.dpms = nv50_dac_dpms,
|
||||
.save = nv50_dac_save,
|
||||
.restore = nv50_dac_restore,
|
||||
.mode_fixup = nv50_dac_mode_fixup,
|
||||
.prepare = nv50_dac_disconnect,
|
||||
.commit = nv50_dac_commit,
|
||||
.mode_set = nv50_dac_mode_set,
|
||||
.get_crtc = nv50_dac_crtc_get,
|
||||
.detect = nv50_dac_detect,
|
||||
.disable = nv50_dac_disconnect
|
||||
};
|
||||
|
||||
static void
|
||||
nv50_dac_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
if (!encoder)
|
||||
return;
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
kfree(nv_encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs nv50_dac_encoder_funcs = {
|
||||
.destroy = nv50_dac_destroy,
|
||||
};
|
||||
|
||||
int
|
||||
nv50_dac_create(struct drm_connector *connector, struct dcb_output *entry)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
||||
if (!nv_encoder)
|
||||
return -ENOMEM;
|
||||
encoder = to_drm_encoder(nv_encoder);
|
||||
|
||||
nv_encoder->dcb = entry;
|
||||
nv_encoder->or = ffs(entry->or) - 1;
|
||||
|
||||
drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs,
|
||||
DRM_MODE_ENCODER_DAC);
|
||||
drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
|
||||
|
||||
encoder->possible_crtcs = entry->heads;
|
||||
encoder->possible_clones = 0;
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,462 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
|
||||
#include "nv50_display.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include "nouveau_fence.h"
|
||||
|
||||
#include <core/gpuobj.h>
|
||||
#include <core/class.h>
|
||||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static inline int
|
||||
nv50_sor_nr(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
|
||||
if (device->chipset < 0x90 ||
|
||||
device->chipset == 0x92 ||
|
||||
device->chipset == 0xa0)
|
||||
return 2;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
u32
|
||||
nv50_display_active_crtcs(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
u32 mask = 0;
|
||||
int i;
|
||||
|
||||
if (device->chipset < 0x90 ||
|
||||
device->chipset == 0x92 ||
|
||||
device->chipset == 0xa0) {
|
||||
for (i = 0; i < 2; i++)
|
||||
mask |= nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_C(i));
|
||||
} else {
|
||||
for (i = 0; i < 4; i++)
|
||||
mask |= nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_C(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
mask |= nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_C(i));
|
||||
|
||||
return mask & 3;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_display_early_init(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nv50_display_late_takedown(struct drm_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
nv50_display_sync(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
struct nouveau_channel *evo = disp->master;
|
||||
int ret;
|
||||
|
||||
ret = RING_SPACE(evo, 6);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(evo, 0, 0x0084, 1);
|
||||
OUT_RING (evo, 0x80000000);
|
||||
BEGIN_NV04(evo, 0, 0x0080, 1);
|
||||
OUT_RING (evo, 0);
|
||||
BEGIN_NV04(evo, 0, 0x0084, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
|
||||
nv_wo32(disp->ramin, 0x2000, 0x00000000);
|
||||
FIRE_RING (evo);
|
||||
|
||||
if (nv_wait_ne(disp->ramin, 0x2000, 0xffffffff, 0x00000000))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_display_init(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_channel *evo;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
nv_wr32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 |
|
||||
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
|
||||
nv_wr32(device, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
nv_wr32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000);
|
||||
if (!nv_wait(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
||||
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
|
||||
NV_ERROR(drm, "timeout: CURSOR_CTRL2_STATUS == 0\n");
|
||||
NV_ERROR(drm, "CURSOR_CTRL2 = 0x%08x\n",
|
||||
nv_rd32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
nv_wr32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
||||
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON);
|
||||
if (!nv_wait(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
||||
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS,
|
||||
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) {
|
||||
NV_ERROR(drm, "timeout: "
|
||||
"CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i);
|
||||
NV_ERROR(drm, "CURSOR_CTRL2(%d) = 0x%08x\n", i,
|
||||
nv_rd32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nv50_evo_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
evo = nv50_display(dev)->master;
|
||||
|
||||
ret = RING_SPACE(evo, 3);
|
||||
if (ret)
|
||||
return ret;
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UNK84, 2);
|
||||
OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
|
||||
OUT_RING (evo, NvEvoSync);
|
||||
|
||||
return nv50_display_sync(dev);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_display_fini(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
struct nouveau_channel *evo = disp->master;
|
||||
struct drm_crtc *drm_crtc;
|
||||
int ret, i;
|
||||
|
||||
list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
|
||||
|
||||
nv50_crtc_blank(crtc, true);
|
||||
}
|
||||
|
||||
ret = RING_SPACE(evo, 2);
|
||||
if (ret == 0) {
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING(evo, 0);
|
||||
}
|
||||
FIRE_RING(evo);
|
||||
|
||||
/* Almost like ack'ing a vblank interrupt, maybe in the spirit of
|
||||
* cleaning up?
|
||||
*/
|
||||
list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
|
||||
struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
|
||||
uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index);
|
||||
|
||||
if (!crtc->base.enabled)
|
||||
continue;
|
||||
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_1, mask);
|
||||
if (!nv_wait(device, NV50_PDISPLAY_INTR_1, mask, mask)) {
|
||||
NV_ERROR(drm, "timeout: (0x610024 & 0x%08x) == "
|
||||
"0x%08x\n", mask, mask);
|
||||
NV_ERROR(drm, "0x610024 = 0x%08x\n",
|
||||
nv_rd32(device, NV50_PDISPLAY_INTR_1));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
nv_wr32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0);
|
||||
if (!nv_wait(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
||||
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
|
||||
NV_ERROR(drm, "timeout: CURSOR_CTRL2_STATUS == 0\n");
|
||||
NV_ERROR(drm, "CURSOR_CTRL2 = 0x%08x\n",
|
||||
nv_rd32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
|
||||
}
|
||||
}
|
||||
|
||||
nv50_evo_fini(dev);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!nv_wait(device, NV50_PDISPLAY_SOR_DPMS_STATE(i),
|
||||
NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
|
||||
NV_ERROR(drm, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i);
|
||||
NV_ERROR(drm, "SOR_DPMS_STATE(%d) = 0x%08x\n", i,
|
||||
nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_STATE(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nv50_display_create(struct drm_device *dev)
|
||||
{
|
||||
static const u16 oclass[] = {
|
||||
NVA3_DISP_CLASS,
|
||||
NV94_DISP_CLASS,
|
||||
NVA0_DISP_CLASS,
|
||||
NV84_DISP_CLASS,
|
||||
NV50_DISP_CLASS,
|
||||
};
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct dcb_table *dcb = &drm->vbios.dcb;
|
||||
struct drm_connector *connector, *ct;
|
||||
struct nv50_display *priv;
|
||||
int ret, i;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
nouveau_display(dev)->priv = priv;
|
||||
nouveau_display(dev)->dtor = nv50_display_destroy;
|
||||
nouveau_display(dev)->init = nv50_display_init;
|
||||
nouveau_display(dev)->fini = nv50_display_fini;
|
||||
|
||||
/* attempt to allocate a supported evo display class */
|
||||
ret = -ENODEV;
|
||||
for (i = 0; ret && i < ARRAY_SIZE(oclass); i++) {
|
||||
ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE,
|
||||
0xd1500000, oclass[i], NULL, 0,
|
||||
&priv->core);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Create CRTC objects */
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret = nv50_crtc_create(dev, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We setup the encoders from the BIOS table */
|
||||
for (i = 0 ; i < dcb->entries; i++) {
|
||||
struct dcb_output *entry = &dcb->entry[i];
|
||||
|
||||
if (entry->location != DCB_LOC_ON_CHIP) {
|
||||
NV_WARN(drm, "Off-chip encoder %d/%d unsupported\n",
|
||||
entry->type, ffs(entry->or) - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
connector = nouveau_connector_create(dev, entry->connector);
|
||||
if (IS_ERR(connector))
|
||||
continue;
|
||||
|
||||
switch (entry->type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
case DCB_OUTPUT_LVDS:
|
||||
case DCB_OUTPUT_DP:
|
||||
nv50_sor_create(connector, entry);
|
||||
break;
|
||||
case DCB_OUTPUT_ANALOG:
|
||||
nv50_dac_create(connector, entry);
|
||||
break;
|
||||
default:
|
||||
NV_WARN(drm, "DCB encoder %d unknown\n", entry->type);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(connector, ct,
|
||||
&dev->mode_config.connector_list, head) {
|
||||
if (!connector->encoder_ids[0]) {
|
||||
NV_WARN(drm, "%s has no encoders, removing\n",
|
||||
drm_get_connector_name(connector));
|
||||
connector->funcs->destroy(connector);
|
||||
}
|
||||
}
|
||||
|
||||
ret = nv50_evo_create(dev);
|
||||
if (ret) {
|
||||
nv50_display_destroy(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nv50_display_destroy(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
|
||||
nv50_evo_destroy(dev);
|
||||
kfree(disp);
|
||||
}
|
||||
|
||||
struct nouveau_bo *
|
||||
nv50_display_crtc_sema(struct drm_device *dev, int crtc)
|
||||
{
|
||||
return nv50_display(dev)->crtc[crtc].sem.bo;
|
||||
}
|
||||
|
||||
void
|
||||
nv50_display_flip_stop(struct drm_crtc *crtc)
|
||||
{
|
||||
struct nv50_display *disp = nv50_display(crtc->dev);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index];
|
||||
struct nouveau_channel *evo = dispc->sync;
|
||||
int ret;
|
||||
|
||||
ret = RING_SPACE(evo, 8);
|
||||
if (ret) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
BEGIN_NV04(evo, 0, 0x0084, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
BEGIN_NV04(evo, 0, 0x0094, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
BEGIN_NV04(evo, 0, 0x00c0, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
BEGIN_NV04(evo, 0, 0x0080, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
FIRE_RING (evo);
|
||||
}
|
||||
|
||||
int
|
||||
nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
||||
struct nouveau_channel *chan)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(crtc->dev);
|
||||
struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
|
||||
struct nv50_display *disp = nv50_display(crtc->dev);
|
||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||
struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index];
|
||||
struct nouveau_channel *evo = dispc->sync;
|
||||
int ret;
|
||||
|
||||
ret = RING_SPACE(evo, chan ? 25 : 27);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
/* synchronise with the rendering channel, if necessary */
|
||||
if (likely(chan)) {
|
||||
ret = RING_SPACE(chan, 10);
|
||||
if (ret) {
|
||||
WIND_RING(evo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (nv_device(drm->device)->chipset < 0xc0) {
|
||||
BEGIN_NV04(chan, 0, 0x0060, 2);
|
||||
OUT_RING (chan, NvEvoSema0 + nv_crtc->index);
|
||||
OUT_RING (chan, dispc->sem.offset);
|
||||
BEGIN_NV04(chan, 0, 0x006c, 1);
|
||||
OUT_RING (chan, 0xf00d0000 | dispc->sem.value);
|
||||
BEGIN_NV04(chan, 0, 0x0064, 2);
|
||||
OUT_RING (chan, dispc->sem.offset ^ 0x10);
|
||||
OUT_RING (chan, 0x74b1e000);
|
||||
BEGIN_NV04(chan, 0, 0x0060, 1);
|
||||
if (nv_device(drm->device)->chipset < 0x84)
|
||||
OUT_RING (chan, NvSema);
|
||||
else
|
||||
OUT_RING (chan, chan->vram);
|
||||
} else {
|
||||
u64 offset = nvc0_fence_crtc(chan, nv_crtc->index);
|
||||
offset += dispc->sem.offset;
|
||||
BEGIN_NVC0(chan, 0, 0x0010, 4);
|
||||
OUT_RING (chan, upper_32_bits(offset));
|
||||
OUT_RING (chan, lower_32_bits(offset));
|
||||
OUT_RING (chan, 0xf00d0000 | dispc->sem.value);
|
||||
OUT_RING (chan, 0x1002);
|
||||
BEGIN_NVC0(chan, 0, 0x0010, 4);
|
||||
OUT_RING (chan, upper_32_bits(offset));
|
||||
OUT_RING (chan, lower_32_bits(offset ^ 0x10));
|
||||
OUT_RING (chan, 0x74b1e000);
|
||||
OUT_RING (chan, 0x1001);
|
||||
}
|
||||
FIRE_RING (chan);
|
||||
} else {
|
||||
nouveau_bo_wr32(dispc->sem.bo, dispc->sem.offset / 4,
|
||||
0xf00d0000 | dispc->sem.value);
|
||||
}
|
||||
|
||||
/* queue the flip on the crtc's "display sync" channel */
|
||||
BEGIN_NV04(evo, 0, 0x0100, 1);
|
||||
OUT_RING (evo, 0xfffe0000);
|
||||
if (chan) {
|
||||
BEGIN_NV04(evo, 0, 0x0084, 1);
|
||||
OUT_RING (evo, 0x00000100);
|
||||
} else {
|
||||
BEGIN_NV04(evo, 0, 0x0084, 1);
|
||||
OUT_RING (evo, 0x00000010);
|
||||
/* allows gamma somehow, PDISP will bitch at you if
|
||||
* you don't wait for vblank before changing this..
|
||||
*/
|
||||
BEGIN_NV04(evo, 0, 0x00e0, 1);
|
||||
OUT_RING (evo, 0x40000000);
|
||||
}
|
||||
BEGIN_NV04(evo, 0, 0x0088, 4);
|
||||
OUT_RING (evo, dispc->sem.offset);
|
||||
OUT_RING (evo, 0xf00d0000 | dispc->sem.value);
|
||||
OUT_RING (evo, 0x74b1e000);
|
||||
OUT_RING (evo, NvEvoSync);
|
||||
BEGIN_NV04(evo, 0, 0x00a0, 2);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
BEGIN_NV04(evo, 0, 0x00c0, 1);
|
||||
OUT_RING (evo, nv_fb->r_dma);
|
||||
BEGIN_NV04(evo, 0, 0x0110, 2);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
BEGIN_NV04(evo, 0, 0x0800, 5);
|
||||
OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8);
|
||||
OUT_RING (evo, 0);
|
||||
OUT_RING (evo, (fb->height << 16) | fb->width);
|
||||
OUT_RING (evo, nv_fb->r_pitch);
|
||||
OUT_RING (evo, nv_fb->r_format);
|
||||
BEGIN_NV04(evo, 0, 0x0080, 1);
|
||||
OUT_RING (evo, 0x00000000);
|
||||
FIRE_RING (evo);
|
||||
|
||||
dispc->sem.offset ^= 0x10;
|
||||
dispc->sem.value++;
|
||||
return 0;
|
||||
}
|
@ -30,65 +30,6 @@
|
||||
#include "nouveau_display.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nouveau_reg.h"
|
||||
#include "nv50_evo.h"
|
||||
|
||||
struct nv50_display_crtc {
|
||||
struct nouveau_channel *sync;
|
||||
struct {
|
||||
struct nouveau_bo *bo;
|
||||
u32 offset;
|
||||
u16 value;
|
||||
} sem;
|
||||
};
|
||||
|
||||
struct nv50_display {
|
||||
struct nouveau_channel *master;
|
||||
|
||||
struct nouveau_object *core;
|
||||
struct nouveau_gpuobj *ramin;
|
||||
u32 dmao;
|
||||
u32 hash;
|
||||
|
||||
struct nv50_display_crtc crtc[2];
|
||||
|
||||
struct tasklet_struct tasklet;
|
||||
struct {
|
||||
struct dcb_output *dcb;
|
||||
u16 script;
|
||||
u32 pclk;
|
||||
} irq;
|
||||
};
|
||||
|
||||
static inline struct nv50_display *
|
||||
nv50_display(struct drm_device *dev)
|
||||
{
|
||||
return nouveau_display(dev)->priv;
|
||||
}
|
||||
|
||||
int nv50_display_early_init(struct drm_device *dev);
|
||||
void nv50_display_late_takedown(struct drm_device *dev);
|
||||
int nv50_display_create(struct drm_device *dev);
|
||||
int nv50_display_init(struct drm_device *dev);
|
||||
void nv50_display_fini(struct drm_device *dev);
|
||||
void nv50_display_destroy(struct drm_device *dev);
|
||||
int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
|
||||
int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
|
||||
|
||||
u32 nv50_display_active_crtcs(struct drm_device *);
|
||||
|
||||
int nv50_display_sync(struct drm_device *);
|
||||
int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
|
||||
struct nouveau_channel *chan);
|
||||
void nv50_display_flip_stop(struct drm_crtc *);
|
||||
|
||||
int nv50_evo_create(struct drm_device *dev);
|
||||
void nv50_evo_destroy(struct drm_device *dev);
|
||||
int nv50_evo_init(struct drm_device *dev);
|
||||
void nv50_evo_fini(struct drm_device *dev);
|
||||
void nv50_evo_dmaobj_init(struct nouveau_gpuobj *, u32 memtype, u64 base,
|
||||
u64 size);
|
||||
int nv50_evo_dmaobj_new(struct nouveau_channel *, u32 handle, u32 memtype,
|
||||
u64 base, u64 size, struct nouveau_gpuobj **);
|
||||
|
||||
int nvd0_display_create(struct drm_device *);
|
||||
void nvd0_display_destroy(struct drm_device *);
|
||||
@ -99,7 +40,6 @@ void nvd0_display_flip_stop(struct drm_crtc *);
|
||||
int nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
|
||||
struct nouveau_channel *, u32 swap_interval);
|
||||
|
||||
struct nouveau_bo *nv50_display_crtc_sema(struct drm_device *, int head);
|
||||
struct nouveau_bo *nvd0_display_crtc_sema(struct drm_device *, int head);
|
||||
|
||||
#endif /* __NV50_DISPLAY_H__ */
|
||||
|
@ -1,396 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
#include <core/gpuobj.h>
|
||||
|
||||
#include <subdev/timer.h>
|
||||
#include <subdev/fb.h>
|
||||
|
||||
static u32
|
||||
nv50_evo_rd32(struct nouveau_object *object, u64 addr)
|
||||
{
|
||||
void __iomem *iomem = object->oclass->ofuncs->rd08;
|
||||
return ioread32_native(iomem + addr);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_evo_wr32(struct nouveau_object *object, u64 addr, u32 data)
|
||||
{
|
||||
void __iomem *iomem = object->oclass->ofuncs->rd08;
|
||||
iowrite32_native(data, iomem + addr);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_evo_channel_del(struct nouveau_channel **pevo)
|
||||
{
|
||||
struct nouveau_channel *evo = *pevo;
|
||||
|
||||
if (!evo)
|
||||
return;
|
||||
*pevo = NULL;
|
||||
|
||||
nouveau_bo_unmap(evo->push.buffer);
|
||||
nouveau_bo_ref(NULL, &evo->push.buffer);
|
||||
|
||||
if (evo->object)
|
||||
iounmap(evo->object->oclass->ofuncs);
|
||||
|
||||
kfree(evo);
|
||||
}
|
||||
|
||||
int
|
||||
nv50_evo_dmaobj_new(struct nouveau_channel *evo, u32 handle, u32 memtype,
|
||||
u64 base, u64 size, struct nouveau_gpuobj **pobj)
|
||||
{
|
||||
struct drm_device *dev = evo->fence;
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
u32 dmao = disp->dmao;
|
||||
u32 hash = disp->hash;
|
||||
u32 flags5;
|
||||
|
||||
if (nv_device(drm->device)->chipset < 0xc0) {
|
||||
/* not supported on 0x50, specified in format mthd */
|
||||
if (nv_device(drm->device)->chipset == 0x50)
|
||||
memtype = 0;
|
||||
flags5 = 0x00010000;
|
||||
} else {
|
||||
if (memtype & 0x80000000)
|
||||
flags5 = 0x00000000; /* large pages */
|
||||
else
|
||||
flags5 = 0x00020000;
|
||||
}
|
||||
|
||||
nv_wo32(disp->ramin, dmao + 0x00, 0x0019003d | (memtype << 22));
|
||||
nv_wo32(disp->ramin, dmao + 0x04, lower_32_bits(base + size - 1));
|
||||
nv_wo32(disp->ramin, dmao + 0x08, lower_32_bits(base));
|
||||
nv_wo32(disp->ramin, dmao + 0x0c, upper_32_bits(base + size - 1) << 24 |
|
||||
upper_32_bits(base));
|
||||
nv_wo32(disp->ramin, dmao + 0x10, 0x00000000);
|
||||
nv_wo32(disp->ramin, dmao + 0x14, flags5);
|
||||
|
||||
nv_wo32(disp->ramin, hash + 0x00, handle);
|
||||
nv_wo32(disp->ramin, hash + 0x04, (evo->handle << 28) | (dmao << 10) |
|
||||
evo->handle);
|
||||
|
||||
disp->dmao += 0x20;
|
||||
disp->hash += 0x08;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_evo_channel_new(struct drm_device *dev, int chid,
|
||||
struct nouveau_channel **pevo)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
struct nouveau_channel *evo;
|
||||
int ret;
|
||||
|
||||
evo = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL);
|
||||
if (!evo)
|
||||
return -ENOMEM;
|
||||
*pevo = evo;
|
||||
|
||||
evo->drm = drm;
|
||||
evo->handle = chid;
|
||||
evo->fence = dev;
|
||||
evo->user_get = 4;
|
||||
evo->user_put = 0;
|
||||
|
||||
ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, NULL,
|
||||
&evo->push.buffer);
|
||||
if (ret == 0)
|
||||
ret = nouveau_bo_pin(evo->push.buffer, TTM_PL_FLAG_VRAM);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "Error creating EVO DMA push buffer: %d\n", ret);
|
||||
nv50_evo_channel_del(pevo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nouveau_bo_map(evo->push.buffer);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "Error mapping EVO DMA push buffer: %d\n", ret);
|
||||
nv50_evo_channel_del(pevo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
evo->object = kzalloc(sizeof(*evo->object), GFP_KERNEL);
|
||||
#ifdef NOUVEAU_OBJECT_MAGIC
|
||||
evo->object->_magic = NOUVEAU_OBJECT_MAGIC;
|
||||
#endif
|
||||
evo->object->parent = nv_object(disp->ramin)->parent;
|
||||
evo->object->engine = nv_object(disp->ramin)->engine;
|
||||
evo->object->oclass =
|
||||
kzalloc(sizeof(*evo->object->oclass), GFP_KERNEL);
|
||||
evo->object->oclass->ofuncs =
|
||||
kzalloc(sizeof(*evo->object->oclass->ofuncs), GFP_KERNEL);
|
||||
evo->object->oclass->ofuncs->rd32 = nv50_evo_rd32;
|
||||
evo->object->oclass->ofuncs->wr32 = nv50_evo_wr32;
|
||||
evo->object->oclass->ofuncs->rd08 =
|
||||
ioremap(pci_resource_start(dev->pdev, 0) +
|
||||
NV50_PDISPLAY_USER(evo->handle), PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_evo_channel_init(struct nouveau_channel *evo)
|
||||
{
|
||||
struct nouveau_drm *drm = evo->drm;
|
||||
struct nouveau_device *device = nv_device(drm->device);
|
||||
int id = evo->handle, ret, i;
|
||||
u64 pushbuf = evo->push.buffer->bo.offset;
|
||||
u32 tmp;
|
||||
|
||||
tmp = nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id));
|
||||
if ((tmp & 0x009f0000) == 0x00020000)
|
||||
nv_wr32(device, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00800000);
|
||||
|
||||
tmp = nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id));
|
||||
if ((tmp & 0x003f0000) == 0x00030000)
|
||||
nv_wr32(device, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00600000);
|
||||
|
||||
/* initialise fifo */
|
||||
nv_wr32(device, NV50_PDISPLAY_EVO_DMA_CB(id), pushbuf >> 8 |
|
||||
NV50_PDISPLAY_EVO_DMA_CB_LOCATION_VRAM |
|
||||
NV50_PDISPLAY_EVO_DMA_CB_VALID);
|
||||
nv_wr32(device, NV50_PDISPLAY_EVO_UNK2(id), 0x00010000);
|
||||
nv_wr32(device, NV50_PDISPLAY_EVO_HASH_TAG(id), id);
|
||||
nv_mask(device, NV50_PDISPLAY_EVO_CTRL(id), NV50_PDISPLAY_EVO_CTRL_DMA,
|
||||
NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED);
|
||||
|
||||
nv_wr32(device, NV50_PDISPLAY_USER_PUT(id), 0x00000000);
|
||||
nv_wr32(device, NV50_PDISPLAY_EVO_CTRL(id), 0x01000003 |
|
||||
NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED);
|
||||
if (!nv_wait(device, NV50_PDISPLAY_EVO_CTRL(id), 0x80000000, 0x00000000)) {
|
||||
NV_ERROR(drm, "EvoCh %d init timeout: 0x%08x\n", id,
|
||||
nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id)));
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* enable error reporting on the channel */
|
||||
nv_mask(device, 0x610028, 0x00000000, 0x00010001 << id);
|
||||
|
||||
evo->dma.max = (4096/4) - 2;
|
||||
evo->dma.max &= ~7;
|
||||
evo->dma.put = 0;
|
||||
evo->dma.cur = evo->dma.put;
|
||||
evo->dma.free = evo->dma.max - evo->dma.cur;
|
||||
|
||||
ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
|
||||
OUT_RING(evo, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_evo_channel_fini(struct nouveau_channel *evo)
|
||||
{
|
||||
struct nouveau_drm *drm = evo->drm;
|
||||
struct nouveau_device *device = nv_device(drm->device);
|
||||
int id = evo->handle;
|
||||
|
||||
nv_mask(device, 0x610028, 0x00010001 << id, 0x00000000);
|
||||
nv_mask(device, NV50_PDISPLAY_EVO_CTRL(id), 0x00001010, 0x00001000);
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_0, (1 << id));
|
||||
nv_mask(device, NV50_PDISPLAY_EVO_CTRL(id), 0x00000003, 0x00000000);
|
||||
if (!nv_wait(device, NV50_PDISPLAY_EVO_CTRL(id), 0x001e0000, 0x00000000)) {
|
||||
NV_ERROR(drm, "EvoCh %d takedown timeout: 0x%08x\n", id,
|
||||
nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nv50_evo_destroy(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (disp->crtc[i].sem.bo) {
|
||||
nouveau_bo_unmap(disp->crtc[i].sem.bo);
|
||||
nouveau_bo_ref(NULL, &disp->crtc[i].sem.bo);
|
||||
}
|
||||
nv50_evo_channel_del(&disp->crtc[i].sync);
|
||||
}
|
||||
nv50_evo_channel_del(&disp->master);
|
||||
}
|
||||
|
||||
int
|
||||
nv50_evo_create(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_fb *pfb = nouveau_fb(drm->device);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
struct nouveau_channel *evo;
|
||||
int ret, i, j;
|
||||
|
||||
/* setup object management on it, any other evo channel will
|
||||
* use this also as there's no per-channel support on the
|
||||
* hardware
|
||||
*/
|
||||
disp->ramin = nv_gpuobj(disp->core->parent);
|
||||
disp->hash = 0x0000;
|
||||
disp->dmao = 0x1000;
|
||||
|
||||
/* create primary evo channel, the one we use for modesetting
|
||||
* purporses
|
||||
*/
|
||||
ret = nv50_evo_channel_new(dev, 0, &disp->master);
|
||||
if (ret)
|
||||
return ret;
|
||||
evo = disp->master;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(disp->master, NvEvoSync, 0x0000,
|
||||
disp->ramin->addr + 0x2000, 0x1000, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* create some default objects for the scanout memtypes we support */
|
||||
ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM, 0x0000,
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM_LP, 0x80000000,
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB32, 0x80000000 |
|
||||
(nv_device(drm->device)->chipset < 0xc0 ? 0x7a : 0xfe),
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB16, 0x80000000 |
|
||||
(nv_device(drm->device)->chipset < 0xc0 ? 0x70 : 0xfe),
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* create "display sync" channels and other structures we need
|
||||
* to implement page flipping
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct nv50_display_crtc *dispc = &disp->crtc[i];
|
||||
u64 offset;
|
||||
|
||||
ret = nv50_evo_channel_new(dev, 1 + i, &dispc->sync);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
|
||||
0, 0x0000, NULL, &dispc->sem.bo);
|
||||
if (!ret) {
|
||||
ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM);
|
||||
if (!ret)
|
||||
ret = nouveau_bo_map(dispc->sem.bo);
|
||||
if (ret)
|
||||
nouveau_bo_ref(NULL, &dispc->sem.bo);
|
||||
offset = dispc->sem.bo->bo.offset;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoSync, 0x0000,
|
||||
offset, 4096, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoVRAM_LP, 0x80000000,
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB32, 0x80000000 |
|
||||
(nv_device(drm->device)->chipset < 0xc0 ?
|
||||
0x7a : 0xfe),
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB16, 0x80000000 |
|
||||
(nv_device(drm->device)->chipset < 0xc0 ?
|
||||
0x70 : 0xfe),
|
||||
0, pfb->ram.size, NULL);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
for (j = 0; j < 4096; j += 4)
|
||||
nouveau_bo_wr32(dispc->sem.bo, j / 4, 0x74b1e000);
|
||||
dispc->sem.offset = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
nv50_evo_destroy(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_evo_init(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
int ret, i;
|
||||
|
||||
ret = nv50_evo_channel_init(disp->master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret = nv50_evo_channel_init(disp->crtc[i].sync);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nv50_evo_fini(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (disp->crtc[i].sync)
|
||||
nv50_evo_channel_fini(disp->crtc[i].sync);
|
||||
}
|
||||
|
||||
if (disp->master)
|
||||
nv50_evo_channel_fini(disp->master);
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NV50_EVO_H__
|
||||
#define __NV50_EVO_H__
|
||||
|
||||
#define NV50_EVO_UPDATE 0x00000080
|
||||
#define NV50_EVO_UNK84 0x00000084
|
||||
#define NV50_EVO_UNK84_NOTIFY 0x40000000
|
||||
#define NV50_EVO_UNK84_NOTIFY_DISABLED 0x00000000
|
||||
#define NV50_EVO_UNK84_NOTIFY_ENABLED 0x40000000
|
||||
#define NV50_EVO_DMA_NOTIFY 0x00000088
|
||||
#define NV50_EVO_DMA_NOTIFY_HANDLE 0xffffffff
|
||||
#define NV50_EVO_DMA_NOTIFY_HANDLE_NONE 0x00000000
|
||||
#define NV50_EVO_UNK8C 0x0000008C
|
||||
|
||||
#define NV50_EVO_DAC(n, r) ((n) * 0x80 + NV50_EVO_DAC_##r)
|
||||
#define NV50_EVO_DAC_MODE_CTRL 0x00000400
|
||||
#define NV50_EVO_DAC_MODE_CTRL_CRTC0 0x00000001
|
||||
#define NV50_EVO_DAC_MODE_CTRL_CRTC1 0x00000002
|
||||
#define NV50_EVO_DAC_MODE_CTRL2 0x00000404
|
||||
#define NV50_EVO_DAC_MODE_CTRL2_NHSYNC 0x00000001
|
||||
#define NV50_EVO_DAC_MODE_CTRL2_NVSYNC 0x00000002
|
||||
|
||||
#define NV50_EVO_SOR(n, r) ((n) * 0x40 + NV50_EVO_SOR_##r)
|
||||
#define NV50_EVO_SOR_MODE_CTRL 0x00000600
|
||||
#define NV50_EVO_SOR_MODE_CTRL_CRTC0 0x00000001
|
||||
#define NV50_EVO_SOR_MODE_CTRL_CRTC1 0x00000002
|
||||
#define NV50_EVO_SOR_MODE_CTRL_TMDS 0x00000100
|
||||
#define NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK 0x00000400
|
||||
#define NV50_EVO_SOR_MODE_CTRL_NHSYNC 0x00001000
|
||||
#define NV50_EVO_SOR_MODE_CTRL_NVSYNC 0x00002000
|
||||
|
||||
#define NV50_EVO_CRTC(n, r) ((n) * 0x400 + NV50_EVO_CRTC_##r)
|
||||
#define NV84_EVO_CRTC(n, r) ((n) * 0x400 + NV84_EVO_CRTC_##r)
|
||||
#define NV50_EVO_CRTC_UNK0800 0x00000800
|
||||
#define NV50_EVO_CRTC_CLOCK 0x00000804
|
||||
#define NV50_EVO_CRTC_INTERLACE 0x00000808
|
||||
#define NV50_EVO_CRTC_DISPLAY_START 0x00000810
|
||||
#define NV50_EVO_CRTC_DISPLAY_TOTAL 0x00000814
|
||||
#define NV50_EVO_CRTC_SYNC_DURATION 0x00000818
|
||||
#define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END 0x0000081c
|
||||
#define NV50_EVO_CRTC_UNK0820 0x00000820
|
||||
#define NV50_EVO_CRTC_UNK0824 0x00000824
|
||||
#define NV50_EVO_CRTC_UNK082C 0x0000082c
|
||||
#define NV50_EVO_CRTC_CLUT_MODE 0x00000840
|
||||
/* You can't have a palette in 8 bit mode (=OFF) */
|
||||
#define NV50_EVO_CRTC_CLUT_MODE_BLANK 0x00000000
|
||||
#define NV50_EVO_CRTC_CLUT_MODE_OFF 0x80000000
|
||||
#define NV50_EVO_CRTC_CLUT_MODE_ON 0xC0000000
|
||||
#define NV50_EVO_CRTC_CLUT_OFFSET 0x00000844
|
||||
#define NV84_EVO_CRTC_CLUT_DMA 0x0000085C
|
||||
#define NV84_EVO_CRTC_CLUT_DMA_HANDLE 0xffffffff
|
||||
#define NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE 0x00000000
|
||||
#define NV50_EVO_CRTC_FB_OFFSET 0x00000860
|
||||
#define NV50_EVO_CRTC_FB_SIZE 0x00000868
|
||||
#define NV50_EVO_CRTC_FB_CONFIG 0x0000086c
|
||||
#define NV50_EVO_CRTC_FB_CONFIG_MODE 0x00100000
|
||||
#define NV50_EVO_CRTC_FB_CONFIG_MODE_TILE 0x00000000
|
||||
#define NV50_EVO_CRTC_FB_CONFIG_MODE_PITCH 0x00100000
|
||||
#define NV50_EVO_CRTC_FB_DEPTH 0x00000870
|
||||
#define NV50_EVO_CRTC_FB_DEPTH_8 0x00001e00
|
||||
#define NV50_EVO_CRTC_FB_DEPTH_15 0x0000e900
|
||||
#define NV50_EVO_CRTC_FB_DEPTH_16 0x0000e800
|
||||
#define NV50_EVO_CRTC_FB_DEPTH_24 0x0000cf00
|
||||
#define NV50_EVO_CRTC_FB_DEPTH_30 0x0000d100
|
||||
#define NV50_EVO_CRTC_FB_DMA 0x00000874
|
||||
#define NV50_EVO_CRTC_FB_DMA_HANDLE 0xffffffff
|
||||
#define NV50_EVO_CRTC_FB_DMA_HANDLE_NONE 0x00000000
|
||||
#define NV50_EVO_CRTC_CURSOR_CTRL 0x00000880
|
||||
#define NV50_EVO_CRTC_CURSOR_CTRL_HIDE 0x05000000
|
||||
#define NV50_EVO_CRTC_CURSOR_CTRL_SHOW 0x85000000
|
||||
#define NV50_EVO_CRTC_CURSOR_OFFSET 0x00000884
|
||||
#define NV84_EVO_CRTC_CURSOR_DMA 0x0000089c
|
||||
#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE 0xffffffff
|
||||
#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE 0x00000000
|
||||
#define NV50_EVO_CRTC_DITHER_CTRL 0x000008a0
|
||||
#define NV50_EVO_CRTC_DITHER_CTRL_OFF 0x00000000
|
||||
#define NV50_EVO_CRTC_DITHER_CTRL_ON 0x00000011
|
||||
#define NV50_EVO_CRTC_SCALE_CTRL 0x000008a4
|
||||
#define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000
|
||||
#define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009
|
||||
#define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8
|
||||
#define NV50_EVO_CRTC_COLOR_CTRL_VIBRANCE 0x000fff00
|
||||
#define NV50_EVO_CRTC_COLOR_CTRL_HUE 0xfff00000
|
||||
#define NV50_EVO_CRTC_FB_POS 0x000008c0
|
||||
#define NV50_EVO_CRTC_REAL_RES 0x000008c8
|
||||
#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4
|
||||
#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) \
|
||||
((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF))
|
||||
/* Both of these are needed, otherwise nothing happens. */
|
||||
#define NV50_EVO_CRTC_SCALE_RES1 0x000008d8
|
||||
#define NV50_EVO_CRTC_SCALE_RES2 0x000008dc
|
||||
#define NV50_EVO_CRTC_UNK900 0x00000900
|
||||
#define NV50_EVO_CRTC_UNK904 0x00000904
|
||||
|
||||
#endif
|
@ -70,7 +70,7 @@ nv50_fence_context_new(struct nouveau_channel *chan)
|
||||
|
||||
/* dma objects for display sync channel semaphore blocks */
|
||||
for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
|
||||
struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i);
|
||||
|
||||
ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
|
||||
NvEvoSema0 + i, 0x003d,
|
||||
|
@ -546,7 +546,7 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
u32 crtc_mask = nv50_display_active_crtcs(dev);
|
||||
u32 crtc_mask = 0; /*XXX: nv50_display_active_crtcs(dev); */
|
||||
struct nouveau_mem_exec_func exec = {
|
||||
.dev = dev,
|
||||
.precharge = mclk_precharge,
|
||||
|
@ -1,339 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
|
||||
#include "nouveau_reg.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
#include "nouveau_encoder.h"
|
||||
#include "nouveau_connector.h"
|
||||
#include "nouveau_crtc.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
#include <core/class.h>
|
||||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void
|
||||
nv50_sor_disconnect(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
int ret;
|
||||
|
||||
if (!nv_encoder->crtc)
|
||||
return;
|
||||
nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true);
|
||||
|
||||
NV_DEBUG(drm, "Disconnecting SOR %d\n", nv_encoder->or);
|
||||
|
||||
ret = RING_SPACE(evo, 4);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while disconnecting SOR\n");
|
||||
return;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
|
||||
OUT_RING (evo, 0);
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1);
|
||||
OUT_RING (evo, 0);
|
||||
|
||||
nouveau_hdmi_mode_set(encoder, NULL);
|
||||
|
||||
nv_encoder->crtc = NULL;
|
||||
nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct nv50_display *priv = nv50_display(encoder->dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct drm_encoder *enc;
|
||||
int or = nv_encoder->or;
|
||||
|
||||
NV_DEBUG(drm, "or %d type %d mode %d\n", or, nv_encoder->dcb->type, mode);
|
||||
|
||||
nv_encoder->last_dpms = mode;
|
||||
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
|
||||
struct nouveau_encoder *nvenc = nouveau_encoder(enc);
|
||||
|
||||
if (nvenc == nv_encoder ||
|
||||
(nvenc->dcb->type != DCB_OUTPUT_TMDS &&
|
||||
nvenc->dcb->type != DCB_OUTPUT_LVDS &&
|
||||
nvenc->dcb->type != DCB_OUTPUT_DP) ||
|
||||
nvenc->dcb->or != nv_encoder->dcb->or)
|
||||
continue;
|
||||
|
||||
if (nvenc->last_dpms == DRM_MODE_DPMS_ON)
|
||||
return;
|
||||
}
|
||||
|
||||
nv_call(priv->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON));
|
||||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
|
||||
nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, priv->core);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_save(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
NV_ERROR(drm, "!!\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_restore(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
NV_ERROR(drm, "!!\n");
|
||||
}
|
||||
|
||||
static bool
|
||||
nv50_sor_mode_fixup(struct drm_encoder *encoder,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_connector *connector;
|
||||
|
||||
NV_DEBUG(drm, "or %d\n", nv_encoder->or);
|
||||
|
||||
connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (!connector) {
|
||||
NV_ERROR(drm, "Encoder has no connector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
|
||||
connector->native_mode)
|
||||
drm_mode_copy(adjusted_mode, connector->native_mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
nv50_sor_disconnect(encoder);
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
|
||||
/* avoid race between link training and supervisor intr */
|
||||
nv50_display_sync(encoder->dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct nouveau_channel *evo = nv50_display(encoder->dev)->master;
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
|
||||
struct nouveau_connector *nv_connector;
|
||||
struct nv50_display *disp = nv50_display(encoder->dev);
|
||||
struct nvbios *bios = &drm->vbios;
|
||||
uint32_t mode_ctl = 0, script;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG(drm, "or %d type %d -> crtc %d\n",
|
||||
nv_encoder->or, nv_encoder->dcb->type, crtc->index);
|
||||
nv_encoder->crtc = encoder->crtc;
|
||||
|
||||
switch (nv_encoder->dcb->type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
if (nv_encoder->dcb->sorconf.link & 1) {
|
||||
if (mode->clock < 165000)
|
||||
mode_ctl = 0x0100;
|
||||
else
|
||||
mode_ctl = 0x0500;
|
||||
} else
|
||||
mode_ctl = 0x0200;
|
||||
|
||||
nouveau_hdmi_mode_set(encoder, mode);
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
script = 0x0000;
|
||||
if (bios->fp_no_ddc) {
|
||||
if (bios->fp.dual_link)
|
||||
script |= 0x0100;
|
||||
if (bios->fp.if_is_24bit)
|
||||
script |= 0x0200;
|
||||
} else {
|
||||
/* determine number of lvds links */
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (nv_connector && nv_connector->edid &&
|
||||
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
|
||||
/* http://www.spwg.org */
|
||||
if (((u8 *)nv_connector->edid)[121] == 2)
|
||||
script |= 0x0100;
|
||||
} else
|
||||
if (mode->clock >= bios->fp.duallink_transition_clk) {
|
||||
script |= 0x0100;
|
||||
}
|
||||
|
||||
/* determine panel depth */
|
||||
if (script & 0x0100) {
|
||||
if (bios->fp.strapless_is_24bit & 2)
|
||||
script |= 0x0200;
|
||||
} else {
|
||||
if (bios->fp.strapless_is_24bit & 1)
|
||||
script |= 0x0200;
|
||||
}
|
||||
|
||||
if (nv_connector && nv_connector->edid &&
|
||||
(nv_connector->edid->revision >= 4) &&
|
||||
(nv_connector->edid->input & 0x70) >= 0x20)
|
||||
script |= 0x0200;
|
||||
}
|
||||
|
||||
nv_call(disp->core, NV50_DISP_SOR_LVDS_SCRIPT + nv_encoder->or, script);
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (nv_connector && nv_connector->base.display_info.bpc == 6) {
|
||||
nv_encoder->dp.datarate = mode->clock * 18 / 8;
|
||||
mode_ctl |= 0x00020000;
|
||||
} else {
|
||||
nv_encoder->dp.datarate = mode->clock * 24 / 8;
|
||||
mode_ctl |= 0x00050000;
|
||||
}
|
||||
|
||||
if (nv_encoder->dcb->sorconf.link & 1)
|
||||
mode_ctl |= 0x00000800;
|
||||
else
|
||||
mode_ctl |= 0x00000900;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (crtc->index == 1)
|
||||
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1;
|
||||
else
|
||||
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
|
||||
|
||||
nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
ret = RING_SPACE(evo, 2);
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "no space while connecting SOR\n");
|
||||
nv_encoder->crtc = NULL;
|
||||
return;
|
||||
}
|
||||
BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
|
||||
OUT_RING(evo, mode_ctl);
|
||||
}
|
||||
|
||||
static struct drm_crtc *
|
||||
nv50_sor_crtc_get(struct drm_encoder *encoder)
|
||||
{
|
||||
return nouveau_encoder(encoder)->crtc;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
|
||||
.dpms = nv50_sor_dpms,
|
||||
.save = nv50_sor_save,
|
||||
.restore = nv50_sor_restore,
|
||||
.mode_fixup = nv50_sor_mode_fixup,
|
||||
.prepare = nv50_sor_prepare,
|
||||
.commit = nv50_sor_commit,
|
||||
.mode_set = nv50_sor_mode_set,
|
||||
.get_crtc = nv50_sor_crtc_get,
|
||||
.detect = NULL,
|
||||
.disable = nv50_sor_disconnect
|
||||
};
|
||||
|
||||
static void
|
||||
nv50_sor_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
kfree(nv_encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
|
||||
.destroy = nv50_sor_destroy,
|
||||
};
|
||||
|
||||
int
|
||||
nv50_sor_create(struct drm_connector *connector, struct dcb_output *entry)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = NULL;
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_encoder *encoder;
|
||||
int type;
|
||||
|
||||
switch (entry->type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
case DCB_OUTPUT_DP:
|
||||
type = DRM_MODE_ENCODER_TMDS;
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
type = DRM_MODE_ENCODER_LVDS;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
||||
if (!nv_encoder)
|
||||
return -ENOMEM;
|
||||
encoder = to_drm_encoder(nv_encoder);
|
||||
|
||||
nv_encoder->dcb = entry;
|
||||
nv_encoder->or = ffs(entry->or) - 1;
|
||||
nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type);
|
||||
drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs);
|
||||
|
||||
encoder->possible_crtcs = entry->heads;
|
||||
encoder->possible_clones = 0;
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
return 0;
|
||||
}
|
@ -127,7 +127,7 @@ nv84_fence_context_new(struct nouveau_channel *chan)
|
||||
|
||||
/* dma objects for display sync channel semaphore blocks */
|
||||
for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
|
||||
struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i);
|
||||
|
||||
ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
|
||||
NvEvoSema0 + i, 0x003d,
|
||||
|
@ -114,17 +114,9 @@ nvc0_fence_context_del(struct nouveau_channel *chan)
|
||||
struct nvc0_fence_chan *fctx = chan->fence;
|
||||
int i;
|
||||
|
||||
if (nv_device(chan->drm->device)->card_type >= NV_D0) {
|
||||
for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i);
|
||||
nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
|
||||
}
|
||||
} else
|
||||
if (nv_device(chan->drm->device)->card_type >= NV_50) {
|
||||
for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i);
|
||||
nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
|
||||
}
|
||||
for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i);
|
||||
nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
|
||||
}
|
||||
|
||||
nouveau_bo_vma_del(priv->bo, &fctx->vma);
|
||||
@ -154,12 +146,7 @@ nvc0_fence_context_new(struct nouveau_channel *chan)
|
||||
|
||||
/* map display semaphore buffers into channel's vm */
|
||||
for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_bo *bo;
|
||||
if (nv_device(chan->drm->device)->card_type >= NV_D0)
|
||||
bo = nvd0_display_crtc_sema(chan->drm->dev, i);
|
||||
else
|
||||
bo = nv50_display_crtc_sema(chan->drm->dev, i);
|
||||
|
||||
struct nouveau_bo *bo = nvd0_display_crtc_sema(chan->drm->dev, i);
|
||||
ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user