Merge tag 'topic/drm-misc-2016-01-17' of git://anongit.freedesktop.org/drm-intel into drm-next

Since your main drm-next pull isn't out of the door yet I figured I might
as well flush out drm-misc instead of delaying for 4.6. It's really just
random stuff all over, biggest thing probably connector_mask tracking from
Maarten.

* tag 'topic/drm-misc-2016-01-17' of git://anongit.freedesktop.org/drm-intel: (24 commits)
  drm/fb_cma_helper: Remove implicit call to disable_unused_functions
  drm/sysfs: use kobj_to_dev()
  drm/i915: Init power domains early in driver load
  drm: Do not set connector->encoder in drivers
  apple-gmux: Add initial documentation
  drm: move MODULE_PARM_DESC to other file
  drm/edid: index CEA/HDMI mode tables using the VIC
  drm/atomic: Remove drm_atomic_connectors_for_crtc.
  drm/i915: Update connector_mask during readout, v2.
  drm: Remove opencoded drm_gem_object_release_handle()
  drm: Do not set outparam on error during GEM handle allocation
  drm/docs: more leftovers from the big vtable documentation pile
  drm/atomic-helper: Reject legacy flips on a disabled pipe
  drm/atomic: add connector mask to drm_crtc_state.
  drm/tegra: Use __drm_atomic_helper_reset_connector for subclassing connector state, v2.
  drm/atomic: Add __drm_atomic_helper_connector_reset, v2.
  drm/i915: Set connector_state->connector using the helper.
  drm: Use a normal idr allocation for the obj->name
  drm: Only bump object-reference count when adding first handle
  drm: Balance error path for GEM handle allocation
  ...
This commit is contained in:
Dave Airlie 2016-01-18 07:01:16 +10:00
commit 28f03607bb
32 changed files with 437 additions and 387 deletions

View File

