Commit 119608a7 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/kms/nv50-: handle degamma LUT from window channels

Required to eventually support DRM colour management APIs, and to
support Volta.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent e349a05d
nouveau-y += dispnv50/disp.o
nouveau-y += dispnv50/lut.o
nouveau-y += dispnv50/core.o
nouveau-y += dispnv50/core507d.o
......
......@@ -16,6 +16,11 @@ struct nv50_atom {
struct nv50_head_atom {
struct drm_crtc_state state;
struct {
u32 mask;
u32 olut;
} wndw;
struct {
u16 iW;
u16 iH;
......@@ -47,8 +52,9 @@ struct nv50_head_atom {
bool visible;
u32 handle;
u64 offset:40;
u8 mode:4;
} ilut;
u8 buffer:1;
u8 mode:4;
} olut;
struct {
bool visible;
......@@ -107,7 +113,7 @@ struct nv50_head_atom {
union nv50_head_atom_mask {
struct {
bool ilut:1;
bool olut:1;
bool core:1;
bool curs:1;
bool view:1;
......@@ -136,6 +142,7 @@ nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc)
struct nv50_wndw_atom {
struct drm_plane_state state;
struct drm_property_blob *ilut;
bool visible;
struct {
......@@ -152,8 +159,14 @@ struct nv50_wndw_atom {
} sema;
struct {
u8 enable:2;
} lut;
u32 handle;
struct {
u64 offset:40;
u8 buffer:1;
u8 enable:2;
u8 mode:4;
} i;
} xlut;
struct {
u8 mode:2;
......@@ -180,8 +193,8 @@ struct nv50_wndw_atom {
struct {
bool ntfy:1;
bool sema:1;
bool xlut:1;
bool image:1;
bool lut:1;
bool point:1;
};
u8 mask;
......
......@@ -17,8 +17,9 @@ void base507c_ntfy_reset(struct nouveau_bo *, u32);
void base507c_ntfy_set(struct nv50_wndw *, struct nv50_wndw_atom *);
void base507c_ntfy_clr(struct nv50_wndw *);
int base507c_ntfy_wait_begun(struct nouveau_bo *, u32, struct nvif_device *);
void base507c_xlut_set(struct nv50_wndw *, struct nv50_wndw_atom *);
void base507c_xlut_clr(struct nv50_wndw *);
void base507c_image_clr(struct nv50_wndw *);
void base507c_lut(struct nv50_wndw *, struct nv50_wndw_atom *);
void base507c_update(struct nv50_wndw *, u32 *);
int base827c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
......
......@@ -39,17 +39,6 @@ base507c_update(struct nv50_wndw *wndw, u32 *interlock)
}
}
void
base507c_lut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
u32 *push;
if ((push = evo_wait(&wndw->wndw, 2))) {
evo_mthd(push, 0x00e0, 1);
evo_data(push, asyw->lut.enable << 30);
evo_kick(push, &wndw->wndw);
}
}
void
base507c_image_clr(struct nv50_wndw *wndw)
{
......@@ -86,6 +75,28 @@ base507c_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
}
}
void
base507c_xlut_clr(struct nv50_wndw *wndw)
{
u32 *push;
if ((push = evo_wait(&wndw->wndw, 2))) {
evo_mthd(push, 0x00e0, 1);
evo_data(push, 0x00000000);
evo_kick(push, &wndw->wndw);
}
}
void
base507c_xlut_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
u32 *push;
if ((push = evo_wait(&wndw->wndw, 2))) {
evo_mthd(push, 0x00e0, 1);
evo_data(push, 0x40000000);
evo_kick(push, &wndw->wndw);
}
}
int
base507c_ntfy_wait_begun(struct nouveau_bo *bo, u32 offset,
struct nvif_device *device)
......@@ -177,14 +188,17 @@ base507c_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
if (ret)
return ret;
if (!wndw->func->ilut) {
if ((asyh->base.cpp != 1) ^ (fb->format->cpp[0] != 1))
asyh->state.color_mgmt_changed = true;
}
asyh->base.depth = fb->format->depth;
asyh->base.cpp = fb->format->cpp[0];
asyh->base.x = asyw->state.src.x1 >> 16;
asyh->base.y = asyw->state.src.y1 >> 16;
asyh->base.w = asyw->state.fb->width;
asyh->base.h = asyw->state.fb->height;
asyw->lut.enable = 1;
return 0;
}
......@@ -213,9 +227,11 @@ base507c = {
.ntfy_set = base507c_ntfy_set,
.ntfy_clr = base507c_ntfy_clr,
.ntfy_wait_begun = base507c_ntfy_wait_begun,
.olut_core = 1,
.xlut_set = base507c_xlut_set,
.xlut_clr = base507c_xlut_clr,
.image_set = base507c_image_set,
.image_clr = base507c_image_clr,
.lut = base507c_lut,
.update = base507c_update,
};
......
......@@ -53,9 +53,11 @@ base827c = {
.ntfy_set = base507c_ntfy_set,
.ntfy_clr = base507c_ntfy_clr,
.ntfy_wait_begun = base507c_ntfy_wait_begun,
.olut_core = 1,
.xlut_set = base507c_xlut_set,
.xlut_clr = base507c_xlut_clr,
.image_set = base827c_image_set,
.image_clr = base507c_image_clr,
.lut = base507c_lut,
.update = base507c_update,
};
......
......@@ -43,6 +43,44 @@ base907c_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
}
}
static void
base907c_xlut_clr(struct nv50_wndw *wndw)
{
u32 *push;
if ((push = evo_wait(&wndw->wndw, 6))) {
evo_mthd(push, 0x00e0, 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x00e8, 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x00fc, 1);
evo_data(push, 0x00000000);
evo_kick(push, &wndw->wndw);
}
}
static void
base907c_xlut_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
u32 *push;
if ((push = evo_wait(&wndw->wndw, 6))) {
evo_mthd(push, 0x00e0, 3);
evo_data(push, asyw->xlut.i.enable << 30 |
asyw->xlut.i.mode << 24);
evo_data(push, asyw->xlut.i.offset >> 8);
evo_data(push, 0x40000000);
evo_mthd(push, 0x00fc, 1);
evo_data(push, asyw->xlut.handle);
evo_kick(push, &wndw->wndw);
}
}
static void
base907c_ilut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
{
asyw->xlut.i.mode = 7;
asyw->xlut.i.enable = 2;
}
static const struct nv50_wndw_func
base907c = {
.acquire = base507c_acquire,
......@@ -53,9 +91,12 @@ base907c = {
.ntfy_set = base507c_ntfy_set,
.ntfy_clr = base507c_ntfy_clr,
.ntfy_wait_begun = base507c_ntfy_wait_begun,
.ilut = base907c_ilut,
.olut_core = true,
.xlut_set = base907c_xlut_set,
.xlut_clr = base907c_xlut_clr,
.image_set = base907c_image_set,
.image_clr = base507c_image_clr,
.lut = base507c_lut,
.update = base507c_update,
};
......
......@@ -1971,8 +1971,19 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
struct nv50_atom *atom = nv50_atom(state);
struct drm_connector_state *old_connector_state, *new_connector_state;
struct drm_connector *connector;
struct drm_crtc_state *new_crtc_state;
struct drm_crtc *crtc;
int ret, i;
/* We need to handle colour management on a per-plane basis. */
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
if (new_crtc_state->color_mgmt_changed) {
ret = drm_atomic_add_affected_planes(state, crtc);
if (ret)
return ret;
}
}
ret = drm_atomic_helper_check(dev, state);
if (ret)
return ret;
......
......@@ -30,56 +30,6 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include "nouveau_connector.h"
#include "nouveau_bo.h"
static void
nv50_head_lut_load(struct drm_property_blob *blob, int mode,
struct nouveau_bo *nvbo)
{
struct drm_color_lut *in = (struct drm_color_lut *)blob->data;
void __iomem *lut = (u8 *)nvbo_kmap_obj_iovirtual(nvbo);
const int size = blob->length / sizeof(*in);
int bits, shift, i;
u16 zero, r, g, b;
/* This can't happen.. But it shuts the compiler up. */
if (WARN_ON(size != 256))
return;
switch (mode) {
case 0: /* LORES. */
case 1: /* HIRES. */
bits = 11;
shift = 3;
zero = 0x0000;
break;
case 7: /* INTERPOLATE_257_UNITY_RANGE. */
bits = 14;
shift = 0;
zero = 0x6000;
break;
default:
WARN_ON(1);
return;
}
for (i = 0; i < size; i++) {
r = (drm_color_lut_extract(in[i]. red, bits) + zero) << shift;
g = (drm_color_lut_extract(in[i].green, bits) + zero) << shift;
b = (drm_color_lut_extract(in[i]. blue, bits) + zero) << shift;
writew(r, lut + (i * 0x08) + 0);
writew(g, lut + (i * 0x08) + 2);
writew(b, lut + (i * 0x08) + 4);
}
/* INTERPOLATE modes require a "next" entry to interpolate with,
* so we replicate the last entry to deal with this for now.
*/
writew(r, lut + (i * 0x08) + 0);
writew(g, lut + (i * 0x08) + 2);
writew(b, lut + (i * 0x08) + 4);
}
void
nv50_head_flush_clr(struct nv50_head *head,
struct nv50_head_atom *asyh, bool flush)
......@@ -87,7 +37,7 @@ nv50_head_flush_clr(struct nv50_head *head,
union nv50_head_atom_mask clr = {
.mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask),
};
if (clr.ilut) head->func->ilut_clr(head);
if (clr.olut) head->func->olut_clr(head);
if (clr.core) head->func->core_clr(head);
if (clr.curs) head->func->curs_clr(head);
}
......@@ -97,16 +47,14 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
{
if (asyh->set.view ) head->func->view (head, asyh);
if (asyh->set.mode ) head->func->mode (head, asyh);
if (asyh->set.ilut ) {
struct nouveau_bo *nvbo = head->ilut.nvbo[head->ilut.next];
struct drm_property_blob *blob = asyh->state.gamma_lut;
if (blob)
nv50_head_lut_load(blob, asyh->ilut.mode, nvbo);
asyh->ilut.offset = nvbo->bo.offset;
head->ilut.next ^= 1;
head->func->ilut_set(head, asyh);
}
if (asyh->set.core ) head->func->core_set(head, asyh);
if (asyh->set.olut ) {
asyh->olut.offset = nv50_lut_load(&head->olut,
asyh->olut.mode <= 1,
asyh->olut.buffer,
asyh->state.gamma_lut);
head->func->olut_set(head, asyh);
}
if (asyh->set.curs ) head->func->curs_set(head, asyh);
if (asyh->set.base ) head->func->base (head, asyh);
if (asyh->set.ovly ) head->func->ovly (head, asyh);
......@@ -240,35 +188,37 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh,
asyh->set.view = true;
}
static void
static int
nv50_head_atomic_check_lut(struct nv50_head *head,
struct nv50_head_atom *armh,
struct nv50_head_atom *asyh)
{
struct nv50_disp *disp = nv50_disp(head->base.base.dev);
/* An I8 surface without an input LUT makes no sense, and
* EVO will throw an error if you try.
*
* Legacy clients actually cause this due to the order in
* which they call ioctls, so we will enable the LUT with
* whatever contents the buffer already contains to avoid
* triggering the error check.
*/
if (!asyh->state.gamma_lut && asyh->base.cpp != 1) {
asyh->ilut.handle = 0;
asyh->clr.ilut = armh->ilut.visible;
return;
struct drm_property_blob *olut = asyh->state.gamma_lut;
/* Determine whether core output LUT should be enabled. */
if (olut) {
/* Check if any window(s) have stolen the core output LUT
* to as an input LUT for legacy gamma + I8 colour format.
*/
if (asyh->wndw.olut) {
/* If any window has stolen the core output LUT,
* all of them must.
*/
if (asyh->wndw.olut != asyh->wndw.mask)
return -EINVAL;
olut = NULL;
}
}
if (disp->disp->object.oclass < GF110_DISP) {
asyh->ilut.mode = (asyh->base.cpp == 1) ? 0 : 1;
asyh->set.ilut = true;
} else {
asyh->ilut.mode = 7;
asyh->set.ilut = asyh->state.color_mgmt_changed;
if (!olut) {
asyh->olut.handle = 0;
return 0;
}
asyh->ilut.handle = disp->core->chan.vram.handle;
asyh->olut.handle = disp->core->chan.vram.handle;
asyh->olut.buffer = !asyh->olut.buffer;
head->func->olut(head, asyh);
return 0;
}
static void
......@@ -360,9 +310,13 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
nv50_head_atomic_check_mode(head, asyh);
if (asyh->state.color_mgmt_changed ||
asyh->base.cpp != armh->base.cpp)
nv50_head_atomic_check_lut(head, armh, asyh);
asyh->ilut.visible = asyh->ilut.handle != 0;
memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw))) {
int ret = nv50_head_atomic_check_lut(head, asyh);
if (ret)
return ret;
asyh->olut.visible = asyh->olut.handle != 0;
}
if (asyc) {
if (asyc->set.scaler)
......@@ -373,13 +327,16 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
nv50_head_atomic_check_procamp(armh, asyh, asyc);
}
if (head->func->core_calc)
if (head->func->core_calc) {
head->func->core_calc(head, asyh);
if (!asyh->core.visible)
asyh->olut.visible = false;
}
asyh->set.base = armh->base.cpp != asyh->base.cpp;
asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp;
} else {
asyh->ilut.visible = false;
asyh->olut.visible = false;
asyh->core.visible = false;
asyh->curs.visible = false;
asyh->base.cpp = 0;
......@@ -402,11 +359,19 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
if (armh->curs.visible) {
asyh->clr.curs = true;
}
if (asyh->olut.visible) {
if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut)))
asyh->set.olut = true;
} else
if (armh->olut.visible) {
asyh->clr.olut = true;
}
} else {
asyh->clr.ilut = armh->ilut.visible;
asyh->clr.olut = armh->olut.visible;
asyh->clr.core = armh->core.visible;
asyh->clr.curs = armh->curs.visible;
asyh->set.ilut = asyh->ilut.visible;
asyh->set.olut = asyh->olut.visible;
asyh->set.core = asyh->core.visible;
asyh->set.curs = asyh->curs.visible;
}
......@@ -438,9 +403,10 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc)
if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL)))
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state);
asyh->wndw = armh->wndw;
asyh->view = armh->view;
asyh->mode = armh->mode;
asyh->ilut = armh->ilut;
asyh->olut = armh->olut;
asyh->core = armh->core;
asyh->curs = armh->curs;
asyh->base = armh->base;
......@@ -477,11 +443,7 @@ static void
nv50_head_destroy(struct drm_crtc *crtc)
{
struct nv50_head *head = nv50_head(crtc);
int i;
for (i = 0; i < ARRAY_SIZE(head->ilut.nvbo); i++)
nouveau_bo_unmap_unpin_unref(&head->ilut.nvbo[i]);
nv50_lut_fini(&head->olut);
drm_crtc_cleanup(crtc);
kfree(head);
}
......@@ -505,7 +467,7 @@ nv50_head_create(struct drm_device *dev, int index)
struct nv50_head *head;
struct nv50_wndw *curs, *wndw;
struct drm_crtc *crtc;
int ret, i;
int ret;
head = kzalloc(sizeof(*head), GFP_KERNEL);
if (!head)
......@@ -527,10 +489,8 @@ nv50_head_create(struct drm_device *dev, int index)
drm_crtc_helper_add(crtc, &nv50_head_help);
drm_mode_crtc_set_gamma_size(crtc, 256);
for (i = 0; i < ARRAY_SIZE(head->ilut.nvbo); i++) {
ret = nouveau_bo_new_pin_map(&drm->client, 1025 * 8, 0x100,
TTM_PL_FLAG_VRAM,
&head->ilut.nvbo[i]);
if (head->func->olut_set) {
ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut);
if (ret)
goto out;
}
......
......@@ -3,16 +3,14 @@
#define nv50_head(c) container_of((c), struct nv50_head, base.base)
#include "disp.h"
#include "atom.h"
#include "lut.h"
#include "nouveau_crtc.h"
struct nv50_head {
const struct nv50_head_func *func;
struct nouveau_crtc base;
struct {
struct nouveau_bo *nvbo[2];
int next;
} ilut;
struct nv50_lut olut;
};
int nv50_head_create(struct drm_device *, int index);
......@@ -22,8 +20,9 @@ void nv50_head_flush_clr(struct nv50_head *, struct nv50_head_atom *, bool y);
struct nv50_head_func {
void (*view)(struct nv50_head *, struct nv50_head_atom *);
void (*mode)(struct nv50_head *, struct nv50_head_atom *);
void (*ilut_set)(struct nv50_head *, struct nv50_head_atom *);
void (*ilut_clr)(struct nv50_head *);
void (*olut)(struct nv50_head *, struct nv50_head_atom *);
void (*olut_set)(struct nv50_head *, struct nv50_head_atom *);
void (*olut_clr)(struct nv50_head *);
void (*core_calc)(struct nv50_head *, struct nv50_head_atom *);
void (*core_set)(struct nv50_head *, struct nv50_head_atom *);
void (*core_clr)(struct nv50_head *);
......@@ -39,6 +38,7 @@ struct nv50_head_func {
extern const struct nv50_head_func head507d;
void head507d_view(struct nv50_head *, struct nv50_head_atom *);
void head507d_mode(struct nv50_head *, struct nv50_head_atom *);
void head507d_olut(struct nv50_head *, struct nv50_head_atom *);
void head507d_core_calc(struct nv50_head *, struct nv50_head_atom *);
void head507d_core_clr(struct nv50_head *);
void head507d_base(struct nv50_head *, struct nv50_head_atom *);
......@@ -51,13 +51,13 @@ extern const struct nv50_head_func head827d;
extern const struct nv50_head_func head907d;
void head907d_view(struct nv50_head *, struct nv50_head_atom *);
void head907d_mode(struct nv50_head *, struct nv50_head_atom *);
void head907d_ilut_set(struct nv50_head *, struct nv50_head_atom *);
void head907d_ilut_clr(struct nv50_head *);
void head907d_olut(struct nv50_head *, struct nv50_head_atom *);
void head907d_olut_set(struct nv50_head *, struct nv50_head_atom *);
void head907d_olut_clr(struct nv50_head *);
void head907d_core_set(struct nv50_head *, struct nv50_head_atom *);
void head907d_core_clr(struct nv50_head *);
void head907d_curs_set(struct nv50_head *, struct nv50_head_atom *);
void head907d_curs_clr(struct nv50_head *);
void head907d_base(struct nv50_head *, struct nv50_head_atom *);
void head907d_ovly(struct nv50_head *, struct nv50_head_atom *);
void head907d_procamp(struct nv50_head *, struct nv50_head_atom *);
void head907d_or(struct nv50_head *, struct nv50_head_atom *);
......
......@@ -165,6 +165,7 @@ head507d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
* without also updating HeadSetOffsetCursor.
*/
asyh->set.curs = asyh->curs.visible;
asyh->set.olut = asyh->olut.handle != 0;
}
}
......@@ -178,8 +179,8 @@ head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
asyh->core.w = asyh->base.w;
asyh->core.h = asyh->base.h;
} else
if ((asyh->core.visible = asyh->curs.visible) ||
(asyh->core.visible = asyh->ilut.visible)) {
if ((asyh->core.visible = (asyh->ovly.cpp != 0)) ||
(asyh->core.visible = asyh->curs.visible)) {
/*XXX: We need to either find some way of having the
* primary base layer appear black, while still
* being able to display the other layers, or we
......@@ -200,30 +201,39 @@ head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
}
static void
head507d_ilut_clr(struct nv50_head *head)
head507d_olut_clr(struct nv50_head *head)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 *push;
if ((push = evo_wait(core, 2))) {
evo_mthd(push, 0x0840 + (head->base.index * 0x400), 1);
evo_data(push, 0x40000000);
evo_data(push, 0x00000000);
evo_kick(push, core);
}
}
static void
head507d_ilut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
head507d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 *push;
if ((push = evo_wait(core, 3))) {
evo_mthd(push, 0x0840 + (head->base.index * 0x400), 2);
evo_data(push, 0x80000000 | asyh->ilut.mode << 30);
evo_data(push, asyh->ilut.offset >> 8);
evo_data(push, 0x80000000 | asyh->olut.mode << 30);
evo_data(push, asyh->olut.offset >> 8);
evo_kick(push, core);
}
}
void
head507d_olut(struct nv50_head *head, struct nv50_head_atom *asyh)
{
if (asyh->base.cpp == 1)
asyh->olut.mode = 0;
else
asyh->olut.mode = 1;
}
void
head507d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
{
......@@ -269,8 +279,9 @@ const struct nv50_head_func
head507d = {
.view = head507d_view,
.mode = head507d_mode,
.ilut_set = head507d_ilut_set,
.ilut_clr = head507d_ilut_clr,
.olut = head507d_olut,
.olut_set = head507d_olut_set,
.olut_clr = head507d_olut_clr,
.core_calc = head507d_core_calc,
.core_set = head507d_core_set,
.core_clr = head507d_core_clr,
......
......@@ -74,13 +74,13 @@ head827d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
}
static void
head827d_ilut_clr(struct nv50_head *head)
head827d_olut_clr(struct nv50_head *head)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 *push;
if ((push = evo_wait(core, 4))) {
evo_mthd(push, 0x0840 + (head->base.index * 0x400), 1);
evo_data(push, 0x40000000);
evo_data(push, 0x00000000);
evo_mthd(push, 0x085c + (head->base.index * 0x400), 1);
evo_data(push, 0x00000000);
evo_kick(push, core);
......@@ -88,16 +88,16 @@ head827d_ilut_clr(struct nv50_head *head)
}
static void
head827d_ilut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
head827d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 *push;
if ((push = evo_wait(core, 5))) {
evo_mthd(push, 0x0840 + (head->base.index * 0x400), 2);
evo_data(push, 0x80000000 | asyh->ilut.mode << 30);
evo_data(push, asyh->ilut.offset >> 8);
evo_data(push, 0x80000000 | asyh->olut.mode << 30);
evo_data(push, asyh->olut.offset >> 8);
evo_mthd(push, 0x085c + (head->base.index * 0x400), 1);
evo_data(push, asyh->ilut.handle);
evo_data(push, asyh->olut.handle);
evo_kick(push, core);
}
}
......@@ -106,8 +106,9 @@ const struct nv50_head_func
head827d = {
.view = head507d_view,
.mode = head507d_mode,
.ilut_set = head827d_ilut_set,
.ilut_clr = head827d_ilut_clr,
.olut = head507d_olut,
.olut_set = head827d_olut_set,
.olut_clr = head827d_olut_clr,
.core_calc = head507d_core_calc,
.core_set = head827d_core_set,
.core_clr = head507d_core_clr,
......
......@@ -91,7 +91,7 @@ head907d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
}
}
void
static void
head907d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
......@@ -182,13 +182,13 @@ head907d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
}
void
head907d_ilut_clr(struct nv50_head *head)
head907d_olut_clr(struct nv50_head *head)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 *push;
if ((push = evo_wait(core, 4))) {
evo_mthd(push, 0x0440 + (head->base.index * 0x300), 1);
evo_data(push, 0x03000000);
evo_mthd(push, 0x0448 + (head->base.index * 0x300), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x045c + (head->base.index * 0x300), 1);
evo_data(push, 0x00000000);
evo_kick(push, core);
......@@ -196,22 +196,26 @@ head907d_ilut_clr(struct nv50_head *head)
}
void
head907d_ilut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
head907d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 *push;
if ((push = evo_wait(core, 7))) {
evo_mthd(push, 0x0440 + (head->base.index * 0x300), 4);
evo_data(push, 0x80000000 | asyh->ilut.mode << 24);
evo_data(push, asyh->ilut.offset >> 8);
evo_data(push, 0x00000000);
evo_data(push, 0x00000000);
if ((push = evo_wait(core, 5))) {
evo_mthd(push, 0x0448 + (head->base.index * 0x300), 2);
evo_data(push, 0x80000000 | asyh->olut.mode << 24);
evo_data(push, asyh->olut.offset >> 8);
evo_mthd(push, 0x045c + (head->base.index * 0x300), 1);
evo_data(push, asyh->ilut.handle);
evo_data(push, asyh->olut.handle);
evo_kick(push, core);
}
}
void
head907d_olut(struct nv50_head *head, struct nv50_head_atom *asyh)
{
asyh->olut.mode = 7;
}
void
head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
{
......@@ -259,8 +263,9 @@ const struct nv50_head_func
head907d = {
.view = head907d_view,
.mode = head907d_mode,
.ilut_set = head907d_ilut_set,
.ilut_clr = head907d_ilut_clr,
.olut = head907d_olut,
.olut_set = head907d_olut_set,
.olut_clr = head907d_olut_clr,
.core_calc = head507d_core_calc,
.core_set = head907d_core_set,
.core_clr = head907d_core_clr,
......
......@@ -36,18 +36,46 @@ head917d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
}
}
static void
head917d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
{
struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
u32 bounds = 0;
u32 *push;
if (asyh->base.cpp) {
switch (asyh->base.cpp) {
case 8: bounds |= 0x00000500; break;
case 4: bounds |= 0x00000300; break;
case 2: bounds |= 0x00000100; break;
case 1: bounds |= 0x00000000; break;
default:
WARN_ON(1);
break;
}
bounds |= 0x00020001;
}
if ((push = evo_wait(core, 2))) {
evo_mthd(push, 0x04d0 + head->base.index * 0x300, 1);
evo_data(push, bounds);
evo_kick(push, core);
}
}
const struct nv50_head_func
head917d = {
.view = head907d_view,
.mode = head907d_mode,
.ilut_set = head907d_ilut_set,
.ilut_clr = head907d_ilut_clr,
.olut = head907d_olut,
.olut_set = head907d_olut_set,
.olut_clr = head907d_olut_clr,
.core_calc = head507d_core_calc,
.core_set = head907d_core_set,
.core_clr = head907d_core_clr,
.curs_set = head907d_curs_set,
.curs_clr = head907d_curs_clr,
.base = head907d_base,
.base = head917d_base,
.ovly = head907d_ovly,
.dither = head917d_dither,
.procamp = head907d_procamp,
......
/*
* Copyright 2018 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.
*/
#include "lut.h"
#include "disp.h"
#include <drm/drm_color_mgmt.h>
#include <drm/drm_mode.h>
#include <drm/drm_property.h>
#include <nvif/class.h>
u32
nv50_lut_load(struct nv50_lut *lut, bool legacy, int buffer,
struct drm_property_blob *blob)
{
struct drm_color_lut *in = (struct drm_color_lut *)blob->data;
void __iomem *mem = lut->mem[buffer].object.map.ptr;
const int size = blob->length / sizeof(*in);
int bits, shift, i;
u16 zero, r, g, b;
u32 addr = lut->mem[buffer].addr;
/* This can't happen.. But it shuts the compiler up. */
if (WARN_ON(size != 256))
return 0;
if (legacy) {
bits = 11;
shift = 3;
zero = 0x0000;
} else {
bits = 14;
shift = 0;
zero = 0x6000;
}
for (i = 0; i < size; i++) {
r = (drm_color_lut_extract(in[i]. red, bits) + zero) << shift;
g = (drm_color_lut_extract(in[i].green, bits) + zero) << shift;
b = (drm_color_lut_extract(in[i]. blue, bits) + zero) << shift;
writew(r, mem + (i * 0x08) + 0);
writew(g, mem + (i * 0x08) + 2);
writew(b, mem + (i * 0x08) + 4);
}
/* INTERPOLATE modes require a "next" entry to interpolate with,
* so we replicate the last entry to deal with this for now.
*/
writew(r, mem + (i * 0x08) + 0);
writew(g, mem + (i * 0x08) + 2);
writew(b, mem + (i * 0x08) + 4);
return addr;
}
void
nv50_lut_fini(struct nv50_lut *lut)
{
int i;
for (i = 0; i < ARRAY_SIZE(lut->mem); i++)
nvif_mem_fini(&lut->mem[i]);
}
int
nv50_lut_init(struct nv50_disp *disp, struct nvif_mmu *mmu,
struct nv50_lut *lut)
{
const u32 size = disp->disp->object.oclass < GF110_DISP ? 257 : 1025;
int i;
for (i = 0; i < ARRAY_SIZE(lut->mem); i++) {
int ret = nvif_mem_init_map(mmu, NVIF_MEM_VRAM, size * 8,
&lut->mem[i]);
if (ret)
return ret;
}
return 0;
}
#ifndef __NV50_KMS_LUT_H__
#define __NV50_KMS_LUT_H__
#include <nvif/mem.h>
struct drm_property_blob;
struct nv50_disp;
struct nv50_lut {
struct nvif_mem mem[2];
};
int nv50_lut_init(struct nv50_disp *, struct nvif_mmu *, struct nv50_lut *);
void nv50_lut_fini(struct nv50_lut *);
u32 nv50_lut_load(struct nv50_lut *, bool legacy, int buffer,
struct drm_property_blob *);
#endif
......@@ -116,6 +116,7 @@ nv50_wndw_flush_clr(struct nv50_wndw *wndw, u32 *interlock, bool flush,
};
if (clr.sema ) wndw->func-> sema_clr(wndw);
if (clr.ntfy ) wndw->func-> ntfy_clr(wndw);
if (clr.xlut ) wndw->func-> xlut_clr(wndw);
if (clr.image) wndw->func->image_clr(wndw);
interlock[wndw->interlock.type] |= wndw->interlock.data;
......@@ -133,7 +134,18 @@ nv50_wndw_flush_set(struct nv50_wndw *wndw, u32 *interlock,
if (asyw->set.sema ) wndw->func->sema_set (wndw, asyw);
if (asyw->set.ntfy ) wndw->func->ntfy_set (wndw, asyw);
if (asyw->set.image) wndw->func->image_set(wndw, asyw);
if (asyw->set.lut ) wndw->func->lut (wndw, asyw);
if (asyw->set.xlut ) {
if (asyw->ilut) {
asyw->xlut.i.offset =
nv50_lut_load(&wndw->ilut,
asyw->xlut.i.mode <= 1,
asyw->xlut.i.buffer,
asyw->ilut);
}
wndw->func->xlut_set(wndw, asyw);
}
if (asyw->set.point) {
wndw->immd->point(wndw, asyw);
wndw->immd->update(wndw, interlock);
......@@ -241,7 +253,56 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
return wndw->func->acquire(wndw, asyw, asyh);
}
int
static void
nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
struct nv50_wndw_atom *armw,
struct nv50_wndw_atom *asyw,
struct nv50_head_atom *asyh)
{
struct drm_property_blob *ilut = asyh->state.degamma_lut;
/* I8 format without an input LUT makes no sense, and the
* HW error-checks for this.
*
* In order to handle legacy gamma, when there's no input
* LUT we need to steal the output LUT and use it instead.
*/
if (!ilut && asyw->state.fb->format->format == DRM_FORMAT_C8) {
/* This should be an error, but there's legacy clients
* that do a modeset before providing a gamma table.
*
* We keep the window disabled to avoid angering HW.
*/
if (!(ilut = asyh->state.gamma_lut)) {
asyw->visible = false;
return;
}
if (wndw->func->ilut)
asyh->wndw.olut |= BIT(wndw->id);
} else {
asyh->wndw.olut &= ~BIT(wndw->id);
}
/* Recalculate LUT state. */
memset(&asyw->xlut, 0x00, sizeof(asyw->xlut));
if ((asyw->ilut = wndw->func->ilut ? ilut : NULL)) {
wndw->func->ilut(wndw, asyw);
asyw->xlut.handle = wndw->wndw.vram.handle;
asyw->xlut.i.buffer = !asyw->xlut.i.buffer;
asyw->set.xlut = true;
}
/* Handle setting base SET_OUTPUT_LUT_LO_ENABLE_USE_CORE_LUT. */
if (wndw->func->olut_core &&
(!armw->visible || (armw->xlut.handle && !asyw->xlut.handle)))
asyw->set.xlut = true;
/* Can't do an immediate flip while changing the LUT. */
asyh->state.pageflip_flags &= ~DRM_MODE_PAGE_FLIP_ASYNC;
}
static int
nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
{
struct nouveau_drm *drm = nouveau_drm(plane->dev);
......@@ -274,15 +335,26 @@ nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
return PTR_ERR(harm);
}
/* LUT configuration can potentially cause the window to be disabled. */
if (asyw->visible && wndw->func->xlut_set &&
(!armw->visible ||
asyh->state.color_mgmt_changed ||
asyw->state.fb->format->format !=
armw->state.fb->format->format))
nv50_wndw_atomic_check_lut(wndw, armw, asyw, asyh);
/* Calculate new window state. */
if (asyw->visible) {
ret = nv50_wndw_atomic_check_acquire(wndw, modeset,
armw, asyw, asyh);
if (ret)
return ret;
asyh->wndw.mask |= BIT(wndw->id);
} else
if (armw->visible) {
nv50_wndw_atomic_check_release(wndw, asyw, harm);
harm->wndw.mask &= ~BIT(wndw->id);
} else {
return 0;
}
......@@ -294,9 +366,9 @@ nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
if (!asyw->visible || modeset) {
asyw->clr.ntfy = armw->ntfy.handle != 0;
asyw->clr.sema = armw->sema.handle != 0;
asyw->clr.xlut = armw->xlut.handle != 0;
if (wndw->func->image_clr)
asyw->clr.image = armw->image.handle[0] != 0;
asyw->set.lut = wndw->func->lut && asyw->visible;
}
return 0;
......@@ -381,9 +453,10 @@ nv50_wndw_atomic_duplicate_state(struct drm_plane *plane)
__drm_atomic_helper_plane_duplicate_state(plane, &asyw->state);
asyw->sema = armw->sema;
asyw->ntfy = armw->ntfy;
asyw->ilut = NULL;
asyw->xlut = armw->xlut;
asyw->image = armw->image;
asyw->point = armw->point;
asyw->lut = armw->lut;
asyw->clr.mask = 0;
asyw->set.mask = 0;
return &asyw->state;
......@@ -417,6 +490,9 @@ nv50_wndw_destroy(struct drm_plane *plane)
nvif_notify_fini(&wndw->notify);
nv50_dmac_destroy(&wndw->wimm);
nv50_dmac_destroy(&wndw->wndw);
nv50_lut_fini(&wndw->ilut);
drm_plane_cleanup(&wndw->plane);
kfree(wndw);
}
......@@ -456,6 +532,9 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
enum nv50_disp_interlock_type interlock_type, u32 interlock_data,
struct nv50_wndw **pwndw)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct nvif_mmu *mmu = &drm->client.mmu;
struct nv50_disp *disp = nv50_disp(dev);
struct nv50_wndw *wndw;
int nformat;
int ret;
......@@ -484,6 +563,12 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
drm_plane_helper_add(&wndw->plane, &nv50_wndw_helper);
if (wndw->func->ilut) {
ret = nv50_lut_init(disp, mmu, &wndw->ilut);
if (ret)
return ret;
}
wndw->notify.func = nv50_wndw_notify;
return 0;
}
......@@ -3,6 +3,7 @@
#define nv50_wndw(p) container_of((p), struct nv50_wndw, plane)
#include "disp.h"
#include "atom.h"
#include "lut.h"
#include <nvif/notify.h>
......@@ -24,6 +25,8 @@ struct nv50_wndw {
struct drm_plane plane;
struct nv50_lut ilut;
struct nv50_dmac wndw;
struct nv50_dmac wimm;
......@@ -61,9 +64,12 @@ struct nv50_wndw_func {
void (*ntfy_clr)(struct nv50_wndw *);
int (*ntfy_wait_begun)(struct nouveau_bo *, u32 offset,
struct nvif_device *);
void (*ilut)(struct nv50_wndw *, struct nv50_wndw_atom *);
bool olut_core;
void (*xlut_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
void (*xlut_clr)(struct nv50_wndw *);
void (*image_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
void (*image_clr)(struct nv50_wndw *);
void (*lut)(struct nv50_wndw *, struct nv50_wndw_atom *);
void (*update)(struct nv50_wndw *, u32 *interlock);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment