mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 07:02:23 +00:00
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
This commit is contained in:
commit
e375922fc5
@ -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;
|
||||
|
||||
drm_mode_object_unregister(dev, &connector->base);
|
||||
connector->funcs->destroy(connector);
|
||||
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:
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user