mirror of
https://github.com/torvalds/linux.git
synced 2026-04-26 18:42:25 -04:00
Turns out that one of the ways that Nvidia's driver handles the pre-LT timeout for eDP panels is by providing a retry timeout in their link training callbacks that we're expected to wait for. Up until now we didn't pay any attention to this parameter. So, start honoring the timeout if link training fails - and retry up to 3 times. The "3 times" bit comes from OpenRM's link training code. [airlied: this fixes the panel on one of my laptops] Signed-off-by: Lyude Paul <lyude@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20231222043308.3090089-12-airlied@gmail.com
1715 lines
43 KiB
C
1715 lines
43 KiB
C
/*
|
|
* Copyright 2023 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 "priv.h"
|
|
#include "chan.h"
|
|
#include "conn.h"
|
|
#include "dp.h"
|
|
#include "head.h"
|
|
#include "ior.h"
|
|
#include "outp.h"
|
|
|
|
#include <core/ramht.h>
|
|
#include <subdev/bios.h>
|
|
#include <subdev/bios/conn.h>
|
|
#include <subdev/gsp.h>
|
|
#include <subdev/mmu.h>
|
|
#include <subdev/vfn.h>
|
|
|
|
#include <nvhw/drf.h>
|
|
|
|
#include <nvrm/nvtypes.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/class/cl2080_notification.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/ctrl/ctrl0073/ctrl0073dfp.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/ctrl/ctrl0073/ctrl0073dp.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/ctrl/ctrl0073/ctrl0073specific.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/ctrl/ctrl0073/ctrl0073system.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/ctrl/ctrl2080/ctrl2080internal.h>
|
|
#include <nvrm/535.113.01/common/sdk/nvidia/inc/nvos.h>
|
|
#include <nvrm/535.113.01/nvidia/generated/g_allclasses.h>
|
|
#include <nvrm/535.113.01/nvidia/generated/g_mem_desc_nvoc.h>
|
|
#include <nvrm/535.113.01/nvidia/inc/kernel/os/nv_memory_type.h>
|
|
|
|
#include <linux/acpi.h>
|
|
|
|
static u64
|
|
r535_chan_user(struct nvkm_disp_chan *chan, u64 *psize)
|
|
{
|
|
switch (chan->object.oclass & 0xff) {
|
|
case 0x7d: *psize = 0x10000; return 0x680000;
|
|
case 0x7e: *psize = 0x01000; return 0x690000 + (chan->head * *psize);
|
|
case 0x7b: *psize = 0x01000; return 0x6b0000 + (chan->head * *psize);
|
|
case 0x7a: *psize = 0x01000; return 0x6d8000 + (chan->head * *psize);
|
|
default:
|
|
BUG_ON(1);
|
|
break;
|
|
}
|
|
|
|
return 0ULL;
|
|
}
|
|
|
|
static void
|
|
r535_chan_intr(struct nvkm_disp_chan *chan, bool en)
|
|
{
|
|
}
|
|
|
|
static void
|
|
r535_chan_fini(struct nvkm_disp_chan *chan)
|
|
{
|
|
nvkm_gsp_rm_free(&chan->rm.object);
|
|
}
|
|
|
|
static int
|
|
r535_chan_push(struct nvkm_disp_chan *chan)
|
|
{
|
|
struct nvkm_gsp *gsp = chan->disp->engine.subdev.device->gsp;
|
|
NV2080_CTRL_INTERNAL_DISPLAY_CHANNEL_PUSHBUFFER_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&gsp->internal.device.subdevice,
|
|
NV2080_CTRL_CMD_INTERNAL_DISPLAY_CHANNEL_PUSHBUFFER,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
if (chan->memory) {
|
|
switch (nvkm_memory_target(chan->memory)) {
|
|
case NVKM_MEM_TARGET_NCOH:
|
|
ctrl->addressSpace = ADDR_SYSMEM;
|
|
ctrl->cacheSnoop = 0;
|
|
break;
|
|
case NVKM_MEM_TARGET_HOST:
|
|
ctrl->addressSpace = ADDR_SYSMEM;
|
|
ctrl->cacheSnoop = 1;
|
|
break;
|
|
case NVKM_MEM_TARGET_VRAM:
|
|
ctrl->addressSpace = ADDR_FBMEM;
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctrl->physicalAddr = nvkm_memory_addr(chan->memory);
|
|
ctrl->limit = nvkm_memory_size(chan->memory) - 1;
|
|
}
|
|
|
|
ctrl->hclass = chan->object.oclass;
|
|
ctrl->channelInstance = chan->head;
|
|
ctrl->valid = ((chan->object.oclass & 0xff) != 0x7a) ? 1 : 0;
|
|
|
|
return nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
|
|
}
|
|
|
|
static int
|
|
r535_curs_init(struct nvkm_disp_chan *chan)
|
|
{
|
|
NV50VAIO_CHANNELPIO_ALLOCATION_PARAMETERS *args;
|
|
int ret;
|
|
|
|
ret = r535_chan_push(chan);
|
|
if (ret)
|
|
return ret;
|
|
|
|
args = nvkm_gsp_rm_alloc_get(&chan->disp->rm.object,
|
|
(chan->object.oclass << 16) | chan->head,
|
|
chan->object.oclass, sizeof(*args), &chan->rm.object);
|
|
if (IS_ERR(args))
|
|
return PTR_ERR(args);
|
|
|
|
args->channelInstance = chan->head;
|
|
|
|
return nvkm_gsp_rm_alloc_wr(&chan->rm.object, args);
|
|
}
|
|
|
|
static const struct nvkm_disp_chan_func
|
|
r535_curs_func = {
|
|
.init = r535_curs_init,
|
|
.fini = r535_chan_fini,
|
|
.intr = r535_chan_intr,
|
|
.user = r535_chan_user,
|
|
};
|
|
|
|
static const struct nvkm_disp_chan_user
|
|
r535_curs = {
|
|
.func = &r535_curs_func,
|
|
.user = 73,
|
|
};
|
|
|
|
static int
|
|
r535_dmac_bind(struct nvkm_disp_chan *chan, struct nvkm_object *object, u32 handle)
|
|
{
|
|
return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -9, handle,
|
|
chan->chid.user << 25 |
|
|
(chan->disp->rm.client.object.handle & 0x3fff));
|
|
}
|
|
|
|
static void
|
|
r535_dmac_fini(struct nvkm_disp_chan *chan)
|
|
{
|
|
struct nvkm_device *device = chan->disp->engine.subdev.device;
|
|
const u32 uoff = (chan->chid.user - 1) * 0x1000;
|
|
|
|
chan->suspend_put = nvkm_rd32(device, 0x690000 + uoff);
|
|
r535_chan_fini(chan);
|
|
}
|
|
|
|
static int
|
|
r535_dmac_init(struct nvkm_disp_chan *chan)
|
|
{
|
|
NV50VAIO_CHANNELDMA_ALLOCATION_PARAMETERS *args;
|
|
int ret;
|
|
|
|
ret = r535_chan_push(chan);
|
|
if (ret)
|
|
return ret;
|
|
|
|
args = nvkm_gsp_rm_alloc_get(&chan->disp->rm.object,
|
|
(chan->object.oclass << 16) | chan->head,
|
|
chan->object.oclass, sizeof(*args), &chan->rm.object);
|
|
if (IS_ERR(args))
|
|
return PTR_ERR(args);
|
|
|
|
args->channelInstance = chan->head;
|
|
args->offset = chan->suspend_put;
|
|
|
|
return nvkm_gsp_rm_alloc_wr(&chan->rm.object, args);
|
|
}
|
|
|
|
static int
|
|
r535_dmac_push(struct nvkm_disp_chan *chan, u64 memory)
|
|
{
|
|
chan->memory = nvkm_umem_search(chan->object.client, memory);
|
|
if (IS_ERR(chan->memory))
|
|
return PTR_ERR(chan->memory);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct nvkm_disp_chan_func
|
|
r535_dmac_func = {
|
|
.push = r535_dmac_push,
|
|
.init = r535_dmac_init,
|
|
.fini = r535_dmac_fini,
|
|
.intr = r535_chan_intr,
|
|
.user = r535_chan_user,
|
|
.bind = r535_dmac_bind,
|
|
};
|
|
|
|
static const struct nvkm_disp_chan_func
|
|
r535_wimm_func = {
|
|
.push = r535_dmac_push,
|
|
.init = r535_dmac_init,
|
|
.fini = r535_dmac_fini,
|
|
.intr = r535_chan_intr,
|
|
.user = r535_chan_user,
|
|
};
|
|
|
|
static const struct nvkm_disp_chan_user
|
|
r535_wimm = {
|
|
.func = &r535_wimm_func,
|
|
.user = 33,
|
|
};
|
|
|
|
static const struct nvkm_disp_chan_user
|
|
r535_wndw = {
|
|
.func = &r535_dmac_func,
|
|
.user = 1,
|
|
};
|
|
|
|
static void
|
|
r535_core_fini(struct nvkm_disp_chan *chan)
|
|
{
|
|
struct nvkm_device *device = chan->disp->engine.subdev.device;
|
|
|
|
chan->suspend_put = nvkm_rd32(device, 0x680000);
|
|
r535_chan_fini(chan);
|
|
}
|
|
|
|
static const struct nvkm_disp_chan_func
|
|
r535_core_func = {
|
|
.push = r535_dmac_push,
|
|
.init = r535_dmac_init,
|
|
.fini = r535_core_fini,
|
|
.intr = r535_chan_intr,
|
|
.user = r535_chan_user,
|
|
.bind = r535_dmac_bind,
|
|
};
|
|
|
|
static const struct nvkm_disp_chan_user
|
|
r535_core = {
|
|
.func = &r535_core_func,
|
|
.user = 0,
|
|
};
|
|
|
|
static int
|
|
r535_sor_bl_set(struct nvkm_ior *sor, int lvl)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_SPECIFIC_BACKLIGHT_BRIGHTNESS_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_SET_BACKLIGHT_BRIGHTNESS,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
ctrl->brightness = lvl;
|
|
|
|
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
|
|
}
|
|
|
|
static int
|
|
r535_sor_bl_get(struct nvkm_ior *sor)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_SPECIFIC_BACKLIGHT_BRIGHTNESS_PARAMS *ctrl;
|
|
int ret, lvl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_GET_BACKLIGHT_BRIGHTNESS,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
lvl = ctrl->brightness;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return lvl;
|
|
}
|
|
|
|
static const struct nvkm_ior_func_bl
|
|
r535_sor_bl = {
|
|
.get = r535_sor_bl_get,
|
|
.set = r535_sor_bl_set,
|
|
};
|
|
|
|
static void
|
|
r535_sor_hda_eld(struct nvkm_ior *sor, int head, u8 *data, u8 size)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_DFP_SET_ELD_AUDIO_CAP_PARAMS *ctrl;
|
|
|
|
if (WARN_ON(size > sizeof(ctrl->bufferELD)))
|
|
return;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DFP_SET_ELD_AUDIO_CAPS, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
ctrl->numELDSize = size;
|
|
memcpy(ctrl->bufferELD, data, size);
|
|
ctrl->maxFreqSupported = 0; //XXX
|
|
ctrl->ctrl = NVDEF(NV0073, CTRL_DFP_ELD_AUDIO_CAPS_CTRL, PD, TRUE);
|
|
ctrl->ctrl |= NVDEF(NV0073, CTRL_DFP_ELD_AUDIO_CAPS_CTRL, ELDV, TRUE);
|
|
ctrl->deviceEntry = head;
|
|
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static void
|
|
r535_sor_hda_hpd(struct nvkm_ior *sor, int head, bool present)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_DFP_SET_ELD_AUDIO_CAP_PARAMS *ctrl;
|
|
|
|
if (present)
|
|
return;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DFP_SET_ELD_AUDIO_CAPS, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
ctrl->deviceEntry = head;
|
|
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static const struct nvkm_ior_func_hda
|
|
r535_sor_hda = {
|
|
.hpd = r535_sor_hda_hpd,
|
|
.eld = r535_sor_hda_eld,
|
|
};
|
|
|
|
static void
|
|
r535_sor_dp_audio_mute(struct nvkm_ior *sor, bool mute)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_DP_SET_AUDIO_MUTESTREAM_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_SET_AUDIO_MUTESTREAM, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
ctrl->mute = mute;
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static void
|
|
r535_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_DFP_SET_AUDIO_ENABLE_PARAMS *ctrl;
|
|
|
|
if (!enable)
|
|
r535_sor_dp_audio_mute(sor, true);
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DFP_SET_AUDIO_ENABLE, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
ctrl->enable = enable;
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
|
|
if (enable)
|
|
r535_sor_dp_audio_mute(sor, false);
|
|
}
|
|
|
|
static void
|
|
r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->head = head;
|
|
ctrl->sorIndex = sor->id;
|
|
ctrl->dpLink = sor->asy.link == 2;
|
|
ctrl->bEnableOverride = 1;
|
|
ctrl->bMST = 1;
|
|
ctrl->hBlankSym = 0;
|
|
ctrl->vBlankSym = 0;
|
|
ctrl->colorFormat = 0;
|
|
ctrl->bEnableTwoHeadOneOr = 0;
|
|
ctrl->singleHeadMultistreamMode = 0;
|
|
ctrl->MST.slotStart = slot;
|
|
ctrl->MST.slotEnd = slot + slot_nr - 1;
|
|
ctrl->MST.PBN = pbn;
|
|
ctrl->MST.Timeslice = aligned_pbn;
|
|
ctrl->MST.sendACT = 0;
|
|
ctrl->MST.singleHeadMSTPipeline = 0;
|
|
ctrl->MST.bEnableAudioOverRightPanel = 0;
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static int
|
|
r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
|
|
u32 watermark, u32 hblanksym, u32 vblanksym)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->head = head;
|
|
ctrl->sorIndex = sor->id;
|
|
ctrl->dpLink = sor->asy.link == 2;
|
|
ctrl->bEnableOverride = 1;
|
|
ctrl->bMST = 0;
|
|
ctrl->hBlankSym = hblanksym;
|
|
ctrl->vBlankSym = vblanksym;
|
|
ctrl->colorFormat = 0;
|
|
ctrl->bEnableTwoHeadOneOr = 0;
|
|
ctrl->SST.bEnhancedFraming = ef;
|
|
ctrl->SST.tuSize = 64;
|
|
ctrl->SST.waterMark = watermark;
|
|
ctrl->SST.bEnableAudioOverRightPanel = 0;
|
|
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
|
|
}
|
|
|
|
static const struct nvkm_ior_func_dp
|
|
r535_sor_dp = {
|
|
.sst = r535_sor_dp_sst,
|
|
.vcpi = r535_sor_dp_vcpi,
|
|
.audio = r535_sor_dp_audio,
|
|
};
|
|
|
|
static void
|
|
r535_sor_hdmi_scdc(struct nvkm_ior *sor, u32 khz, bool support, bool scrambling,
|
|
bool scrambling_low_rates)
|
|
{
|
|
struct nvkm_outp *outp = sor->asy.outp;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
NV0073_CTRL_SPECIFIC_SET_HDMI_SINK_CAPS_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_SET_HDMI_SINK_CAPS, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->caps = 0;
|
|
if (support)
|
|
ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, SCDC_SUPPORTED, TRUE);
|
|
if (scrambling)
|
|
ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, GT_340MHZ_CLOCK_SUPPORTED, TRUE);
|
|
if (scrambling_low_rates)
|
|
ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, LTE_340MHZ_SCRAMBLING_SUPPORTED, TRUE);
|
|
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static void
|
|
r535_sor_hdmi_ctrl_audio_mute(struct nvkm_outp *outp, bool mute)
|
|
{
|
|
struct nvkm_disp *disp = outp->disp;
|
|
NV0073_CTRL_CMD_SPECIFIC_SET_HDMI_AUDIO_MUTESTREAM_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_SET_HDMI_AUDIO_MUTESTREAM, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->mute = mute;
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static void
|
|
r535_sor_hdmi_ctrl_audio(struct nvkm_outp *outp, bool enable)
|
|
{
|
|
struct nvkm_disp *disp = outp->disp;
|
|
NV0073_CTRL_SPECIFIC_SET_OD_PACKET_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_SET_OD_PACKET, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->transmitControl =
|
|
NVDEF(NV0073_CTRL_SPECIFIC, SET_OD_PACKET_TRANSMIT_CONTROL, ENABLE, YES) |
|
|
NVDEF(NV0073_CTRL_SPECIFIC, SET_OD_PACKET_TRANSMIT_CONTROL, OTHER_FRAME, DISABLE) |
|
|
NVDEF(NV0073_CTRL_SPECIFIC, SET_OD_PACKET_TRANSMIT_CONTROL, SINGLE_FRAME, DISABLE) |
|
|
NVDEF(NV0073_CTRL_SPECIFIC, SET_OD_PACKET_TRANSMIT_CONTROL, ON_HBLANK, DISABLE) |
|
|
NVDEF(NV0073_CTRL_SPECIFIC, SET_OD_PACKET_TRANSMIT_CONTROL, VIDEO_FMT, SW_CONTROLLED) |
|
|
NVDEF(NV0073_CTRL_SPECIFIC, SET_OD_PACKET_TRANSMIT_CONTROL, RESERVED_LEGACY_MODE, NO);
|
|
ctrl->packetSize = 10;
|
|
ctrl->aPacket[0] = 0x03;
|
|
ctrl->aPacket[1] = 0x00;
|
|
ctrl->aPacket[2] = 0x00;
|
|
ctrl->aPacket[3] = enable ? 0x10 : 0x01;
|
|
ctrl->aPacket[4] = 0x00;
|
|
ctrl->aPacket[5] = 0x00;
|
|
ctrl->aPacket[6] = 0x00;
|
|
ctrl->aPacket[7] = 0x00;
|
|
ctrl->aPacket[8] = 0x00;
|
|
ctrl->aPacket[9] = 0x00;
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static void
|
|
r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable)
|
|
{
|
|
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
|
const u32 hdmi = head * 0x400;
|
|
|
|
r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable);
|
|
r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable);
|
|
|
|
/* General Control (GCP). */
|
|
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
|
|
nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
|
|
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
|
|
}
|
|
|
|
static void
|
|
r535_sor_hdmi_ctrl(struct nvkm_ior *sor, int head, bool enable, u8 max_ac_packet, u8 rekey)
|
|
{
|
|
struct nvkm_disp *disp = sor->disp;
|
|
NV0073_CTRL_SPECIFIC_SET_HDMI_ENABLE_PARAMS *ctrl;
|
|
|
|
if (!enable)
|
|
return;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_SET_HDMI_ENABLE, sizeof(*ctrl));
|
|
if (WARN_ON(IS_ERR(ctrl)))
|
|
return;
|
|
|
|
ctrl->displayId = BIT(sor->asy.outp->index);
|
|
ctrl->enable = enable;
|
|
|
|
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
|
|
}
|
|
|
|
static const struct nvkm_ior_func_hdmi
|
|
r535_sor_hdmi = {
|
|
.ctrl = r535_sor_hdmi_ctrl,
|
|
.scdc = r535_sor_hdmi_scdc,
|
|
/*TODO: SF_USER -> KMS. */
|
|
.infoframe_avi = gv100_sor_hdmi_infoframe_avi,
|
|
.infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
|
|
.audio = r535_sor_hdmi_audio,
|
|
};
|
|
|
|
static const struct nvkm_ior_func
|
|
r535_sor = {
|
|
.hdmi = &r535_sor_hdmi,
|
|
.dp = &r535_sor_dp,
|
|
.hda = &r535_sor_hda,
|
|
.bl = &r535_sor_bl,
|
|
};
|
|
|
|
static int
|
|
r535_sor_new(struct nvkm_disp *disp, int id)
|
|
{
|
|
return nvkm_ior_new_(&r535_sor, disp, SOR, id, true/*XXX: hda cap*/);
|
|
}
|
|
|
|
static int
|
|
r535_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask)
|
|
{
|
|
*pmask = 0xf;
|
|
return 4;
|
|
}
|
|
|
|
static void
|
|
r535_head_vblank_put(struct nvkm_head *head)
|
|
{
|
|
struct nvkm_device *device = head->disp->engine.subdev.device;
|
|
|
|
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000);
|
|
}
|
|
|
|
static void
|
|
r535_head_vblank_get(struct nvkm_head *head)
|
|
{
|
|
struct nvkm_device *device = head->disp->engine.subdev.device;
|
|
|
|
nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
|
|
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
|
|
}
|
|
|
|
static void
|
|
r535_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
|
|
{
|
|
}
|
|
|
|
static const struct nvkm_head_func
|
|
r535_head = {
|
|
.state = r535_head_state,
|
|
.vblank_get = r535_head_vblank_get,
|
|
.vblank_put = r535_head_vblank_put,
|
|
};
|
|
|
|
static struct nvkm_conn *
|
|
r535_conn_new(struct nvkm_disp *disp, u32 id)
|
|
{
|
|
NV0073_CTRL_SPECIFIC_GET_CONNECTOR_DATA_PARAMS *ctrl;
|
|
struct nvbios_connE dcbE = {};
|
|
struct nvkm_conn *conn;
|
|
int ret, index;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_GET_CONNECTOR_DATA, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return (void *)ctrl;
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(id);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
list_for_each_entry(conn, &disp->conns, head) {
|
|
if (conn->index == ctrl->data[0].index) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return conn;
|
|
}
|
|
}
|
|
|
|
dcbE.type = ctrl->data[0].type;
|
|
index = ctrl->data[0].index;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
ret = nvkm_conn_new(disp, index, &dcbE, &conn);
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
|
|
list_add_tail(&conn->head, &disp->conns);
|
|
return conn;
|
|
}
|
|
|
|
static void
|
|
r535_outp_release(struct nvkm_outp *outp)
|
|
{
|
|
outp->disp->rm.assigned_sors &= ~BIT(outp->ior->id);
|
|
outp->ior->asy.outp = NULL;
|
|
outp->ior = NULL;
|
|
}
|
|
|
|
static int
|
|
r535_outp_acquire(struct nvkm_outp *outp, bool hda)
|
|
{
|
|
struct nvkm_disp *disp = outp->disp;
|
|
struct nvkm_ior *ior;
|
|
NV0073_CTRL_DFP_ASSIGN_SOR_PARAMS *ctrl;
|
|
int ret, or;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DFP_ASSIGN_SOR, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->sorExcludeMask = disp->rm.assigned_sors;
|
|
if (hda)
|
|
ctrl->flags |= NVDEF(NV0073_CTRL, DFP_ASSIGN_SOR_FLAGS, AUDIO, OPTIMAL);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
for (or = 0; or < ARRAY_SIZE(ctrl->sorAssignListWithTag); or++) {
|
|
if (ctrl->sorAssignListWithTag[or].displayMask & BIT(outp->index)) {
|
|
disp->rm.assigned_sors |= BIT(or);
|
|
break;
|
|
}
|
|
}
|
|
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
if (WARN_ON(or == ARRAY_SIZE(ctrl->sorAssignListWithTag)))
|
|
return -EINVAL;
|
|
|
|
ior = nvkm_ior_find(disp, SOR, or);
|
|
if (WARN_ON(!ior))
|
|
return -EINVAL;
|
|
|
|
nvkm_outp_acquire_ior(outp, NVKM_OUTP_USER, ior);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
r535_disp_head_displayid(struct nvkm_disp *disp, int head, u32 *displayid)
|
|
{
|
|
NV0073_CTRL_SYSTEM_GET_ACTIVE_PARAMS *ctrl;
|
|
int ret;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SYSTEM_GET_ACTIVE, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->head = head;
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
*displayid = ctrl->displayId;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return 0;
|
|
}
|
|
|
|
static struct nvkm_ior *
|
|
r535_outp_inherit(struct nvkm_outp *outp)
|
|
{
|
|
struct nvkm_disp *disp = outp->disp;
|
|
struct nvkm_head *head;
|
|
u32 displayid;
|
|
int ret;
|
|
|
|
list_for_each_entry(head, &disp->heads, head) {
|
|
ret = r535_disp_head_displayid(disp, head->id, &displayid);
|
|
if (WARN_ON(ret))
|
|
return NULL;
|
|
|
|
if (displayid == BIT(outp->index)) {
|
|
NV0073_CTRL_SPECIFIC_OR_GET_INFO_PARAMS *ctrl;
|
|
u32 id, proto;
|
|
struct nvkm_ior *ior;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_OR_GET_INFO,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return NULL;
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = displayid;
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return NULL;
|
|
}
|
|
|
|
id = ctrl->index;
|
|
proto = ctrl->protocol;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
ior = nvkm_ior_find(disp, SOR, id);
|
|
if (WARN_ON(!ior))
|
|
return NULL;
|
|
|
|
switch (proto) {
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_SINGLE_TMDS_A:
|
|
ior->arm.proto = TMDS;
|
|
ior->arm.link = 1;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_SINGLE_TMDS_B:
|
|
ior->arm.proto = TMDS;
|
|
ior->arm.link = 2;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DUAL_TMDS:
|
|
ior->arm.proto = TMDS;
|
|
ior->arm.link = 3;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_A:
|
|
ior->arm.proto = DP;
|
|
ior->arm.link = 1;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_B:
|
|
ior->arm.proto = DP;
|
|
ior->arm.link = 2;
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
return NULL;
|
|
}
|
|
|
|
ior->arm.proto_evo = proto;
|
|
ior->arm.head = BIT(head->id);
|
|
disp->rm.assigned_sors |= BIT(ior->id);
|
|
return ior;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
r535_outp_dfp_get_info(struct nvkm_outp *outp)
|
|
{
|
|
NV0073_CTRL_DFP_GET_INFO_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
int ret;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_DFP_GET_INFO, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->displayId = BIT(outp->index);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
nvkm_debug(&disp->engine.subdev, "DFP %08x: flags:%08x flags2:%08x\n",
|
|
ctrl->displayId, ctrl->flags, ctrl->flags2);
|
|
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
r535_outp_detect(struct nvkm_outp *outp)
|
|
{
|
|
NV0073_CTRL_SYSTEM_GET_CONNECT_STATE_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
int ret;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SYSTEM_GET_CONNECT_STATE, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayMask = BIT(outp->index);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
if (ctrl->displayMask & BIT(outp->index)) {
|
|
ret = r535_outp_dfp_get_info(outp);
|
|
if (ret == 0)
|
|
ret = 1;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
r535_dp_mst_id_put(struct nvkm_outp *outp, u32 id)
|
|
{
|
|
NV0073_CTRL_CMD_DP_TOPOLOGY_FREE_DISPLAYID_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_TOPOLOGY_FREE_DISPLAYID, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = id;
|
|
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
|
|
}
|
|
|
|
static int
|
|
r535_dp_mst_id_get(struct nvkm_outp *outp, u32 *pid)
|
|
{
|
|
NV0073_CTRL_CMD_DP_TOPOLOGY_ALLOCATE_DISPLAYID_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
int ret;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_TOPOLOGY_ALLOCATE_DISPLAYID,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(outp->index);
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
*pid = ctrl->displayIdAssigned;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
r535_dp_drive(struct nvkm_outp *outp, u8 lanes, u8 pe[4], u8 vs[4])
|
|
{
|
|
NV0073_CTRL_DP_LANE_DATA_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_SET_LANE_DATA, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->numLanes = lanes;
|
|
for (int i = 0; i < lanes; i++)
|
|
ctrl->data[i] = NVVAL(NV0073_CTRL, DP_LANE_DATA, PREEMPHASIS, pe[i]) |
|
|
NVVAL(NV0073_CTRL, DP_LANE_DATA, DRIVECURRENT, vs[i]);
|
|
|
|
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
|
|
}
|
|
|
|
static int
|
|
r535_dp_train_target(struct nvkm_outp *outp, u8 target, bool mst, u8 link_nr, u8 link_bw)
|
|
{
|
|
struct nvkm_disp *disp = outp->disp;
|
|
NV0073_CTRL_DP_CTRL_PARAMS *ctrl;
|
|
int ret, retries;
|
|
u32 cmd, data;
|
|
|
|
cmd = NVDEF(NV0073_CTRL, DP_CMD, SET_LANE_COUNT, TRUE) |
|
|
NVDEF(NV0073_CTRL, DP_CMD, SET_LINK_BW, TRUE) |
|
|
NVDEF(NV0073_CTRL, DP_CMD, TRAIN_PHY_REPEATER, YES);
|
|
data = NVVAL(NV0073_CTRL, DP_DATA, SET_LANE_COUNT, link_nr) |
|
|
NVVAL(NV0073_CTRL, DP_DATA, SET_LINK_BW, link_bw) |
|
|
NVVAL(NV0073_CTRL, DP_DATA, TARGET, target);
|
|
|
|
if (mst)
|
|
cmd |= NVDEF(NV0073_CTRL, DP_CMD, SET_FORMAT_MODE, MULTI_STREAM);
|
|
|
|
if (outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
|
|
cmd |= NVDEF(NV0073_CTRL, DP_CMD, SET_ENHANCED_FRAMING, TRUE);
|
|
|
|
if (target == 0 &&
|
|
(outp->dp.dpcd[DPCD_RC02] & 0x20) &&
|
|
!(outp->dp.dpcd[DPCD_RC03] & DPCD_RC03_TPS4_SUPPORTED))
|
|
cmd |= NVDEF(NV0073_CTRL, DP_CMD, POST_LT_ADJ_REQ_GRANTED, YES);
|
|
|
|
/* We should retry up to 3 times, but only if GSP asks politely */
|
|
for (retries = 0; retries < 3; ++retries) {
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_DP_CTRL,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->retryTimeMs = 0;
|
|
ctrl->cmd = cmd;
|
|
ctrl->data = data;
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret == -EAGAIN && ctrl->retryTimeMs) {
|
|
/*
|
|
* Device (likely an eDP panel) isn't ready yet, wait for the time specified
|
|
* by GSP before retrying again
|
|
*/
|
|
nvkm_debug(&disp->engine.subdev,
|
|
"Waiting %dms for GSP LT panel delay before retrying\n",
|
|
ctrl->retryTimeMs);
|
|
msleep(ctrl->retryTimeMs);
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
} else {
|
|
/* GSP didn't say to retry, or we were successful */
|
|
if (ctrl->err)
|
|
ret = -EIO;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
r535_dp_train(struct nvkm_outp *outp, bool retrain)
|
|
{
|
|
for (int target = outp->dp.lttprs; target >= 0; target--) {
|
|
int ret = r535_dp_train_target(outp, target, outp->dp.lt.mst,
|
|
outp->dp.lt.nr,
|
|
outp->dp.lt.bw);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
r535_dp_rates(struct nvkm_outp *outp)
|
|
{
|
|
NV0073_CTRL_CMD_DP_CONFIG_INDEXED_LINK_RATES_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
|
|
if (outp->conn->info.type != DCB_CONNECTOR_eDP ||
|
|
!outp->dp.rates || outp->dp.rate[0].dpcd < 0)
|
|
return 0;
|
|
|
|
if (WARN_ON(outp->dp.rates > ARRAY_SIZE(ctrl->linkRateTbl)))
|
|
return -EINVAL;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_CONFIG_INDEXED_LINK_RATES, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->displayId = BIT(outp->index);
|
|
for (int i = 0; i < outp->dp.rates; i++)
|
|
ctrl->linkRateTbl[outp->dp.rate[i].dpcd] = outp->dp.rate[i].rate * 10 / 200;
|
|
|
|
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
|
|
}
|
|
|
|
static int
|
|
r535_dp_aux_xfer(struct nvkm_outp *outp, u8 type, u32 addr, u8 *data, u8 *psize)
|
|
{
|
|
struct nvkm_disp *disp = outp->disp;
|
|
NV0073_CTRL_DP_AUXCH_CTRL_PARAMS *ctrl;
|
|
u8 size = *psize;
|
|
int ret;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_DP_AUXCH_CTRL, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(outp->index);
|
|
ctrl->bAddrOnly = !size;
|
|
ctrl->cmd = type;
|
|
if (ctrl->bAddrOnly) {
|
|
ctrl->cmd = NVDEF_SET(ctrl->cmd, NV0073_CTRL, DP_AUXCH_CMD, REQ_TYPE, WRITE);
|
|
ctrl->cmd = NVDEF_SET(ctrl->cmd, NV0073_CTRL, DP_AUXCH_CMD, I2C_MOT, FALSE);
|
|
}
|
|
ctrl->addr = addr;
|
|
ctrl->size = !ctrl->bAddrOnly ? (size - 1) : 0;
|
|
memcpy(ctrl->data, data, size);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return PTR_ERR(ctrl);
|
|
}
|
|
|
|
memcpy(data, ctrl->data, size);
|
|
*psize = ctrl->size;
|
|
ret = ctrl->replyType;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
r535_dp_aux_pwr(struct nvkm_outp *outp, bool pu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
r535_dp_release(struct nvkm_outp *outp)
|
|
{
|
|
if (!outp->dp.lt.bw) {
|
|
if (!WARN_ON(!outp->dp.rates))
|
|
outp->dp.lt.bw = outp->dp.rate[0].rate / 27000;
|
|
else
|
|
outp->dp.lt.bw = 0x06;
|
|
}
|
|
|
|
outp->dp.lt.nr = 0;
|
|
|
|
r535_dp_train_target(outp, 0, outp->dp.lt.mst, outp->dp.lt.nr, outp->dp.lt.bw);
|
|
r535_outp_release(outp);
|
|
}
|
|
|
|
static int
|
|
r535_dp_acquire(struct nvkm_outp *outp, bool hda)
|
|
{
|
|
int ret;
|
|
|
|
ret = r535_outp_acquire(outp, hda);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct nvkm_outp_func
|
|
r535_dp = {
|
|
.detect = r535_outp_detect,
|
|
.inherit = r535_outp_inherit,
|
|
.acquire = r535_dp_acquire,
|
|
.release = r535_dp_release,
|
|
.dp.aux_pwr = r535_dp_aux_pwr,
|
|
.dp.aux_xfer = r535_dp_aux_xfer,
|
|
.dp.mst_id_get = r535_dp_mst_id_get,
|
|
.dp.mst_id_put = r535_dp_mst_id_put,
|
|
.dp.rates = r535_dp_rates,
|
|
.dp.train = r535_dp_train,
|
|
.dp.drive = r535_dp_drive,
|
|
};
|
|
|
|
static int
|
|
r535_tmds_edid_get(struct nvkm_outp *outp, u8 *data, u16 *psize)
|
|
{
|
|
NV0073_CTRL_SPECIFIC_GET_EDID_V2_PARAMS *ctrl;
|
|
struct nvkm_disp *disp = outp->disp;
|
|
int ret = -E2BIG;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_GET_EDID_V2, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(outp->index);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
ret = -E2BIG;
|
|
if (ctrl->bufferSize <= *psize) {
|
|
memcpy(data, ctrl->edidBuffer, ctrl->bufferSize);
|
|
*psize = ctrl->bufferSize;
|
|
ret = 0;
|
|
}
|
|
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
static const struct nvkm_outp_func
|
|
r535_tmds = {
|
|
.detect = r535_outp_detect,
|
|
.inherit = r535_outp_inherit,
|
|
.acquire = r535_outp_acquire,
|
|
.release = r535_outp_release,
|
|
.edid_get = r535_tmds_edid_get,
|
|
};
|
|
|
|
static int
|
|
r535_outp_new(struct nvkm_disp *disp, u32 id)
|
|
{
|
|
NV0073_CTRL_SPECIFIC_OR_GET_INFO_PARAMS *ctrl;
|
|
enum nvkm_ior_proto proto;
|
|
struct dcb_output dcbE = {};
|
|
struct nvkm_conn *conn;
|
|
struct nvkm_outp *outp;
|
|
u8 locn, link = 0;
|
|
int ret;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_OR_GET_INFO, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->subDeviceInstance = 0;
|
|
ctrl->displayId = BIT(id);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
switch (ctrl->type) {
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_NONE:
|
|
return 0;
|
|
case NV0073_CTRL_SPECIFIC_OR_TYPE_SOR:
|
|
switch (ctrl->protocol) {
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_SINGLE_TMDS_A:
|
|
proto = TMDS;
|
|
link = 1;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_SINGLE_TMDS_B:
|
|
proto = TMDS;
|
|
link = 2;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DUAL_TMDS:
|
|
proto = TMDS;
|
|
link = 3;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_A:
|
|
proto = DP;
|
|
link = 1;
|
|
break;
|
|
case NV0073_CTRL_SPECIFIC_OR_PROTOCOL_SOR_DP_B:
|
|
proto = DP;
|
|
link = 2;
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
locn = ctrl->location;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
conn = r535_conn_new(disp, id);
|
|
if (IS_ERR(conn))
|
|
return PTR_ERR(conn);
|
|
|
|
switch (proto) {
|
|
case TMDS: dcbE.type = DCB_OUTPUT_TMDS; break;
|
|
case DP: dcbE.type = DCB_OUTPUT_DP; break;
|
|
default:
|
|
WARN_ON(1);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dcbE.location = locn;
|
|
dcbE.connector = conn->index;
|
|
dcbE.heads = disp->head.mask;
|
|
dcbE.i2c_index = 0xff;
|
|
dcbE.link = dcbE.sorconf.link = link;
|
|
|
|
if (proto == TMDS) {
|
|
ret = nvkm_outp_new_(&r535_tmds, disp, id, &dcbE, &outp);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
NV0073_CTRL_CMD_DP_GET_CAPS_PARAMS *ctrl;
|
|
bool mst, wm;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_GET_CAPS, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->sorIndex = ~0;
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(&disp->rm.objcom, &ctrl, sizeof(*ctrl));
|
|
if (ret) {
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
return ret;
|
|
}
|
|
|
|
switch (NVVAL_GET(ctrl->maxLinkRate, NV0073_CTRL_CMD, DP_GET_CAPS, MAX_LINK_RATE)) {
|
|
case NV0073_CTRL_CMD_DP_GET_CAPS_MAX_LINK_RATE_1_62:
|
|
dcbE.dpconf.link_bw = 0x06;
|
|
break;
|
|
case NV0073_CTRL_CMD_DP_GET_CAPS_MAX_LINK_RATE_2_70:
|
|
dcbE.dpconf.link_bw = 0x0a;
|
|
break;
|
|
case NV0073_CTRL_CMD_DP_GET_CAPS_MAX_LINK_RATE_5_40:
|
|
dcbE.dpconf.link_bw = 0x14;
|
|
break;
|
|
case NV0073_CTRL_CMD_DP_GET_CAPS_MAX_LINK_RATE_8_10:
|
|
dcbE.dpconf.link_bw = 0x1e;
|
|
break;
|
|
default:
|
|
dcbE.dpconf.link_bw = 0x00;
|
|
break;
|
|
}
|
|
|
|
mst = ctrl->bIsMultistreamSupported;
|
|
wm = ctrl->bHasIncreasedWatermarkLimits;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
if (WARN_ON(!dcbE.dpconf.link_bw))
|
|
return -EINVAL;
|
|
|
|
dcbE.dpconf.link_nr = 4;
|
|
|
|
ret = nvkm_outp_new_(&r535_dp, disp, id, &dcbE, &outp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
outp->dp.mst = mst;
|
|
outp->dp.increased_wm = wm;
|
|
}
|
|
|
|
|
|
outp->conn = conn;
|
|
list_add_tail(&outp->head, &disp->outps);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
r535_disp_irq(struct nvkm_gsp_event *event, void *repv, u32 repc)
|
|
{
|
|
struct nvkm_disp *disp = container_of(event, typeof(*disp), rm.irq);
|
|
Nv2080DpIrqNotification *irq = repv;
|
|
|
|
if (WARN_ON(repc < sizeof(*irq)))
|
|
return;
|
|
|
|
nvkm_debug(&disp->engine.subdev, "event: dp irq displayId %08x\n", irq->displayId);
|
|
|
|
if (irq->displayId)
|
|
nvkm_event_ntfy(&disp->rm.event, fls(irq->displayId) - 1, NVKM_DPYID_IRQ);
|
|
}
|
|
|
|
static void
|
|
r535_disp_hpd(struct nvkm_gsp_event *event, void *repv, u32 repc)
|
|
{
|
|
struct nvkm_disp *disp = container_of(event, typeof(*disp), rm.hpd);
|
|
Nv2080HotplugNotification *hpd = repv;
|
|
|
|
if (WARN_ON(repc < sizeof(*hpd)))
|
|
return;
|
|
|
|
nvkm_debug(&disp->engine.subdev, "event: hpd plug %08x unplug %08x\n",
|
|
hpd->plugDisplayMask, hpd->unplugDisplayMask);
|
|
|
|
for (int i = 0; i < 31; i++) {
|
|
u32 mask = 0;
|
|
|
|
if (hpd->plugDisplayMask & BIT(i))
|
|
mask |= NVKM_DPYID_PLUG;
|
|
if (hpd->unplugDisplayMask & BIT(i))
|
|
mask |= NVKM_DPYID_UNPLUG;
|
|
|
|
if (mask)
|
|
nvkm_event_ntfy(&disp->rm.event, i, mask);
|
|
}
|
|
}
|
|
|
|
static const struct nvkm_event_func
|
|
r535_disp_event = {
|
|
};
|
|
|
|
static void
|
|
r535_disp_intr_head_timing(struct nvkm_disp *disp, int head)
|
|
{
|
|
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
|
struct nvkm_device *device = subdev->device;
|
|
u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04));
|
|
|
|
if (stat & 0x00000002) {
|
|
nvkm_disp_vblank(disp, head);
|
|
|
|
nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002);
|
|
}
|
|
}
|
|
|
|
static irqreturn_t
|
|
r535_disp_intr(struct nvkm_inth *inth)
|
|
{
|
|
struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
|
|
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
|
struct nvkm_device *device = subdev->device;
|
|
unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff;
|
|
int head;
|
|
|
|
for_each_set_bit(head, &mask, 8)
|
|
r535_disp_intr_head_timing(disp, head);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void
|
|
r535_disp_fini(struct nvkm_disp *disp, bool suspend)
|
|
{
|
|
if (!disp->engine.subdev.use.enabled)
|
|
return;
|
|
|
|
nvkm_gsp_rm_free(&disp->rm.object);
|
|
|
|
if (!suspend) {
|
|
nvkm_gsp_event_dtor(&disp->rm.irq);
|
|
nvkm_gsp_event_dtor(&disp->rm.hpd);
|
|
nvkm_event_fini(&disp->rm.event);
|
|
|
|
nvkm_gsp_rm_free(&disp->rm.objcom);
|
|
nvkm_gsp_device_dtor(&disp->rm.device);
|
|
nvkm_gsp_client_dtor(&disp->rm.client);
|
|
}
|
|
}
|
|
|
|
static int
|
|
r535_disp_init(struct nvkm_disp *disp)
|
|
{
|
|
int ret;
|
|
|
|
ret = nvkm_gsp_rm_alloc(&disp->rm.device.object, disp->func->root.oclass << 16,
|
|
disp->func->root.oclass, 0, &disp->rm.object);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
r535_disp_oneinit(struct nvkm_disp *disp)
|
|
{
|
|
struct nvkm_device *device = disp->engine.subdev.device;
|
|
struct nvkm_gsp *gsp = device->gsp;
|
|
NV2080_CTRL_INTERNAL_DISPLAY_WRITE_INST_MEM_PARAMS *ctrl;
|
|
int ret, i;
|
|
|
|
/* RAMIN. */
|
|
ret = nvkm_gpuobj_new(device, 0x10000, 0x10000, false, NULL, &disp->inst);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (WARN_ON(nvkm_memory_target(disp->inst->memory) != NVKM_MEM_TARGET_VRAM))
|
|
return -EINVAL;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&gsp->internal.device.subdevice,
|
|
NV2080_CTRL_CMD_INTERNAL_DISPLAY_WRITE_INST_MEM,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->instMemPhysAddr = nvkm_memory_addr(disp->inst->memory);
|
|
ctrl->instMemSize = nvkm_memory_size(disp->inst->memory);
|
|
ctrl->instMemAddrSpace = ADDR_FBMEM;
|
|
ctrl->instMemCpuCacheAttr = NV_MEMORY_WRITECOMBINED;
|
|
|
|
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* OBJs. */
|
|
ret = nvkm_gsp_client_device_ctor(gsp, &disp->rm.client, &disp->rm.device);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = nvkm_gsp_rm_alloc(&disp->rm.device.object, 0x00730000, NV04_DISPLAY_COMMON, 0,
|
|
&disp->rm.objcom);
|
|
if (ret)
|
|
return ret;
|
|
|
|
{
|
|
NV2080_CTRL_INTERNAL_DISPLAY_GET_STATIC_INFO_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_rd(&gsp->internal.device.subdevice,
|
|
NV2080_CTRL_CMD_INTERNAL_DISPLAY_GET_STATIC_INFO,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
disp->wndw.mask = ctrl->windowPresentMask;
|
|
disp->wndw.nr = fls(disp->wndw.mask);
|
|
nvkm_gsp_rm_ctrl_done(&gsp->internal.device.subdevice, ctrl);
|
|
}
|
|
|
|
/* */
|
|
{
|
|
#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
|
|
NV2080_CTRL_INTERNAL_INIT_BRIGHTC_STATE_LOAD_PARAMS *ctrl;
|
|
struct nvkm_gsp_object *subdevice = &disp->rm.client.gsp->internal.device.subdevice;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(subdevice,
|
|
NV2080_CTRL_CMD_INTERNAL_INIT_BRIGHTC_STATE_LOAD,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ctrl->status = 0x56; /* NV_ERR_NOT_SUPPORTED */
|
|
|
|
{
|
|
const guid_t NBCI_DSM_GUID =
|
|
GUID_INIT(0xD4A50B75, 0x65C7, 0x46F7,
|
|
0xBF, 0xB7, 0x41, 0x51, 0x4C, 0xEA, 0x02, 0x44);
|
|
u64 NBCI_DSM_REV = 0x00000102;
|
|
const guid_t NVHG_DSM_GUID =
|
|
GUID_INIT(0x9D95A0A0, 0x0060, 0x4D48,
|
|
0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4);
|
|
u64 NVHG_DSM_REV = 0x00000102;
|
|
acpi_handle handle = ACPI_HANDLE(device->dev);
|
|
|
|
if (handle && acpi_has_method(handle, "_DSM")) {
|
|
bool nbci = acpi_check_dsm(handle, &NBCI_DSM_GUID, NBCI_DSM_REV,
|
|
1ULL << 0x00000014);
|
|
bool nvhg = acpi_check_dsm(handle, &NVHG_DSM_GUID, NVHG_DSM_REV,
|
|
1ULL << 0x00000014);
|
|
|
|
if (nbci || nvhg) {
|
|
union acpi_object argv4 = {
|
|
.buffer.type = ACPI_TYPE_BUFFER,
|
|
.buffer.length = sizeof(ctrl->backLightData),
|
|
.buffer.pointer = kmalloc(argv4.buffer.length, GFP_KERNEL),
|
|
}, *obj;
|
|
|
|
obj = acpi_evaluate_dsm(handle, nbci ? &NBCI_DSM_GUID : &NVHG_DSM_GUID,
|
|
0x00000102, 0x14, &argv4);
|
|
if (!obj) {
|
|
acpi_handle_info(handle, "failed to evaluate _DSM\n");
|
|
} else {
|
|
for (int i = 0; i < obj->package.count; i++) {
|
|
union acpi_object *elt = &obj->package.elements[i];
|
|
u32 size;
|
|
|
|
if (elt->integer.value & ~0xffffffffULL)
|
|
size = 8;
|
|
else
|
|
size = 4;
|
|
|
|
memcpy(&ctrl->backLightData[ctrl->backLightDataSize], &elt->integer.value, size);
|
|
ctrl->backLightDataSize += size;
|
|
}
|
|
|
|
ctrl->status = 0;
|
|
ACPI_FREE(obj);
|
|
}
|
|
|
|
kfree(argv4.buffer.pointer);
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = nvkm_gsp_rm_ctrl_wr(subdevice, ctrl);
|
|
if (ret)
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
/* */
|
|
{
|
|
NV0073_CTRL_CMD_DP_SET_MANUAL_DISPLAYPORT_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_DP_SET_MANUAL_DISPLAYPORT,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
ret = nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* */
|
|
{
|
|
NV0073_CTRL_SYSTEM_GET_NUM_HEADS_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_rd(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SYSTEM_GET_NUM_HEADS, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
disp->head.nr = ctrl->numHeads;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
}
|
|
|
|
/* */
|
|
{
|
|
NV0073_CTRL_SPECIFIC_GET_ALL_HEAD_MASK_PARAMS *ctrl;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_rd(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SPECIFIC_GET_ALL_HEAD_MASK,
|
|
sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
disp->head.mask = ctrl->headMask;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
for_each_set_bit(i, &disp->head.mask, disp->head.nr) {
|
|
ret = nvkm_head_new_(&r535_head, disp, i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
disp->sor.nr = disp->func->sor.cnt(disp, &disp->sor.mask);
|
|
nvkm_debug(&disp->engine.subdev, " SOR(s): %d (%02lx)\n", disp->sor.nr, disp->sor.mask);
|
|
for_each_set_bit(i, &disp->sor.mask, disp->sor.nr) {
|
|
ret = disp->func->sor.new(disp, i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* */
|
|
{
|
|
NV0073_CTRL_SYSTEM_GET_SUPPORTED_PARAMS *ctrl;
|
|
unsigned long mask;
|
|
int i;
|
|
|
|
ctrl = nvkm_gsp_rm_ctrl_rd(&disp->rm.objcom,
|
|
NV0073_CTRL_CMD_SYSTEM_GET_SUPPORTED, sizeof(*ctrl));
|
|
if (IS_ERR(ctrl))
|
|
return PTR_ERR(ctrl);
|
|
|
|
mask = ctrl->displayMask;
|
|
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
|
|
|
|
for_each_set_bit(i, &mask, 32) {
|
|
ret = r535_outp_new(disp, i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = nvkm_event_init(&r535_disp_event, &gsp->subdev, 3, 32, &disp->rm.event);
|
|
if (WARN_ON(ret))
|
|
return ret;
|
|
|
|
ret = nvkm_gsp_device_event_ctor(&disp->rm.device, 0x007e0000, NV2080_NOTIFIERS_HOTPLUG,
|
|
r535_disp_hpd, &disp->rm.hpd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = nvkm_gsp_device_event_ctor(&disp->rm.device, 0x007e0001, NV2080_NOTIFIERS_DP_IRQ,
|
|
r535_disp_irq, &disp->rm.irq);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* RAMHT. */
|
|
ret = nvkm_ramht_new(device, disp->func->ramht_size ? disp->func->ramht_size :
|
|
0x1000, 0, disp->inst, &disp->ramht);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev,
|
|
r535_disp_intr, &disp->engine.subdev.inth);
|
|
if (ret)
|
|
return ret;
|
|
|
|
nvkm_inth_allow(&disp->engine.subdev.inth);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
r535_disp_dtor(struct nvkm_disp *disp)
|
|
{
|
|
kfree(disp->func);
|
|
}
|
|
|
|
int
|
|
r535_disp_new(const struct nvkm_disp_func *hw, struct nvkm_device *device,
|
|
enum nvkm_subdev_type type, int inst, struct nvkm_disp **pdisp)
|
|
{
|
|
struct nvkm_disp_func *rm;
|
|
int ret;
|
|
|
|
if (!(rm = kzalloc(sizeof(*rm) + 6 * sizeof(rm->user[0]), GFP_KERNEL)))
|
|
return -ENOMEM;
|
|
|
|
rm->dtor = r535_disp_dtor;
|
|
rm->oneinit = r535_disp_oneinit;
|
|
rm->init = r535_disp_init;
|
|
rm->fini = r535_disp_fini;
|
|
rm->uevent = hw->uevent;
|
|
rm->sor.cnt = r535_sor_cnt;
|
|
rm->sor.new = r535_sor_new;
|
|
rm->ramht_size = hw->ramht_size;
|
|
|
|
rm->root = hw->root;
|
|
|
|
for (int i = 0; hw->user[i].ctor; i++) {
|
|
switch (hw->user[i].base.oclass & 0xff) {
|
|
case 0x73: rm->user[i] = hw->user[i]; break;
|
|
case 0x7d: rm->user[i] = hw->user[i]; rm->user[i].chan = &r535_core; break;
|
|
case 0x7e: rm->user[i] = hw->user[i]; rm->user[i].chan = &r535_wndw; break;
|
|
case 0x7b: rm->user[i] = hw->user[i]; rm->user[i].chan = &r535_wimm; break;
|
|
case 0x7a: rm->user[i] = hw->user[i]; rm->user[i].chan = &r535_curs; break;
|
|
default:
|
|
WARN_ON(1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ret = nvkm_disp_new_(rm, device, type, inst, pdisp);
|
|
if (ret)
|
|
kfree(rm);
|
|
|
|
mutex_init(&(*pdisp)->super.mutex); //XXX
|
|
return ret;
|
|
}
|