@ -1578,194 +1578,6 @@ void intel_crt_init(struct drm_device *dev)
To use it, a driver must provide bottom functions for all of the three KMS
entities.
</para>
<sect2>
<title>Legacy CRTC Helper Operations</title>
<itemizedlist>
<listitem id="drm-helper-crtc-mode-fixup">
<synopsis>bool (*mode_fixup)(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);</synopsis>
<para>
Let CRTCs adjust the requested mode or reject it completely. This
operation returns true if the mode is accepted (possibly after being
adjusted) or false if it is rejected.
</para>
<para>
The <methodname>mode_fixup</methodname> operation should reject the
mode if it can't reasonably use it. The definition of "reasonable"
is currently fuzzy in this context. One possible behaviour would be
to set the adjusted mode to the panel timings when a fixed-mode
panel is used with hardware capable of scaling. Another behaviour
would be to accept any input mode and adjust it to the closest mode
supported by the hardware (FIXME: This needs to be clarified).
</para>
</listitem>
<listitem>
<synopsis>int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)</synopsis>
<para>
Move the CRTC on the current frame buffer (stored in
<literal>crtc-&gt;fb</literal>) to position (x,y). Any of the frame
buffer, x position or y position may have been modified.
</para>
<para>
This helper operation is optional. If not provided, the
<function>drm_crtc_helper_set_config</function> function will fall
back to the <methodname>mode_set</methodname> helper operation.
</para>
<note><para>
FIXME: Why are x and y passed as arguments, as they can be accessed
through <literal>crtc-&gt;x</literal> and
<literal>crtc-&gt;y</literal>?
</para></note>
</listitem>
<listitem>
<synopsis>void (*prepare)(struct drm_crtc *crtc);</synopsis>
<para>
Prepare the CRTC for mode setting. This operation is called after
validating the requested mode. Drivers use it to perform
device-specific operations required before setting the new mode.
</para>
</listitem>
<listitem>
<synopsis>int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb);</synopsis>
<para>
Set a new mode, position and frame buffer. Depending on the device
requirements, the mode can be stored internally by the driver and
applied in the <methodname>commit</methodname> operation, or
programmed to the hardware immediately.
</para>
<para>
The <methodname>mode_set</methodname> operation returns 0 on success
or a negative error code if an error occurs.
</para>
</listitem>
<listitem>
<synopsis>void (*commit)(struct drm_crtc *crtc);</synopsis>
<para>
Commit a mode. This operation is called after setting the new mode.
Upon return the device must use the new mode and be fully
operational.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Encoder Helper Operations</title>
<itemizedlist>
<listitem>
<synopsis>bool (*mode_fixup)(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);</synopsis>
<para>
Let encoders adjust the requested mode or reject it completely. This
operation returns true if the mode is accepted (possibly after being
adjusted) or false if it is rejected. See the
<link linkend="drm-helper-crtc-mode-fixup">mode_fixup CRTC helper
operation</link> for an explanation of the allowed adjustments.
</para>
</listitem>
<listitem>
<synopsis>void (*prepare)(struct drm_encoder *encoder);</synopsis>
<para>
Prepare the encoder for mode setting. This operation is called after
validating the requested mode. Drivers use it to perform
device-specific operations required before setting the new mode.
</para>
</listitem>
<listitem>
<synopsis>void (*mode_set)(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);</synopsis>
<para>
Set a new mode. Depending on the device requirements, the mode can
be stored internally by the driver and applied in the
<methodname>commit</methodname> operation, or programmed to the
hardware immediately.
</para>
</listitem>
<listitem>
<synopsis>void (*commit)(struct drm_encoder *encoder);</synopsis>
<para>
Commit a mode. This operation is called after setting the new mode.
Upon return the device must use the new mode and be fully
operational.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Connector Helper Operations</title>
<itemizedlist>
<listitem>
<synopsis>struct drm_encoder *(*best_encoder)(struct drm_connector *connector);</synopsis>
<para>
Return a pointer to the best encoder for the connecter. Device that
map connectors to encoders 1:1 simply return the pointer to the
associated encoder. This operation is mandatory.
</para>
</listitem>
<listitem>
<synopsis>int (*get_modes)(struct drm_connector *connector);</synopsis>
<para>
Fill the connector's <structfield>probed_modes</structfield> list
by parsing EDID data with <function>drm_add_edid_modes</function>,
adding standard VESA DMT modes with <function>drm_add_modes_noedid</function>,
or calling <function>drm_mode_probed_add</function> directly for every
supported mode and return the number of modes it has detected. This
operation is mandatory.
</para>
<para>
Note that the caller function will automatically add standard VESA
DMT modes up to 1024x768 if the <methodname>get_modes</methodname>
helper operation returns no mode and if the connector status is
connector_status_connected. There is no need to call
<function>drm_add_edid_modes</function> manually in that case.
</para>
<para>
The <structfield>vrefresh</structfield> value is computed by
<function>drm_helper_probe_single_connector_modes</function>.
</para>
<para>
When parsing EDID data, <function>drm_add_edid_modes</function> fills the
connector <structfield>display_info</structfield>
<structfield>width_mm</structfield> and
<structfield>height_mm</structfield> fields. When creating modes
manually the <methodname>get_modes</methodname> helper operation must
set the <structfield>display_info</structfield>
<structfield>width_mm</structfield> and
<structfield>height_mm</structfield> fields if they haven't been set
already (for instance at initialization time when a fixed-size panel is
attached to the connector). The mode <structfield>width_mm</structfield>
and <structfield>height_mm</structfield> fields are only used internally
during EDID parsing and should not be set when creating modes manually.
</para>
</listitem>
<listitem>
<synopsis>int (*mode_valid)(struct drm_connector *connector,
struct drm_display_mode *mode);</synopsis>
<para>
Verify whether a mode is valid for the connector. Return MODE_OK for
supported modes and one of the enum drm_mode_status values (MODE_*)
for unsupported modes. This operation is optional.
</para>
<para>
As the mode rejection reason is currently not used beside for
immediately removing the unsupported mode, an implementation can
return MODE_BAD regardless of the exact reason why the mode is not
valid.
</para>
<note><para>
Note that the <methodname>mode_valid</methodname> helper operation is
only called for modes detected by the device, and
<emphasis>not</emphasis> for modes set by the user through the CRTC
<methodname>set_config</methodname> operation.
</para></note>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Atomic Modeset Helper Functions Reference</title>
<sect3>
@ -3625,41 +3437,63 @@ int num_ioctls;</synopsis>
<chapter id="modes_of_use">
<title>Modes of Use</title>
<sect1>
<title>Manual switching and manual power control</title>
<sect1>
<title>Manual switching and manual power control</title>
!Pdrivers/gpu/vga/vga_switcheroo.c Manual switching and manual power control
</sect1>
<sect1>
<title>Driver power control</title>
</sect1>
<sect1>
<title>Driver power control</title>
!Pdrivers/gpu/vga/vga_switcheroo.c Driver power control
</sect1>
</sect1>
</chapter>
<chapter id="pubfunctions">
<title>Public functions</title>
<chapter id="api">
<title>API</title>
<sect1>
<title>Public functions</title>
!Edrivers/gpu/vga/vga_switcheroo.c
</chapter>
<chapter id="pubstructures">
<title>Public structures</title>
</sect1>
<sect1>
<title>Public structures</title>
!Finclude/linux/vga_switcheroo.h vga_switcheroo_handler
!Finclude/linux/vga_switcheroo.h vga_switcheroo_client_ops
</chapter>
<chapter id="pubconstants">
<title>Public constants</title>
</sect1>
<sect1>
<title>Public constants</title>
!Finclude/linux/vga_switcheroo.h vga_switcheroo_client_id
!Finclude/linux/vga_switcheroo.h vga_switcheroo_state
</chapter>
<chapter id="privstructures">
<title>Private structures</title>
</sect1>
<sect1>
<title>Private structures</title>
!Fdrivers/gpu/vga/vga_switcheroo.c vgasr_priv
!Fdrivers/gpu/vga/vga_switcheroo.c vga_switcheroo_client
</sect1>
</chapter>
<chapter id="handlers">
<title>Handlers</title>
<sect1>
<title>apple-gmux Handler</title>
!Pdrivers/platform/x86/apple-gmux.c Overview
!Pdrivers/platform/x86/apple-gmux.c Interrupt
<sect2>
<title>Graphics mux</title>
!Pdrivers/platform/x86/apple-gmux.c Graphics mux
</sect2>
<sect2>
<title>Power control</title>
!Pdrivers/platform/x86/apple-gmux.c Power control
</sect2>
<sect2>
<title>Backlight control</title>
!Pdrivers/platform/x86/apple-gmux.c Backlight control
</sect2>
</sect1>
</chapter>
!Cdrivers/gpu/vga/vga_switcheroo.c
!Cinclude/linux/vga_switcheroo.h
!Cdrivers/platform/x86/apple-gmux.c
</part>
</book>

View File

@ -1667,8 +1667,6 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
&dw_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
hdmi->connector.encoder = encoder;
drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
return 0;

View File

@ -508,6 +508,22 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
return -EINVAL;
}
/*
* Reject event generation for when a CRTC is off and stays off.
* It wouldn't be hard to implement this, but userspace has a track
* record of happily burning through 100% cpu (or worse, crash) when the
* display pipe is suspended. To avoid all that fun just reject updates
* that ask for events since likely that indicates a bug in the
* compositor's drawing loop. This is consistent with the vblank IOCTL
* and legacy page_flip IOCTL which also reject service on a disabled
* pipe.
*/
if (state->event && !state->active && !crtc->state->active) {
DRM_DEBUG_ATOMIC("[CRTC:%d] requesting event but off\n",
crtc->base.id);
return -EINVAL;
}
return 0;
}
@ -1063,10 +1079,21 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
{
struct drm_crtc_state *crtc_state;
if (conn_state->crtc && conn_state->crtc != crtc) {
crtc_state = drm_atomic_get_existing_crtc_state(conn_state->state,
conn_state->crtc);
crtc_state->connector_mask &=
~(1 << drm_connector_index(conn_state->connector));
}
if (crtc) {
crtc_state = drm_atomic_get_crtc_state(conn_state->state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
crtc_state->connector_mask |=
1 << drm_connector_index(conn_state->connector);
}
conn_state->crtc = crtc;
@ -1171,36 +1198,6 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state,
}
EXPORT_SYMBOL(drm_atomic_add_affected_planes);
/**
* drm_atomic_connectors_for_crtc - count number of connected outputs
* @state: atomic state
* @crtc: DRM crtc
*
* This function counts all connectors which will be connected to @crtc
* according to @state. Useful to recompute the enable state for @crtc.
*/
int
drm_atomic_connectors_for_crtc(struct drm_atomic_state *state,
struct drm_crtc *crtc)
{
struct drm_connector *connector;
struct drm_connector_state *conn_state;
int i, num_connected_connectors = 0;
for_each_connector_in_state(state, connector, conn_state, i) {
if (conn_state->crtc == crtc)
num_connected_connectors++;
}
DRM_DEBUG_ATOMIC("State %p has %i connectors for [CRTC:%d:%s]\n",
state, num_connected_connectors,
crtc->base.id, crtc->name);
return num_connected_connectors;
}
EXPORT_SYMBOL(drm_atomic_connectors_for_crtc);
/**
* drm_atomic_legacy_backoff - locking backoff for legacy ioctls
* @state: atomic state

View File

@ -463,7 +463,8 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
* crtc only changed its mode but has the same set of connectors.
*/
for_each_crtc_in_state(state, crtc, crtc_state, i) {
int num_connectors;
bool has_connectors =
!!crtc_state->connector_mask;
/*
* We must set ->active_changed after walking connectors for
@ -492,10 +493,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
if (ret != 0)
return ret;
num_connectors = drm_atomic_connectors_for_crtc(state,
crtc);
if (crtc_state->enable != !!num_connectors) {
if (crtc_state->enable != has_connectors) {
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n",
crtc->base.id, crtc->name);
@ -1754,7 +1752,7 @@ static int update_output_state(struct drm_atomic_state *state,
if (crtc == set->crtc)
continue;
if (!drm_atomic_connectors_for_crtc(state, crtc)) {
if (!crtc_state->connector_mask) {
ret = drm_atomic_set_mode_prop_for_crtc(crtc_state,
NULL);
if (ret < 0)
@ -2284,6 +2282,15 @@ retry:
goto fail;
drm_atomic_set_fb_for_plane(plane_state, fb);
/* Make sure we don't accidentally do a full modeset. */
state->allow_modeset = false;
if (!crtc_state->active) {
DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n",
crtc->base.id);
ret = -EINVAL;
goto fail;
}
ret = drm_atomic_async_commit(state);
if (ret != 0)
goto fail;
@ -2605,6 +2612,28 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
}
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
/**
* __drm_atomic_helper_connector_reset - reset state on connector
* @connector: drm connector
* @conn_state: connector state to assign
*
* Initializes the newly allocated @conn_state and assigns it to
* #connector ->state, usually required when initializing the drivers
* or when called from the ->reset hook.
*
* This is useful for drivers that subclass the connector state.
*/
void
__drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state)
{
if (conn_state)
conn_state->connector = connector;
connector->state = conn_state;
}
EXPORT_SYMBOL(__drm_atomic_helper_connector_reset);
/**
* drm_atomic_helper_connector_reset - default ->reset hook for connectors
* @connector: drm connector
@ -2615,11 +2644,11 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
*/
void drm_atomic_helper_connector_reset(struct drm_connector *connector)
{
kfree(connector->state);
connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL);
struct drm_connector_state *conn_state =
kzalloc(sizeof(*conn_state), GFP_KERNEL);
if (connector->state)
connector->state->connector = connector;
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, conn_state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);

View File

@ -5054,6 +5054,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
{
int i;
/*
* In the past, drivers have attempted to model the static association
* of connector to encoder in simple connector/encoder devices using a
* direct assignment of connector->encoder = encoder. This connection
* is a logical one and the responsibility of the core, so drivers are
* expected not to mess with this.
*
* Note that the error return should've been enough here, but a large
* majority of drivers ignores the return value, so add in a big WARN
* to get people's attention.
*/
if (WARN_ON(connector->encoder))
return -EINVAL;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] == 0) {
connector->encoder_ids[i] = encoder->base.id;

View File

@ -44,10 +44,6 @@ MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC);
MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output");
MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
module_param_named(debug, drm_debug, int, 0600);
static DEFINE_SPINLOCK(drm_minor_lock);

View File

@ -637,8 +637,12 @@ static const struct minimode extra_modes[] = {
/*
* Probably taken from CEA-861 spec.
* This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c.
*
* Index using the VIC.
*/
static const struct drm_display_mode edid_cea_modes[] = {
/* 0 - dummy, VICs start at 1 */
{ },
/* 1 - 640x480@60Hz */
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
752, 800, 0, 480, 490, 492, 525, 0,
@ -987,9 +991,11 @@ static const struct drm_display_mode edid_cea_modes[] = {
};
/*
* HDMI 1.4 4k modes.
* HDMI 1.4 4k modes. Index using the VIC.
*/
static const struct drm_display_mode edid_4k_modes[] = {
/* 0 - dummy, VICs start at 1 */
{ },
/* 1 - 3840x2160@30Hz */
{ DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000,
3840, 4016, 4104, 4400, 0,
@ -2548,13 +2554,13 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_match,
unsigned int clock_tolerance)
{
u8 mode;
u8 vic;
if (!to_match->clock)
return 0;
for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) {
const struct drm_display_mode *cea_mode = &edid_cea_modes[mode];
for (vic = 1; vic < ARRAY_SIZE(edid_cea_modes); vic++) {
const struct drm_display_mode *cea_mode = &edid_cea_modes[vic];
unsigned int clock1, clock2;
/* Check both 60Hz and 59.94Hz */
@ -2566,7 +2572,7 @@ static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_m
continue;
if (drm_mode_equal_no_clocks(to_match, cea_mode))
return mode + 1;
return vic;
}
return 0;
@ -2581,13 +2587,13 @@ static u8 drm_match_cea_mode_clock_tolerance(const struct drm_display_mode *to_m
*/
u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
{
u8 mode;
u8 vic;
if (!to_match->clock)
return 0;
for (mode = 0; mode < ARRAY_SIZE(edid_cea_modes); mode++) {
const struct drm_display_mode *cea_mode = &edid_cea_modes[mode];
for (vic = 1; vic < ARRAY_SIZE(edid_cea_modes); vic++) {
const struct drm_display_mode *cea_mode = &edid_cea_modes[vic];
unsigned int clock1, clock2;
/* Check both 60Hz and 59.94Hz */
@ -2597,12 +2603,17 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode))
return mode + 1;
return vic;
}
return 0;
}
EXPORT_SYMBOL(drm_match_cea_mode);
static bool drm_valid_cea_vic(u8 vic)
{
return vic > 0 && vic < ARRAY_SIZE(edid_cea_modes);
}
/**
* drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to
* the input VIC from the CEA mode list
@ -2612,10 +2623,7 @@ EXPORT_SYMBOL(drm_match_cea_mode);
*/
enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
{
/* return picture aspect ratio for video_code - 1 to access the
* right array element
*/
return edid_cea_modes[video_code-1].picture_aspect_ratio;
return edid_cea_modes[video_code].picture_aspect_ratio;
}
EXPORT_SYMBOL(drm_get_cea_aspect_ratio);
@ -2639,13 +2647,13 @@ hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode)
static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_match,
unsigned int clock_tolerance)
{
u8 mode;
u8 vic;
if (!to_match->clock)
return 0;
for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) {
const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode];
for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
unsigned int clock1, clock2;
/* Make sure to also match alternate clocks */
@ -2657,7 +2665,7 @@ static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_
continue;
if (drm_mode_equal_no_clocks(to_match, hdmi_mode))
return mode + 1;
return vic;
}
return 0;
@ -2673,13 +2681,13 @@ static u8 drm_match_hdmi_mode_clock_tolerance(const struct drm_display_mode *to_
*/
static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
{
u8 mode;
u8 vic;
if (!to_match->clock)
return 0;
for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) {
const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode];
for (vic = 1; vic < ARRAY_SIZE(edid_4k_modes); vic++) {
const struct drm_display_mode *hdmi_mode = &edid_4k_modes[vic];
unsigned int clock1, clock2;
/* Make sure to also match alternate clocks */
@ -2689,11 +2697,16 @@ static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)
if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||
KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) &&
drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode))
return mode + 1;
return vic;
}
return 0;
}
static bool drm_valid_hdmi_vic(u8 vic)
{
return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes);
}
static int
add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
{
@ -2713,16 +2726,16 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)
list_for_each_entry(mode, &connector->probed_modes, head) {
const struct drm_display_mode *cea_mode = NULL;
struct drm_display_mode *newmode;
u8 mode_idx = drm_match_cea_mode(mode) - 1;
u8 vic = drm_match_cea_mode(mode);
unsigned int clock1, clock2;
if (mode_idx < ARRAY_SIZE(edid_cea_modes)) {
cea_mode = &edid_cea_modes[mode_idx];
if (drm_valid_cea_vic(vic)) {
cea_mode = &edid_cea_modes[vic];
clock2 = cea_mode_alternate_clock(cea_mode);
} else {
mode_idx = drm_match_hdmi_mode(mode) - 1;
if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
cea_mode = &edid_4k_modes[mode_idx];
vic = drm_match_hdmi_mode(mode);
if (drm_valid_hdmi_vic(vic)) {
cea_mode = &edid_4k_modes[vic];
clock2 = hdmi_mode_alternate_clock(cea_mode);
}
}
@ -2773,17 +2786,17 @@ drm_display_mode_from_vic_index(struct drm_connector *connector,
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *newmode;
u8 cea_mode;
u8 vic;
if (video_db == NULL || video_index >= video_len)
return NULL;
/* CEA modes are numbered 1..127 */
cea_mode = (video_db[video_index] & 127) - 1;
if (cea_mode >= ARRAY_SIZE(edid_cea_modes))
vic = (video_db[video_index] & 127);
if (!drm_valid_cea_vic(vic))
return NULL;
newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]);
if (!newmode)
return NULL;
@ -2878,8 +2891,7 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
struct drm_device *dev = connector->dev;
struct drm_display_mode *newmode;
vic--; /* VICs start at 1 */
if (vic >= ARRAY_SIZE(edid_4k_modes)) {
if (!drm_valid_hdmi_vic(vic)) {
DRM_ERROR("Unknown HDMI VIC: %d\n", vic);
return 0;
}
@ -3170,24 +3182,24 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
{
const struct drm_display_mode *cea_mode;
int clock1, clock2, clock;
u8 mode_idx;
u8 vic;
const char *type;
/*
* allow 5kHz clock difference either way to account for
* the 10kHz clock resolution limit of detailed timings.
*/
mode_idx = drm_match_cea_mode_clock_tolerance(mode, 5) - 1;
if (mode_idx < ARRAY_SIZE(edid_cea_modes)) {
vic = drm_match_cea_mode_clock_tolerance(mode, 5);
if (drm_valid_cea_vic(vic)) {
type = "CEA";
cea_mode = &edid_cea_modes[mode_idx];
cea_mode = &edid_cea_modes[vic];
clock1 = cea_mode->clock;
clock2 = cea_mode_alternate_clock(cea_mode);
} else {
mode_idx = drm_match_hdmi_mode_clock_tolerance(mode, 5) - 1;
if (mode_idx < ARRAY_SIZE(edid_4k_modes)) {
vic = drm_match_hdmi_mode_clock_tolerance(mode, 5);
if (drm_valid_hdmi_vic(vic)) {
type = "HDMI";
cea_mode = &edid_4k_modes[mode_idx];
cea_mode = &edid_4k_modes[vic];
clock1 = cea_mode->clock;
clock2 = hdmi_mode_alternate_clock(cea_mode);
} else {
@ -3205,7 +3217,7 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
return;
DRM_DEBUG("detailed mode matches %s VIC %d, adjusting clock %d -> %d\n",
type, mode_idx + 1, mode->clock, clock);
type, vic, mode->clock, clock);
mode->clock = clock;
}

View File

@ -348,9 +348,6 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
}
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
ret = drm_fb_helper_initial_config(helper, preferred_bpp);
if (ret < 0) {
dev_err(dev->dev, "Failed to set initial hw configuration.\n");

View File

@ -1251,7 +1251,7 @@ retry:
goto fail;
plane = mode_set->crtc->primary;
plane_mask |= drm_plane_index(plane);
plane_mask |= (1 << drm_plane_index(plane));
plane->old_fb = plane->fb;
}

View File

@ -220,6 +220,9 @@ static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj)
static void
drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
bool final = false;
if (WARN_ON(obj->handle_count == 0))
return;
@ -229,14 +232,39 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
* checked for a name
*/
mutex_lock(&obj->dev->object_name_lock);
mutex_lock(&dev->object_name_lock);
if (--obj->handle_count == 0) {
drm_gem_object_handle_free(obj);
drm_gem_object_exported_dma_buf_free(obj);
final = true;
}
mutex_unlock(&obj->dev->object_name_lock);
mutex_unlock(&dev->object_name_lock);
drm_gem_object_unreference_unlocked(obj);
if (final)
drm_gem_object_unreference_unlocked(obj);
}
/*
* Called at device or object close to release the file's
* handle references on objects.
*/
static int
drm_gem_object_release_handle(int id, void *ptr, void *data)
{
struct drm_file *file_priv = data;
struct drm_gem_object *obj = ptr;
struct drm_device *dev = obj->dev;
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_gem_remove_prime_handles(obj, file_priv);
drm_vma_node_revoke(&obj->vma_node, file_priv->filp);
if (dev->driver->gem_close_object)
dev->driver->gem_close_object(obj, file_priv);
drm_gem_object_handle_unreference_unlocked(obj);
return 0;
}
/**
@ -277,14 +305,7 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle)
idr_remove(&filp->object_idr, handle);
spin_unlock(&filp->table_lock);
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_gem_remove_prime_handles(obj, filp);
drm_vma_node_revoke(&obj->vma_node, filp->filp);
if (dev->driver->gem_close_object)
dev->driver->gem_close_object(obj, filp);
drm_gem_object_handle_unreference_unlocked(obj);
drm_gem_object_release_handle(handle, obj, filp);
return 0;
}
EXPORT_SYMBOL(drm_gem_handle_delete);
@ -326,9 +347,12 @@ drm_gem_handle_create_tail(struct drm_file *file_priv,
u32 *handlep)
{
struct drm_device *dev = obj->dev;
u32 handle;
int ret;
WARN_ON(!mutex_is_locked(&dev->object_name_lock));
if (obj->handle_count++ == 0)
drm_gem_object_reference(obj);
/*
* Get the user-visible handle using idr. Preload and perform
@ -338,32 +362,38 @@ drm_gem_handle_create_tail(struct drm_file *file_priv,
spin_lock(&file_priv->table_lock);
ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT);
drm_gem_object_reference(obj);
obj->handle_count++;
spin_unlock(&file_priv->table_lock);
idr_preload_end();
mutex_unlock(&dev->object_name_lock);
if (ret < 0) {
drm_gem_object_handle_unreference_unlocked(obj);
return ret;
}
*handlep = ret;
if (ret < 0)
goto err_unref;
handle = ret;
ret = drm_vma_node_allow(&obj->vma_node, file_priv->filp);
if (ret) {
drm_gem_handle_delete(file_priv, *handlep);
return ret;
}
if (ret)
goto err_remove;
if (dev->driver->gem_open_object) {
ret = dev->driver->gem_open_object(obj, file_priv);
if (ret) {
drm_gem_handle_delete(file_priv, *handlep);
return ret;
}
if (ret)
goto err_revoke;
}
*handlep = handle;
return 0;
err_revoke:
drm_vma_node_revoke(&obj->vma_node, file_priv->filp);
err_remove:
spin_lock(&file_priv->table_lock);
idr_remove(&file_priv->object_idr, handle);
spin_unlock(&file_priv->table_lock);
err_unref:
drm_gem_object_handle_unreference_unlocked(obj);
return ret;
}
/**
@ -630,7 +660,6 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
mutex_lock(&dev->object_name_lock);
idr_preload(GFP_KERNEL);
/* prevent races with concurrent gem_close. */
if (obj->handle_count == 0) {
ret = -ENOENT;
@ -638,7 +667,7 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
}
if (!obj->name) {
ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT);
ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_KERNEL);
if (ret < 0)
goto err;
@ -649,7 +678,6 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
ret = 0;
err:
idr_preload_end();
mutex_unlock(&dev->object_name_lock);
drm_gem_object_unreference_unlocked(obj);
return ret;
@ -714,29 +742,6 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private)
spin_lock_init(&file_private->table_lock);
}
/*
* Called at device close to release the file's
* handle references on objects.
*/
static int
drm_gem_object_release_handle(int id, void *ptr, void *data)
{
struct drm_file *file_priv = data;
struct drm_gem_object *obj = ptr;
struct drm_device *dev = obj->dev;
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_gem_remove_prime_handles(obj, file_priv);
drm_vma_node_revoke(&obj->vma_node, file_priv->filp);
if (dev->driver->gem_close_object)
dev->driver->gem_close_object(obj, file_priv);
drm_gem_object_handle_unreference_unlocked(obj);
return 0;
}
/**
* drm_gem_release - release file-private GEM resources
* @dev: drm_device which is being closed by userspace

View File

@ -73,6 +73,9 @@ static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
static void store_vblank(struct drm_device *dev, unsigned int pipe,
u32 vblank_count_inc,

View File

@ -240,7 +240,7 @@ static ssize_t edid_show(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off,
size_t count)
{
struct device *connector_dev = container_of(kobj, struct device, kobj);
struct device *connector_dev = kobj_to_dev(kobj);
struct drm_connector *connector = to_drm_connector(connector_dev);
unsigned char *edid;
size_t size;

View File

@ -187,7 +187,7 @@ static bool gma_resume_pci(struct pci_dev *pdev)
*/
int gma_power_suspend(struct device *_dev)
{
struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev);
struct pci_dev *pdev = to_pci_dev(_dev);
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_psb_private *dev_priv = dev->dev_private;
@ -214,7 +214,7 @@ int gma_power_suspend(struct device *_dev)
*/
int gma_power_resume(struct device *_dev)
{
struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev);
struct pci_dev *pdev = to_pci_dev(_dev);
struct drm_device *dev = pci_get_drvdata(pdev);
mutex_lock(&power_mutex);

View File

@ -1446,7 +1446,6 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data)
if (ret)
goto err_sysfs;
priv->connector.encoder = &priv->encoder;
drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
return 0;

View File

@ -406,6 +406,8 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto cleanup_gem_stolen;
intel_setup_gmbus(dev);
/* Important: The output setup functions called by modeset_init need
* working irqs for e.g. gmbus and dp aux transfers. */
intel_modeset_init(dev);
@ -455,6 +457,7 @@ cleanup_gem:
cleanup_irq:
intel_guc_ucode_fini(dev);
drm_irq_uninstall(dev);
intel_teardown_gmbus(dev);
cleanup_gem_stolen:
i915_gem_cleanup_stolen(dev);
cleanup_vga_switcheroo:
@ -1028,7 +1031,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
/* Try to make sure MCHBAR is enabled before poking at it */
intel_setup_mchbar(dev);
intel_setup_gmbus(dev);
intel_opregion_setup(dev);
i915_gem_load(dev);
@ -1101,7 +1103,6 @@ out_gem_unload:
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
pm_qos_remove_request(&dev_priv->pm_qos);
destroy_workqueue(dev_priv->gpu_error.hangcheck_wq);
@ -1203,7 +1204,6 @@ int i915_driver_unload(struct drm_device *dev)
intel_csr_ucode_fini(dev_priv);
intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
destroy_workqueue(dev_priv->hotplug.dp_wq);

