/* * Copyright © 2016 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include <linux/delay.h> #include <linux/errno.h> #include <linux/export.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/string.h> #include <drm/display/drm_dp_dual_mode_helper.h> #include <drm/drm_device.h> #include <drm/drm_print.h> /** * DOC: dp dual mode helpers * * Helper functions to deal with DP dual mode (aka. DP++) adaptors. * * Type 1: * Adaptor registers (if any) and the sink DDC bus may be accessed via I2C. * * Type 2: * Adaptor registers and sink DDC bus can be accessed either via I2C or * I2C-over-AUX. Source devices may choose to implement either of these * access methods. */ #define DP_DUAL_MODE_SLAVE_ADDRESS … /** * drm_dp_dual_mode_read - Read from the DP dual mode adaptor register(s) * @adapter: I2C adapter for the DDC bus * @offset: register offset * @buffer: buffer for return data * @size: size of the buffer * * Reads @size bytes from the DP dual mode adaptor registers * starting at @offset. * * Returns: * 0 on success, negative error code on failure */ ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter, u8 offset, void *buffer, size_t size) { … } EXPORT_SYMBOL(…); /** * drm_dp_dual_mode_write - Write to the DP dual mode adaptor register(s) * @adapter: I2C adapter for the DDC bus * @offset: register offset * @buffer: buffer for write data * @size: size of the buffer * * Writes @size bytes to the DP dual mode adaptor registers * starting at @offset. * * Returns: * 0 on success, negative error code on failure */ ssize_t drm_dp_dual_mode_write(struct i2c_adapter *adapter, u8 offset, const void *buffer, size_t size) { … } EXPORT_SYMBOL(…); static bool is_hdmi_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN]) { … } static bool is_type1_adaptor(uint8_t adaptor_id) { … } static bool is_type2_adaptor(uint8_t adaptor_id) { … } static bool is_lspcon_adaptor(const char hdmi_id[DP_DUAL_MODE_HDMI_ID_LEN], const uint8_t adaptor_id) { … } /** * drm_dp_dual_mode_detect - Identify the DP dual mode adaptor * @dev: &drm_device to use * @adapter: I2C adapter for the DDC bus * * Attempt to identify the type of the DP dual mode adaptor used. * * Note that when the answer is @DRM_DP_DUAL_MODE_UNKNOWN it's not * certain whether we're dealing with a native HDMI port or * a type 1 DVI dual mode adaptor. The driver will have to use * some other hardware/driver specific mechanism to make that * distinction. * * Returns: * The type of the DP dual mode adaptor used */ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev, struct i2c_adapter *adapter) { … } EXPORT_SYMBOL(…); /** * drm_dp_dual_mode_max_tmds_clock - Max TMDS clock for DP dual mode adaptor * @dev: &drm_device to use * @type: DP dual mode adaptor type * @adapter: I2C adapter for the DDC bus * * Determine the max TMDS clock the adaptor supports based on the * type of the dual mode adaptor and the DP_DUAL_MODE_MAX_TMDS_CLOCK * register (on type2 adaptors). As some type 1 adaptors have * problems with registers (see comments in drm_dp_dual_mode_detect()) * we don't read the register on those, instead we simply assume * a 165 MHz limit based on the specification. * * Returns: * Maximum supported TMDS clock rate for the DP dual mode adaptor in kHz. */ int drm_dp_dual_mode_max_tmds_clock(const struct drm_device *dev, enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter) { … } EXPORT_SYMBOL(…); /** * drm_dp_dual_mode_get_tmds_output - Get the state of the TMDS output buffers in the DP dual mode adaptor * @dev: &drm_device to use * @type: DP dual mode adaptor type * @adapter: I2C adapter for the DDC bus * @enabled: current state of the TMDS output buffers * * Get the state of the TMDS output buffers in the adaptor. For * type2 adaptors this is queried from the DP_DUAL_MODE_TMDS_OEN * register. As some type 1 adaptors have problems with registers * (see comments in drm_dp_dual_mode_detect()) we don't read the * register on those, instead we simply assume that the buffers * are always enabled. * * Returns: * 0 on success, negative error code on failure */ int drm_dp_dual_mode_get_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter, bool *enabled) { … } EXPORT_SYMBOL(…); /** * drm_dp_dual_mode_set_tmds_output - Enable/disable TMDS output buffers in the DP dual mode adaptor * @dev: &drm_device to use * @type: DP dual mode adaptor type * @adapter: I2C adapter for the DDC bus * @enable: enable (as opposed to disable) the TMDS output buffers * * Set the state of the TMDS output buffers in the adaptor. For * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. * Type1 adaptors do not support any register writes. * * Returns: * 0 on success, negative error code on failure */ int drm_dp_dual_mode_set_tmds_output(const struct drm_device *dev, enum drm_dp_dual_mode_type type, struct i2c_adapter *adapter, bool enable) { … } EXPORT_SYMBOL(…); /** * drm_dp_get_dual_mode_type_name - Get the name of the DP dual mode adaptor type as a string * @type: DP dual mode adaptor type * * Returns: * String representation of the DP dual mode adaptor type */ const char *drm_dp_get_dual_mode_type_name(enum drm_dp_dual_mode_type type) { … } EXPORT_SYMBOL(…); /** * drm_lspcon_get_mode: Get LSPCON's current mode of operation by * reading offset (0x80, 0x41) * @dev: &drm_device to use * @adapter: I2C-over-aux adapter * @mode: current lspcon mode of operation output variable * * Returns: * 0 on success, sets the current_mode value to appropriate mode * -error on failure */ int drm_lspcon_get_mode(const struct drm_device *dev, struct i2c_adapter *adapter, enum drm_lspcon_mode *mode) { … } EXPORT_SYMBOL(…); /** * drm_lspcon_set_mode: Change LSPCON's mode of operation by * writing offset (0x80, 0x40) * @dev: &drm_device to use * @adapter: I2C-over-aux adapter * @mode: required mode of operation * * Returns: * 0 on success, -error on failure/timeout */ int drm_lspcon_set_mode(const struct drm_device *dev, struct i2c_adapter *adapter, enum drm_lspcon_mode mode) { … } EXPORT_SYMBOL(…);