Home | History | Annotate | Line # | Download | only in display
      1 /*	$NetBSD: intel_connector.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2007 Dave Airlie <airlied (at) linux.ie>
      5  * Copyright (c) 2007, 2010 Intel Corporation
      6  *   Jesse Barnes <jesse.barnes (at) intel.com>
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the "Software"),
     10  * to deal in the Software without restriction, including without limitation
     11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     12  * and/or sell copies of the Software, and to permit persons to whom the
     13  * Software is furnished to do so, subject to the following conditions:
     14  *
     15  * The above copyright notice and this permission notice (including the next
     16  * paragraph) shall be included in all copies or substantial portions of the
     17  * Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     25  * DEALINGS IN THE SOFTWARE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: intel_connector.c,v 1.2 2021/12/18 23:45:29 riastradh Exp $");
     30 
     31 #include <linux/i2c.h>
     32 #include <linux/slab.h>
     33 
     34 #include <drm/drm_atomic_helper.h>
     35 #include <drm/drm_edid.h>
     36 
     37 #include "display/intel_panel.h"
     38 
     39 #include "i915_drv.h"
     40 #include "intel_connector.h"
     41 #include "intel_display_types.h"
     42 #include "intel_hdcp.h"
     43 
     44 int intel_connector_init(struct intel_connector *connector)
     45 {
     46 	struct intel_digital_connector_state *conn_state;
     47 
     48 	/*
     49 	 * Allocate enough memory to hold intel_digital_connector_state,
     50 	 * This might be a few bytes too many, but for connectors that don't
     51 	 * need it we'll free the state and allocate a smaller one on the first
     52 	 * successful commit anyway.
     53 	 */
     54 	conn_state = kzalloc(sizeof(*conn_state), GFP_KERNEL);
     55 	if (!conn_state)
     56 		return -ENOMEM;
     57 
     58 	__drm_atomic_helper_connector_reset(&connector->base,
     59 					    &conn_state->base);
     60 
     61 	return 0;
     62 }
     63 
     64 struct intel_connector *intel_connector_alloc(void)
     65 {
     66 	struct intel_connector *connector;
     67 
     68 	connector = kzalloc(sizeof(*connector), GFP_KERNEL);
     69 	if (!connector)
     70 		return NULL;
     71 
     72 	if (intel_connector_init(connector) < 0) {
     73 		kfree(connector);
     74 		return NULL;
     75 	}
     76 
     77 	return connector;
     78 }
     79 
     80 /*
     81  * Free the bits allocated by intel_connector_alloc.
     82  * This should only be used after intel_connector_alloc has returned
     83  * successfully, and before drm_connector_init returns successfully.
     84  * Otherwise the destroy callbacks for the connector and the state should
     85  * take care of proper cleanup/free (see intel_connector_destroy).
     86  */
     87 void intel_connector_free(struct intel_connector *connector)
     88 {
     89 	kfree(to_intel_digital_connector_state(connector->base.state));
     90 	kfree(connector);
     91 }
     92 
     93 /*
     94  * Connector type independent destroy hook for drm_connector_funcs.
     95  */
     96 void intel_connector_destroy(struct drm_connector *connector)
     97 {
     98 	struct intel_connector *intel_connector = to_intel_connector(connector);
     99 
    100 	kfree(intel_connector->detect_edid);
    101 
    102 	intel_hdcp_cleanup(intel_connector);
    103 
    104 	if (!IS_ERR_OR_NULL(intel_connector->edid))
    105 		kfree(intel_connector->edid);
    106 
    107 	intel_panel_fini(&intel_connector->panel);
    108 
    109 	drm_connector_cleanup(connector);
    110 
    111 	if (intel_connector->port)
    112 		drm_dp_mst_put_port_malloc(intel_connector->port);
    113 
    114 	kfree(connector);
    115 }
    116 
    117 int intel_connector_register(struct drm_connector *connector)
    118 {
    119 	struct intel_connector *intel_connector = to_intel_connector(connector);
    120 	int ret;
    121 
    122 	ret = intel_backlight_device_register(intel_connector);
    123 	if (ret)
    124 		goto err;
    125 
    126 	if (i915_inject_probe_failure(to_i915(connector->dev))) {
    127 		ret = -EFAULT;
    128 		goto err_backlight;
    129 	}
    130 
    131 	return 0;
    132 
    133 err_backlight:
    134 	intel_backlight_device_unregister(intel_connector);
    135 err:
    136 	return ret;
    137 }
    138 
    139 void intel_connector_unregister(struct drm_connector *connector)
    140 {
    141 	struct intel_connector *intel_connector = to_intel_connector(connector);
    142 
    143 	intel_backlight_device_unregister(intel_connector);
    144 }
    145 
    146 void intel_connector_attach_encoder(struct intel_connector *connector,
    147 				    struct intel_encoder *encoder)
    148 {
    149 	connector->encoder = encoder;
    150 	drm_connector_attach_encoder(&connector->base, &encoder->base);
    151 }
    152 
    153 /*
    154  * Simple connector->get_hw_state implementation for encoders that support only
    155  * one connector and no cloning and hence the encoder state determines the state
    156  * of the connector.
    157  */
    158 bool intel_connector_get_hw_state(struct intel_connector *connector)
    159 {
    160 	enum pipe pipe = 0;
    161 	struct intel_encoder *encoder = connector->encoder;
    162 
    163 	return encoder->get_hw_state(encoder, &pipe);
    164 }
    165 
    166 enum pipe intel_connector_get_pipe(struct intel_connector *connector)
    167 {
    168 	struct drm_device *dev = connector->base.dev;
    169 
    170 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
    171 
    172 	if (!connector->base.state->crtc)
    173 		return INVALID_PIPE;
    174 
    175 	return to_intel_crtc(connector->base.state->crtc)->pipe;
    176 }
    177 
    178 /**
    179  * intel_connector_update_modes - update connector from edid
    180  * @connector: DRM connector device to use
    181  * @edid: previously read EDID information
    182  */
    183 int intel_connector_update_modes(struct drm_connector *connector,
    184 				struct edid *edid)
    185 {
    186 	int ret;
    187 
    188 	drm_connector_update_edid_property(connector, edid);
    189 	ret = drm_add_edid_modes(connector, edid);
    190 
    191 	return ret;
    192 }
    193 
    194 /**
    195  * intel_ddc_get_modes - get modelist from monitor
    196  * @connector: DRM connector device to use
    197  * @adapter: i2c adapter
    198  *
    199  * Fetch the EDID information from @connector using the DDC bus.
    200  */
    201 int intel_ddc_get_modes(struct drm_connector *connector,
    202 			struct i2c_adapter *adapter)
    203 {
    204 	struct edid *edid;
    205 	int ret;
    206 
    207 	edid = drm_get_edid(connector, adapter);
    208 	if (!edid)
    209 		return 0;
    210 
    211 	ret = intel_connector_update_modes(connector, edid);
    212 	kfree(edid);
    213 
    214 	return ret;
    215 }
    216 
    217 static const struct drm_prop_enum_list force_audio_names[] = {
    218 	{ HDMI_AUDIO_OFF_DVI, "force-dvi" },
    219 	{ HDMI_AUDIO_OFF, "off" },
    220 	{ HDMI_AUDIO_AUTO, "auto" },
    221 	{ HDMI_AUDIO_ON, "on" },
    222 };
    223 
    224 void
    225 intel_attach_force_audio_property(struct drm_connector *connector)
    226 {
    227 	struct drm_device *dev = connector->dev;
    228 	struct drm_i915_private *dev_priv = to_i915(dev);
    229 	struct drm_property *prop;
    230 
    231 	prop = dev_priv->force_audio_property;
    232 	if (prop == NULL) {
    233 		prop = drm_property_create_enum(dev, 0,
    234 					   "audio",
    235 					   force_audio_names,
    236 					   ARRAY_SIZE(force_audio_names));
    237 		if (prop == NULL)
    238 			return;
    239 
    240 		dev_priv->force_audio_property = prop;
    241 	}
    242 	drm_object_attach_property(&connector->base, prop, 0);
    243 }
    244 
    245 static const struct drm_prop_enum_list broadcast_rgb_names[] = {
    246 	{ INTEL_BROADCAST_RGB_AUTO, "Automatic" },
    247 	{ INTEL_BROADCAST_RGB_FULL, "Full" },
    248 	{ INTEL_BROADCAST_RGB_LIMITED, "Limited 16:235" },
    249 };
    250 
    251 void
    252 intel_attach_broadcast_rgb_property(struct drm_connector *connector)
    253 {
    254 	struct drm_device *dev = connector->dev;
    255 	struct drm_i915_private *dev_priv = to_i915(dev);
    256 	struct drm_property *prop;
    257 
    258 	prop = dev_priv->broadcast_rgb_property;
    259 	if (prop == NULL) {
    260 		prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
    261 					   "Broadcast RGB",
    262 					   broadcast_rgb_names,
    263 					   ARRAY_SIZE(broadcast_rgb_names));
    264 		if (prop == NULL)
    265 			return;
    266 
    267 		dev_priv->broadcast_rgb_property = prop;
    268 	}
    269 
    270 	drm_object_attach_property(&connector->base, prop, 0);
    271 }
    272 
    273 void
    274 intel_attach_aspect_ratio_property(struct drm_connector *connector)
    275 {
    276 	if (!drm_mode_create_aspect_ratio_property(connector->dev))
    277 		drm_object_attach_property(&connector->base,
    278 			connector->dev->mode_config.aspect_ratio_property,
    279 			DRM_MODE_PICTURE_ASPECT_NONE);
    280 }
    281 
    282 void
    283 intel_attach_colorspace_property(struct drm_connector *connector)
    284 {
    285 	switch (connector->connector_type) {
    286 	case DRM_MODE_CONNECTOR_HDMIA:
    287 	case DRM_MODE_CONNECTOR_HDMIB:
    288 		if (drm_mode_create_hdmi_colorspace_property(connector))
    289 			return;
    290 		break;
    291 	case DRM_MODE_CONNECTOR_DisplayPort:
    292 	case DRM_MODE_CONNECTOR_eDP:
    293 		if (drm_mode_create_dp_colorspace_property(connector))
    294 			return;
    295 		break;
    296 	default:
    297 		DRM_DEBUG_KMS("Colorspace property not supported\n");
    298 		return;
    299 	}
    300 
    301 	drm_object_attach_property(&connector->base,
    302 				   connector->colorspace_property, 0);
    303 }
    304