View File

@ -6492,13 +6492,11 @@ static void intel_connector_check_state(struct intel_connector *connector)
int intel_connector_init(struct intel_connector *connector)
{
struct drm_connector_state *connector_state;
drm_atomic_helper_connector_reset(&connector->base);
connector_state = kzalloc(sizeof *connector_state, GFP_KERNEL);
if (!connector_state)
if (!connector->base.state)
return -ENOMEM;
connector->base.state = connector_state;
return 0;
}
@ -15446,6 +15444,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
WARN_ON(drm_atomic_set_mode_for_crtc(crtc->base.state, NULL) < 0);
crtc->base.state->active = crtc->active;
crtc->base.enabled = crtc->active;
crtc->base.state->connector_mask = 0;
/* Because we only establish the connector -> encoder ->
* crtc links if something is active, this means the
@ -15648,7 +15647,21 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
for_each_intel_connector(dev, connector) {
if (connector->get_hw_state(connector)) {
connector->base.dpms = DRM_MODE_DPMS_ON;
connector->base.encoder = &connector->encoder->base;
encoder = connector->encoder;
connector->base.encoder = &encoder->base;
if (encoder->base.crtc &&
encoder->base.crtc->state->active) {
/*
* This has to be done during hardware readout
* because anything calling .crtc_disable may
* rely on the connector_mask being accurate.
*/
encoder->base.crtc->state->connector_mask |=
1 << drm_connector_index(&connector->base);
}
} else {
connector->base.dpms = DRM_MODE_DPMS_OFF;
connector->base.encoder = NULL;
@ -15893,6 +15906,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
mutex_lock(&dev->struct_mutex);
intel_cleanup_gt_powersave(dev);
mutex_unlock(&dev->struct_mutex);
intel_teardown_gmbus(dev);
}
/*

View File

@ -534,7 +534,7 @@ static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
drm_kms_helper_hotplug_event(dev);
}
static struct drm_dp_mst_topology_cbs mst_cbs = {
static const struct drm_dp_mst_topology_cbs mst_cbs = {
.add_connector = intel_dp_add_mst_connector,
.register_connector = intel_dp_register_mst_connector,
.destroy_connector = intel_dp_destroy_mst_connector,

View File

@ -305,6 +305,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n");
legacyfb_depth = 16;
}
drm_helper_disable_unused_functions(drm);
imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
drm->mode_config.num_crtc, MAX_CRTC);
if (IS_ERR(imxdrm->fbhelper)) {

View File

@ -204,8 +204,6 @@ static int imx_pd_register(struct drm_device *drm,
drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder);
imxpd->connector.encoder = &imxpd->encoder;
return 0;
}

View File

@ -329,7 +329,7 @@ static void radeon_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
drm_kms_helper_hotplug_event(dev);
}
struct drm_dp_mst_topology_cbs mst_cbs = {
const struct drm_dp_mst_topology_cbs mst_cbs = {
.add_connector = radeon_dp_add_mst_connector,
.register_connector = radeon_dp_register_mst_connector,
.destroy_connector = radeon_dp_destroy_mst_connector,

View File

@ -739,8 +739,6 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev,
if (ret < 0)
goto err_backlight;
connector->encoder = encoder;
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
drm_object_property_set_value(&connector->base,
sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);

View File

@ -160,6 +160,7 @@ static int sti_load(struct drm_device *dev, unsigned long flags)
drm_mode_config_reset(dev);
drm_helper_disable_unused_functions(dev);
drm_fbdev_cma_init(dev, 32,
dev->mode_config.num_crtc,
dev->mode_config.num_connector);

View File

@ -745,14 +745,13 @@ static void tegra_dsi_soft_reset(struct tegra_dsi *dsi)
static void tegra_dsi_connector_reset(struct drm_connector *connector)
{
struct tegra_dsi_state *state;
struct tegra_dsi_state *state =
kzalloc(sizeof(*state), GFP_KERNEL);
kfree(connector->state);
connector->state = NULL;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (state)
connector->state = &state->base;
if (state) {
kfree(connector->state);
__drm_atomic_helper_connector_reset(connector, &state->base);
}
}
static struct drm_connector_state *

View File

@ -294,6 +294,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
break;
}
drm_helper_disable_unused_functions(dev);
priv->fbdev = drm_fbdev_cma_init(dev, bpp,
dev->mode_config.num_crtc,
dev->mode_config.num_connector);

View File

@ -328,7 +328,7 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
/* The pixelvalve can only feed one encoder (and encoders are
* 1:1 with connectors.)
*/
if (drm_atomic_connectors_for_crtc(state->state, crtc) > 1)
if (hweight32(state->connector_mask) > 1)
return -EINVAL;
drm_atomic_crtc_state_for_each_plane(plane, state) {

View File

@ -63,7 +63,7 @@
* for the inactive GPU.) Also, muxes are often used to cut power to the
* discrete GPU while it is not used.
*
* DRM drivers register GPUs with vga_switcheroo, these are heretoforth called
* DRM drivers register GPUs with vga_switcheroo, these are henceforth called
* clients. The mux is called the handler. Muxless machines also register a
* handler to control the power state of the discrete GPU, its ->switchto
* callback is a no-op for obvious reasons. The discrete GPU is often equipped

