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) ...@@ -152,14 +152,23 @@ static void drm_connector_free(struct kref *kref)
connector->funcs->destroy(connector); 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 = struct drm_connector *connector, *n;
container_of(work, struct drm_connector, free_work); struct drm_device *dev =
struct drm_device *dev = connector->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); drm_mode_object_unregister(dev, &connector->base);
connector->funcs->destroy(connector); connector->funcs->destroy(connector);
}
} }
/** /**
...@@ -191,8 +200,6 @@ int drm_connector_init(struct drm_device *dev, ...@@ -191,8 +200,6 @@ int drm_connector_init(struct drm_device *dev,
if (ret) if (ret)
return ret; return ret;
INIT_WORK(&connector->free_work, drm_connector_free_work_fn);
connector->base.properties = &connector->properties; connector->base.properties = &connector->properties;
connector->dev = dev; connector->dev = dev;
connector->funcs = funcs; connector->funcs = funcs;
...@@ -547,10 +554,17 @@ EXPORT_SYMBOL(drm_connector_list_iter_begin); ...@@ -547,10 +554,17 @@ EXPORT_SYMBOL(drm_connector_list_iter_begin);
* actually release the connector when dropping our final reference. * actually release the connector when dropping our final reference.
*/ */
static void 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)) struct drm_mode_config *config = &conn->dev->mode_config;
schedule_work(&conn->free_work);
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) ...@@ -582,10 +596,10 @@ drm_connector_list_iter_next(struct drm_connector_list_iter *iter)
/* loop until it's not a zombie connector */ /* loop until it's not a zombie connector */
} while (!kref_get_unless_zero(&iter->conn->base.refcount)); } while (!kref_get_unless_zero(&iter->conn->base.refcount));
spin_unlock_irqrestore(&config->connector_list_lock, flags);
if (old_conn) 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; return iter->conn;
} }
...@@ -602,9 +616,15 @@ EXPORT_SYMBOL(drm_connector_list_iter_next); ...@@ -602,9 +616,15 @@ EXPORT_SYMBOL(drm_connector_list_iter_next);
*/ */
void drm_connector_list_iter_end(struct drm_connector_list_iter *iter) 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; iter->dev = NULL;
if (iter->conn) if (iter->conn) {
drm_connector_put_safe(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_); lock_release(&connector_list_iter_dep_map, 0, _RET_IP_);
} }
EXPORT_SYMBOL(drm_connector_list_iter_end); EXPORT_SYMBOL(drm_connector_list_iter_end);
...@@ -1231,6 +1251,19 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, ...@@ -1231,6 +1251,19 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
if (edid) if (edid)
size = EDID_LENGTH * (1 + edid->extensions); 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, drm_object_property_set_value(&connector->base,
dev->mode_config.non_desktop_property, dev->mode_config.non_desktop_property,
connector->display_info.non_desktop); connector->display_info.non_desktop);
......
...@@ -142,6 +142,7 @@ int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, ...@@ -142,6 +142,7 @@ int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
uint64_t value); uint64_t value);
int drm_connector_create_standard_properties(struct drm_device *dev); int drm_connector_create_standard_properties(struct drm_device *dev);
const char *drm_get_connector_force_name(enum drm_connector_force force); const char *drm_get_connector_force_name(enum drm_connector_force force);
void drm_connector_free_work_fn(struct work_struct *work);
/* IOCTL */ /* IOCTL */
int drm_mode_connector_property_set_ioctl(struct drm_device *dev, int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
......
...@@ -1731,7 +1731,7 @@ EXPORT_SYMBOL(drm_edid_duplicate); ...@@ -1731,7 +1731,7 @@ EXPORT_SYMBOL(drm_edid_duplicate);
* *
* Returns true if @vendor is in @edid, false otherwise * 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]; char edid_vendor[3];
...@@ -1749,7 +1749,7 @@ static bool edid_vendor(struct edid *edid, const char *vendor) ...@@ -1749,7 +1749,7 @@ static bool edid_vendor(struct edid *edid, const char *vendor)
* *
* This tells subsequent routines what fixes they need to apply. * 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; const struct edid_quirk *quirk;
int i; int i;
...@@ -2813,7 +2813,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, ...@@ -2813,7 +2813,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
/* /*
* Search EDID for CEA extension block. * 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; u8 *edid_ext = NULL;
int i; int i;
...@@ -2835,12 +2835,12 @@ static u8 *drm_find_edid_extension(struct edid *edid, int ext_id) ...@@ -2835,12 +2835,12 @@ static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
return edid_ext; 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); 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); return drm_find_edid_extension(edid, DISPLAYID_EXT);
} }
...@@ -4363,7 +4363,7 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) ...@@ -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, 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; struct drm_display_info *info = &connector->display_info;
const u8 *edid_ext; const u8 *edid_ext;
...@@ -4397,11 +4397,33 @@ static void drm_parse_cea_ext(struct drm_connector *connector, ...@@ -4397,11 +4397,33 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
} }
} }
static void drm_add_display_info(struct drm_connector *connector, /* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
struct edid *edid, u32 quirks) * 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; 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->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10; info->height_mm = edid->height_cm * 10;
...@@ -4414,11 +4436,13 @@ static void drm_add_display_info(struct drm_connector *connector, ...@@ -4414,11 +4436,13 @@ static void drm_add_display_info(struct drm_connector *connector,
info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP); info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);
DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);
if (edid->revision < 3) if (edid->revision < 3)
return; return quirks;
if (!(edid->input & DRM_EDID_INPUT_DIGITAL)) if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
return; return quirks;
drm_parse_cea_ext(connector, edid); drm_parse_cea_ext(connector, edid);
...@@ -4438,7 +4462,7 @@ static void drm_add_display_info(struct drm_connector *connector, ...@@ -4438,7 +4462,7 @@ static void drm_add_display_info(struct drm_connector *connector,
/* Only defined for 1.4 with digital displays */ /* Only defined for 1.4 with digital displays */
if (edid->revision < 4) if (edid->revision < 4)
return; return quirks;
switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) { switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
case DRM_EDID_DIGITAL_DEPTH_6: case DRM_EDID_DIGITAL_DEPTH_6:
...@@ -4473,7 +4497,9 @@ static void drm_add_display_info(struct drm_connector *connector, ...@@ -4473,7 +4497,9 @@ static void drm_add_display_info(struct drm_connector *connector,
info->color_formats |= DRM_COLOR_FORMAT_YCRCB444; info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_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) 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) ...@@ -4627,14 +4653,12 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
return 0; return 0;
} }
quirks = edid_get_quirks(edid);
/* /*
* CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
* To avoid multiple parsing of same block, lets parse that map * To avoid multiple parsing of same block, lets parse that map
* from sink info, before parsing CEA modes. * 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: * 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 ...@@ -254,10 +254,10 @@ static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr
return lessee; return lessee;
out_lessee: out_lessee:
drm_master_put(&lessee);
mutex_unlock(&dev->mode_config.idr_mutex); mutex_unlock(&dev->mode_config.idr_mutex);
drm_master_put(&lessee);
return ERR_PTR(error); return ERR_PTR(error);
} }
......
...@@ -382,6 +382,9 @@ void drm_mode_config_init(struct drm_device *dev) ...@@ -382,6 +382,9 @@ void drm_mode_config_init(struct drm_device *dev)
ida_init(&dev->mode_config.connector_ida); ida_init(&dev->mode_config.connector_ida);
spin_lock_init(&dev->mode_config.connector_list_lock); 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); drm_mode_create_standard_properties(dev);
/* Just to be sure */ /* Just to be sure */
...@@ -432,7 +435,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) ...@@ -432,7 +435,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
} }
drm_connector_list_iter_end(&conn_iter); drm_connector_list_iter_end(&conn_iter);
/* connector_iter drops references in a work item. */ /* 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))) { if (WARN_ON(!list_empty(&dev->mode_config.connector_list))) {
drm_connector_list_iter_begin(dev, &conn_iter); drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &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) ...@@ -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 /* If we got force-completed because of GPU reset rather than
* through our IRQ handler, signal the fence now. * through our IRQ handler, signal the fence now.
*/ */
if (exec->fence) if (exec->fence) {
dma_fence_signal(exec->fence); dma_fence_signal(exec->fence);
dma_fence_put(exec->fence);
}
if (exec->bo) { if (exec->bo) {
for (i = 0; i < exec->bo_count; i++) { for (i = 0; i < exec->bo_count; i++) {
......
...@@ -139,6 +139,7 @@ vc4_irq_finish_render_job(struct drm_device *dev) ...@@ -139,6 +139,7 @@ vc4_irq_finish_render_job(struct drm_device *dev)
list_move_tail(&exec->head, &vc4->job_done_list); list_move_tail(&exec->head, &vc4->job_done_list);
if (exec->fence) { if (exec->fence) {
dma_fence_signal_locked(exec->fence); dma_fence_signal_locked(exec->fence);
dma_fence_put(exec->fence);
exec->fence = NULL; exec->fence = NULL;
} }
vc4_submit_next_render_job(dev); vc4_submit_next_render_job(dev);
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#define __DRM_CONNECTOR_H__ #define __DRM_CONNECTOR_H__
#include <linux/list.h> #include <linux/list.h>
#include <linux/llist.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/hdmi.h> #include <linux/hdmi.h>
#include <drm/drm_mode_object.h> #include <drm/drm_mode_object.h>
...@@ -918,12 +919,13 @@ struct drm_connector { ...@@ -918,12 +919,13 @@ struct drm_connector {
uint16_t tile_h_size, tile_v_size; 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 * List used only by &drm_connector_iter to be able to clean up a
* connector from any context. * 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) #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, ...@@ -465,6 +465,8 @@ struct edid *drm_get_edid(struct drm_connector *connector,
struct edid *drm_get_edid_switcheroo(struct drm_connector *connector, struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
struct i2c_adapter *adapter); struct i2c_adapter *adapter);
struct edid *drm_edid_duplicate(const struct edid *edid); 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); int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
u8 drm_match_cea_mode(const struct drm_display_mode *to_match); u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/llist.h>
#include <drm/drm_modeset_lock.h> #include <drm/drm_modeset_lock.h>
...@@ -393,7 +394,7 @@ struct drm_mode_config { ...@@ -393,7 +394,7 @@ struct drm_mode_config {
/** /**
* @connector_list_lock: Protects @num_connector and * @connector_list_lock: Protects @num_connector and
* @connector_list. * @connector_list and @connector_free_list.
*/ */
spinlock_t connector_list_lock; spinlock_t connector_list_lock;
/** /**
...@@ -413,6 +414,21 @@ struct drm_mode_config { ...@@ -413,6 +414,21 @@ struct drm_mode_config {
* &struct drm_connector_list_iter to walk this list. * &struct drm_connector_list_iter to walk this list.
*/ */
struct list_head connector_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: * @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