Commit e375922f authored by Linus Torvalds's avatar Linus Torvalds

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
......@@ -152,14 +152,23 @@ 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);
}
}
/**
......@@ -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);
......
......@@ -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,
......
......@@ -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:
......
......@@ -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);
}
......
......@@ -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)
......
......@@ -888,8 +888,10 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
/* If we got force-completed because of GPU reset rather than
* through our IRQ handler, signal the fence now.
*/
if (exec->fence)
if (exec->fence) {
dma_fence_signal(exec->fence);
dma_fence_put(exec->fence);
}
if (exec->bo) {
for (i = 0; i < exec->bo_count; i++) {
......
......@@ -139,6 +139,7 @@ vc4_irq_finish_render_job(struct drm_device *dev)
list_move_tail(&exec->head, &vc4->job_done_list);
if (exec->fence) {
dma_fence_signal_locked(exec->fence);
dma_fence_put(exec->fence);
exec->fence = NULL;
}
vc4_submit_next_render_job(dev);
......
......@@ -24,6 +24,7 @@
#define __DRM_CONNECTOR_H__
#include <linux/list.h>
#include <linux/llist.h>
#include <linux/ctype.h>
#include <linux/hdmi.h>
#include <drm/drm_mode_object.h>
......@@ -918,12 +919,13 @@ struct drm_connector {
uint16_t tile_h_size, tile_v_size;
/**
* @free_work:
* @free_node:
*
* Work used only by &drm_connector_iter to be able to clean up a
* connector from any context.
* List used only by &drm_connector_iter to be able to clean up a
* connector from any context, in conjunction with
* &drm_mode_config.connector_free_work.
*/
struct work_struct free_work;
struct llist_node free_node;
};
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
......
......@@ -465,6 +465,8 @@ struct edid *drm_get_edid(struct drm_connector *connector,
struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
struct i2c_adapter *adapter);
struct edid *drm_edid_duplicate(const struct edid *edid);
void drm_reset_display_info(struct drm_connector *connector);
u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid);
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
......
......@@ -27,6 +27,7 @@
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/workqueue.h>
#include <linux/llist.h>
#include <drm/drm_modeset_lock.h>
......@@ -393,7 +394,7 @@ struct drm_mode_config {
/**
* @connector_list_lock: Protects @num_connector and
* @connector_list.
* @connector_list and @connector_free_list.
*/
spinlock_t connector_list_lock;
/**
......@@ -413,6 +414,21 @@ struct drm_mode_config {
* &struct drm_connector_list_iter to walk this list.
*/
struct list_head connector_list;
/**
* @connector_free_list:
*
* List of connector objects linked with &drm_connector.free_head.
* Protected by @connector_list_lock. Used by
* drm_for_each_connector_iter() and
* &struct drm_connector_list_iter to savely free connectors using
* @connector_free_work.
*/
struct llist_head connector_free_list;
/**
* @connector_free_work: Work to clean up @connector_free_list.
*/
struct work_struct connector_free_work;
/**
* @num_encoder:
*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment