2018-05-08 18:39:47 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
drm/nouveau/kms/nvd9-: Add CRC support
This introduces support for CRC readback on gf119+, using the
documentation generously provided to us by Nvidia:
https://github.com/NVIDIA/open-gpu-doc/blob/master/Display-CRC/display-crc.txt
We expose all available CRC sources. SF, SOR, PIOR, and DAC are exposed
through a single set of "outp" sources: outp-active/auto for a CRC of
the scanout region, outp-complete for a CRC of both the scanout and
blanking/sync region combined, and outp-inactive for a CRC of only the
blanking/sync region. For each source, nouveau selects the appropriate
tap point based on the output path in use. We also expose an "rg"
source, which allows for capturing CRCs of the scanout raster before
it's encoded into a video signal in the output path. This tap point is
referred to as the raster generator.
Note that while there's some other neat features that can be used with
CRC capture on nvidia hardware, like capturing from two CRC sources
simultaneously, I couldn't see any usecase for them and did not
implement them.
Nvidia only allows for accessing CRCs through a shared DMA region that
we program through the core EVO/NvDisplay channel which is referred to
as the notifier context. The notifier context is limited to either 255
(for Fermi-Pascal) or 2047 (Volta+) entries to store CRCs in, and
unfortunately the hardware simply drops CRCs and reports an overflow
once all available entries in the notifier context are filled.
Since the DRM CRC API and igt-gpu-tools don't expect there to be a limit
on how many CRCs can be captured, we work around this in nouveau by
allocating two separate notifier contexts for each head instead of one.
We schedule a vblank worker ahead of time so that once we start getting
close to filling up all of the available entries in the notifier
context, we can swap the currently used notifier context out with
another pre-prepared notifier context in a manner similar to page
flipping.
Unfortunately, the hardware only allows us to this by flushing two
separate updates on the core channel: one to release the current
notifier context handle, and one to program the next notifier context's
handle. When the hardware processes the first update, the CRC for the
current frame is lost. However, the second update can be flushed
immediately without waiting for the first to complete so that CRC
generation resumes on the next frame. According to Nvidia's hardware
engineers, there isn't any cleaner way of flipping notifier contexts
that would avoid this.
Since using vblank workers to swap out the notifier context will ensure
we can usually flush both updates to hardware within the timespan of a
single frame, we can also ensure that there will only be exactly one
frame lost between the first and second update being executed by the
hardware. This gives us the guarantee that we're always correctly
matching each CRC entry with it's respective frame even after a context
flip. And since IGT will retrieve the CRC entry for a frame by waiting
until it receives a CRC for any subsequent frames, this doesn't cause an
issue with any tests and is much simpler than trying to change the
current DRM API to accommodate.
In order to facilitate testing of correct handling of this limitation,
we also expose a debugfs interface to manually control the threshold for
when we start trying to flip the notifier context. We will use this in
igt to trigger a context flip for testing purposes without needing to
wait for the notifier to completely fill up. This threshold is reset
to the default value set by nouveau after each capture, and is exposed
in a separate folder within each CRTC's debugfs directory labelled
"nv_crc".
Changes since v1:
* Forgot to finish saving crc.h before saving, whoops. This just adds
some corrections to the empty function declarations that we use if
CONFIG_DEBUG_FS isn't enabled.
Changes since v2:
* Don't check return code from debugfs_create_dir() or
debugfs_create_file() - Greg K-H
Changes since v3:
(no functional changes)
* Fix SPDX license identifiers (checkpatch)
* s/uint32_t/u32/ (checkpatch)
* Fix indenting in switch cases (checkpatch)
Changes since v4:
* Remove unneeded param changes with nv50_head_flush_clr/set
* Rebase
Changes since v5:
* Remove set but unused variable (outp) in nv50_crc_atomic_check() -
Kbuild bot
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
Acked-by: Dave Airlie <airlied@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-10-lyude@redhat.com
2019-10-08 02:20:12 +08:00
|
|
|
#include <drm/drm_connector.h>
|
|
|
|
#include <drm/drm_mode_config.h>
|
|
|
|
#include <drm/drm_vblank.h>
|
|
|
|
#include "nouveau_drv.h"
|
|
|
|
#include "nouveau_bios.h"
|
|
|
|
#include "nouveau_connector.h"
|
2018-05-08 18:39:47 +08:00
|
|
|
#include "head.h"
|
|
|
|
#include "core.h"
|
drm/nouveau/kms/nvd9-: Add CRC support
This introduces support for CRC readback on gf119+, using the
documentation generously provided to us by Nvidia:
https://github.com/NVIDIA/open-gpu-doc/blob/master/Display-CRC/display-crc.txt
We expose all available CRC sources. SF, SOR, PIOR, and DAC are exposed
through a single set of "outp" sources: outp-active/auto for a CRC of
the scanout region, outp-complete for a CRC of both the scanout and
blanking/sync region combined, and outp-inactive for a CRC of only the
blanking/sync region. For each source, nouveau selects the appropriate
tap point based on the output path in use. We also expose an "rg"
source, which allows for capturing CRCs of the scanout raster before
it's encoded into a video signal in the output path. This tap point is
referred to as the raster generator.
Note that while there's some other neat features that can be used with
CRC capture on nvidia hardware, like capturing from two CRC sources
simultaneously, I couldn't see any usecase for them and did not
implement them.
Nvidia only allows for accessing CRCs through a shared DMA region that
we program through the core EVO/NvDisplay channel which is referred to
as the notifier context. The notifier context is limited to either 255
(for Fermi-Pascal) or 2047 (Volta+) entries to store CRCs in, and
unfortunately the hardware simply drops CRCs and reports an overflow
once all available entries in the notifier context are filled.
Since the DRM CRC API and igt-gpu-tools don't expect there to be a limit
on how many CRCs can be captured, we work around this in nouveau by
allocating two separate notifier contexts for each head instead of one.
We schedule a vblank worker ahead of time so that once we start getting
close to filling up all of the available entries in the notifier
context, we can swap the currently used notifier context out with
another pre-prepared notifier context in a manner similar to page
flipping.
Unfortunately, the hardware only allows us to this by flushing two
separate updates on the core channel: one to release the current
notifier context handle, and one to program the next notifier context's
handle. When the hardware processes the first update, the CRC for the
current frame is lost. However, the second update can be flushed
immediately without waiting for the first to complete so that CRC
generation resumes on the next frame. According to Nvidia's hardware
engineers, there isn't any cleaner way of flipping notifier contexts
that would avoid this.
Since using vblank workers to swap out the notifier context will ensure
we can usually flush both updates to hardware within the timespan of a
single frame, we can also ensure that there will only be exactly one
frame lost between the first and second update being executed by the
hardware. This gives us the guarantee that we're always correctly
matching each CRC entry with it's respective frame even after a context
flip. And since IGT will retrieve the CRC entry for a frame by waiting
until it receives a CRC for any subsequent frames, this doesn't cause an
issue with any tests and is much simpler than trying to change the
current DRM API to accommodate.
In order to facilitate testing of correct handling of this limitation,
we also expose a debugfs interface to manually control the threshold for
when we start trying to flip the notifier context. We will use this in
igt to trigger a context flip for testing purposes without needing to
wait for the notifier to completely fill up. This threshold is reset
to the default value set by nouveau after each capture, and is exposed
in a separate folder within each CRTC's debugfs directory labelled
"nv_crc".
Changes since v1:
* Forgot to finish saving crc.h before saving, whoops. This just adds
some corrections to the empty function declarations that we use if
CONFIG_DEBUG_FS isn't enabled.
Changes since v2:
* Don't check return code from debugfs_create_dir() or
debugfs_create_file() - Greg K-H
Changes since v3:
(no functional changes)
* Fix SPDX license identifiers (checkpatch)
* s/uint32_t/u32/ (checkpatch)
* Fix indenting in switch cases (checkpatch)
Changes since v4:
* Remove unneeded param changes with nv50_head_flush_clr/set
* Rebase
Changes since v5:
* Remove set but unused variable (outp) in nv50_crc_atomic_check() -
Kbuild bot
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
Acked-by: Dave Airlie <airlied@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-10-lyude@redhat.com
2019-10-08 02:20:12 +08:00
|
|
|
#include "crc.h"
|
2018-05-08 18:39:47 +08:00
|
|
|
|
2020-06-20 16:34:00 +08:00
|
|
|
#include <nvif/push507c.h>
|
|
|
|
|
2020-06-21 08:55:21 +08:00
|
|
|
#include <nvhw/class/cl907d.h>
|
|
|
|
|
2020-06-21 07:28:36 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:28:36 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 3)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
PUSH_NVSQ(push, NV907D, 0x0404 + (i * 0x300), asyh->or.depth << 6 |
|
|
|
|
asyh->or.nvsync << 4 |
|
|
|
|
asyh->or.nhsync << 3 |
|
|
|
|
asyh->or.crc_raster,
|
|
|
|
0x0408 + (i * 0x300), 0x31ec6000 |
|
|
|
|
head->base.index << 25 |
|
|
|
|
asyh->mode.interlace);
|
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:25:19 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:25:19 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 2)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
PUSH_NVSQ(push, NV907D, 0x0498 + (i * 0x300), asyh->procamp.sat.sin << 20 |
|
|
|
|
asyh->procamp.sat.cos << 8);
|
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:22:47 +08:00
|
|
|
static int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:22:47 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 2)))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
PUSH_NVSQ(push, NV907D, 0x0490 + (i * 0x300), asyh->dither.mode << 3 |
|
|
|
|
asyh->dither.bits << 1 |
|
|
|
|
asyh->dither.enable);
|
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:20:29 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:20:29 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
2018-05-08 18:39:47 +08:00
|
|
|
u32 bounds = 0;
|
2020-06-21 07:20:29 +08:00
|
|
|
int ret;
|
2018-05-08 18:39:47 +08:00
|
|
|
|
|
|
|
if (asyh->ovly.cpp) {
|
|
|
|
switch (asyh->ovly.cpp) {
|
2020-06-21 10:31:42 +08:00
|
|
|
case 8: bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_64); break;
|
|
|
|
case 4: bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_32); break;
|
|
|
|
case 2: bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16); break;
|
2018-05-08 18:39:47 +08:00
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
|
|
|
break;
|
|
|
|
}
|
2020-06-21 10:31:42 +08:00
|
|
|
bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, USABLE, TRUE);
|
2018-05-08 18:39:47 +08:00
|
|
|
} else {
|
2020-06-21 10:31:42 +08:00
|
|
|
bounds |= NVDEF(NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16);
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:20:29 +08:00
|
|
|
if ((ret = PUSH_WAIT(push, 2)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 10:31:42 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_OVERLAY_USAGE_BOUNDS(i), bounds);
|
2020-06-21 07:20:29 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:19:28 +08:00
|
|
|
static int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:19:28 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
2018-05-08 18:39:47 +08:00
|
|
|
u32 bounds = 0;
|
2020-06-21 07:19:28 +08:00
|
|
|
int ret;
|
2018-05-08 18:39:47 +08:00
|
|
|
|
|
|
|
if (asyh->base.cpp) {
|
|
|
|
switch (asyh->base.cpp) {
|
2020-06-21 10:31:24 +08:00
|
|
|
case 8: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_64); break;
|
|
|
|
case 4: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_32); break;
|
|
|
|
case 2: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_16); break;
|
|
|
|
case 1: bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, PIXEL_DEPTH, BPP_8); break;
|
2018-05-08 18:39:47 +08:00
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
|
|
|
break;
|
|
|
|
}
|
2020-06-21 10:31:24 +08:00
|
|
|
bounds |= NVDEF(NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS, USABLE, TRUE);
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:19:28 +08:00
|
|
|
if ((ret = PUSH_WAIT(push, 2)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 10:31:24 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_BASE_CHANNEL_USAGE_BOUNDS(i), bounds);
|
2020-06-21 07:19:28 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:17:11 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_curs_clr(struct nv50_head *head)
|
|
|
|
{
|
2020-06-21 07:17:11 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 4)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 10:16:24 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_CURSOR(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, ENABLE, DISABLE) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, FORMAT, A8R8G8B8) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, SIZE, W64_H64));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CURSOR(i), 0x00000000);
|
2020-06-21 07:17:11 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:14:25 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:14:25 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 5)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 10:10:42 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_CURSOR(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, ENABLE, ENABLE) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, FORMAT, asyh->curs.format) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, SIZE, asyh->curs.layout) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_X, 0) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTROL_CURSOR, HOT_SPOT_Y, 0) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_CURSOR, COMPOSITION, ALPHA_BLEND),
|
|
|
|
|
|
|
|
HEAD_SET_OFFSET_CURSOR(i), asyh->curs.offset >> 8);
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_CURSOR(i), asyh->curs.handle);
|
2020-06-21 07:14:25 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:07:28 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_core_clr(struct nv50_head *head)
|
|
|
|
{
|
2020-06-21 07:07:28 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 2)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 09:59:54 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMAS_ISO(i), 0x00000000);
|
2020-06-21 07:07:28 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:06:18 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 07:06:18 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 9)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 09:59:09 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_OFFSET(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_OFFSET, ORIGIN, asyh->core.offset >> 8));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_SIZE(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_SIZE, WIDTH, asyh->core.w) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_SIZE, HEIGHT, asyh->core.h),
|
|
|
|
|
|
|
|
HEAD_SET_STORAGE(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_STORAGE, BLOCK_HEIGHT, asyh->core.blockh) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_STORAGE, PITCH, asyh->core.pitch >> 8) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_STORAGE, PITCH, asyh->core.blocks) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_STORAGE, MEMORY_LAYOUT, asyh->core.layout),
|
|
|
|
|
|
|
|
HEAD_SET_PARAMS(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_PARAMS, FORMAT, asyh->core.format) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_PARAMS, SUPER_SAMPLE, X1_AA) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_PARAMS, GAMMA, LINEAR),
|
|
|
|
|
|
|
|
HEAD_SET_CONTEXT_DMAS_ISO(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTEXT_DMAS_ISO, HANDLE, asyh->core.handle));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_POINT_IN(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_POINT_IN, X, asyh->core.x) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_POINT_IN, Y, asyh->core.y));
|
2020-06-21 07:06:18 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 07:00:52 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_olut_clr(struct nv50_head *head)
|
2018-05-08 18:39:47 +08:00
|
|
|
{
|
2020-06-21 07:00:52 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 4)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 09:47:21 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_OUTPUT_LUT_LO(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, ENABLE, DISABLE));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_LUT(i), 0x00000000);
|
2020-06-21 07:00:52 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 06:58:11 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
|
2018-05-08 18:39:47 +08:00
|
|
|
{
|
2020-06-21 06:58:11 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 5)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 09:44:39 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_OUTPUT_LUT_LO(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, ENABLE, ENABLE) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_OUTPUT_LUT_LO, MODE, asyh->olut.mode) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_OUTPUT_LUT_LO, NEVER_YIELD_TO_BASE, DISABLE),
|
|
|
|
|
|
|
|
HEAD_SET_OUTPUT_LUT_HI(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_OUTPUT_LUT_HI, ORIGIN, asyh->olut.offset >> 8));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTEXT_DMA_LUT(i), asyh->olut.handle);
|
2020-06-21 06:58:11 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2018-12-11 12:50:02 +08:00
|
|
|
void
|
|
|
|
head907d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem)
|
|
|
|
{
|
|
|
|
for (; size--; in++, mem += 8) {
|
|
|
|
writew(drm_color_lut_extract(in-> red, 14) + 0x6000, mem + 0);
|
|
|
|
writew(drm_color_lut_extract(in->green, 14) + 0x6000, mem + 2);
|
|
|
|
writew(drm_color_lut_extract(in-> blue, 14) + 0x6000, mem + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* INTERPOLATE modes require a "next" entry to interpolate with,
|
|
|
|
* so we replicate the last entry to deal with this for now.
|
|
|
|
*/
|
|
|
|
writew(readw(mem - 8), mem + 0);
|
|
|
|
writew(readw(mem - 6), mem + 2);
|
|
|
|
writew(readw(mem - 4), mem + 4);
|
|
|
|
}
|
|
|
|
|
2019-09-06 12:13:59 +08:00
|
|
|
bool
|
|
|
|
head907d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
|
2018-05-08 18:39:47 +08:00
|
|
|
{
|
2019-09-06 12:13:59 +08:00
|
|
|
if (size != 256 && size != 1024)
|
|
|
|
return false;
|
|
|
|
|
2020-06-21 09:44:39 +08:00
|
|
|
if (size == 1024)
|
|
|
|
asyh->olut.mode = NV907D_HEAD_SET_OUTPUT_LUT_LO_MODE_INTERPOLATE_1025_UNITY_RANGE;
|
|
|
|
else
|
|
|
|
asyh->olut.mode = NV907D_HEAD_SET_OUTPUT_LUT_LO_MODE_INTERPOLATE_257_UNITY_RANGE;
|
|
|
|
|
2018-12-11 12:50:02 +08:00
|
|
|
asyh->olut.load = head907d_olut_load;
|
2019-09-06 12:13:59 +08:00
|
|
|
return true;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-21 06:51:03 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-21 06:51:03 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
2018-05-08 18:39:47 +08:00
|
|
|
struct nv50_head_mode *m = &asyh->mode;
|
2020-06-21 06:51:03 +08:00
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 14)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 09:25:06 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_OVERSCAN_COLOR(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, RED, 0) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, GRN, 0) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_OVERSCAN_COLOR, BLU, 0),
|
|
|
|
|
|
|
|
HEAD_SET_RASTER_SIZE(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_SIZE, WIDTH, m->h.active) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_SIZE, HEIGHT, m->v.active),
|
|
|
|
|
|
|
|
HEAD_SET_RASTER_SYNC_END(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_SYNC_END, X, m->h.synce) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_SYNC_END, Y, m->v.synce),
|
|
|
|
|
|
|
|
HEAD_SET_RASTER_BLANK_END(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_BLANK_END, X, m->h.blanke) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_BLANK_END, Y, m->v.blanke),
|
|
|
|
|
|
|
|
HEAD_SET_RASTER_BLANK_START(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_BLANK_START, X, m->h.blanks) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_BLANK_START, Y, m->v.blanks),
|
|
|
|
|
|
|
|
HEAD_SET_RASTER_VERT_BLANK2(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_VERT_BLANK2, YSTART, m->v.blank2s) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_RASTER_VERT_BLANK2, YEND, m->v.blank2e));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_DEFAULT_BASE_COLOR(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, RED, 0) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, GREEN, 0) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_DEFAULT_BASE_COLOR, BLUE, 0),
|
|
|
|
|
|
|
|
HEAD_SET_CRC_CONTROL(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, CORE) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, TIMESTAMP_MODE, FALSE) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, PRIMARY_OUTPUT, NONE) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CRC_CONTROL, SECONDARY_OUTPUT, NONE));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, HERTZ, m->clock * 1000) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY, ADJ1000DIV1001, FALSE),
|
|
|
|
|
|
|
|
HEAD_SET_PIXEL_CLOCK_CONFIGURATION(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, MODE, CLK_CUSTOM) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, NOT_DRIVER, FALSE) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_CONFIGURATION, ENABLE_HOPPING, FALSE),
|
|
|
|
|
|
|
|
HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, HERTZ, m->clock * 1000) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_PIXEL_CLOCK_FREQUENCY_MAX, ADJ1000DIV1001, FALSE));
|
2020-06-21 06:51:03 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
2020-06-20 16:34:00 +08:00
|
|
|
int
|
2018-05-08 18:39:47 +08:00
|
|
|
head907d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|
|
|
{
|
2020-06-20 16:34:00 +08:00
|
|
|
struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
|
|
|
|
const int i = head->base.index;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((ret = PUSH_WAIT(push, 8)))
|
|
|
|
return ret;
|
|
|
|
|
2020-06-21 08:55:21 +08:00
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER(i),
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, VERTICAL_TAPS, TAPS_1) |
|
|
|
|
NVDEF(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, HORIZONTAL_TAPS, TAPS_1) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, HRESPONSE_BIAS, 0) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_CONTROL_OUTPUT_SCALER, VRESPONSE_BIAS, 0));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_SIZE_IN(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_IN, WIDTH, asyh->view.iW) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_IN, HEIGHT, asyh->view.iH));
|
|
|
|
|
|
|
|
PUSH_MTHD(push, NV907D, HEAD_SET_VIEWPORT_SIZE_OUT(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT, WIDTH, asyh->view.oW) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT, HEIGHT, asyh->view.oH),
|
|
|
|
|
|
|
|
HEAD_SET_VIEWPORT_SIZE_OUT_MIN(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, WIDTH, asyh->view.oW) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MIN, HEIGHT, asyh->view.oH),
|
|
|
|
|
|
|
|
HEAD_SET_VIEWPORT_SIZE_OUT_MAX(i),
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MAX, WIDTH, asyh->view.oW) |
|
|
|
|
NVVAL(NV907D, HEAD_SET_VIEWPORT_SIZE_OUT_MAX, HEIGHT, asyh->view.oH));
|
2020-06-20 16:34:00 +08:00
|
|
|
return 0;
|
2018-05-08 18:39:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const struct nv50_head_func
|
|
|
|
head907d = {
|
|
|
|
.view = head907d_view,
|
|
|
|
.mode = head907d_mode,
|
2018-05-08 18:39:47 +08:00
|
|
|
.olut = head907d_olut,
|
2019-09-06 12:13:59 +08:00
|
|
|
.olut_size = 1024,
|
2018-05-08 18:39:47 +08:00
|
|
|
.olut_set = head907d_olut_set,
|
|
|
|
.olut_clr = head907d_olut_clr,
|
2018-05-08 18:39:47 +08:00
|
|
|
.core_calc = head507d_core_calc,
|
|
|
|
.core_set = head907d_core_set,
|
|
|
|
.core_clr = head907d_core_clr,
|
2018-05-08 18:39:47 +08:00
|
|
|
.curs_layout = head507d_curs_layout,
|
|
|
|
.curs_format = head507d_curs_format,
|
2018-05-08 18:39:47 +08:00
|
|
|
.curs_set = head907d_curs_set,
|
|
|
|
.curs_clr = head907d_curs_clr,
|
|
|
|
.base = head907d_base,
|
|
|
|
.ovly = head907d_ovly,
|
|
|
|
.dither = head907d_dither,
|
|
|
|
.procamp = head907d_procamp,
|
|
|
|
.or = head907d_or,
|
|
|
|
};
|