1633 lines
48 KiB
C
1633 lines
48 KiB
C
/*
|
|
* Copyright (C) 2019 Red Hat
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/meta-output.h"
|
|
#include "backends/native/meta-kms-connector.h"
|
|
#include "backends/native/meta-kms-connector-private.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include "backends/native/meta-kms-crtc.h"
|
|
#include "backends/native/meta-kms-device-private.h"
|
|
#include "backends/native/meta-kms-impl-device.h"
|
|
#include "backends/native/meta-kms-mode-private.h"
|
|
#include "backends/native/meta-kms-update-private.h"
|
|
|
|
/* CTA-861.3 HDR Static Metadata Extension, Table 3,
|
|
* Electro-Optical Transfer Function */
|
|
typedef enum
|
|
{
|
|
HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR = 0,
|
|
HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR = 1,
|
|
HDR_METADATA_EOTF_PERCEPTUAL_QUANTIZER = 2,
|
|
HDR_METADATA_EOTF_HYBRID_LOG_GAMMA = 3,
|
|
} HdrMetadataEotf;
|
|
|
|
/* CTA-861.3 HDR Static Metadata Extension, Table 4,
|
|
* Static_Metadata_Descriptor_ID */
|
|
typedef enum
|
|
{
|
|
HDR_STATIC_METADATA_TYPE_1 = 0,
|
|
} HdrStaticMetadataType;
|
|
|
|
typedef struct _MetaKmsConnectorPropTable
|
|
{
|
|
MetaKmsProp props[META_KMS_CONNECTOR_N_PROPS];
|
|
MetaKmsEnum dpms_enum[META_KMS_CONNECTOR_DPMS_N_PROPS];
|
|
MetaKmsEnum underscan_enum[META_KMS_CONNECTOR_UNDERSCAN_N_PROPS];
|
|
MetaKmsEnum privacy_screen_sw_enum[META_KMS_CONNECTOR_PRIVACY_SCREEN_N_PROPS];
|
|
MetaKmsEnum privacy_screen_hw_enum[META_KMS_CONNECTOR_PRIVACY_SCREEN_N_PROPS];
|
|
MetaKmsEnum scaling_mode_enum[META_KMS_CONNECTOR_SCALING_MODE_N_PROPS];
|
|
MetaKmsEnum panel_orientation_enum[META_KMS_CONNECTOR_PANEL_ORIENTATION_N_PROPS];
|
|
MetaKmsEnum colorspace_enum[META_KMS_CONNECTOR_COLORSPACE_N_PROPS];
|
|
} MetaKmsConnectorPropTable;
|
|
|
|
struct _MetaKmsConnector
|
|
{
|
|
GObject parent;
|
|
|
|
MetaKmsDevice *device;
|
|
|
|
uint32_t id;
|
|
uint32_t type;
|
|
uint32_t type_id;
|
|
char *name;
|
|
|
|
drmModeConnection connection;
|
|
MetaKmsConnectorState *current_state;
|
|
|
|
MetaKmsConnectorPropTable prop_table;
|
|
|
|
uint32_t edid_blob_id;
|
|
uint32_t tile_blob_id;
|
|
|
|
gboolean fd_held;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT)
|
|
|
|
typedef enum _MetaKmsPrivacyScreenHwState
|
|
{
|
|
META_KMS_PRIVACY_SCREEN_HW_STATE_DISABLED,
|
|
META_KMS_PRIVACY_SCREEN_HW_STATE_ENABLED,
|
|
META_KMS_PRIVACY_SCREEN_HW_STATE_DISABLED_LOCKED,
|
|
META_KMS_PRIVACY_SCREEN_HW_STATE_ENABLED_LOCKED,
|
|
} MetaKmsPrivacyScreenHwState;
|
|
|
|
MetaKmsDevice *
|
|
meta_kms_connector_get_device (MetaKmsConnector *connector)
|
|
{
|
|
return connector->device;
|
|
}
|
|
|
|
uint32_t
|
|
meta_kms_connector_get_prop_id (MetaKmsConnector *connector,
|
|
MetaKmsConnectorProp prop)
|
|
{
|
|
return connector->prop_table.props[prop].prop_id;
|
|
}
|
|
|
|
const char *
|
|
meta_kms_connector_get_prop_name (MetaKmsConnector *connector,
|
|
MetaKmsConnectorProp prop)
|
|
{
|
|
return connector->prop_table.props[prop].name;
|
|
}
|
|
|
|
uint64_t
|
|
meta_kms_connector_get_prop_drm_value (MetaKmsConnector *connector,
|
|
MetaKmsConnectorProp property,
|
|
uint64_t value)
|
|
{
|
|
MetaKmsProp *prop = &connector->prop_table.props[property];
|
|
return meta_kms_prop_convert_value (prop, value);
|
|
}
|
|
|
|
uint32_t
|
|
meta_kms_connector_get_connector_type (MetaKmsConnector *connector)
|
|
{
|
|
return connector->type;
|
|
}
|
|
|
|
uint32_t
|
|
meta_kms_connector_get_id (MetaKmsConnector *connector)
|
|
{
|
|
return connector->id;
|
|
}
|
|
|
|
const char *
|
|
meta_kms_connector_get_name (MetaKmsConnector *connector)
|
|
{
|
|
return connector->name;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_connector_can_clone (MetaKmsConnector *connector,
|
|
MetaKmsConnector *other_connector)
|
|
{
|
|
MetaKmsConnectorState *state = connector->current_state;
|
|
MetaKmsConnectorState *other_state = other_connector->current_state;
|
|
|
|
if (state->common_possible_clones == 0 ||
|
|
other_state->common_possible_clones == 0)
|
|
return FALSE;
|
|
|
|
if (state->encoder_device_idxs != other_state->encoder_device_idxs)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
MetaKmsMode *
|
|
meta_kms_connector_get_preferred_mode (MetaKmsConnector *connector)
|
|
{
|
|
const MetaKmsConnectorState *state;
|
|
GList *l;
|
|
|
|
state = meta_kms_connector_get_current_state (connector);
|
|
for (l = state->modes; l; l = l->next)
|
|
{
|
|
MetaKmsMode *mode = l->data;
|
|
const drmModeModeInfo *drm_mode;
|
|
|
|
drm_mode = meta_kms_mode_get_drm_mode (mode);
|
|
if (drm_mode->type & DRM_MODE_TYPE_PREFERRED)
|
|
return mode;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const MetaKmsConnectorState *
|
|
meta_kms_connector_get_current_state (MetaKmsConnector *connector)
|
|
{
|
|
return connector->current_state;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector)
|
|
{
|
|
uint32_t underscan_prop_id;
|
|
|
|
underscan_prop_id =
|
|
meta_kms_connector_get_prop_id (connector,
|
|
META_KMS_CONNECTOR_PROP_UNDERSCAN);
|
|
|
|
return underscan_prop_id != 0;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_connector_is_privacy_screen_supported (MetaKmsConnector *connector)
|
|
{
|
|
return meta_kms_connector_get_prop_id (connector,
|
|
META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_HW_STATE) != 0;
|
|
}
|
|
|
|
static gboolean
|
|
has_privacy_screen_software_toggle (MetaKmsConnector *connector)
|
|
{
|
|
return meta_kms_connector_get_prop_id (connector,
|
|
META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_SW_STATE) != 0;
|
|
}
|
|
|
|
const MetaKmsRange *
|
|
meta_kms_connector_get_max_bpc (MetaKmsConnector *connector)
|
|
{
|
|
const MetaKmsRange *range = NULL;
|
|
|
|
if (connector->current_state &&
|
|
meta_kms_connector_get_prop_id (connector,
|
|
META_KMS_CONNECTOR_PROP_MAX_BPC))
|
|
range = &connector->current_state->max_bpc;
|
|
|
|
return range;
|
|
}
|
|
|
|
static void
|
|
sync_fd_held (MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device)
|
|
{
|
|
gboolean should_hold_fd;
|
|
|
|
should_hold_fd =
|
|
connector->current_state &&
|
|
connector->current_state->current_crtc_id != 0;
|
|
|
|
if (connector->fd_held == should_hold_fd)
|
|
return;
|
|
|
|
if (should_hold_fd)
|
|
meta_kms_impl_device_hold_fd (impl_device);
|
|
else
|
|
meta_kms_impl_device_unhold_fd (impl_device);
|
|
|
|
connector->fd_held = should_hold_fd;
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_connector_is_color_space_supported (MetaKmsConnector *connector,
|
|
MetaOutputColorspace color_space)
|
|
{
|
|
return !!(connector->current_state->colorspace.supported & (1 << color_space));
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_connector_is_hdr_metadata_supported (MetaKmsConnector *connector)
|
|
{
|
|
return connector->current_state->hdr.supported;
|
|
}
|
|
|
|
static void
|
|
set_panel_orientation (MetaKmsConnectorState *state,
|
|
MetaKmsProp *panel_orientation)
|
|
{
|
|
MetaMonitorTransform transform;
|
|
MetaKmsConnectorPanelOrientation orientation = panel_orientation->value;
|
|
|
|
switch (orientation)
|
|
{
|
|
case META_KMS_CONNECTOR_PANEL_ORIENTATION_UPSIDE_DOWN:
|
|
transform = META_MONITOR_TRANSFORM_180;
|
|
break;
|
|
case META_KMS_CONNECTOR_PANEL_ORIENTATION_LEFT_SIDE_UP:
|
|
transform = META_MONITOR_TRANSFORM_90;
|
|
break;
|
|
case META_KMS_CONNECTOR_PANEL_ORIENTATION_RIGHT_SIDE_UP:
|
|
transform = META_MONITOR_TRANSFORM_270;
|
|
break;
|
|
default:
|
|
transform = META_MONITOR_TRANSFORM_NORMAL;
|
|
break;
|
|
}
|
|
|
|
state->panel_orientation_transform = transform;
|
|
}
|
|
|
|
static void
|
|
set_privacy_screen (MetaKmsConnectorState *state,
|
|
MetaKmsConnector *connector,
|
|
MetaKmsProp *hw_state)
|
|
{
|
|
MetaKmsConnectorPrivacyScreen privacy_screen = hw_state->value;
|
|
|
|
if (!meta_kms_connector_is_privacy_screen_supported (connector))
|
|
return;
|
|
|
|
switch (privacy_screen)
|
|
{
|
|
case META_KMS_PRIVACY_SCREEN_HW_STATE_DISABLED:
|
|
state->privacy_screen_state = META_PRIVACY_SCREEN_DISABLED;
|
|
break;
|
|
case META_KMS_PRIVACY_SCREEN_HW_STATE_DISABLED_LOCKED:
|
|
state->privacy_screen_state = META_PRIVACY_SCREEN_DISABLED;
|
|
state->privacy_screen_state |= META_PRIVACY_SCREEN_LOCKED;
|
|
break;
|
|
case META_KMS_PRIVACY_SCREEN_HW_STATE_ENABLED:
|
|
state->privacy_screen_state = META_PRIVACY_SCREEN_ENABLED;
|
|
break;
|
|
case META_KMS_PRIVACY_SCREEN_HW_STATE_ENABLED_LOCKED:
|
|
state->privacy_screen_state = META_PRIVACY_SCREEN_ENABLED;
|
|
state->privacy_screen_state |= META_PRIVACY_SCREEN_LOCKED;
|
|
break;
|
|
default:
|
|
state->privacy_screen_state = META_PRIVACY_SCREEN_DISABLED;
|
|
g_warning ("Unknown privacy screen state: %u", privacy_screen);
|
|
}
|
|
|
|
if (!has_privacy_screen_software_toggle (connector))
|
|
state->privacy_screen_state |= META_PRIVACY_SCREEN_LOCKED;
|
|
}
|
|
|
|
static MetaOutputColorspace
|
|
drm_color_spaces_to_output_color_spaces (uint64_t drm_color_space)
|
|
{
|
|
switch (drm_color_space)
|
|
{
|
|
case META_KMS_CONNECTOR_COLORSPACE_DEFAULT:
|
|
return META_OUTPUT_COLORSPACE_DEFAULT;
|
|
case META_KMS_CONNECTOR_COLORSPACE_BT2020_RGB:
|
|
return META_OUTPUT_COLORSPACE_BT2020;
|
|
default:
|
|
return META_OUTPUT_COLORSPACE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
static uint64_t
|
|
supported_drm_color_spaces_to_output_color_spaces (uint64_t drm_support)
|
|
{
|
|
uint64_t supported = 0;
|
|
|
|
if (drm_support & (1 << META_KMS_CONNECTOR_COLORSPACE_DEFAULT))
|
|
supported |= (1 << META_OUTPUT_COLORSPACE_DEFAULT);
|
|
if (drm_support & (1 << META_KMS_CONNECTOR_COLORSPACE_BT2020_RGB))
|
|
supported |= (1 << META_OUTPUT_COLORSPACE_BT2020);
|
|
|
|
return supported;
|
|
}
|
|
|
|
uint64_t
|
|
meta_output_color_space_to_drm_color_space (MetaOutputColorspace color_space)
|
|
{
|
|
switch (color_space)
|
|
{
|
|
case META_OUTPUT_COLORSPACE_BT2020:
|
|
return META_KMS_CONNECTOR_COLORSPACE_BT2020_RGB;
|
|
case META_OUTPUT_COLORSPACE_UNKNOWN:
|
|
case META_OUTPUT_COLORSPACE_DEFAULT:
|
|
default:
|
|
return META_KMS_CONNECTOR_COLORSPACE_DEFAULT;
|
|
}
|
|
}
|
|
|
|
static void
|
|
state_set_properties (MetaKmsConnectorState *state,
|
|
MetaKmsImplDevice *impl_device,
|
|
MetaKmsConnector *connector,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
MetaKmsProp *props = connector->prop_table.props;
|
|
MetaKmsProp *prop;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_SUGGESTED_X];
|
|
if (prop->prop_id)
|
|
state->suggested_x = prop->value;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_SUGGESTED_Y];
|
|
if (prop->prop_id)
|
|
state->suggested_y = prop->value;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_HOTPLUG_MODE_UPDATE];
|
|
if (prop->prop_id)
|
|
state->hotplug_mode_update = prop->value;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_SCALING_MODE];
|
|
if (prop->prop_id)
|
|
state->has_scaling = TRUE;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_PANEL_ORIENTATION];
|
|
if (prop->prop_id)
|
|
set_panel_orientation (state, prop);
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_NON_DESKTOP];
|
|
if (prop->prop_id)
|
|
state->non_desktop = prop->value;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_HW_STATE];
|
|
if (prop->prop_id)
|
|
set_privacy_screen (state, connector, prop);
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_MAX_BPC];
|
|
if (prop->prop_id)
|
|
{
|
|
state->max_bpc.value = prop->value;
|
|
state->max_bpc.min_value = prop->range_min;
|
|
state->max_bpc.max_value = prop->range_max;
|
|
}
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_COLORSPACE];
|
|
if (prop->prop_id)
|
|
{
|
|
state->colorspace.value =
|
|
drm_color_spaces_to_output_color_spaces (prop->value);
|
|
state->colorspace.supported =
|
|
supported_drm_color_spaces_to_output_color_spaces (prop->supported_variants);
|
|
}
|
|
}
|
|
|
|
static CoglSubpixelOrder
|
|
drm_subpixel_order_to_cogl_subpixel_order (drmModeSubPixel subpixel)
|
|
{
|
|
switch (subpixel)
|
|
{
|
|
case DRM_MODE_SUBPIXEL_NONE:
|
|
return COGL_SUBPIXEL_ORDER_NONE;
|
|
break;
|
|
case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
|
|
return COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
|
|
break;
|
|
case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
|
|
return COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
|
|
break;
|
|
case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
|
|
return COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
|
|
break;
|
|
case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
|
|
return COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
|
|
break;
|
|
case DRM_MODE_SUBPIXEL_UNKNOWN:
|
|
return COGL_SUBPIXEL_ORDER_UNKNOWN;
|
|
}
|
|
return COGL_SUBPIXEL_ORDER_UNKNOWN;
|
|
}
|
|
|
|
static void
|
|
state_set_edid (MetaKmsConnectorState *state,
|
|
MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
uint32_t blob_id)
|
|
{
|
|
int fd;
|
|
drmModePropertyBlobPtr edid_blob;
|
|
GBytes *edid_data;
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
edid_blob = drmModeGetPropertyBlob (fd, blob_id);
|
|
if (!edid_blob)
|
|
{
|
|
g_warning ("Failed to read EDID of connector %s: %s",
|
|
connector->name, g_strerror (errno));
|
|
return;
|
|
}
|
|
|
|
edid_data = g_bytes_new (edid_blob->data, edid_blob->length);
|
|
drmModeFreePropertyBlob (edid_blob);
|
|
|
|
state->edid_data = edid_data;
|
|
}
|
|
|
|
static void
|
|
state_set_tile_info (MetaKmsConnectorState *state,
|
|
MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
uint32_t blob_id)
|
|
{
|
|
int fd;
|
|
drmModePropertyBlobPtr tile_blob;
|
|
|
|
state->tile_info = (MetaTileInfo) { 0 };
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
tile_blob = drmModeGetPropertyBlob (fd, blob_id);
|
|
if (!tile_blob)
|
|
{
|
|
g_warning ("Failed to read TILE of connector %s: %s",
|
|
connector->name, strerror (errno));
|
|
return;
|
|
}
|
|
|
|
if (tile_blob->length > 0)
|
|
{
|
|
if (sscanf ((char *) tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d",
|
|
&state->tile_info.group_id,
|
|
&state->tile_info.flags,
|
|
&state->tile_info.max_h_tiles,
|
|
&state->tile_info.max_v_tiles,
|
|
&state->tile_info.loc_h_tile,
|
|
&state->tile_info.loc_v_tile,
|
|
&state->tile_info.tile_w,
|
|
&state->tile_info.tile_h) != 8)
|
|
{
|
|
g_warning ("Couldn't understand TILE property blob of connector %s",
|
|
connector->name);
|
|
state->tile_info = (MetaTileInfo) { 0 };
|
|
}
|
|
}
|
|
|
|
drmModeFreePropertyBlob (tile_blob);
|
|
}
|
|
|
|
static double
|
|
decode_u16_chromaticity (uint16_t value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
return MIN (value * 0.00002, 1.0);
|
|
}
|
|
|
|
static double
|
|
decode_u16_min_luminance (uint16_t value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
return value * 0.0001;
|
|
}
|
|
|
|
gboolean
|
|
set_output_hdr_metadata (struct hdr_output_metadata *drm_metadata,
|
|
MetaOutputHdrMetadata *metadata)
|
|
{
|
|
struct hdr_metadata_infoframe *infoframe;
|
|
|
|
if (drm_metadata->metadata_type != HDR_STATIC_METADATA_TYPE_1)
|
|
return FALSE;
|
|
|
|
infoframe = &drm_metadata->hdmi_metadata_type1;
|
|
|
|
if (infoframe->metadata_type != HDR_STATIC_METADATA_TYPE_1)
|
|
return FALSE;
|
|
|
|
switch (infoframe->eotf)
|
|
{
|
|
case HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR:
|
|
metadata->eotf = META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR;
|
|
break;
|
|
case HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR:
|
|
metadata->eotf = META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR;
|
|
break;
|
|
case HDR_METADATA_EOTF_PERCEPTUAL_QUANTIZER:
|
|
metadata->eotf = META_OUTPUT_HDR_METADATA_EOTF_PQ;
|
|
break;
|
|
case HDR_METADATA_EOTF_HYBRID_LOG_GAMMA:
|
|
metadata->eotf = META_OUTPUT_HDR_METADATA_EOTF_HLG;
|
|
break;
|
|
}
|
|
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
metadata->mastering_display_primaries[0].x =
|
|
decode_u16_chromaticity (infoframe->display_primaries[0].x);
|
|
metadata->mastering_display_primaries[0].y =
|
|
decode_u16_chromaticity (infoframe->display_primaries[0].y);
|
|
metadata->mastering_display_primaries[1].x =
|
|
decode_u16_chromaticity (infoframe->display_primaries[1].x);
|
|
metadata->mastering_display_primaries[1].y =
|
|
decode_u16_chromaticity (infoframe->display_primaries[1].y);
|
|
metadata->mastering_display_primaries[2].x =
|
|
decode_u16_chromaticity (infoframe->display_primaries[2].x);
|
|
metadata->mastering_display_primaries[2].y =
|
|
decode_u16_chromaticity (infoframe->display_primaries[2].y);
|
|
metadata->mastering_display_white_point.x =
|
|
decode_u16_chromaticity (infoframe->white_point.x);
|
|
metadata->mastering_display_white_point.y =
|
|
decode_u16_chromaticity (infoframe->white_point.y);
|
|
|
|
metadata->mastering_display_max_luminance =
|
|
infoframe->max_display_mastering_luminance;
|
|
metadata->mastering_display_min_luminance =
|
|
decode_u16_min_luminance (infoframe->min_display_mastering_luminance);
|
|
|
|
metadata->max_cll = infoframe->max_cll;
|
|
metadata->max_fall = infoframe->max_fall;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static uint16_t
|
|
encode_u16_chromaticity (double value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
value = MAX (MIN (value, 1.0), 0.0);
|
|
return round (value / 0.00002);
|
|
}
|
|
|
|
static uint16_t
|
|
encode_u16_max_luminance (double value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
return round (MAX (MIN (value, 65535.0), 0.0));
|
|
}
|
|
|
|
static uint16_t
|
|
encode_u16_min_luminance (double value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
value = MAX (MIN (value, 6.5535), 0.0);
|
|
return round (value / 0.0001);
|
|
}
|
|
|
|
static uint16_t
|
|
encode_u16_max_cll (double value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
return round (MAX (MIN (value, 65535.0), 0.0));
|
|
}
|
|
|
|
static uint16_t
|
|
encode_u16_max_fall (double value)
|
|
{
|
|
/* CTA-861.3 HDR Static Metadata Extension, 3.2.1 Static Metadata Type 1 */
|
|
return round (MAX (MIN (value, 65535.0), 0.0));
|
|
}
|
|
|
|
void
|
|
meta_set_drm_hdr_metadata (MetaOutputHdrMetadata *metadata,
|
|
struct hdr_output_metadata *drm_metadata)
|
|
{
|
|
struct hdr_metadata_infoframe *infoframe = &drm_metadata->hdmi_metadata_type1;
|
|
|
|
drm_metadata->metadata_type = HDR_STATIC_METADATA_TYPE_1;
|
|
infoframe->metadata_type = HDR_STATIC_METADATA_TYPE_1;
|
|
|
|
switch (metadata->eotf)
|
|
{
|
|
case META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR:
|
|
infoframe->eotf = HDR_METADATA_EOTF_TRADITIONAL_GAMMA_SDR;
|
|
break;
|
|
case META_OUTPUT_HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR:
|
|
infoframe->eotf = HDR_METADATA_EOTF_TRADITIONAL_GAMMA_HDR;
|
|
break;
|
|
case META_OUTPUT_HDR_METADATA_EOTF_PQ:
|
|
infoframe->eotf = HDR_METADATA_EOTF_PERCEPTUAL_QUANTIZER;
|
|
break;
|
|
case META_OUTPUT_HDR_METADATA_EOTF_HLG:
|
|
infoframe->eotf = HDR_METADATA_EOTF_HYBRID_LOG_GAMMA;
|
|
break;
|
|
}
|
|
|
|
infoframe->display_primaries[0].x =
|
|
encode_u16_chromaticity (metadata->mastering_display_primaries[0].x);
|
|
infoframe->display_primaries[0].y =
|
|
encode_u16_chromaticity (metadata->mastering_display_primaries[0].y);
|
|
infoframe->display_primaries[1].x =
|
|
encode_u16_chromaticity (metadata->mastering_display_primaries[1].x);
|
|
infoframe->display_primaries[1].y =
|
|
encode_u16_chromaticity (metadata->mastering_display_primaries[1].y);
|
|
infoframe->display_primaries[2].x =
|
|
encode_u16_chromaticity (metadata->mastering_display_primaries[2].x);
|
|
infoframe->display_primaries[2].y =
|
|
encode_u16_chromaticity (metadata->mastering_display_primaries[2].y);
|
|
infoframe->white_point.x =
|
|
encode_u16_chromaticity (metadata->mastering_display_white_point.x);
|
|
infoframe->white_point.y =
|
|
encode_u16_chromaticity (metadata->mastering_display_white_point.y);
|
|
|
|
infoframe->max_display_mastering_luminance =
|
|
encode_u16_max_luminance (metadata->mastering_display_max_luminance);
|
|
infoframe->min_display_mastering_luminance =
|
|
encode_u16_min_luminance (metadata->mastering_display_min_luminance);
|
|
|
|
infoframe->max_cll = encode_u16_max_cll (metadata->max_cll);
|
|
infoframe->max_fall = encode_u16_max_fall (metadata->max_fall);
|
|
}
|
|
|
|
static void
|
|
state_set_hdr_output_metadata (MetaKmsConnectorState *state,
|
|
MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
uint32_t blob_id)
|
|
{
|
|
int fd;
|
|
drmModePropertyBlobPtr hdr_blob;
|
|
MetaOutputHdrMetadata *metadata = &state->hdr.value;
|
|
struct hdr_output_metadata *drm_metadata;
|
|
|
|
state->hdr.supported = TRUE;
|
|
state->hdr.unknown = FALSE;
|
|
metadata->active = TRUE;
|
|
|
|
if (!blob_id)
|
|
{
|
|
metadata->active = FALSE;
|
|
return;
|
|
}
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
hdr_blob = drmModeGetPropertyBlob (fd, blob_id);
|
|
if (!hdr_blob)
|
|
{
|
|
metadata->active = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (hdr_blob->length < sizeof (*drm_metadata))
|
|
{
|
|
g_warning ("HDR_OUTPUT_METADATA smaller than expected for type 1");
|
|
state->hdr.unknown = TRUE;
|
|
goto out;
|
|
}
|
|
|
|
drm_metadata = hdr_blob->data;
|
|
if (!set_output_hdr_metadata (drm_metadata, metadata))
|
|
{
|
|
state->hdr.unknown = TRUE;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
drmModeFreePropertyBlob (hdr_blob);
|
|
}
|
|
|
|
static void
|
|
state_set_blobs (MetaKmsConnectorState *state,
|
|
MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
MetaKmsProp *props = connector->prop_table.props;
|
|
MetaKmsProp *prop;
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_EDID];
|
|
if (prop->prop_id && prop->value)
|
|
state_set_edid (state, connector, impl_device, prop->value);
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_TILE];
|
|
if (prop->prop_id && prop->value)
|
|
state_set_tile_info (state, connector, impl_device, prop->value);
|
|
|
|
prop = &props[META_KMS_CONNECTOR_PROP_HDR_OUTPUT_METADATA];
|
|
if (prop->prop_id)
|
|
state_set_hdr_output_metadata (state, connector, impl_device, prop->value);
|
|
}
|
|
|
|
static void
|
|
state_set_physical_dimensions (MetaKmsConnectorState *state,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
state->width_mm = drm_connector->mmWidth;
|
|
state->height_mm = drm_connector->mmHeight;
|
|
}
|
|
|
|
static void
|
|
state_set_modes (MetaKmsConnectorState *state,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < drm_connector->count_modes; i++)
|
|
{
|
|
MetaKmsMode *mode;
|
|
|
|
mode = meta_kms_mode_new (impl_device, &drm_connector->modes[i],
|
|
META_KMS_MODE_FLAG_NONE);
|
|
state->modes = g_list_prepend (state->modes, mode);
|
|
}
|
|
state->modes = g_list_reverse (state->modes);
|
|
}
|
|
|
|
static void
|
|
set_encoder_device_idx_bit (uint32_t *encoder_device_idxs,
|
|
uint32_t encoder_id,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeRes *drm_resources)
|
|
{
|
|
int fd;
|
|
int i;
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
|
|
for (i = 0; i < drm_resources->count_encoders; i++)
|
|
{
|
|
drmModeEncoder *drm_encoder;
|
|
|
|
drm_encoder = drmModeGetEncoder (fd, drm_resources->encoders[i]);
|
|
if (!drm_encoder)
|
|
continue;
|
|
|
|
if (drm_encoder->encoder_id == encoder_id)
|
|
{
|
|
*encoder_device_idxs |= (1 << i);
|
|
drmModeFreeEncoder (drm_encoder);
|
|
break;
|
|
}
|
|
|
|
drmModeFreeEncoder (drm_encoder);
|
|
}
|
|
}
|
|
|
|
static void
|
|
state_set_crtc_state (MetaKmsConnectorState *state,
|
|
drmModeConnector *drm_connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeRes *drm_resources)
|
|
{
|
|
int fd;
|
|
int i;
|
|
uint32_t common_possible_crtcs;
|
|
uint32_t common_possible_clones;
|
|
uint32_t encoder_device_idxs;
|
|
|
|
fd = meta_kms_impl_device_get_fd (impl_device);
|
|
|
|
common_possible_crtcs = UINT32_MAX;
|
|
common_possible_clones = UINT32_MAX;
|
|
encoder_device_idxs = 0;
|
|
for (i = 0; i < drm_connector->count_encoders; i++)
|
|
{
|
|
drmModeEncoder *drm_encoder;
|
|
|
|
drm_encoder = drmModeGetEncoder (fd, drm_connector->encoders[i]);
|
|
if (!drm_encoder)
|
|
continue;
|
|
|
|
common_possible_crtcs &= drm_encoder->possible_crtcs;
|
|
common_possible_clones &= drm_encoder->possible_clones;
|
|
|
|
set_encoder_device_idx_bit (&encoder_device_idxs,
|
|
drm_encoder->encoder_id,
|
|
impl_device,
|
|
drm_resources);
|
|
|
|
if (drm_connector->encoder_id == drm_encoder->encoder_id)
|
|
state->current_crtc_id = drm_encoder->crtc_id;
|
|
|
|
drmModeFreeEncoder (drm_encoder);
|
|
}
|
|
|
|
state->common_possible_crtcs = common_possible_crtcs;
|
|
state->common_possible_clones = common_possible_clones;
|
|
state->encoder_device_idxs = encoder_device_idxs;
|
|
}
|
|
|
|
static MetaKmsConnectorState *
|
|
meta_kms_connector_state_new (void)
|
|
{
|
|
MetaKmsConnectorState *state;
|
|
|
|
state = g_new0 (MetaKmsConnectorState, 1);
|
|
state->suggested_x = -1;
|
|
state->suggested_y = -1;
|
|
|
|
return state;
|
|
}
|
|
|
|
static void
|
|
meta_kms_connector_state_free (MetaKmsConnectorState *state)
|
|
{
|
|
g_clear_pointer (&state->edid_data, g_bytes_unref);
|
|
g_list_free_full (state->modes, (GDestroyNotify) meta_kms_mode_free);
|
|
g_free (state);
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsConnectorState,
|
|
meta_kms_connector_state_free);
|
|
|
|
static gboolean
|
|
kms_modes_equal (GList *modes,
|
|
GList *other_modes)
|
|
{
|
|
GList *l;
|
|
|
|
if (g_list_length (modes) != g_list_length (other_modes))
|
|
return FALSE;
|
|
|
|
for (l = modes; l; l = l->next)
|
|
{
|
|
GList *k;
|
|
MetaKmsMode *mode = l->data;
|
|
|
|
for (k = other_modes; k; k = k->next)
|
|
{
|
|
MetaKmsMode *other_mode = k->data;
|
|
|
|
if (!meta_kms_mode_equal (mode, other_mode))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
hdr_primaries_equal (double x1, double x2)
|
|
{
|
|
return fabs (x1 - x2) < (0.00002 - DBL_EPSILON);
|
|
}
|
|
|
|
static gboolean
|
|
hdr_nits_equal (double x1, double x2)
|
|
{
|
|
return fabs (x1 - x2) < (1.0 - DBL_EPSILON);
|
|
}
|
|
|
|
static gboolean
|
|
hdr_min_luminance_equal (double x1, double x2)
|
|
{
|
|
return fabs (x1 - x2) < (0.0001 - DBL_EPSILON);
|
|
}
|
|
|
|
gboolean
|
|
hdr_metadata_equal (MetaOutputHdrMetadata *metadata,
|
|
MetaOutputHdrMetadata *other_metadata)
|
|
{
|
|
if (!metadata->active && !other_metadata->active)
|
|
return TRUE;
|
|
|
|
if (metadata->active != other_metadata->active)
|
|
return FALSE;
|
|
|
|
if (metadata->eotf != other_metadata->eotf)
|
|
return FALSE;
|
|
|
|
if (!hdr_primaries_equal (metadata->mastering_display_primaries[0].x,
|
|
other_metadata->mastering_display_primaries[0].x) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_primaries[0].y,
|
|
other_metadata->mastering_display_primaries[0].y) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_primaries[1].x,
|
|
other_metadata->mastering_display_primaries[1].x) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_primaries[1].y,
|
|
other_metadata->mastering_display_primaries[1].y) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_primaries[2].x,
|
|
other_metadata->mastering_display_primaries[2].x) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_primaries[2].y,
|
|
other_metadata->mastering_display_primaries[2].y) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_white_point.x,
|
|
other_metadata->mastering_display_white_point.x) ||
|
|
!hdr_primaries_equal (metadata->mastering_display_white_point.y,
|
|
other_metadata->mastering_display_white_point.y))
|
|
return FALSE;
|
|
|
|
if (!hdr_nits_equal (metadata->mastering_display_max_luminance,
|
|
other_metadata->mastering_display_max_luminance))
|
|
return FALSE;
|
|
|
|
if (!hdr_min_luminance_equal (metadata->mastering_display_min_luminance,
|
|
other_metadata->mastering_display_min_luminance))
|
|
return FALSE;
|
|
|
|
if (!hdr_nits_equal (metadata->max_cll, other_metadata->max_cll) ||
|
|
!hdr_nits_equal (metadata->max_fall, other_metadata->max_fall))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static MetaKmsResourceChanges
|
|
meta_kms_connector_state_changes (MetaKmsConnectorState *state,
|
|
MetaKmsConnectorState *new_state)
|
|
{
|
|
if (state->current_crtc_id != new_state->current_crtc_id)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->common_possible_crtcs != new_state->common_possible_crtcs)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->common_possible_clones != new_state->common_possible_clones)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->encoder_device_idxs != new_state->encoder_device_idxs)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->width_mm != new_state->width_mm)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->height_mm != new_state->height_mm)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->has_scaling != new_state->has_scaling)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->non_desktop != new_state->non_desktop)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->subpixel_order != new_state->subpixel_order)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->suggested_x != new_state->suggested_x)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->suggested_y != new_state->suggested_y)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->hotplug_mode_update != new_state->hotplug_mode_update)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->panel_orientation_transform !=
|
|
new_state->panel_orientation_transform)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (!meta_tile_info_equal (&state->tile_info, &new_state->tile_info))
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if ((state->edid_data && !new_state->edid_data) || !state->edid_data ||
|
|
!g_bytes_equal (state->edid_data, new_state->edid_data))
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (!kms_modes_equal (state->modes, new_state->modes))
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->max_bpc.value != new_state->max_bpc.value ||
|
|
state->max_bpc.min_value != new_state->max_bpc.min_value ||
|
|
state->max_bpc.max_value != new_state->max_bpc.max_value)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->colorspace.value != new_state->colorspace.value ||
|
|
state->colorspace.supported != new_state->colorspace.supported)
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->hdr.supported != new_state->hdr.supported ||
|
|
state->hdr.unknown != new_state->hdr.unknown ||
|
|
!hdr_metadata_equal (&state->hdr.value, &new_state->hdr.value))
|
|
return META_KMS_RESOURCE_CHANGE_FULL;
|
|
|
|
if (state->privacy_screen_state != new_state->privacy_screen_state)
|
|
return META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN;
|
|
|
|
return META_KMS_RESOURCE_CHANGE_NONE;
|
|
}
|
|
|
|
static void
|
|
meta_kms_connector_update_state_changes (MetaKmsConnector *connector,
|
|
MetaKmsResourceChanges changes,
|
|
MetaKmsConnectorState *new_state)
|
|
{
|
|
MetaKmsConnectorState *current_state = connector->current_state;
|
|
|
|
g_return_if_fail (changes != META_KMS_RESOURCE_CHANGE_FULL);
|
|
|
|
if (changes & META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN)
|
|
current_state->privacy_screen_state = new_state->privacy_screen_state;
|
|
}
|
|
|
|
static MetaKmsResourceChanges
|
|
meta_kms_connector_read_state (MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeConnector *drm_connector,
|
|
drmModeRes *drm_resources)
|
|
{
|
|
g_autoptr (MetaKmsConnectorState) state = NULL;
|
|
g_autoptr (MetaKmsConnectorState) current_state = NULL;
|
|
MetaKmsResourceChanges connector_changes;
|
|
MetaKmsResourceChanges changes;
|
|
|
|
current_state = g_steal_pointer (&connector->current_state);
|
|
changes = META_KMS_RESOURCE_CHANGE_NONE;
|
|
|
|
meta_kms_impl_device_update_prop_table (impl_device,
|
|
drm_connector->props,
|
|
drm_connector->prop_values,
|
|
drm_connector->count_props,
|
|
connector->prop_table.props,
|
|
META_KMS_CONNECTOR_N_PROPS);
|
|
|
|
if (!drm_connector)
|
|
{
|
|
if (current_state)
|
|
changes = META_KMS_RESOURCE_CHANGE_FULL;
|
|
goto out;
|
|
}
|
|
|
|
if (drm_connector->connection != DRM_MODE_CONNECTED)
|
|
{
|
|
if (drm_connector->connection != connector->connection)
|
|
{
|
|
connector->connection = drm_connector->connection;
|
|
changes |= META_KMS_RESOURCE_CHANGE_FULL;
|
|
}
|
|
|
|
goto out;
|
|
}
|
|
|
|
state = meta_kms_connector_state_new ();
|
|
|
|
state_set_blobs (state, connector, impl_device, drm_connector);
|
|
|
|
state_set_properties (state, impl_device, connector, drm_connector);
|
|
|
|
state->subpixel_order =
|
|
drm_subpixel_order_to_cogl_subpixel_order (drm_connector->subpixel);
|
|
|
|
state_set_physical_dimensions (state, drm_connector);
|
|
|
|
state_set_modes (state, impl_device, drm_connector);
|
|
|
|
state_set_crtc_state (state, drm_connector, impl_device, drm_resources);
|
|
|
|
if (drm_connector->connection != connector->connection)
|
|
{
|
|
connector->connection = drm_connector->connection;
|
|
changes |= META_KMS_RESOURCE_CHANGE_FULL;
|
|
}
|
|
|
|
if (!current_state)
|
|
connector_changes = META_KMS_RESOURCE_CHANGE_FULL;
|
|
else
|
|
connector_changes = meta_kms_connector_state_changes (current_state, state);
|
|
|
|
changes |= connector_changes;
|
|
|
|
if (!(changes & META_KMS_RESOURCE_CHANGE_FULL))
|
|
{
|
|
meta_kms_connector_update_state_changes (connector,
|
|
connector_changes,
|
|
state);
|
|
connector->current_state = g_steal_pointer (¤t_state);
|
|
}
|
|
else
|
|
{
|
|
connector->current_state = g_steal_pointer (&state);
|
|
}
|
|
|
|
out:
|
|
sync_fd_held (connector, impl_device);
|
|
|
|
return changes;
|
|
}
|
|
|
|
MetaKmsResourceChanges
|
|
meta_kms_connector_update_state_in_impl (MetaKmsConnector *connector,
|
|
drmModeRes *drm_resources,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
MetaKmsImplDevice *impl_device;
|
|
MetaKmsResourceChanges changes;
|
|
|
|
impl_device = meta_kms_device_get_impl_device (connector->device);
|
|
changes = meta_kms_connector_read_state (connector, impl_device,
|
|
drm_connector,
|
|
drm_resources);
|
|
|
|
return changes;
|
|
}
|
|
|
|
void
|
|
meta_kms_connector_disable_in_impl (MetaKmsConnector *connector)
|
|
{
|
|
MetaKmsConnectorState *current_state;
|
|
|
|
current_state = connector->current_state;
|
|
if (!current_state)
|
|
return;
|
|
|
|
current_state->current_crtc_id = 0;
|
|
}
|
|
|
|
MetaKmsResourceChanges
|
|
meta_kms_connector_predict_state_in_impl (MetaKmsConnector *connector,
|
|
MetaKmsUpdate *update)
|
|
{
|
|
MetaKmsImplDevice *impl_device;
|
|
MetaKmsConnectorState *current_state;
|
|
GList *mode_sets;
|
|
GList *l;
|
|
MetaKmsResourceChanges changes = META_KMS_RESOURCE_CHANGE_NONE;
|
|
GList *connector_updates;
|
|
|
|
current_state = connector->current_state;
|
|
if (!current_state)
|
|
return META_KMS_RESOURCE_CHANGE_NONE;
|
|
|
|
mode_sets = meta_kms_update_get_mode_sets (update);
|
|
for (l = mode_sets; l; l = l->next)
|
|
{
|
|
MetaKmsModeSet *mode_set = l->data;
|
|
MetaKmsCrtc *crtc = mode_set->crtc;
|
|
|
|
if (current_state->current_crtc_id == meta_kms_crtc_get_id (crtc))
|
|
{
|
|
if (g_list_find (mode_set->connectors, connector))
|
|
break;
|
|
else
|
|
current_state->current_crtc_id = 0;
|
|
}
|
|
else
|
|
{
|
|
if (g_list_find (mode_set->connectors, connector))
|
|
{
|
|
current_state->current_crtc_id = meta_kms_crtc_get_id (crtc);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
connector_updates = meta_kms_update_get_connector_updates (update);
|
|
for (l = connector_updates; l; l = l->next)
|
|
{
|
|
MetaKmsConnectorUpdate *connector_update = l->data;
|
|
|
|
if (connector_update->connector != connector)
|
|
continue;
|
|
|
|
if (has_privacy_screen_software_toggle (connector) &&
|
|
connector_update->privacy_screen.has_update &&
|
|
!(current_state->privacy_screen_state &
|
|
META_PRIVACY_SCREEN_LOCKED))
|
|
{
|
|
if (connector_update->privacy_screen.is_enabled)
|
|
{
|
|
if (current_state->privacy_screen_state !=
|
|
META_PRIVACY_SCREEN_ENABLED)
|
|
changes |= META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN;
|
|
|
|
current_state->privacy_screen_state =
|
|
META_PRIVACY_SCREEN_ENABLED;
|
|
}
|
|
else
|
|
{
|
|
if (current_state->privacy_screen_state !=
|
|
META_PRIVACY_SCREEN_DISABLED)
|
|
changes |= META_KMS_RESOURCE_CHANGE_PRIVACY_SCREEN;
|
|
|
|
current_state->privacy_screen_state =
|
|
META_PRIVACY_SCREEN_DISABLED;
|
|
}
|
|
}
|
|
|
|
if (connector_update->colorspace.has_update)
|
|
{
|
|
g_warn_if_fail (meta_kms_connector_is_color_space_supported (
|
|
connector,
|
|
connector_update->colorspace.value));
|
|
current_state->colorspace.value = connector_update->colorspace.value;
|
|
}
|
|
|
|
if (connector_update->hdr.has_update)
|
|
{
|
|
g_warn_if_fail (meta_kms_connector_is_hdr_metadata_supported (
|
|
connector));
|
|
current_state->hdr.value = connector_update->hdr.value;
|
|
}
|
|
}
|
|
|
|
impl_device = meta_kms_device_get_impl_device (connector->device);
|
|
sync_fd_held (connector, impl_device);
|
|
|
|
return changes;
|
|
}
|
|
|
|
static void
|
|
init_properties (MetaKmsConnector *connector,
|
|
MetaKmsImplDevice *impl_device,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
MetaKmsConnectorPropTable *prop_table = &connector->prop_table;
|
|
|
|
*prop_table = (MetaKmsConnectorPropTable) {
|
|
.props = {
|
|
[META_KMS_CONNECTOR_PROP_CRTC_ID] =
|
|
{
|
|
.name = "CRTC_ID",
|
|
.type = DRM_MODE_PROP_OBJECT,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_DPMS] =
|
|
{
|
|
.name = "DPMS",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->dpms_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_DPMS_N_PROPS,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_UNDERSCAN] =
|
|
{
|
|
.name = "underscan",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->underscan_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_UNDERSCAN_N_PROPS,
|
|
.default_value = META_KMS_CONNECTOR_UNDERSCAN_UNKNOWN,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_UNDERSCAN_HBORDER] =
|
|
{
|
|
.name = "underscan hborder",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_UNDERSCAN_VBORDER] =
|
|
{
|
|
.name = "underscan vborder",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_SW_STATE] =
|
|
{
|
|
.name = "privacy-screen sw-state",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->privacy_screen_sw_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_PRIVACY_SCREEN_N_PROPS,
|
|
.default_value = META_KMS_CONNECTOR_PRIVACY_SCREEN_UNKNOWN,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_PRIVACY_SCREEN_HW_STATE] =
|
|
{
|
|
.name = "privacy-screen hw-state",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->privacy_screen_hw_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_PRIVACY_SCREEN_N_PROPS,
|
|
.default_value = META_KMS_CONNECTOR_PRIVACY_SCREEN_UNKNOWN,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_EDID] =
|
|
{
|
|
.name = "EDID",
|
|
.type = DRM_MODE_PROP_BLOB,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_TILE] =
|
|
{
|
|
.name = "TILE",
|
|
.type = DRM_MODE_PROP_BLOB,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_SUGGESTED_X] =
|
|
{
|
|
.name = "suggested X",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_SUGGESTED_Y] =
|
|
{
|
|
.name = "suggested Y",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_HOTPLUG_MODE_UPDATE] =
|
|
{
|
|
.name = "hotplug_mode_update",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_SCALING_MODE] =
|
|
{
|
|
.name = "scaling mode",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->scaling_mode_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_SCALING_MODE_N_PROPS,
|
|
.default_value = META_KMS_CONNECTOR_SCALING_MODE_UNKNOWN,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_PANEL_ORIENTATION] =
|
|
{
|
|
.name = "panel orientation",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->panel_orientation_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_PANEL_ORIENTATION_N_PROPS,
|
|
.default_value = META_KMS_CONNECTOR_PANEL_ORIENTATION_UNKNOWN,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_NON_DESKTOP] =
|
|
{
|
|
.name = "non-desktop",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_MAX_BPC] =
|
|
{
|
|
.name = "max bpc",
|
|
.type = DRM_MODE_PROP_RANGE,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_COLORSPACE] =
|
|
{
|
|
.name = "Colorspace",
|
|
.type = DRM_MODE_PROP_ENUM,
|
|
.enum_values = prop_table->colorspace_enum,
|
|
.num_enum_values = META_KMS_CONNECTOR_COLORSPACE_N_PROPS,
|
|
.default_value = META_KMS_CONNECTOR_COLORSPACE_UNKNOWN,
|
|
},
|
|
[META_KMS_CONNECTOR_PROP_HDR_OUTPUT_METADATA] =
|
|
{
|
|
.name = "HDR_OUTPUT_METADATA",
|
|
.type = DRM_MODE_PROP_BLOB,
|
|
},
|
|
},
|
|
.dpms_enum = {
|
|
[META_KMS_CONNECTOR_DPMS_ON] =
|
|
{
|
|
.name = "On",
|
|
},
|
|
[META_KMS_CONNECTOR_DPMS_STANDBY] =
|
|
{
|
|
.name = "Standby",
|
|
},
|
|
[META_KMS_CONNECTOR_DPMS_SUSPEND] =
|
|
{
|
|
.name = "Suspend",
|
|
},
|
|
[META_KMS_CONNECTOR_DPMS_OFF] =
|
|
{
|
|
.name = "Off",
|
|
},
|
|
},
|
|
.underscan_enum = {
|
|
[META_KMS_CONNECTOR_UNDERSCAN_OFF] =
|
|
{
|
|
.name = "off",
|
|
},
|
|
[META_KMS_CONNECTOR_UNDERSCAN_ON] =
|
|
{
|
|
.name = "on",
|
|
},
|
|
[META_KMS_CONNECTOR_UNDERSCAN_AUTO] =
|
|
{
|
|
.name = "auto",
|
|
},
|
|
},
|
|
.privacy_screen_sw_enum = {
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_ENABLED] =
|
|
{
|
|
.name = "Enabled",
|
|
},
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_DISABLED] =
|
|
{
|
|
.name = "Disabled",
|
|
},
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_ENABLED_LOCKED] =
|
|
{
|
|
.name = "Enabled-locked",
|
|
},
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_DISABLED_LOCKED] =
|
|
{
|
|
.name = "Disabled-locked",
|
|
},
|
|
},
|
|
.privacy_screen_hw_enum = {
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_ENABLED] =
|
|
{
|
|
.name = "Enabled",
|
|
},
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_DISABLED] =
|
|
{
|
|
.name = "Disabled",
|
|
},
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_ENABLED_LOCKED] =
|
|
{
|
|
.name = "Enabled-locked",
|
|
},
|
|
[META_KMS_CONNECTOR_PRIVACY_SCREEN_DISABLED_LOCKED] =
|
|
{
|
|
.name = "Disabled-locked",
|
|
},
|
|
},
|
|
.scaling_mode_enum = {
|
|
[META_KMS_CONNECTOR_SCALING_MODE_NONE] =
|
|
{
|
|
.name = "None",
|
|
},
|
|
[META_KMS_CONNECTOR_SCALING_MODE_FULL] =
|
|
{
|
|
.name = "Full",
|
|
},
|
|
[META_KMS_CONNECTOR_SCALING_MODE_CENTER] =
|
|
{
|
|
.name = "Center",
|
|
},
|
|
[META_KMS_CONNECTOR_SCALING_MODE_FULL_ASPECT] =
|
|
{
|
|
.name = "Full aspect",
|
|
},
|
|
},
|
|
.panel_orientation_enum = {
|
|
[META_KMS_CONNECTOR_PANEL_ORIENTATION_NORMAL] =
|
|
{
|
|
.name = "Normal",
|
|
},
|
|
[META_KMS_CONNECTOR_PANEL_ORIENTATION_UPSIDE_DOWN] =
|
|
{
|
|
.name = "Upside Down",
|
|
},
|
|
[META_KMS_CONNECTOR_PANEL_ORIENTATION_LEFT_SIDE_UP] =
|
|
{
|
|
.name = "Left Side Up",
|
|
},
|
|
[META_KMS_CONNECTOR_PANEL_ORIENTATION_RIGHT_SIDE_UP] =
|
|
{
|
|
.name = "Right Side Up",
|
|
},
|
|
},
|
|
.colorspace_enum = {
|
|
[META_KMS_CONNECTOR_COLORSPACE_DEFAULT] =
|
|
{
|
|
.name = "Default",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_RGB_WIDE_GAMUT_FIXED_POINT] =
|
|
{
|
|
.name = "RGB_Wide_Gamut_Fixed_Point",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_RGB_WIDE_GAMUT_FLOATING_POINT] =
|
|
{
|
|
.name = "RGB_Wide_Gamut_Floating_Point",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_RGB_OPRGB] =
|
|
{
|
|
.name = "opRGB",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_RGB_DCI_P3_RGB_D65] =
|
|
{
|
|
.name = "DCI-P3_RGB_D65",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_BT2020_RGB] =
|
|
{
|
|
.name = "BT2020_RGB",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_BT601_YCC] =
|
|
{
|
|
.name = "BT601_YCC",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_BT709_YCC] =
|
|
{
|
|
.name = "BT709_YCC",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_XVYCC_601] =
|
|
{
|
|
.name = "XVYCC_601",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_XVYCC_709] =
|
|
{
|
|
.name = "XVYCC_709",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_SYCC_601] =
|
|
{
|
|
.name = "SYCC_601",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_OPYCC_601] =
|
|
{
|
|
.name = "opYCC_601",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_BT2020_CYCC] =
|
|
{
|
|
.name = "BT2020_CYCC",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_BT2020_YCC] =
|
|
{
|
|
.name = "BT2020_YCC",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_SMPTE_170M_YCC] =
|
|
{
|
|
.name = "SMPTE_170M_YCC",
|
|
},
|
|
[META_KMS_CONNECTOR_COLORSPACE_DCI_P3_RGB_THEATER] =
|
|
{
|
|
.name = "DCI-P3_RGB_Theater",
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
static char *
|
|
make_connector_name (drmModeConnector *drm_connector)
|
|
{
|
|
static const char * const connector_type_names[] = {
|
|
"None",
|
|
"VGA",
|
|
"DVI-I",
|
|
"DVI-D",
|
|
"DVI-A",
|
|
"Composite",
|
|
"SVIDEO",
|
|
"LVDS",
|
|
"Component",
|
|
"DIN",
|
|
"DP",
|
|
"HDMI",
|
|
"HDMI-B",
|
|
"TV",
|
|
"eDP",
|
|
"Virtual",
|
|
"DSI",
|
|
};
|
|
|
|
if (drm_connector->connector_type < G_N_ELEMENTS (connector_type_names))
|
|
return g_strdup_printf ("%s-%d",
|
|
connector_type_names[drm_connector->connector_type],
|
|
drm_connector->connector_type_id);
|
|
else
|
|
return g_strdup_printf ("Unknown%d-%d",
|
|
drm_connector->connector_type,
|
|
drm_connector->connector_type_id);
|
|
}
|
|
|
|
gboolean
|
|
meta_kms_connector_is_same_as (MetaKmsConnector *connector,
|
|
drmModeConnector *drm_connector)
|
|
{
|
|
return (connector->id == drm_connector->connector_id &&
|
|
connector->type == drm_connector->connector_type &&
|
|
connector->type_id == drm_connector->connector_type_id);
|
|
}
|
|
|
|
MetaKmsConnector *
|
|
meta_kms_connector_new (MetaKmsImplDevice *impl_device,
|
|
drmModeConnector *drm_connector,
|
|
drmModeRes *drm_resources)
|
|
{
|
|
MetaKmsConnector *connector;
|
|
|
|
g_assert (drm_connector);
|
|
connector = g_object_new (META_TYPE_KMS_CONNECTOR, NULL);
|
|
connector->device = meta_kms_impl_device_get_device (impl_device);
|
|
connector->id = drm_connector->connector_id;
|
|
connector->type = drm_connector->connector_type;
|
|
connector->type_id = drm_connector->connector_type_id;
|
|
connector->name = make_connector_name (drm_connector);
|
|
|
|
init_properties (connector, impl_device, drm_connector);
|
|
|
|
meta_kms_connector_read_state (connector, impl_device,
|
|
drm_connector,
|
|
drm_resources);
|
|
|
|
return connector;
|
|
}
|
|
|
|
static void
|
|
meta_kms_connector_finalize (GObject *object)
|
|
{
|
|
MetaKmsConnector *connector = META_KMS_CONNECTOR (object);
|
|
|
|
if (connector->fd_held)
|
|
{
|
|
MetaKmsImplDevice *impl_device;
|
|
|
|
impl_device = meta_kms_device_get_impl_device (connector->device);
|
|
meta_kms_impl_device_unhold_fd (impl_device);
|
|
}
|
|
|
|
g_clear_pointer (&connector->current_state, meta_kms_connector_state_free);
|
|
g_free (connector->name);
|
|
|
|
G_OBJECT_CLASS (meta_kms_connector_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_kms_connector_init (MetaKmsConnector *connector)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_kms_connector_class_init (MetaKmsConnectorClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_kms_connector_finalize;
|
|
}
|