Path: blob/master/drivers/gpu/drm/display/drm_scdc_helper.c
26493 views
/*1* Copyright (c) 2015 NVIDIA Corporation. All rights reserved.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sub license,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the11* next paragraph) shall be included in all copies or substantial portions12* of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER20* DEALINGS IN THE SOFTWARE.21*/2223#include <linux/export.h>24#include <linux/i2c.h>25#include <linux/slab.h>26#include <linux/delay.h>2728#include <drm/display/drm_scdc_helper.h>29#include <drm/drm_connector.h>30#include <drm/drm_device.h>31#include <drm/drm_print.h>3233/**34* DOC: scdc helpers35*36* Status and Control Data Channel (SCDC) is a mechanism introduced by the37* HDMI 2.0 specification. It is a point-to-point protocol that allows the38* HDMI source and HDMI sink to exchange data. The same I2C interface that39* is used to access EDID serves as the transport mechanism for SCDC.40*41* Note: The SCDC status is going to be lost when the display is42* disconnected. This can happen physically when the user disconnects43* the cable, but also when a display is switched on (such as waking up44* a TV).45*46* This is further complicated by the fact that, upon a disconnection /47* reconnection, KMS won't change the mode on its own. This means that48* one can't just rely on setting the SCDC status on enable, but also49* has to track the connector status changes using interrupts and50* restore the SCDC status. The typical solution for this is to trigger an51* empty modeset in drm_connector_helper_funcs.detect_ctx(), like what vc4 does52* in vc4_hdmi_reset_link().53*/5455#define SCDC_I2C_SLAVE_ADDRESS 0x545657/**58* drm_scdc_read - read a block of data from SCDC59* @adapter: I2C controller60* @offset: start offset of block to read61* @buffer: return location for the block to read62* @size: size of the block to read63*64* Reads a block of data from SCDC, starting at a given offset.65*66* Returns:67* 0 on success, negative error code on failure.68*/69ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,70size_t size)71{72int ret;73struct i2c_msg msgs[2] = {74{75.addr = SCDC_I2C_SLAVE_ADDRESS,76.flags = 0,77.len = 1,78.buf = &offset,79}, {80.addr = SCDC_I2C_SLAVE_ADDRESS,81.flags = I2C_M_RD,82.len = size,83.buf = buffer,84}85};8687ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));88if (ret < 0)89return ret;90if (ret != ARRAY_SIZE(msgs))91return -EPROTO;9293return 0;94}95EXPORT_SYMBOL(drm_scdc_read);9697/**98* drm_scdc_write - write a block of data to SCDC99* @adapter: I2C controller100* @offset: start offset of block to write101* @buffer: block of data to write102* @size: size of the block to write103*104* Writes a block of data to SCDC, starting at a given offset.105*106* Returns:107* 0 on success, negative error code on failure.108*/109ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,110const void *buffer, size_t size)111{112struct i2c_msg msg = {113.addr = SCDC_I2C_SLAVE_ADDRESS,114.flags = 0,115.len = 1 + size,116.buf = NULL,117};118void *data;119int err;120121data = kmalloc(1 + size, GFP_KERNEL);122if (!data)123return -ENOMEM;124125msg.buf = data;126127memcpy(data, &offset, sizeof(offset));128memcpy(data + 1, buffer, size);129130err = i2c_transfer(adapter, &msg, 1);131132kfree(data);133134if (err < 0)135return err;136if (err != 1)137return -EPROTO;138139return 0;140}141EXPORT_SYMBOL(drm_scdc_write);142143/**144* drm_scdc_get_scrambling_status - what is status of scrambling?145* @connector: connector146*147* Reads the scrambler status over SCDC, and checks the148* scrambling status.149*150* Returns:151* True if the scrambling is enabled, false otherwise.152*/153bool drm_scdc_get_scrambling_status(struct drm_connector *connector)154{155u8 status;156int ret;157158ret = drm_scdc_readb(connector->ddc, SCDC_SCRAMBLER_STATUS, &status);159if (ret < 0) {160drm_dbg_kms(connector->dev,161"[CONNECTOR:%d:%s] Failed to read scrambling status: %d\n",162connector->base.id, connector->name, ret);163return false;164}165166return status & SCDC_SCRAMBLING_STATUS;167}168EXPORT_SYMBOL(drm_scdc_get_scrambling_status);169170/**171* drm_scdc_set_scrambling - enable scrambling172* @connector: connector173* @enable: bool to indicate if scrambling is to be enabled/disabled174*175* Writes the TMDS config register over SCDC channel, and:176* enables scrambling when enable = 1177* disables scrambling when enable = 0178*179* Returns:180* True if scrambling is set/reset successfully, false otherwise.181*/182bool drm_scdc_set_scrambling(struct drm_connector *connector,183bool enable)184{185u8 config;186int ret;187188ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config);189if (ret < 0) {190drm_dbg_kms(connector->dev,191"[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n",192connector->base.id, connector->name, ret);193return false;194}195196if (enable)197config |= SCDC_SCRAMBLING_ENABLE;198else199config &= ~SCDC_SCRAMBLING_ENABLE;200201ret = drm_scdc_writeb(connector->ddc, SCDC_TMDS_CONFIG, config);202if (ret < 0) {203drm_dbg_kms(connector->dev,204"[CONNECTOR:%d:%s] Failed to enable scrambling: %d\n",205connector->base.id, connector->name, ret);206return false;207}208209return true;210}211EXPORT_SYMBOL(drm_scdc_set_scrambling);212213/**214* drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio215* @connector: connector216* @set: ret or reset the high clock ratio217*218*219* TMDS clock ratio calculations go like this:220* TMDS character = 10 bit TMDS encoded value221*222* TMDS character rate = The rate at which TMDS characters are223* transmitted (Mcsc)224*225* TMDS bit rate = 10x TMDS character rate226*227* As per the spec:228* TMDS clock rate for pixel clock < 340 MHz = 1x the character229* rate = 1/10 pixel clock rate230*231* TMDS clock rate for pixel clock > 340 MHz = 0.25x the character232* rate = 1/40 pixel clock rate233*234* Writes to the TMDS config register over SCDC channel, and:235* sets TMDS clock ratio to 1/40 when set = 1236*237* sets TMDS clock ratio to 1/10 when set = 0238*239* Returns:240* True if write is successful, false otherwise.241*/242bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector,243bool set)244{245u8 config;246int ret;247248ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config);249if (ret < 0) {250drm_dbg_kms(connector->dev,251"[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n",252connector->base.id, connector->name, ret);253return false;254}255256if (set)257config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;258else259config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;260261ret = drm_scdc_writeb(connector->ddc, SCDC_TMDS_CONFIG, config);262if (ret < 0) {263drm_dbg_kms(connector->dev,264"[CONNECTOR:%d:%s] Failed to set TMDS clock ratio: %d\n",265connector->base.id, connector->name, ret);266return false;267}268269/*270* The spec says that a source should wait minimum 1ms and maximum271* 100ms after writing the TMDS config for clock ratio. Lets allow a272* wait of up to 2ms here.273*/274usleep_range(1000, 2000);275return true;276}277EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);278279280