linux/drivers/gpu/drm/drm_scdc_helper.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2015 NVIDIA Corporation. All rights reserved.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice (including the
  12 * next paragraph) shall be included in all copies or substantial portions
  13 * of the Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21 * DEALINGS IN THE SOFTWARE.
  22 */
  23
  24#include <linux/slab.h>
  25#include <linux/delay.h>
  26
  27#include <drm/drm_scdc_helper.h>
  28#include <drm/drmP.h>
  29
  30/**
  31 * DOC: scdc helpers
  32 *
  33 * Status and Control Data Channel (SCDC) is a mechanism introduced by the
  34 * HDMI 2.0 specification. It is a point-to-point protocol that allows the
  35 * HDMI source and HDMI sink to exchange data. The same I2C interface that
  36 * is used to access EDID serves as the transport mechanism for SCDC.
  37 */
  38
  39#define SCDC_I2C_SLAVE_ADDRESS 0x54
  40
  41/**
  42 * drm_scdc_read - read a block of data from SCDC
  43 * @adapter: I2C controller
  44 * @offset: start offset of block to read
  45 * @buffer: return location for the block to read
  46 * @size: size of the block to read
  47 *
  48 * Reads a block of data from SCDC, starting at a given offset.
  49 *
  50 * Returns:
  51 * 0 on success, negative error code on failure.
  52 */
  53ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
  54                      size_t size)
  55{
  56        int ret;
  57        struct i2c_msg msgs[2] = {
  58                {
  59                        .addr = SCDC_I2C_SLAVE_ADDRESS,
  60                        .flags = 0,
  61                        .len = 1,
  62                        .buf = &offset,
  63                }, {
  64                        .addr = SCDC_I2C_SLAVE_ADDRESS,
  65                        .flags = I2C_M_RD,
  66                        .len = size,
  67                        .buf = buffer,
  68                }
  69        };
  70
  71        ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
  72        if (ret < 0)
  73                return ret;
  74        if (ret != ARRAY_SIZE(msgs))
  75                return -EPROTO;
  76
  77        return 0;
  78}
  79EXPORT_SYMBOL(drm_scdc_read);
  80
  81/**
  82 * drm_scdc_write - write a block of data to SCDC
  83 * @adapter: I2C controller
  84 * @offset: start offset of block to write
  85 * @buffer: block of data to write
  86 * @size: size of the block to write
  87 *
  88 * Writes a block of data to SCDC, starting at a given offset.
  89 *
  90 * Returns:
  91 * 0 on success, negative error code on failure.
  92 */
  93ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
  94                       const void *buffer, size_t size)
  95{
  96        struct i2c_msg msg = {
  97                .addr = SCDC_I2C_SLAVE_ADDRESS,
  98                .flags = 0,
  99                .len = 1 + size,
 100                .buf = NULL,
 101        };
 102        void *data;
 103        int err;
 104
 105        data = kmalloc(1 + size, GFP_KERNEL);
 106        if (!data)
 107                return -ENOMEM;
 108
 109        msg.buf = data;
 110
 111        memcpy(data, &offset, sizeof(offset));
 112        memcpy(data + 1, buffer, size);
 113
 114        err = i2c_transfer(adapter, &msg, 1);
 115
 116        kfree(data);
 117
 118        if (err < 0)
 119                return err;
 120        if (err != 1)
 121                return -EPROTO;
 122
 123        return 0;
 124}
 125EXPORT_SYMBOL(drm_scdc_write);
 126
 127/**
 128 * drm_scdc_check_scrambling_status - what is status of scrambling?
 129 * @adapter: I2C adapter for DDC channel
 130 *
 131 * Reads the scrambler status over SCDC, and checks the
 132 * scrambling status.
 133 *
 134 * Returns:
 135 * True if the scrambling is enabled, false otherwise.
 136 */
 137bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
 138{
 139        u8 status;
 140        int ret;
 141
 142        ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
 143        if (ret < 0) {
 144                DRM_DEBUG_KMS("Failed to read scrambling status: %d\n", ret);
 145                return false;
 146        }
 147
 148        return status & SCDC_SCRAMBLING_STATUS;
 149}
 150EXPORT_SYMBOL(drm_scdc_get_scrambling_status);
 151
 152/**
 153 * drm_scdc_set_scrambling - enable scrambling
 154 * @adapter: I2C adapter for DDC channel
 155 * @enable: bool to indicate if scrambling is to be enabled/disabled
 156 *
 157 * Writes the TMDS config register over SCDC channel, and:
 158 * enables scrambling when enable = 1
 159 * disables scrambling when enable = 0
 160 *
 161 * Returns:
 162 * True if scrambling is set/reset successfully, false otherwise.
 163 */
 164bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
 165{
 166        u8 config;
 167        int ret;
 168
 169        ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
 170        if (ret < 0) {
 171                DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
 172                return false;
 173        }
 174
 175        if (enable)
 176                config |= SCDC_SCRAMBLING_ENABLE;
 177        else
 178                config &= ~SCDC_SCRAMBLING_ENABLE;
 179
 180        ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
 181        if (ret < 0) {
 182                DRM_DEBUG_KMS("Failed to enable scrambling: %d\n", ret);
 183                return false;
 184        }
 185
 186        return true;
 187}
 188EXPORT_SYMBOL(drm_scdc_set_scrambling);
 189
 190/**
 191 * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio
 192 * @adapter: I2C adapter for DDC channel
 193 * @set: ret or reset the high clock ratio
 194 *
 195 *
 196 *      TMDS clock ratio calculations go like this:
 197 *              TMDS character = 10 bit TMDS encoded value
 198 *
 199 *              TMDS character rate = The rate at which TMDS characters are
 200 *              transmitted (Mcsc)
 201 *
 202 *              TMDS bit rate = 10x TMDS character rate
 203 *
 204 *      As per the spec:
 205 *              TMDS clock rate for pixel clock < 340 MHz = 1x the character
 206 *              rate = 1/10 pixel clock rate
 207 *
 208 *              TMDS clock rate for pixel clock > 340 MHz = 0.25x the character
 209 *              rate = 1/40 pixel clock rate
 210 *
 211 *      Writes to the TMDS config register over SCDC channel, and:
 212 *              sets TMDS clock ratio to 1/40 when set = 1
 213 *
 214 *              sets TMDS clock ratio to 1/10 when set = 0
 215 *
 216 * Returns:
 217 * True if write is successful, false otherwise.
 218 */
 219bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
 220{
 221        u8 config;
 222        int ret;
 223
 224        ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
 225        if (ret < 0) {
 226                DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
 227                return false;
 228        }
 229
 230        if (set)
 231                config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
 232        else
 233                config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
 234
 235        ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
 236        if (ret < 0) {
 237                DRM_DEBUG_KMS("Failed to set TMDS clock ratio: %d\n", ret);
 238                return false;
 239        }
 240
 241        /*
 242         * The spec says that a source should wait minimum 1ms and maximum
 243         * 100ms after writing the TMDS config for clock ratio. Lets allow a
 244         * wait of upto 2ms here.
 245         */
 246        usleep_range(1000, 2000);
 247        return true;
 248}
 249EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);
 250