}
EXPORT_SYMBOL(drm_connector_unplug_all);
+int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
+ const struct drm_bridge_funcs *funcs)
+{
+ int ret;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ ret = drm_mode_object_get(dev, &bridge->base, DRM_MODE_OBJECT_BRIDGE);
+ if (ret)
+ goto out;
+
+ bridge->dev = dev;
+ bridge->funcs = funcs;
+
+ list_add_tail(&bridge->head, &dev->mode_config.bridge_list);
+ dev->mode_config.num_bridge++;
+
+ out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+}
+EXPORT_SYMBOL(drm_bridge_init);
+
+void drm_bridge_cleanup(struct drm_bridge *bridge)
+{
+ struct drm_device *dev = bridge->dev;
+
+ mutex_lock(&dev->mode_config.mutex);
+ drm_mode_object_put(dev, &bridge->base);
+ list_del(&bridge->head);
+ dev->mode_config.num_bridge--;
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_bridge_cleanup);
+
int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
INIT_LIST_HEAD(&dev->mode_config.fb_list);
INIT_LIST_HEAD(&dev->mode_config.crtc_list);
INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ INIT_LIST_HEAD(&dev->mode_config.bridge_list);
INIT_LIST_HEAD(&dev->mode_config.encoder_list);
INIT_LIST_HEAD(&dev->mode_config.property_list);
INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
/* Just to be sure */
dev->mode_config.num_fb = 0;
dev->mode_config.num_connector = 0;
+ dev->mode_config.num_bridge = 0;
dev->mode_config.num_crtc = 0;
dev->mode_config.num_encoder = 0;
}
total_objects += dev->mode_config.num_crtc;
total_objects += dev->mode_config.num_connector;
total_objects += dev->mode_config.num_encoder;
+ total_objects += dev->mode_config.num_bridge;
group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL);
if (!group->id_list)
group->num_crtcs = 0;
group->num_connectors = 0;
group->num_encoders = 0;
+ group->num_bridges = 0;
return 0;
}
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
+ struct drm_bridge *bridge;
int ret;
if ((ret = drm_mode_group_init(dev, group)))
group->id_list[group->num_crtcs + group->num_encoders +
group->num_connectors++] = connector->base.id;
+ list_for_each_entry(bridge, &dev->mode_config.bridge_list, head)
+ group->id_list[group->num_crtcs + group->num_encoders +
+ group->num_connectors + group->num_bridges++] =
+ bridge->base.id;
+
return 0;
}
EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
void drm_mode_config_cleanup(struct drm_device *dev)
{
struct drm_connector *connector, *ot;
+ struct drm_bridge *bridge, *bt;
struct drm_crtc *crtc, *ct;
struct drm_encoder *encoder, *enct;
struct drm_framebuffer *fb, *fbt;
connector->funcs->destroy(connector);
}
+ list_for_each_entry_safe(bridge, bt,
+ &dev->mode_config.bridge_list, head) {
+ bridge->funcs->destroy(bridge);
+ }
+
list_for_each_entry_safe(property, pt, &dev->mode_config.property_list,
head) {
drm_property_destroy(dev, property);
}
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
+/**
+ * drm_helper_bridge_in_use - check if a given bridge is in use
+ * @bridge: bridge to check
+ *
+ * LOCKING:
+ * Caller must hold mode config lock.
+ *
+ * Walk @bridge's DRM device's mode_config and see if it's in use.
+ *
+ * RETURNS:
+ * True if @bridge is part of the mode_config, false otherwise.
+ */
+bool drm_helper_bridge_in_use(struct drm_bridge *bridge)
+{
+ struct drm_connector *connector;
+ struct drm_device *dev = bridge->dev;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->bridge == bridge)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL(drm_helper_bridge_in_use);
+
/**
* drm_helper_encoder_in_use - check if a given encoder is in use
* @encoder: encoder to check
*/
void drm_helper_disable_unused_functions(struct drm_device *dev)
{
+ struct drm_bridge *bridge;
+ struct drm_bridge_helper_funcs *bridge_funcs;
struct drm_encoder *encoder;
struct drm_connector *connector;
struct drm_crtc *crtc;
connector->encoder = NULL;
}
+ list_for_each_entry(bridge, &dev->mode_config.bridge_list, head) {
+ if (!drm_helper_bridge_in_use(bridge)) {
+ bridge_funcs = bridge->helper_private;
+
+ if (bridge_funcs->dpms)
+ bridge_funcs->dpms(bridge, DRM_MODE_DPMS_OFF);
+
+ /* disconnect crtc from the bridge */
+ bridge->crtc = NULL;
+ }
+ }
+
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (!drm_helper_encoder_in_use(encoder)) {
drm_encoder_disable(encoder);
struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_bridge *bridge;
+ struct drm_bridge_helper_funcs *bridge_funcs;
int saved_x, saved_y;
struct drm_encoder *encoder;
bool ret = true;
}
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+ list_for_each_entry(bridge, &dev->mode_config.bridge_list, head) {
+ if (bridge->crtc != crtc)
+ continue;
+
+ bridge_funcs = bridge->helper_private;
+ if (bridge_funcs->prepare)
+ bridge_funcs->prepare(bridge);
+ }
+
/* Prepare the encoders and CRTCs before setting the mode. */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct drm_device *dev;
struct drm_crtc *save_crtcs, *new_crtc, *crtc;
struct drm_encoder *save_encoders, *new_encoder, *encoder;
+ struct drm_bridge *new_bridge;
struct drm_framebuffer *old_fb = NULL;
bool mode_changed = false; /* if true do a full mode set */
bool fb_changed = false; /* if true and !mode_changed just do a flip */
mode_changed = true;
}
+ /* try to find a bridge for the connectors in the connector list */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_connector_helper_funcs *connector_funcs =
+ connector->helper_private;
+
+ new_bridge = connector->bridge;
+ for (ro = 0; ro < set->num_connectors; ro++) {
+ if (set->connectors[ro] != connector)
+ continue;
+
+ if (connector_funcs->best_bridge)
+ new_bridge = connector_funcs->best_bridge(
+ connector);
+ break;
+ }
+
+ if (new_bridge != connector->bridge) {
+ DRM_DEBUG_KMS("bridge changed, full mode switch\n");
+ mode_changed = true;
+ if (connector->bridge)
+ connector->bridge->crtc = NULL;
+ connector->bridge = new_bridge;
+ }
+ }
+
/* a) traverse passed in connector list and get encoders for them */
count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
mode_changed = true;
connector->encoder->crtc = new_crtc;
}
+ if (connector->bridge && new_crtc != connector->bridge->crtc) {
+ DRM_DEBUG_KMS("crtc changed, full mode switch\n");
+ mode_changed = true;
+ connector->bridge->crtc = new_crtc;
+ }
if (new_crtc) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
connector->base.id, drm_get_connector_name(connector),
void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
{
struct drm_encoder *encoder = connector->encoder;
+ struct drm_bridge *bridge = connector->bridge;
+
struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
int old_dpms;
(*crtc_funcs->dpms) (crtc,
drm_helper_choose_crtc_dpms(crtc));
}
+ if (bridge) {
+ struct drm_bridge_helper_funcs *bridge_funcs =
+ bridge->helper_private;
+
+ if (bridge_funcs->dpms)
+ (*bridge_funcs->dpms)(bridge, mode);
+ }
if (encoder) {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
if (encoder_funcs->dpms)
(*encoder_funcs->dpms) (encoder,
drm_helper_choose_encoder_dpms(encoder));
}
+ if (bridge) {
+ struct drm_bridge_helper_funcs *bridge_funcs =
+ bridge->helper_private;
+
+ if (bridge_funcs->dpms)
+ (*bridge_funcs->dpms)(bridge, mode);
+ }
if (crtc) {
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
if (crtc_funcs->dpms)
#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
+#define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
struct drm_mode_object {
uint32_t id;
struct drm_encoder;
struct drm_pending_vblank_event;
struct drm_plane;
+struct drm_bridge;
/**
* drm_crtc_funcs - control CRTCs for a given device
* @force: a %DRM_FORCE_<foo> state for forced mode sets
* @encoder_ids: valid encoders for this connector
* @encoder: encoder driving this connector, if any
+ * @bridge: bridge connected to this connector
* @eld: EDID-like data, if present
* @dvi_dual: dual link DVI, if found
* @max_tmds_clock: max clock rate, if found
enum drm_connector_force force;
uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
struct drm_encoder *encoder; /* currently active encoder */
+ struct drm_bridge *bridge; /* currently active bridge */
/* EDID bits */
uint8_t eld[MAX_ELD_BYTES];
void *helper_private;
};
+/**
+ * drm_bridge_funcs - drm_bridge control functions
+ * @destroy: make object go away
+ */
+struct drm_bridge_funcs {
+ void (*destroy)(struct drm_bridge *bridge);
+};
+
+/**
+ * drm_bridge - central DRM bridge control structure
+ * @dev: DRM device this bridge belongs to
+ * @head: list management
+ * @base: base mode object
+ * @crtc: crtc we're currently attached to
+ * @connector_type: the type of connector this bridge can attach to
+ * @funcs: control functions
+ * @helper_private: mid-layer private data
+ */
+struct drm_bridge {
+ struct drm_device *dev;
+ struct list_head head;
+
+ struct drm_mode_object base;
+
+ struct drm_crtc *crtc;
+
+ int connector_type;
+
+ const struct drm_bridge_funcs *funcs;
+ void *helper_private;
+};
+
/**
* drm_mode_set - new values for a CRTC config change
* @head: list management
uint32_t num_crtcs;
uint32_t num_encoders;
uint32_t num_connectors;
+ uint32_t num_bridges;
/* list of object IDs for this group */
uint32_t *id_list;
* @fb_list: list of framebuffers available
* @num_connector: number of connectors on this device
* @connector_list: list of connector objects
+ * @num_bridge: number of bridges on this device
+ * @bridge_list: list of bridge objects
* @num_encoder: number of encoders on this device
* @encoder_list: list of encoder objects
* @num_crtc: number of CRTCs on this device
struct list_head fb_list;
int num_connector;
struct list_head connector_list;
+ int num_bridge;
+ struct list_head bridge_list;
int num_encoder;
struct list_head encoder_list;
int num_plane;
/* helper to unplug all connectors from sysfs for device */
extern void drm_connector_unplug_all(struct drm_device *dev);
+extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
+ const struct drm_bridge_funcs *funcs);
+extern void drm_bridge_cleanup(struct drm_bridge *bridge);
+
extern int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int (*mode_valid)(struct drm_connector *connector,
struct drm_display_mode *mode);
struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
+ struct drm_bridge *(*best_bridge)(struct drm_connector *connector);
+};
+
+struct drm_bridge_helper_funcs {
+ void (*dpms)(struct drm_bridge *bridge, int mode);
+ void (*prepare)(struct drm_bridge *bridge);
};
extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
struct drm_framebuffer *old_fb);
extern bool drm_helper_crtc_in_use(struct drm_crtc *crtc);
extern bool drm_helper_encoder_in_use(struct drm_encoder *encoder);
+extern bool drm_helper_bridge_in_use(struct drm_bridge *bridge);
extern void drm_helper_connector_dpms(struct drm_connector *connector, int mode);
connector->helper_private = (void *)funcs;
}
+static inline void drm_bridge_helper_add(struct drm_bridge *bridge,
+ const struct drm_bridge_helper_funcs *funcs)
+{
+ bridge->helper_private = (void *)funcs;
+}
+
extern int drm_helper_resume_force_mode(struct drm_device *dev);
extern void drm_kms_helper_poll_init(struct drm_device *dev);
extern void drm_kms_helper_poll_fini(struct drm_device *dev);