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

Commit e375922f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'drm-misc-fixes-2017-12-14' of git://anongit.freedesktop.org/drm/drm-misc

Pull drm fixes from Daniel Vetter:

 - two fixes for new core features

 - a corner case fix for the connnector_iter fix from last week (this
   one is cc: stable)

 - one vc4 fix

* tag 'drm-misc-fixes-2017-12-14' of git://anongit.freedesktop.org/drm/drm-misc:
  drm/drm_lease: Prevent deadlock in case drm_lease_create() fails
  drm: rework delayed connector cleanup in connector_iter
  drm: Update edid-derived drm_display_info fields at edid property set [v2]
  drm/vc4: Release fence after signalling
parents 7c5cac1b bd36d3ba
Loading
Loading
Loading
Loading
+48 −15
Original line number Diff line number Diff line
@@ -152,15 +152,24 @@ static void drm_connector_free(struct kref *kref)
	connector->funcs->destroy(connector);
}

static void drm_connector_free_work_fn(struct work_struct *work)
void drm_connector_free_work_fn(struct work_struct *work)
{
	struct drm_connector *connector =
		container_of(work, struct drm_connector, free_work);
	struct drm_device *dev = connector->dev;
	struct drm_connector *connector, *n;
	struct drm_device *dev =
		container_of(work, struct drm_device, mode_config.connector_free_work);
	struct drm_mode_config *config = &dev->mode_config;
	unsigned long flags;
	struct llist_node *freed;

	spin_lock_irqsave(&config->connector_list_lock, flags);
	freed = llist_del_all(&config->connector_free_list);
	spin_unlock_irqrestore(&config->connector_list_lock, flags);

	llist_for_each_entry_safe(connector, n, freed, free_node) {
		drm_mode_object_unregister(dev, &connector->base);
		connector->funcs->destroy(connector);
	}
}

/**
 * drm_connector_init - Init a preallocated connector
@@ -191,8 +200,6 @@ int drm_connector_init(struct drm_device *dev,
	if (ret)
		return ret;

	INIT_WORK(&connector->free_work, drm_connector_free_work_fn);

	connector->base.properties = &connector->properties;
	connector->dev = dev;
	connector->funcs = funcs;
@@ -547,10 +554,17 @@ EXPORT_SYMBOL(drm_connector_list_iter_begin);
 * actually release the connector when dropping our final reference.
 */
static void
drm_connector_put_safe(struct drm_connector *conn)
__drm_connector_put_safe(struct drm_connector *conn)
{
	if (refcount_dec_and_test(&conn->base.refcount.refcount))
		schedule_work(&conn->free_work);
	struct drm_mode_config *config = &conn->dev->mode_config;

	lockdep_assert_held(&config->connector_list_lock);

	if (!refcount_dec_and_test(&conn->base.refcount.refcount))
		return;

	llist_add(&conn->free_node, &config->connector_free_list);
	schedule_work(&config->connector_free_work);
}

/**
@@ -582,10 +596,10 @@ drm_connector_list_iter_next(struct drm_connector_list_iter *iter)

		/* loop until it's not a zombie connector */
	} while (!kref_get_unless_zero(&iter->conn->base.refcount));
	spin_unlock_irqrestore(&config->connector_list_lock, flags);

	if (old_conn)
		drm_connector_put_safe(old_conn);
		__drm_connector_put_safe(old_conn);
	spin_unlock_irqrestore(&config->connector_list_lock, flags);

	return iter->conn;
}
@@ -602,9 +616,15 @@ EXPORT_SYMBOL(drm_connector_list_iter_next);
 */
void drm_connector_list_iter_end(struct drm_connector_list_iter *iter)
{
	struct drm_mode_config *config = &iter->dev->mode_config;
	unsigned long flags;

	iter->dev = NULL;
	if (iter->conn)
		drm_connector_put_safe(iter->conn);
	if (iter->conn) {
		spin_lock_irqsave(&config->connector_list_lock, flags);
		__drm_connector_put_safe(iter->conn);
		spin_unlock_irqrestore(&config->connector_list_lock, flags);
	}
	lock_release(&connector_list_iter_dep_map, 0, _RET_IP_);
}
EXPORT_SYMBOL(drm_connector_list_iter_end);
@@ -1231,6 +1251,19 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
	if (edid)
		size = EDID_LENGTH * (1 + edid->extensions);

	/* Set the display info, using edid if available, otherwise
	 * reseting the values to defaults. This duplicates the work
	 * done in drm_add_edid_modes, but that function is not
	 * consistently called before this one in all drivers and the
	 * computation is cheap enough that it seems better to
	 * duplicate it rather than attempt to ensure some arbitrary
	 * ordering of calls.
	 */
	if (edid)
		drm_add_display_info(connector, edid);
	else
		drm_reset_display_info(connector);

	drm_object_property_set_value(&connector->base,
				      dev->mode_config.non_desktop_property,
				      connector->display_info.non_desktop);
+1 −0
Original line number Diff line number Diff line
@@ -142,6 +142,7 @@ int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
				    uint64_t value);
int drm_connector_create_standard_properties(struct drm_device *dev);
const char *drm_get_connector_force_name(enum drm_connector_force force);
void drm_connector_free_work_fn(struct work_struct *work);

/* IOCTL */
int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+38 −14
Original line number Diff line number Diff line
@@ -1731,7 +1731,7 @@ EXPORT_SYMBOL(drm_edid_duplicate);
 *
 * Returns true if @vendor is in @edid, false otherwise
 */
static bool edid_vendor(struct edid *edid, const char *vendor)
static bool edid_vendor(const struct edid *edid, const char *vendor)
{
	char edid_vendor[3];

@@ -1749,7 +1749,7 @@ static bool edid_vendor(struct edid *edid, const char *vendor)
 *
 * This tells subsequent routines what fixes they need to apply.
 */
static u32 edid_get_quirks(struct edid *edid)
static u32 edid_get_quirks(const struct edid *edid)
{
	const struct edid_quirk *quirk;
	int i;
@@ -2813,7 +2813,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
/*
 * Search EDID for CEA extension block.
 */
static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id)
{
	u8 *edid_ext = NULL;
	int i;
@@ -2835,12 +2835,12 @@ static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
	return edid_ext;
}

static u8 *drm_find_cea_extension(struct edid *edid)
static u8 *drm_find_cea_extension(const struct edid *edid)
{
	return drm_find_edid_extension(edid, CEA_EXT);
}

static u8 *drm_find_displayid_extension(struct edid *edid)
static u8 *drm_find_displayid_extension(const struct edid *edid)
{
	return drm_find_edid_extension(edid, DISPLAYID_EXT);
}
@@ -4363,7 +4363,7 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
}

static void drm_parse_cea_ext(struct drm_connector *connector,
			      struct edid *edid)
			      const struct edid *edid)
{
	struct drm_display_info *info = &connector->display_info;
	const u8 *edid_ext;
@@ -4397,11 +4397,33 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
	}
}

static void drm_add_display_info(struct drm_connector *connector,
				 struct edid *edid, u32 quirks)
/* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
 * all of the values which would have been set from EDID
 */
void
drm_reset_display_info(struct drm_connector *connector)
{
	struct drm_display_info *info = &connector->display_info;

	info->width_mm = 0;
	info->height_mm = 0;

	info->bpc = 0;
	info->color_formats = 0;
	info->cea_rev = 0;
	info->max_tmds_clock = 0;
	info->dvi_dual = false;

	info->non_desktop = 0;
}
EXPORT_SYMBOL_GPL(drm_reset_display_info);

u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
{
	struct drm_display_info *info = &connector->display_info;

	u32 quirks = edid_get_quirks(edid);

	info->width_mm = edid->width_cm * 10;
	info->height_mm = edid->height_cm * 10;

@@ -4414,11 +4436,13 @@ static void drm_add_display_info(struct drm_connector *connector,

	info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);

	DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);

	if (edid->revision < 3)
		return;
		return quirks;

	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
		return;
		return quirks;

	drm_parse_cea_ext(connector, edid);

@@ -4438,7 +4462,7 @@ static void drm_add_display_info(struct drm_connector *connector,

	/* Only defined for 1.4 with digital displays */
	if (edid->revision < 4)
		return;
		return quirks;

	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
	case DRM_EDID_DIGITAL_DEPTH_6:
@@ -4473,7 +4497,9 @@ static void drm_add_display_info(struct drm_connector *connector,
		info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
		info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
	return quirks;
}
EXPORT_SYMBOL_GPL(drm_add_display_info);

static int validate_displayid(u8 *displayid, int length, int idx)
{
@@ -4627,14 +4653,12 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
		return 0;
	}

	quirks = edid_get_quirks(edid);

	/*
	 * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
	 * To avoid multiple parsing of same block, lets parse that map
	 * from sink info, before parsing CEA modes.
	 */
	drm_add_display_info(connector, edid, quirks);
	quirks = drm_add_display_info(connector, edid);

	/*
	 * EDID spec says modes should be preferred in this order:
+2 −2
Original line number Diff line number Diff line
@@ -254,10 +254,10 @@ static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr
	return lessee;

out_lessee:
	drm_master_put(&lessee);

	mutex_unlock(&dev->mode_config.idr_mutex);

	drm_master_put(&lessee);

	return ERR_PTR(error);
}

+4 −1
Original line number Diff line number Diff line
@@ -382,6 +382,9 @@ void drm_mode_config_init(struct drm_device *dev)
	ida_init(&dev->mode_config.connector_ida);
	spin_lock_init(&dev->mode_config.connector_list_lock);

	init_llist_head(&dev->mode_config.connector_free_list);
	INIT_WORK(&dev->mode_config.connector_free_work, drm_connector_free_work_fn);

	drm_mode_create_standard_properties(dev);

	/* Just to be sure */
@@ -432,7 +435,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
	}
	drm_connector_list_iter_end(&conn_iter);
	/* connector_iter drops references in a work item. */
	flush_scheduled_work();
	flush_work(&dev->mode_config.connector_free_work);
	if (WARN_ON(!list_empty(&dev->mode_config.connector_list))) {
		drm_connector_list_iter_begin(dev, &conn_iter);
		drm_for_each_connector_iter(connector, &conn_iter)
Loading