Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e6191d4 authored by Dave Airlie's avatar Dave Airlie
Browse files

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: default avatarDave Airlie <airlied@redhat.com>
From: Ben Skeggs <bskeggs@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CABDvA=mgEm9JxP7AX7Sff-AEs7a75M4SqwFHmLPZhJojm4k=OA@mail.gmail.com
parents 62e681f7 74a07c0a
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <drm/drm_dp_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_scdc_helper.h>
#include <drm/drm_edid.h>

#include <nvif/class.h>
@@ -531,6 +532,7 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
static void
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_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
	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 */
	};
	struct nouveau_connector *nv_connector;
	struct drm_hdmi_info *hdmi;
	u32 max_ac_packet;
	union hdmi_infoframe avi_frame;
	union hdmi_infoframe vendor_frame;
	bool scdc_supported, high_tmds_clock_ratio = false, scrambling = false;
	u8 config;
	int ret;
	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))
		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,
						       false);
						       scdc_supported);
	if (!ret) {
		/* We have an AVI InfoFrame, populate it to the display */
		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 */
	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)
		+ sizeof(args.pwr)
		+ args.pwr.avi_infoframe_length
		+ args.pwr.vendor_infoframe_length;
	nvif_mthd(&disp->disp->object, 0, &args, size);

	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);
}

