Merge branch 'linux-4.20' of git://github.com/skeggsb/linux into drm-next
Just initial HDMI 2.0 support, and a bunch of other cleanups. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Ben Skeggs <bskeggs@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/CABDvA=mgEm9JxP7AX7Sff-AEs7a75M4SqwFHmLPZhJojm4k=OA@mail.gmail.com
This commit is contained in:
commit
7e6191d436
@ -36,6 +36,7 @@
|
|||||||
#include <drm/drm_dp_helper.h>
|
#include <drm/drm_dp_helper.h>
|
||||||
#include <drm/drm_fb_helper.h>
|
#include <drm/drm_fb_helper.h>
|
||||||
#include <drm/drm_plane_helper.h>
|
#include <drm/drm_plane_helper.h>
|
||||||
|
#include <drm/drm_scdc_helper.h>
|
||||||
#include <drm/drm_edid.h>
|
#include <drm/drm_edid.h>
|
||||||
|
|
||||||
#include <nvif/class.h>
|
#include <nvif/class.h>
|
||||||
@ -531,6 +532,7 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
|
|||||||
static void
|
static void
|
||||||
nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
||||||
{
|
{
|
||||||
|
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
|
||||||
struct nv50_disp *disp = nv50_disp(encoder->dev);
|
struct nv50_disp *disp = nv50_disp(encoder->dev);
|
||||||
@ -548,9 +550,12 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||||||
.pwr.rekey = 56, /* binary driver, and tegra, constant */
|
.pwr.rekey = 56, /* binary driver, and tegra, constant */
|
||||||
};
|
};
|
||||||
struct nouveau_connector *nv_connector;
|
struct nouveau_connector *nv_connector;
|
||||||
|
struct drm_hdmi_info *hdmi;
|
||||||
u32 max_ac_packet;
|
u32 max_ac_packet;
|
||||||
union hdmi_infoframe avi_frame;
|
union hdmi_infoframe avi_frame;
|
||||||
union hdmi_infoframe vendor_frame;
|
union hdmi_infoframe vendor_frame;
|
||||||
|
bool scdc_supported, high_tmds_clock_ratio = false, scrambling = false;
|
||||||
|
u8 config;
|
||||||
int ret;
|
int ret;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
@ -558,8 +563,11 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||||||
if (!drm_detect_hdmi_monitor(nv_connector->edid))
|
if (!drm_detect_hdmi_monitor(nv_connector->edid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
hdmi = &nv_connector->base.display_info.hdmi;
|
||||||
|
scdc_supported = hdmi->scdc.supported;
|
||||||
|
|
||||||
ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode,
|
ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode,
|
||||||
false);
|
scdc_supported);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
/* We have an AVI InfoFrame, populate it to the display */
|
/* We have an AVI InfoFrame, populate it to the display */
|
||||||
args.pwr.avi_infoframe_length
|
args.pwr.avi_infoframe_length
|
||||||
@ -582,12 +590,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||||||
max_ac_packet -= 18; /* constant from tegra */
|
max_ac_packet -= 18; /* constant from tegra */
|
||||||
args.pwr.max_ac_packet = max_ac_packet / 32;
|
args.pwr.max_ac_packet = max_ac_packet / 32;
|
||||||
|
|
||||||
|
if (hdmi->scdc.scrambling.supported) {
|
||||||
|
high_tmds_clock_ratio = mode->clock > 340000;
|
||||||
|
scrambling = high_tmds_clock_ratio ||
|
||||||
|
hdmi->scdc.scrambling.low_rates;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.pwr.scdc =
|
||||||
|
NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE * scrambling |
|
||||||
|
NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 * high_tmds_clock_ratio;
|
||||||
|
|
||||||
size = sizeof(args.base)
|
size = sizeof(args.base)
|
||||||
+ sizeof(args.pwr)
|
+ sizeof(args.pwr)
|
||||||
+ args.pwr.avi_infoframe_length
|
+ args.pwr.avi_infoframe_length
|
||||||
+ args.pwr.vendor_infoframe_length;
|
+ args.pwr.vendor_infoframe_length;
|
||||||
nvif_mthd(&disp->disp->object, 0, &args, size);
|
nvif_mthd(&disp->disp->object, 0, &args, size);
|
||||||
|
|
||||||
nv50_audio_enable(encoder, mode);
|
nv50_audio_enable(encoder, mode);
|
||||||
|
|
||||||
|
/* If SCDC is supported by the downstream monitor, update
|
||||||
|
* divider / scrambling settings to what we programmed above.
|
||||||
|
*/
|
||||||
|
if (!hdmi->scdc.scrambling.supported)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config);
|
||||||
|
if (ret < 0) {
|
||||||
|
NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE);
|
||||||
|
config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 * high_tmds_clock_ratio;
|
||||||
|
config |= SCDC_SCRAMBLING_ENABLE * scrambling;
|
||||||
|
ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config);
|
||||||
|
if (ret < 0)
|
||||||
|
NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n",
|
||||||
|
config, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
|
@ -69,7 +69,10 @@ struct nv50_disp_sor_hdmi_pwr_v0 {
|
|||||||
__u8 rekey;
|
__u8 rekey;
|
||||||
__u8 avi_infoframe_length;
|
__u8 avi_infoframe_length;
|
||||||
__u8 vendor_infoframe_length;
|
__u8 vendor_infoframe_length;
|
||||||
__u8 pad06[2];
|
#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE (1 << 0)
|
||||||
|
#define NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 (1 << 1)
|
||||||
|
__u8 scdc;
|
||||||
|
__u8 pad07[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nv50_disp_sor_lvds_script_v0 {
|
struct nv50_disp_sor_lvds_script_v0 {
|
||||||
|
@ -37,18 +37,19 @@
|
|||||||
#include "nouveau_drv.h"
|
#include "nouveau_drv.h"
|
||||||
#include "nouveau_reg.h"
|
#include "nouveau_reg.h"
|
||||||
#include "nouveau_encoder.h"
|
#include "nouveau_encoder.h"
|
||||||
|
#include "nouveau_connector.h"
|
||||||
|
|
||||||
static struct ida bl_ida;
|
static struct ida bl_ida;
|
||||||
#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
|
#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'
|
||||||
|
|
||||||
struct backlight_connector {
|
struct nouveau_backlight {
|
||||||
struct list_head head;
|
struct backlight_device *dev;
|
||||||
int id;
|
int id;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_connector
|
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
|
||||||
*connector)
|
struct nouveau_backlight *bl)
|
||||||
{
|
{
|
||||||
const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
|
const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
|
||||||
if (nb < 0 || nb >= 100)
|
if (nb < 0 || nb >= 100)
|
||||||
@ -57,17 +58,18 @@ nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_c
|
|||||||
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
|
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
|
||||||
else
|
else
|
||||||
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
|
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
|
||||||
connector->id = nb;
|
bl->id = nb;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nv40_get_intensity(struct backlight_device *bd)
|
nv40_get_intensity(struct backlight_device *bd)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = bl_get_data(bd);
|
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
|
||||||
|
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
|
||||||
struct nvif_object *device = &drm->client.device.object;
|
struct nvif_object *device = &drm->client.device.object;
|
||||||
int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
|
int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
|
||||||
NV40_PMC_BACKLIGHT_MASK) >> 16;
|
NV40_PMC_BACKLIGHT_MASK) >> 16;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@ -75,13 +77,14 @@ nv40_get_intensity(struct backlight_device *bd)
|
|||||||
static int
|
static int
|
||||||
nv40_set_intensity(struct backlight_device *bd)
|
nv40_set_intensity(struct backlight_device *bd)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = bl_get_data(bd);
|
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
|
||||||
|
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
|
||||||
struct nvif_object *device = &drm->client.device.object;
|
struct nvif_object *device = &drm->client.device.object;
|
||||||
int val = bd->props.brightness;
|
int val = bd->props.brightness;
|
||||||
int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
|
int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
|
||||||
|
|
||||||
nvif_wr32(device, NV40_PMC_BACKLIGHT,
|
nvif_wr32(device, NV40_PMC_BACKLIGHT,
|
||||||
(val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
|
(val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -93,38 +96,19 @@ static const struct backlight_ops nv40_bl_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nv40_backlight_init(struct drm_connector *connector)
|
nv40_backlight_init(struct nouveau_encoder *encoder,
|
||||||
|
struct backlight_properties *props,
|
||||||
|
const struct backlight_ops **ops)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
struct nouveau_drm *drm = nouveau_drm(encoder->base.base.dev);
|
||||||
struct nvif_object *device = &drm->client.device.object;
|
struct nvif_object *device = &drm->client.device.object;
|
||||||
struct backlight_properties props;
|
|
||||||
struct backlight_device *bd;
|
|
||||||
struct backlight_connector bl_connector;
|
|
||||||
char backlight_name[BL_NAME_SIZE];
|
|
||||||
|
|
||||||
if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
|
if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
|
||||||
return 0;
|
return -ENODEV;
|
||||||
|
|
||||||
memset(&props, 0, sizeof(struct backlight_properties));
|
|
||||||
props.type = BACKLIGHT_RAW;
|
|
||||||
props.max_brightness = 31;
|
|
||||||
if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) {
|
|
||||||
NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
bd = backlight_device_register(backlight_name , connector->kdev, drm,
|
|
||||||
&nv40_bl_ops, &props);
|
|
||||||
|
|
||||||
if (IS_ERR(bd)) {
|
|
||||||
if (bl_connector.id > 0)
|
|
||||||
ida_simple_remove(&bl_ida, bl_connector.id);
|
|
||||||
return PTR_ERR(bd);
|
|
||||||
}
|
|
||||||
list_add(&bl_connector.head, &drm->bl_connectors);
|
|
||||||
drm->backlight = bd;
|
|
||||||
bd->props.brightness = nv40_get_intensity(bd);
|
|
||||||
backlight_update_status(bd);
|
|
||||||
|
|
||||||
|
props->type = BACKLIGHT_RAW;
|
||||||
|
props->max_brightness = 31;
|
||||||
|
*ops = &nv40_bl_ops;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +138,7 @@ nv50_set_intensity(struct backlight_device *bd)
|
|||||||
u32 val = (bd->props.brightness * div) / 100;
|
u32 val = (bd->props.brightness * div) / 100;
|
||||||
|
|
||||||
nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
|
nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
|
||||||
NV50_PDISP_SOR_PWM_CTL_NEW | val);
|
NV50_PDISP_SOR_PWM_CTL_NEW | val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,9 +178,10 @@ nva3_set_intensity(struct backlight_device *bd)
|
|||||||
div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
|
div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
|
||||||
val = (bd->props.brightness * div) / 100;
|
val = (bd->props.brightness * div) / 100;
|
||||||
if (div) {
|
if (div) {
|
||||||
nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
|
nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
|
||||||
NV50_PDISP_SOR_PWM_CTL_NEW |
|
val |
|
||||||
NVA3_PDISP_SOR_PWM_CTL_UNK);
|
NV50_PDISP_SOR_PWM_CTL_NEW |
|
||||||
|
NVA3_PDISP_SOR_PWM_CTL_UNK);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,110 +195,119 @@ static const struct backlight_ops nva3_bl_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nv50_backlight_init(struct drm_connector *connector)
|
nv50_backlight_init(struct nouveau_encoder *nv_encoder,
|
||||||
|
struct backlight_properties *props,
|
||||||
|
const struct backlight_ops **ops)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
|
||||||
struct nvif_object *device = &drm->client.device.object;
|
struct nvif_object *device = &drm->client.device.object;
|
||||||
struct nouveau_encoder *nv_encoder;
|
|
||||||
struct backlight_properties props;
|
|
||||||
struct backlight_device *bd;
|
|
||||||
const struct backlight_ops *ops;
|
|
||||||
struct backlight_connector bl_connector;
|
|
||||||
char backlight_name[BL_NAME_SIZE];
|
|
||||||
|
|
||||||
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
|
|
||||||
if (!nv_encoder) {
|
|
||||||
nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
|
|
||||||
if (!nv_encoder)
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)))
|
if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)))
|
||||||
return 0;
|
return -ENODEV;
|
||||||
|
|
||||||
if (drm->client.device.info.chipset <= 0xa0 ||
|
if (drm->client.device.info.chipset <= 0xa0 ||
|
||||||
drm->client.device.info.chipset == 0xaa ||
|
drm->client.device.info.chipset == 0xaa ||
|
||||||
drm->client.device.info.chipset == 0xac)
|
drm->client.device.info.chipset == 0xac)
|
||||||
ops = &nv50_bl_ops;
|
*ops = &nv50_bl_ops;
|
||||||
else
|
else
|
||||||
ops = &nva3_bl_ops;
|
*ops = &nva3_bl_ops;
|
||||||
|
|
||||||
memset(&props, 0, sizeof(struct backlight_properties));
|
props->type = BACKLIGHT_RAW;
|
||||||
props.type = BACKLIGHT_RAW;
|
props->max_brightness = 100;
|
||||||
props.max_brightness = 100;
|
|
||||||
if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) {
|
|
||||||
NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
bd = backlight_device_register(backlight_name , connector->kdev,
|
|
||||||
nv_encoder, ops, &props);
|
|
||||||
|
|
||||||
if (IS_ERR(bd)) {
|
|
||||||
if (bl_connector.id > 0)
|
|
||||||
ida_simple_remove(&bl_ida, bl_connector.id);
|
|
||||||
return PTR_ERR(bd);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_add(&bl_connector.head, &drm->bl_connectors);
|
|
||||||
drm->backlight = bd;
|
|
||||||
bd->props.brightness = bd->ops->get_brightness(bd);
|
|
||||||
backlight_update_status(bd);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
nouveau_backlight_init(struct drm_device *dev)
|
nouveau_backlight_init(struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
||||||
|
struct nouveau_backlight *bl;
|
||||||
|
struct nouveau_encoder *nv_encoder = NULL;
|
||||||
struct nvif_device *device = &drm->client.device;
|
struct nvif_device *device = &drm->client.device;
|
||||||
struct drm_connector *connector;
|
char backlight_name[BL_NAME_SIZE];
|
||||||
struct drm_connector_list_iter conn_iter;
|
struct backlight_properties props = {0};
|
||||||
|
const struct backlight_ops *ops;
|
||||||
INIT_LIST_HEAD(&drm->bl_connectors);
|
int ret;
|
||||||
|
|
||||||
if (apple_gmux_present()) {
|
if (apple_gmux_present()) {
|
||||||
NV_INFO(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
|
NV_INFO_ONCE(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
||||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
|
||||||
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
|
else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
|
||||||
connector->connector_type != DRM_MODE_CONNECTOR_eDP)
|
nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
|
||||||
continue;
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
switch (device->info.family) {
|
if (!nv_encoder)
|
||||||
case NV_DEVICE_INFO_V0_CURIE:
|
return 0;
|
||||||
return nv40_backlight_init(connector);
|
|
||||||
case NV_DEVICE_INFO_V0_TESLA:
|
switch (device->info.family) {
|
||||||
case NV_DEVICE_INFO_V0_FERMI:
|
case NV_DEVICE_INFO_V0_CURIE:
|
||||||
case NV_DEVICE_INFO_V0_KEPLER:
|
ret = nv40_backlight_init(nv_encoder, &props, &ops);
|
||||||
case NV_DEVICE_INFO_V0_MAXWELL:
|
break;
|
||||||
return nv50_backlight_init(connector);
|
case NV_DEVICE_INFO_V0_TESLA:
|
||||||
default:
|
case NV_DEVICE_INFO_V0_FERMI:
|
||||||
break;
|
case NV_DEVICE_INFO_V0_KEPLER:
|
||||||
}
|
case NV_DEVICE_INFO_V0_MAXWELL:
|
||||||
|
ret = nv50_backlight_init(nv_encoder, &props, &ops);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
drm_connector_list_iter_end(&conn_iter);
|
|
||||||
|
if (ret == -ENODEV)
|
||||||
|
return 0;
|
||||||
|
else if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
bl = kzalloc(sizeof(*bl), GFP_KERNEL);
|
||||||
|
if (!bl)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!nouveau_get_backlight_name(backlight_name, bl)) {
|
||||||
|
NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n");
|
||||||
|
goto fail_alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bl->dev = backlight_device_register(backlight_name, connector->kdev,
|
||||||
|
nv_encoder, ops, &props);
|
||||||
|
if (IS_ERR(bl->dev)) {
|
||||||
|
if (bl->id >= 0)
|
||||||
|
ida_simple_remove(&bl_ida, bl->id);
|
||||||
|
ret = PTR_ERR(bl->dev);
|
||||||
|
goto fail_alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
nouveau_connector(connector)->backlight = bl;
|
||||||
|
bl->dev->props.brightness = bl->dev->ops->get_brightness(bl->dev);
|
||||||
|
backlight_update_status(bl->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail_alloc:
|
||||||
|
kfree(bl);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nouveau_backlight_exit(struct drm_device *dev)
|
nouveau_backlight_fini(struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
struct nouveau_connector *nv_conn = nouveau_connector(connector);
|
||||||
struct backlight_connector *connector;
|
struct nouveau_backlight *bl = nv_conn->backlight;
|
||||||
|
|
||||||
list_for_each_entry(connector, &drm->bl_connectors, head) {
|
if (!bl)
|
||||||
if (connector->id >= 0)
|
return;
|
||||||
ida_simple_remove(&bl_ida, connector->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drm->backlight) {
|
if (bl->id >= 0)
|
||||||
backlight_device_unregister(drm->backlight);
|
ida_simple_remove(&bl_ida, bl->id);
|
||||||
drm->backlight = NULL;
|
|
||||||
}
|
backlight_device_unregister(bl->dev);
|
||||||
|
nv_conn->backlight = NULL;
|
||||||
|
kfree(bl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -885,6 +885,22 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
|
|||||||
connector->display_info.bpc = 8;
|
connector->display_info.bpc = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nouveau_connector_late_register(struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nouveau_backlight_init(connector);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nouveau_connector_early_unregister(struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
nouveau_backlight_fini(connector);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nouveau_connector_get_modes(struct drm_connector *connector)
|
nouveau_connector_get_modes(struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
@ -953,18 +969,33 @@ nouveau_connector_get_modes(struct drm_connector *connector)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static unsigned
|
static unsigned
|
||||||
get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi)
|
get_tmds_link_bandwidth(struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||||
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
||||||
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
struct nouveau_drm *drm = nouveau_drm(connector->dev);
|
||||||
struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
|
struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
|
||||||
|
struct drm_display_info *info = NULL;
|
||||||
|
const unsigned duallink_scale =
|
||||||
|
nouveau_duallink && nv_encoder->dcb->duallink_possible ? 2 : 1;
|
||||||
|
|
||||||
if (hdmi) {
|
if (drm_detect_hdmi_monitor(nv_connector->edid))
|
||||||
|
info = &nv_connector->base.display_info;
|
||||||
|
|
||||||
|
if (info) {
|
||||||
if (nouveau_hdmimhz > 0)
|
if (nouveau_hdmimhz > 0)
|
||||||
return nouveau_hdmimhz * 1000;
|
return nouveau_hdmimhz * 1000;
|
||||||
/* Note: these limits are conservative, some Fermi's
|
/* Note: these limits are conservative, some Fermi's
|
||||||
* can do 297 MHz. Unclear how this can be determined.
|
* can do 297 MHz. Unclear how this can be determined.
|
||||||
*/
|
*/
|
||||||
|
if (drm->client.device.info.chipset >= 0x120) {
|
||||||
|
const int max_tmds_clock =
|
||||||
|
info->hdmi.scdc.scrambling.supported ?
|
||||||
|
594000 : 340000;
|
||||||
|
return info->max_tmds_clock ?
|
||||||
|
min(info->max_tmds_clock, max_tmds_clock) :
|
||||||
|
max_tmds_clock;
|
||||||
|
}
|
||||||
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
|
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
|
||||||
return 297000;
|
return 297000;
|
||||||
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
|
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
|
||||||
@ -972,13 +1003,13 @@ get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi)
|
|||||||
}
|
}
|
||||||
if (dcb->location != DCB_LOC_ON_CHIP ||
|
if (dcb->location != DCB_LOC_ON_CHIP ||
|
||||||
drm->client.device.info.chipset >= 0x46)
|
drm->client.device.info.chipset >= 0x46)
|
||||||
return 165000;
|
return 165000 * duallink_scale;
|
||||||
else if (drm->client.device.info.chipset >= 0x40)
|
else if (drm->client.device.info.chipset >= 0x40)
|
||||||
return 155000;
|
return 155000 * duallink_scale;
|
||||||
else if (drm->client.device.info.chipset >= 0x18)
|
else if (drm->client.device.info.chipset >= 0x18)
|
||||||
return 135000;
|
return 135000 * duallink_scale;
|
||||||
else
|
else
|
||||||
return 112000;
|
return 112000 * duallink_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum drm_mode_status
|
static enum drm_mode_status
|
||||||
@ -990,7 +1021,6 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
|||||||
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
|
||||||
unsigned min_clock = 25000, max_clock = min_clock;
|
unsigned min_clock = 25000, max_clock = min_clock;
|
||||||
unsigned clock = mode->clock;
|
unsigned clock = mode->clock;
|
||||||
bool hdmi;
|
|
||||||
|
|
||||||
switch (nv_encoder->dcb->type) {
|
switch (nv_encoder->dcb->type) {
|
||||||
case DCB_OUTPUT_LVDS:
|
case DCB_OUTPUT_LVDS:
|
||||||
@ -1003,11 +1033,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
|||||||
max_clock = 400000;
|
max_clock = 400000;
|
||||||
break;
|
break;
|
||||||
case DCB_OUTPUT_TMDS:
|
case DCB_OUTPUT_TMDS:
|
||||||
hdmi = drm_detect_hdmi_monitor(nv_connector->edid);
|
max_clock = get_tmds_link_bandwidth(connector);
|
||||||
max_clock = get_tmds_link_bandwidth(connector, hdmi);
|
|
||||||
if (!hdmi && nouveau_duallink &&
|
|
||||||
nv_encoder->dcb->duallink_possible)
|
|
||||||
max_clock *= 2;
|
|
||||||
break;
|
break;
|
||||||
case DCB_OUTPUT_ANALOG:
|
case DCB_OUTPUT_ANALOG:
|
||||||
max_clock = nv_encoder->dcb->crtconf.maxfreq;
|
max_clock = nv_encoder->dcb->crtconf.maxfreq;
|
||||||
@ -1069,6 +1095,8 @@ nouveau_connector_funcs = {
|
|||||||
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
|
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
|
||||||
.atomic_set_property = nouveau_conn_atomic_set_property,
|
.atomic_set_property = nouveau_conn_atomic_set_property,
|
||||||
.atomic_get_property = nouveau_conn_atomic_get_property,
|
.atomic_get_property = nouveau_conn_atomic_get_property,
|
||||||
|
.late_register = nouveau_connector_late_register,
|
||||||
|
.early_unregister = nouveau_connector_early_unregister,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_connector_funcs
|
static const struct drm_connector_funcs
|
||||||
@ -1084,6 +1112,8 @@ nouveau_connector_funcs_lvds = {
|
|||||||
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
|
.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
|
||||||
.atomic_set_property = nouveau_conn_atomic_set_property,
|
.atomic_set_property = nouveau_conn_atomic_set_property,
|
||||||
.atomic_get_property = nouveau_conn_atomic_get_property,
|
.atomic_get_property = nouveau_conn_atomic_get_property,
|
||||||
|
.late_register = nouveau_connector_late_register,
|
||||||
|
.early_unregister = nouveau_connector_early_unregister,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -39,6 +39,10 @@
|
|||||||
|
|
||||||
struct nvkm_i2c_port;
|
struct nvkm_i2c_port;
|
||||||
|
|
||||||
|
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
||||||
|
struct nouveau_backlight;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct nouveau_connector {
|
struct nouveau_connector {
|
||||||
struct drm_connector base;
|
struct drm_connector base;
|
||||||
enum dcb_connector_type type;
|
enum dcb_connector_type type;
|
||||||
@ -55,6 +59,9 @@ struct nouveau_connector {
|
|||||||
struct nouveau_encoder *detected_encoder;
|
struct nouveau_encoder *detected_encoder;
|
||||||
struct edid *edid;
|
struct edid *edid;
|
||||||
struct drm_display_mode *native_mode;
|
struct drm_display_mode *native_mode;
|
||||||
|
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
||||||
|
struct nouveau_backlight *backlight;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct nouveau_connector *nouveau_connector(
|
static inline struct nouveau_connector *nouveau_connector(
|
||||||
@ -181,4 +188,30 @@ int nouveau_conn_atomic_get_property(struct drm_connector *,
|
|||||||
const struct drm_connector_state *,
|
const struct drm_connector_state *,
|
||||||
struct drm_property *, u64 *);
|
struct drm_property *, u64 *);
|
||||||
struct drm_display_mode *nouveau_conn_native_mode(struct drm_connector *);
|
struct drm_display_mode *nouveau_conn_native_mode(struct drm_connector *);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
||||||
|
extern int nouveau_backlight_init(struct drm_connector *);
|
||||||
|
extern void nouveau_backlight_fini(struct drm_connector *);
|
||||||
|
extern void nouveau_backlight_ctor(void);
|
||||||
|
extern void nouveau_backlight_dtor(void);
|
||||||
|
#else
|
||||||
|
static inline int
|
||||||
|
nouveau_backlight_init(struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nouveau_backlight_fini(struct drm_connector *connector) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nouveau_backlight_ctor(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nouveau_backlight_dtor(void) {
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __NOUVEAU_CONNECTOR_H__ */
|
#endif /* __NOUVEAU_CONNECTOR_H__ */
|
||||||
|
@ -582,7 +582,6 @@ nouveau_display_create(struct drm_device *dev)
|
|||||||
goto vblank_err;
|
goto vblank_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
nouveau_backlight_init(dev);
|
|
||||||
INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work);
|
INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work);
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
|
drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
|
||||||
@ -607,7 +606,6 @@ nouveau_display_destroy(struct drm_device *dev)
|
|||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb);
|
unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb);
|
||||||
#endif
|
#endif
|
||||||
nouveau_backlight_exit(dev);
|
|
||||||
nouveau_display_vblank_fini(dev);
|
nouveau_display_vblank_fini(dev);
|
||||||
|
|
||||||
drm_kms_helper_poll_fini(dev);
|
drm_kms_helper_poll_fini(dev);
|
||||||
|
@ -85,31 +85,6 @@ int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
|
|||||||
|
|
||||||
void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
|
void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
|
||||||
|
|
||||||
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
|
||||||
extern int nouveau_backlight_init(struct drm_device *);
|
|
||||||
extern void nouveau_backlight_exit(struct drm_device *);
|
|
||||||
extern void nouveau_backlight_ctor(void);
|
|
||||||
extern void nouveau_backlight_dtor(void);
|
|
||||||
#else
|
|
||||||
static inline int
|
|
||||||
nouveau_backlight_init(struct drm_device *dev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nouveau_backlight_exit(struct drm_device *dev) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nouveau_backlight_ctor(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nouveau_backlight_dtor(void) {
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct drm_framebuffer *
|
struct drm_framebuffer *
|
||||||
nouveau_user_framebuffer_create(struct drm_device *, struct drm_file *,
|
nouveau_user_framebuffer_create(struct drm_device *, struct drm_file *,
|
||||||
const struct drm_mode_fb_cmd2 *);
|
const struct drm_mode_fb_cmd2 *);
|
||||||
|
@ -458,75 +458,8 @@ nouveau_accel_init(struct nouveau_drm *drm)
|
|||||||
nouveau_bo_move_init(drm);
|
nouveau_bo_move_init(drm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nouveau_drm_probe(struct pci_dev *pdev,
|
|
||||||
const struct pci_device_id *pent)
|
|
||||||
{
|
|
||||||
struct nvkm_device *device;
|
|
||||||
struct apertures_struct *aper;
|
|
||||||
bool boot = false;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (vga_switcheroo_client_probe_defer(pdev))
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
/* We need to check that the chipset is supported before booting
|
|
||||||
* fbdev off the hardware, as there's no way to put it back.
|
|
||||||
*/
|
|
||||||
ret = nvkm_device_pci_new(pdev, NULL, "error", true, false, 0, &device);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
nvkm_device_del(&device);
|
|
||||||
|
|
||||||
/* Remove conflicting drivers (vesafb, efifb etc). */
|
|
||||||
aper = alloc_apertures(3);
|
|
||||||
if (!aper)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
aper->ranges[0].base = pci_resource_start(pdev, 1);
|
|
||||||
aper->ranges[0].size = pci_resource_len(pdev, 1);
|
|
||||||
aper->count = 1;
|
|
||||||
|
|
||||||
if (pci_resource_len(pdev, 2)) {
|
|
||||||
aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
|
|
||||||
aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
|
|
||||||
aper->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pci_resource_len(pdev, 3)) {
|
|
||||||
aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
|
|
||||||
aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
|
|
||||||
aper->count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86
|
|
||||||
boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
|
|
||||||
#endif
|
|
||||||
if (nouveau_modeset != 2)
|
|
||||||
drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot);
|
|
||||||
kfree(aper);
|
|
||||||
|
|
||||||
ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug,
|
|
||||||
true, true, ~0ULL, &device);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
pci_set_master(pdev);
|
|
||||||
|
|
||||||
if (nouveau_atomic)
|
|
||||||
driver_pci.driver_features |= DRIVER_ATOMIC;
|
|
||||||
|
|
||||||
ret = drm_get_pci_dev(pdev, pent, &driver_pci);
|
|
||||||
if (ret) {
|
|
||||||
nvkm_device_del(&device);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nouveau_drm_load(struct drm_device *dev, unsigned long flags)
|
nouveau_drm_device_init(struct drm_device *dev)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm;
|
struct nouveau_drm *drm;
|
||||||
int ret;
|
int ret;
|
||||||
@ -538,11 +471,11 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
|
|||||||
|
|
||||||
ret = nouveau_cli_init(drm, "DRM-master", &drm->master);
|
ret = nouveau_cli_init(drm, "DRM-master", &drm->master);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto fail_alloc;
|
||||||
|
|
||||||
ret = nouveau_cli_init(drm, "DRM", &drm->client);
|
ret = nouveau_cli_init(drm, "DRM", &drm->client);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto fail_master;
|
||||||
|
|
||||||
dev->irq_enabled = true;
|
dev->irq_enabled = true;
|
||||||
|
|
||||||
@ -605,13 +538,15 @@ fail_bios:
|
|||||||
fail_ttm:
|
fail_ttm:
|
||||||
nouveau_vga_fini(drm);
|
nouveau_vga_fini(drm);
|
||||||
nouveau_cli_fini(&drm->client);
|
nouveau_cli_fini(&drm->client);
|
||||||
|
fail_master:
|
||||||
nouveau_cli_fini(&drm->master);
|
nouveau_cli_fini(&drm->master);
|
||||||
|
fail_alloc:
|
||||||
kfree(drm);
|
kfree(drm);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nouveau_drm_unload(struct drm_device *dev)
|
nouveau_drm_device_fini(struct drm_device *dev)
|
||||||
{
|
{
|
||||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||||
|
|
||||||
@ -640,18 +575,116 @@ nouveau_drm_unload(struct drm_device *dev)
|
|||||||
kfree(drm);
|
kfree(drm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nouveau_drm_probe(struct pci_dev *pdev,
|
||||||
|
const struct pci_device_id *pent)
|
||||||
|
{
|
||||||
|
struct nvkm_device *device;
|
||||||
|
struct drm_device *drm_dev;
|
||||||
|
struct apertures_struct *aper;
|
||||||
|
bool boot = false;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (vga_switcheroo_client_probe_defer(pdev))
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
/* We need to check that the chipset is supported before booting
|
||||||
|
* fbdev off the hardware, as there's no way to put it back.
|
||||||
|
*/
|
||||||
|
ret = nvkm_device_pci_new(pdev, NULL, "error", true, false, 0, &device);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
nvkm_device_del(&device);
|
||||||
|
|
||||||
|
/* Remove conflicting drivers (vesafb, efifb etc). */
|
||||||
|
aper = alloc_apertures(3);
|
||||||
|
if (!aper)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
aper->ranges[0].base = pci_resource_start(pdev, 1);
|
||||||
|
aper->ranges[0].size = pci_resource_len(pdev, 1);
|
||||||
|
aper->count = 1;
|
||||||
|
|
||||||
|
if (pci_resource_len(pdev, 2)) {
|
||||||
|
aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
|
||||||
|
aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
|
||||||
|
aper->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pci_resource_len(pdev, 3)) {
|
||||||
|
aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
|
||||||
|
aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
|
||||||
|
aper->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_X86
|
||||||
|
boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
|
||||||
|
#endif
|
||||||
|
if (nouveau_modeset != 2)
|
||||||
|
drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot);
|
||||||
|
kfree(aper);
|
||||||
|
|
||||||
|
ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug,
|
||||||
|
true, true, ~0ULL, &device);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
pci_set_master(pdev);
|
||||||
|
|
||||||
|
if (nouveau_atomic)
|
||||||
|
driver_pci.driver_features |= DRIVER_ATOMIC;
|
||||||
|
|
||||||
|
drm_dev = drm_dev_alloc(&driver_pci, &pdev->dev);
|
||||||
|
if (IS_ERR(drm_dev)) {
|
||||||
|
ret = PTR_ERR(drm_dev);
|
||||||
|
goto fail_nvkm;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pci_enable_device(pdev);
|
||||||
|
if (ret)
|
||||||
|
goto fail_drm;
|
||||||
|
|
||||||
|
drm_dev->pdev = pdev;
|
||||||
|
pci_set_drvdata(pdev, drm_dev);
|
||||||
|
|
||||||
|
ret = nouveau_drm_device_init(drm_dev);
|
||||||
|
if (ret)
|
||||||
|
goto fail_pci;
|
||||||
|
|
||||||
|
ret = drm_dev_register(drm_dev, pent->driver_data);
|
||||||
|
if (ret)
|
||||||
|
goto fail_drm_dev_init;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_drm_dev_init:
|
||||||
|
nouveau_drm_device_fini(drm_dev);
|
||||||
|
fail_pci:
|
||||||
|
pci_disable_device(pdev);
|
||||||
|
fail_drm:
|
||||||
|
drm_dev_put(drm_dev);
|
||||||
|
fail_nvkm:
|
||||||
|
nvkm_device_del(&device);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nouveau_drm_device_remove(struct drm_device *dev)
|
nouveau_drm_device_remove(struct drm_device *dev)
|
||||||
{
|
{
|
||||||
|
struct pci_dev *pdev = dev->pdev;
|
||||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||||
struct nvkm_client *client;
|
struct nvkm_client *client;
|
||||||
struct nvkm_device *device;
|
struct nvkm_device *device;
|
||||||
|
|
||||||
|
drm_dev_unregister(dev);
|
||||||
|
|
||||||
dev->irq_enabled = false;
|
dev->irq_enabled = false;
|
||||||
client = nvxx_client(&drm->client.base);
|
client = nvxx_client(&drm->client.base);
|
||||||
device = nvkm_device_find(client->device);
|
device = nvkm_device_find(client->device);
|
||||||
drm_put_dev(dev);
|
|
||||||
|
|
||||||
|
nouveau_drm_device_fini(dev);
|
||||||
|
pci_disable_device(pdev);
|
||||||
|
drm_dev_put(dev);
|
||||||
nvkm_device_del(&device);
|
nvkm_device_del(&device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1018,8 +1051,6 @@ driver_stub = {
|
|||||||
DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER |
|
DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER |
|
||||||
DRIVER_KMS_LEGACY_CONTEXT,
|
DRIVER_KMS_LEGACY_CONTEXT,
|
||||||
|
|
||||||
.load = nouveau_drm_load,
|
|
||||||
.unload = nouveau_drm_unload,
|
|
||||||
.open = nouveau_drm_open,
|
.open = nouveau_drm_open,
|
||||||
.postclose = nouveau_drm_postclose,
|
.postclose = nouveau_drm_postclose,
|
||||||
.lastclose = nouveau_vga_lastclose,
|
.lastclose = nouveau_vga_lastclose,
|
||||||
|
@ -194,8 +194,6 @@ struct nouveau_drm {
|
|||||||
/* modesetting */
|
/* modesetting */
|
||||||
struct nvbios vbios;
|
struct nvbios vbios;
|
||||||
struct nouveau_display *display;
|
struct nouveau_display *display;
|
||||||
struct backlight_device *backlight;
|
|
||||||
struct list_head bl_connectors;
|
|
||||||
struct work_struct hpd_work;
|
struct work_struct hpd_work;
|
||||||
struct work_struct fbcon_work;
|
struct work_struct fbcon_work;
|
||||||
int fbcon_new_state;
|
int fbcon_new_state;
|
||||||
@ -244,10 +242,12 @@ void nouveau_drm_device_remove(struct drm_device *dev);
|
|||||||
struct nouveau_cli *_cli = (c); \
|
struct nouveau_cli *_cli = (c); \
|
||||||
dev_##l(_cli->drm->dev->dev, "%s: "f, _cli->name, ##a); \
|
dev_##l(_cli->drm->dev->dev, "%s: "f, _cli->name, ##a); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
#define NV_FATAL(drm,f,a...) NV_PRINTK(crit, &(drm)->client, f, ##a)
|
#define NV_FATAL(drm,f,a...) NV_PRINTK(crit, &(drm)->client, f, ##a)
|
||||||
#define NV_ERROR(drm,f,a...) NV_PRINTK(err, &(drm)->client, f, ##a)
|
#define NV_ERROR(drm,f,a...) NV_PRINTK(err, &(drm)->client, f, ##a)
|
||||||
#define NV_WARN(drm,f,a...) NV_PRINTK(warn, &(drm)->client, f, ##a)
|
#define NV_WARN(drm,f,a...) NV_PRINTK(warn, &(drm)->client, f, ##a)
|
||||||
#define NV_INFO(drm,f,a...) NV_PRINTK(info, &(drm)->client, f, ##a)
|
#define NV_INFO(drm,f,a...) NV_PRINTK(info, &(drm)->client, f, ##a)
|
||||||
|
|
||||||
#define NV_DEBUG(drm,f,a...) do { \
|
#define NV_DEBUG(drm,f,a...) do { \
|
||||||
if (unlikely(drm_debug & DRM_UT_DRIVER)) \
|
if (unlikely(drm_debug & DRM_UT_DRIVER)) \
|
||||||
NV_PRINTK(info, &(drm)->client, f, ##a); \
|
NV_PRINTK(info, &(drm)->client, f, ##a); \
|
||||||
@ -257,6 +257,12 @@ void nouveau_drm_device_remove(struct drm_device *dev);
|
|||||||
NV_PRINTK(info, &(drm)->client, f, ##a); \
|
NV_PRINTK(info, &(drm)->client, f, ##a); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
#define NV_PRINTK_ONCE(l,c,f,a...) NV_PRINTK(l##_once,c,f, ##a)
|
||||||
|
|
||||||
|
#define NV_ERROR_ONCE(drm,f,a...) NV_PRINTK_ONCE(err, &(drm)->client, f, ##a)
|
||||||
|
#define NV_WARN_ONCE(drm,f,a...) NV_PRINTK_ONCE(warn, &(drm)->client, f, ##a)
|
||||||
|
#define NV_INFO_ONCE(drm,f,a...) NV_PRINTK_ONCE(info, &(drm)->client, f, ##a)
|
||||||
|
|
||||||
extern int nouveau_modeset;
|
extern int nouveau_modeset;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -50,6 +50,7 @@ nvkm-y += nvkm/engine/disp/hdmig84.o
|
|||||||
nvkm-y += nvkm/engine/disp/hdmigt215.o
|
nvkm-y += nvkm/engine/disp/hdmigt215.o
|
||||||
nvkm-y += nvkm/engine/disp/hdmigf119.o
|
nvkm-y += nvkm/engine/disp/hdmigf119.o
|
||||||
nvkm-y += nvkm/engine/disp/hdmigk104.o
|
nvkm-y += nvkm/engine/disp/hdmigk104.o
|
||||||
|
nvkm-y += nvkm/engine/disp/hdmigm200.o
|
||||||
nvkm-y += nvkm/engine/disp/hdmigv100.o
|
nvkm-y += nvkm/engine/disp/hdmigv100.o
|
||||||
|
|
||||||
nvkm-y += nvkm/engine/disp/conn.o
|
nvkm-y += nvkm/engine/disp/conn.o
|
||||||
|
36
drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c
Normal file
36
drivers/gpu/drm/nouveau/nvkm/engine/disp/hdmigm200.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Ilia Mirkin
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Authors: Ilia Mirkin
|
||||||
|
*/
|
||||||
|
#include "hdmi.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
gm200_hdmi_scdc(struct nvkm_ior *ior, int head, u8 scdc)
|
||||||
|
{
|
||||||
|
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||||
|
const u32 hoff = head * 0x800;
|
||||||
|
const u32 ctrl = scdc & 0x3;
|
||||||
|
|
||||||
|
nvkm_mask(device, 0x61c5bc + hoff, 0x00000003, ctrl);
|
||||||
|
|
||||||
|
ior->tmds.high_speed = !!(scdc & 0x2);
|
||||||
|
}
|
@ -41,6 +41,11 @@ struct nvkm_ior {
|
|||||||
u8 nr;
|
u8 nr;
|
||||||
u8 bw;
|
u8 bw;
|
||||||
} dp;
|
} dp;
|
||||||
|
|
||||||
|
/* Armed TMDS state. */
|
||||||
|
struct {
|
||||||
|
bool high_speed;
|
||||||
|
} tmds;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nvkm_ior_func {
|
struct nvkm_ior_func {
|
||||||
@ -61,6 +66,7 @@ struct nvkm_ior_func {
|
|||||||
void (*ctrl)(struct nvkm_ior *, int head, bool enable,
|
void (*ctrl)(struct nvkm_ior *, int head, bool enable,
|
||||||
u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size,
|
u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size,
|
||||||
u8 *vendor, u8 vendor_size);
|
u8 *vendor, u8 vendor_size);
|
||||||
|
void (*scdc)(struct nvkm_ior *, int head, u8 scdc);
|
||||||
} hdmi;
|
} hdmi;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -144,6 +150,8 @@ void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
|||||||
void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
||||||
void gv100_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
void gv100_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
||||||
|
|
||||||
|
void gm200_hdmi_scdc(struct nvkm_ior *, int, u8);
|
||||||
|
|
||||||
void gt215_hda_hpd(struct nvkm_ior *, int, bool);
|
void gt215_hda_hpd(struct nvkm_ior *, int, bool);
|
||||||
void gt215_hda_eld(struct nvkm_ior *, u8 *, u8);
|
void gt215_hda_eld(struct nvkm_ior *, u8 *, u8);
|
||||||
|
|
||||||
|
@ -176,9 +176,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||||||
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
||||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
||||||
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
||||||
"max_ac_packet %d rekey %d\n",
|
"max_ac_packet %d rekey %d scdc %d\n",
|
||||||
args->v0.version, args->v0.state,
|
args->v0.version, args->v0.state,
|
||||||
args->v0.max_ac_packet, args->v0.rekey);
|
args->v0.max_ac_packet, args->v0.rekey,
|
||||||
|
args->v0.scdc);
|
||||||
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if ((args->v0.avi_infoframe_length
|
if ((args->v0.avi_infoframe_length
|
||||||
@ -202,6 +203,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||||||
args->v0.max_ac_packet,
|
args->v0.max_ac_packet,
|
||||||
args->v0.rekey, avi, avi_size,
|
args->v0.rekey, avi, avi_size,
|
||||||
vendor, vendor_size);
|
vendor, vendor_size);
|
||||||
|
|
||||||
|
if (outp->ior->func->hdmi.scdc)
|
||||||
|
outp->ior->func->hdmi.scdc(
|
||||||
|
outp->ior, hidx, args->v0.scdc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -120,13 +120,16 @@ void
|
|||||||
gf119_sor_clock(struct nvkm_ior *sor)
|
gf119_sor_clock(struct nvkm_ior *sor)
|
||||||
{
|
{
|
||||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||||
const int div = sor->asy.link == 3;
|
|
||||||
const u32 soff = nv50_ior_base(sor);
|
const u32 soff = nv50_ior_base(sor);
|
||||||
|
u32 div1 = sor->asy.link == 3;
|
||||||
|
u32 div2 = sor->asy.link == 3;
|
||||||
if (sor->asy.proto == TMDS) {
|
if (sor->asy.proto == TMDS) {
|
||||||
/* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */
|
const u32 speed = sor->tmds.high_speed ? 0x14 : 0x0a;
|
||||||
nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18);
|
nvkm_mask(device, 0x612300 + soff, 0x007c0000, speed << 18);
|
||||||
|
if (sor->tmds.high_speed)
|
||||||
|
div2 = 1;
|
||||||
}
|
}
|
||||||
nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div);
|
nvkm_mask(device, 0x612300 + soff, 0x00000707, (div2 << 8) | div1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -99,6 +99,7 @@ gm200_sor = {
|
|||||||
.clock = gf119_sor_clock,
|
.clock = gf119_sor_clock,
|
||||||
.hdmi = {
|
.hdmi = {
|
||||||
.ctrl = gk104_hdmi_ctrl,
|
.ctrl = gk104_hdmi_ctrl,
|
||||||
|
.scdc = gm200_hdmi_scdc,
|
||||||
},
|
},
|
||||||
.dp = {
|
.dp = {
|
||||||
.lanes = { 0, 1, 2, 3 },
|
.lanes = { 0, 1, 2, 3 },
|
||||||
|
@ -88,6 +88,7 @@ gv100_sor = {
|
|||||||
.clock = gf119_sor_clock,
|
.clock = gf119_sor_clock,
|
||||||
.hdmi = {
|
.hdmi = {
|
||||||
.ctrl = gv100_hdmi_ctrl,
|
.ctrl = gv100_hdmi_ctrl,
|
||||||
|
.scdc = gm200_hdmi_scdc,
|
||||||
},
|
},
|
||||||
.dp = {
|
.dp = {
|
||||||
.lanes = { 0, 1, 2, 3 },
|
.lanes = { 0, 1, 2, 3 },
|
||||||
|
@ -801,6 +801,7 @@ acr_r352_load(struct nvkm_acr *_acr, struct nvkm_falcon *falcon,
|
|||||||
bl = acr->hsbl_unload_blob;
|
bl = acr->hsbl_unload_blob;
|
||||||
} else {
|
} else {
|
||||||
nvkm_error(_acr->subdev, "invalid secure boot blob!\n");
|
nvkm_error(_acr->subdev, "invalid secure boot blob!\n");
|
||||||
|
kfree(bl_desc);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user