View File

@ -3,6 +3,7 @@
*
* Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
* Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
* Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -26,6 +27,24 @@
#include <acpi/video.h>
#include <asm/io.h>
/**
* DOC: Overview
*
* :1: http://www.latticesemi.com/en/Products/FPGAandCPLD/LatticeXP2.aspx
* :2: http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp
*
* gmux is a microcontroller built into the MacBook Pro to support dual GPUs:
* A {1}[Lattice XP2] on pre-retinas, a {2}[Renesas R4F2113] on retinas.
*
* (The MacPro6,1 2013 also has a gmux, however it is unclear why since it has
* dual GPUs but no built-in display.)
*
* gmux is connected to the LPC bus of the southbridge. Its I/O ports are
* accessed differently depending on the microcontroller: Driver functions
* to access a pre-retina gmux are infixed `_pio_`, those for a retina gmux
* are infixed `_index_`.
*/
struct apple_gmux_data {
unsigned long iostart;
unsigned long iolen;
@ -247,6 +266,20 @@ static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
return false;
}
/**
* DOC: Backlight control
*
* :3: http://www.ti.com/lit/ds/symlink/lp8543.pdf
* :4: http://www.ti.com/lit/ds/symlink/lp8545.pdf
*
* On single GPU MacBooks, the PWM signal for the backlight is generated by
* the GPU. On dual GPU MacBook Pros by contrast, either GPU may be suspended
* to conserve energy. Hence the PWM signal needs to be generated by a separate
* backlight driver which is controlled by gmux. The earliest generation
* MBP5 2008/09 uses a {3}[TI LP8543] backlight driver. All newer models
* use a {4}[TI LP8545].
*/
static int gmux_get_brightness(struct backlight_device *bd)
{
struct apple_gmux_data *gmux_data = bl_get_data(bd);
@ -273,6 +306,68 @@ static const struct backlight_ops gmux_bl_ops = {
.update_status = gmux_update_status,
};
/**
* DOC: Graphics mux
*
* :5: http://pimg-fpiw.uspto.gov/fdd/07/870/086/0.pdf
* :6: http://www.nxp.com/documents/data_sheet/CBTL06141.pdf
* :7: http://www.ti.com/lit/ds/symlink/hd3ss212.pdf
* :8: https://www.pericom.com/assets/Datasheets/PI3VDP12412.pdf
* :9: http://www.ti.com/lit/ds/symlink/sn74lv4066a.pdf
* :10: http://pdf.datasheetarchive.com/indexerfiles/Datasheets-SW16/DSASW00308511.pdf
* :11: http://www.ti.com/lit/ds/symlink/ts3ds10224.pdf
*
* On pre-retinas, the LVDS outputs of both GPUs feed into gmux which muxes
* either of them to the panel. One of the tricks gmux has up its sleeve is
* to lengthen the blanking interval of its output during a switch to
* synchronize it with the GPU switched to. This allows for a flicker-free
* switch that is imperceptible by the user ({5}[US 8,687,007 B2]).
*
* On retinas, muxing is no longer done by gmux itself, but by a separate
* chip which is controlled by gmux. The chip is triple sourced, it is
* either an {6}[NXP CBTL06142], {7}[TI HD3SS212] or {8}[Pericom PI3VDP12412].
* The panel is driven with eDP instead of LVDS since the pixel clock
* required for retina resolution exceeds LVDS' limits.
*
* Pre-retinas are able to switch the panel's DDC pins separately.
* This is handled by a {9}[TI SN74LV4066A] which is controlled by gmux.
* The inactive GPU can thus probe the panel's EDID without switching over
* the entire panel. Retinas lack this functionality as the chips used for
* eDP muxing are incapable of switching the AUX channel separately (see
* the linked data sheets, Pericom would be capable but this is unused).
* However the retina panel has the NO_AUX_HANDSHAKE_LINK_TRAINING bit set
* in its DPCD, allowing the inactive GPU to skip the AUX handshake and
* set up the output with link parameters pre-calibrated by the active GPU.
*
* The external DP port is only fully switchable on the first two unibody
* MacBook Pro generations, MBP5 2008/09 and MBP6 2010. This is done by an
* {6}[NXP CBTL06141] which is controlled by gmux. It's the predecessor of the
* eDP mux on retinas, the difference being support for 2.7 versus 5.4 Gbit/s.
*
* The following MacBook Pro generations replaced the external DP port with a
* combined DP/Thunderbolt port and lost the ability to switch it between GPUs,
* connecting it either to the discrete GPU or the Thunderbolt controller.
* Oddly enough, while the full port is no longer switchable, AUX and HPD
* are still switchable by way of an {10}[NXP CBTL03062] (on pre-retinas
* MBP8 2011 and MBP9 2012) or two {11}[TI TS3DS10224] (on retinas) under the
* control of gmux. Since the integrated GPU is missing the main link,
* external displays appear to it as phantoms which fail to link-train.
*
* gmux receives the HPD signal of all display connectors and sends an
* interrupt on hotplug. On generations which cannot switch external ports,
* the discrete GPU can then be woken to drive the newly connected display.
* The ability to switch AUX on these generations could be used to improve
* reliability of hotplug detection by having the integrated GPU poll the
* ports while the discrete GPU is asleep, but currently we do not make use
* of this feature.
*
* gmux' initial switch state on bootup is user configurable via the EFI
* variable `gpu-power-prefs-fa4ce28d-b62f-4c99-9cc3-6815686e30f9` (5th byte,
* 1 = IGD, 0 = DIS). Based on this setting, the EFI firmware tells gmux to
* switch the panel and the external DP connector and allocates a framebuffer
* for the selected GPU.
*/
static int gmux_switchto(enum vga_switcheroo_client_id id)
{
if (id == VGA_SWITCHEROO_IGD) {
@ -288,6 +383,14 @@ static int gmux_switchto(enum vga_switcheroo_client_id id)
return 0;
}
/**
* DOC: Power control
*
* gmux is able to cut power to the discrete GPU. It automatically takes care
* of the correct sequence to tear down and bring up the power rails for
* core voltage, VRAM and PCIe.
*/
static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
enum vga_switcheroo_state state)
{
@ -352,6 +455,16 @@ static const struct vga_switcheroo_handler gmux_handler = {
.get_client_id = gmux_get_client_id,
};
/**
* DOC: Interrupt
*
* gmux is also connected to a GPIO pin of the southbridge and thereby is able
* to trigger an ACPI GPE. On the MBP5 2008/09 it's GPIO pin 22 of the Nvidia
* MCP79, on all following generations it's GPIO pin 6 of the Intel PCH.
* The GPE merely signals that an interrupt occurred, the actual type of event
* is identified by reading a gmux register.
*/
static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
{
gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,

View File

@ -130,10 +130,6 @@ int __must_check
drm_atomic_add_affected_planes(struct drm_atomic_state *state,
struct drm_crtc *crtc);
int
drm_atomic_connectors_for_crtc(struct drm_atomic_state *state,
struct drm_crtc *crtc);
void drm_atomic_legacy_backoff(struct drm_atomic_state *state);
void

View File

@ -126,6 +126,8 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state);
void __drm_atomic_helper_connector_reset(struct drm_connector *connector,
struct drm_connector_state *conn_state);
void drm_atomic_helper_connector_reset(struct drm_connector *connector);
void
__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,

View File

@ -306,6 +306,7 @@ struct drm_plane_helper_funcs;
* @active_changed: crtc_state->active has been toggled.
* @connectors_changed: connectors to this crtc have been updated
* @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes
* @connector_mask: bitmask of (1 << drm_connector_index(connector)) of attached connectors
* @last_vblank_count: for helpers and drivers to capture the vblank of the
* update to ensure framebuffer cleanup isn't done too early
* @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings
@ -339,6 +340,8 @@ struct drm_crtc_state {
*/
u32 plane_mask;
u32 connector_mask;
/* last_vblank_count: for vblank waits before cleanup */
u32 last_vblank_count;
@ -548,7 +551,8 @@ struct drm_crtc_funcs {
* ->page_flip() operation is already pending the callback should return
* -EBUSY. Pageflips on a disabled CRTC (either by setting a NULL mode
* or just runtime disabled through DPMS respectively the new atomic
* "ACTIVE" state) should result in an -EINVAL error code.
* "ACTIVE" state) should result in an -EINVAL error code. Note that
* drm_atomic_helper_page_flip() checks this already for atomic drivers.
*/
int (*page_flip)(struct drm_crtc *crtc,
struct drm_framebuffer *fb,

View File

@ -421,7 +421,7 @@ struct drm_dp_payload {
struct drm_dp_mst_topology_mgr {
struct device *dev;
struct drm_dp_mst_topology_cbs *cbs;
const struct drm_dp_mst_topology_cbs *cbs;
int max_dpcd_transaction_bytes;
struct drm_dp_aux *aux; /* auxch for this topology mgr to use */
int max_payloads;

View File

@ -131,6 +131,20 @@ struct drm_crtc_helper_funcs {
* Atomic drivers which need to inspect and adjust more state should
* instead use the @atomic_check callback.
*
* Also beware that neither core nor helpers filter modes before
* passing them to the driver: While the list of modes that is
* advertised to userspace is filtered using the connector's
* ->mode_valid() callback, neither the core nor the helpers do any
* filtering on modes passed in from userspace when setting a mode. It
* is therefore possible for userspace to pass in a mode that was
* previously filtered out using ->mode_valid() or add a custom mode
* that wasn't probed from EDID or similar to begin with. Even though
* this is an advanced feature and rarely used nowadays, some users rely
* on being able to specify modes manually so drivers must be prepared
* to deal with it. Specifically this means that all drivers need not
* only validate modes in ->mode_valid() but also in ->mode_fixup() to
* make sure invalid modes passed in from userspace are rejected.
*
* RETURNS:
*
* True if an acceptable configuration is possible, false if the modeset
@ -188,7 +202,9 @@ struct drm_crtc_helper_funcs {
* This callback is used by the legacy CRTC helpers to set a new
* framebuffer and scanout position. It is optional and used as an
* optimized fast-path instead of a full mode set operation with all the
* resulting flickering. Since it can't update other planes it's
* resulting flickering. If it is not present
* drm_crtc_helper_set_config() will fall back to a full modeset, using
* the ->mode_set() callback. Since it can't update other planes it's
* incompatible with atomic modeset support.
*
* This callback is only used by the CRTC helpers and deprecated.
@ -439,6 +455,20 @@ struct drm_encoder_helper_funcs {
* Atomic drivers which need to inspect and adjust more state should
* instead use the @atomic_check callback.
*
* Also beware that neither core nor helpers filter modes before
* passing them to the driver: While the list of modes that is
* advertised to userspace is filtered using the connector's
* ->mode_valid() callback, neither the core nor the helpers do any
* filtering on modes passed in from userspace when setting a mode. It
* is therefore possible for userspace to pass in a mode that was
* previously filtered out using ->mode_valid() or add a custom mode
* that wasn't probed from EDID or similar to begin with. Even though
* this is an advanced feature and rarely used nowadays, some users rely
* on being able to specify modes manually so drivers must be prepared
* to deal with it. Specifically this means that all drivers need not
* only validate modes in ->mode_valid() but also in ->mode_fixup() to
* make sure invalid modes passed in from userspace are rejected.
*
* RETURNS:
*
* True if an acceptable configuration is possible, false if the modeset
@ -640,8 +670,16 @@ struct drm_connector_helper_funcs {
* In this function drivers then parse the modes in the EDID and add
* them by calling drm_add_edid_modes(). But connectors that driver a
* fixed panel can also manually add specific modes using
* drm_mode_probed_add(). Finally drivers that support audio probably
* want to update the ELD data, too, using drm_edid_to_eld().
* drm_mode_probed_add(). Drivers which manually add modes should also
* make sure that the @display_info, @width_mm and @height_mm fields of the
* struct #drm_connector are filled in.
*
* Virtual drivers that just want some standard VESA mode with a given
* resolution can call drm_add_modes_noedid(), and mark the preferred
* one using drm_set_preferred_mode().
*
* Finally drivers that support audio probably want to update the ELD
* data, too, using drm_edid_to_eld().
*
* This function is only called after the ->detect() hook has indicated
* that a sink is connected and when the EDID isn't overridden through