/******************************************************************************
+4 −1
Original line number Diff line number Diff line
@@ -69,7 +69,10 @@ struct nv50_disp_sor_hdmi_pwr_v0 {
	__u8  rekey;
	__u8  avi_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 {
+107 −113
Original line number Diff line number Diff line
@@ -37,18 +37,19 @@
#include "nouveau_drv.h"
#include "nouveau_reg.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"

static struct ida bl_ida;
#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0'

struct backlight_connector {
	struct list_head head;
struct nouveau_backlight {
	struct backlight_device *dev;
	int id;
};

static bool
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_connector
		*connector)
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
			   struct nouveau_backlight *bl)
{
	const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
	if (nb < 0 || nb >= 100)
@@ -57,14 +58,15 @@ nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_c
		snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
	else
		snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight");
	connector->id = nb;
	bl->id = nb;
	return true;
}

static int
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;
	int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
		   NV40_PMC_BACKLIGHT_MASK) >> 16;
@@ -75,7 +77,8 @@ nv40_get_intensity(struct backlight_device *bd)
static int
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;
	int val = bd->props.brightness;
	int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
@@ -93,38 +96,19 @@ static const struct backlight_ops nv40_bl_ops = {
};

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 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))
		return 0;

	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);
		return -ENODEV;

	props->type = BACKLIGHT_RAW;
	props->max_brightness = 31;
	*ops = &nv40_bl_ops;
	return 0;
}

@@ -194,7 +178,8 @@ nva3_set_intensity(struct backlight_device *bd)
	div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
	val = (bd->props.brightness * div) / 100;
	if (div) {
		nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
		nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
			  val |
			  NV50_PDISP_SOR_PWM_CTL_NEW |
			  NVA3_PDISP_SOR_PWM_CTL_UNK);
		return 0;
@@ -210,110 +195,119 @@ static const struct backlight_ops nva3_bl_ops = {
};

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 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)))
		return 0;
		return -ENODEV;

	if (drm->client.device.info.chipset <= 0xa0 ||
	    drm->client.device.info.chipset == 0xaa ||
	    drm->client.device.info.chipset == 0xac)
		ops = &nv50_bl_ops;
		*ops = &nv50_bl_ops;
	else
		ops = &nva3_bl_ops;
		*ops = &nva3_bl_ops;

	memset(&props, 0, sizeof(struct backlight_properties));
	props.type = BACKLIGHT_RAW;
	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);
	props->type = BACKLIGHT_RAW;
	props->max_brightness = 100;

	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;
}

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 drm_connector *connector;
	struct drm_connector_list_iter conn_iter;

	INIT_LIST_HEAD(&drm->bl_connectors);
	char backlight_name[BL_NAME_SIZE];
	struct backlight_properties props = {0};
	const struct backlight_ops *ops;
	int ret;

	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;
	}

	drm_connector_list_iter_begin(dev, &conn_iter);
	drm_for_each_connector_iter(connector, &conn_iter) {
		if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
		    connector->connector_type != DRM_MODE_CONNECTOR_eDP)
			continue;
	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
		nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS);
	else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
		nv_encoder = find_encoder(connector, DCB_OUTPUT_DP);
	else
		return 0;

	if (!nv_encoder)
		return 0;

	switch (device->info.family) {
	case NV_DEVICE_INFO_V0_CURIE:
			return nv40_backlight_init(connector);
		ret = nv40_backlight_init(nv_encoder, &props, &ops);
		break;
	case NV_DEVICE_INFO_V0_TESLA:
	case NV_DEVICE_INFO_V0_FERMI:
	case NV_DEVICE_INFO_V0_KEPLER:
	case NV_DEVICE_INFO_V0_MAXWELL:
			return nv50_backlight_init(connector);
		default:
		ret = nv50_backlight_init(nv_encoder, &props, &ops);
		break;
	default:
		return 0;
	}

	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;
	}
	drm_connector_list_iter_end(&conn_iter);

	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;

fail_alloc:
	kfree(bl);
	return ret;
}

void
nouveau_backlight_exit(struct drm_device *dev)
nouveau_backlight_fini(struct drm_connector *connector)
{
	struct nouveau_drm *drm = nouveau_drm(dev);
	struct backlight_connector *connector;
	struct nouveau_connector *nv_conn = nouveau_connector(connector);
	struct nouveau_backlight *bl = nv_conn->backlight;

	list_for_each_entry(connector, &drm->bl_connectors, head) {
		if (connector->id >= 0)
			ida_simple_remove(&bl_ida, connector->id);
	}
	if (!bl)
		return;

	if (drm->backlight) {
		backlight_device_unregister(drm->backlight);
		drm->backlight = NULL;
	}
	if (bl->id >= 0)
		ida_simple_remove(&bl_ida, bl->id);

	backlight_device_unregister(bl->dev);
	nv_conn->backlight = NULL;
	kfree(bl);
}

void
+42 −12
Original line number Diff line number Diff line
@@ -885,6 +885,22 @@ nouveau_connector_detect_depth(struct drm_connector *connector)
		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
nouveau_connector_get_modes(struct drm_connector *connector)
{
@@ -953,18 +969,33 @@ nouveau_connector_get_modes(struct drm_connector *connector)
}

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_encoder *nv_encoder = nv_connector->detected_encoder;
	struct nouveau_drm *drm = nouveau_drm(connector->dev);
	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 (drm_detect_hdmi_monitor(nv_connector->edid))
		info = &nv_connector->base.display_info;

	if (hdmi) {
	if (info) {
		if (nouveau_hdmimhz > 0)
			return nouveau_hdmimhz * 1000;
		/* Note: these limits are conservative, some Fermi's
		 * 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)
			return 297000;
		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 ||
	    drm->client.device.info.chipset >= 0x46)
		return 165000;
		return 165000 * duallink_scale;
	else if (drm->client.device.info.chipset >= 0x40)
		return 155000;
		return 155000 * duallink_scale;
	else if (drm->client.device.info.chipset >= 0x18)
		return 135000;
		return 135000 * duallink_scale;
	else
		return 112000;
		return 112000 * duallink_scale;
}

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);
	unsigned min_clock = 25000, max_clock = min_clock;
	unsigned clock = mode->clock;
	bool hdmi;

	switch (nv_encoder->dcb->type) {
	case DCB_OUTPUT_LVDS:
@@ -1003,11 +1033,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
		max_clock = 400000;
		break;
	case DCB_OUTPUT_TMDS:
		hdmi = drm_detect_hdmi_monitor(nv_connector->edid);
		max_clock = get_tmds_link_bandwidth(connector, hdmi);
		if (!hdmi && nouveau_duallink &&
		    nv_encoder->dcb->duallink_possible)
			max_clock *= 2;
		max_clock = get_tmds_link_bandwidth(connector);
		break;
	case DCB_OUTPUT_ANALOG:
		max_clock = nv_encoder->dcb->crtconf.maxfreq;
@@ -1069,6 +1095,8 @@ nouveau_connector_funcs = {
	.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
	.atomic_set_property = nouveau_conn_atomic_set_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
@@ -1084,6 +1112,8 @@ nouveau_connector_funcs_lvds = {
	.atomic_destroy_state = nouveau_conn_atomic_destroy_state,
	.atomic_set_property = nouveau_conn_atomic_set_property,
	.atomic_get_property = nouveau_conn_atomic_get_property,
	.late_register = nouveau_connector_late_register,
	.early_unregister = nouveau_connector_early_unregister,
};

static int
+33 −0
Original line number Diff line number Diff line
@@ -39,6 +39,10 @@

struct nvkm_i2c_port;

#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
struct nouveau_backlight;
#endif

struct nouveau_connector {
	struct drm_connector base;
	enum dcb_connector_type type;
@@ -55,6 +59,9 @@ struct nouveau_connector {
	struct nouveau_encoder *detected_encoder;
	struct edid *edid;
	struct drm_display_mode *native_mode;
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
	struct nouveau_backlight *backlight;
#endif
};

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 *,
				     struct drm_property *, u64 *);
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__ */
Loading