Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2015 NVIDIA Corporation. All rights reserved.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sub license,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice (including the
0012  * next paragraph) shall be included in all copies or substantial portions
0013  * of the Software.
0014  *
0015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
0018  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0020  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
0021  * DEALINGS IN THE SOFTWARE.
0022  */
0023 
0024 #include <linux/i2c.h>
0025 #include <linux/slab.h>
0026 #include <linux/delay.h>
0027 
0028 #include <drm/display/drm_scdc_helper.h>
0029 #include <drm/drm_print.h>
0030 
0031 /**
0032  * DOC: scdc helpers
0033  *
0034  * Status and Control Data Channel (SCDC) is a mechanism introduced by the
0035  * HDMI 2.0 specification. It is a point-to-point protocol that allows the
0036  * HDMI source and HDMI sink to exchange data. The same I2C interface that
0037  * is used to access EDID serves as the transport mechanism for SCDC.
0038  */
0039 
0040 #define SCDC_I2C_SLAVE_ADDRESS 0x54
0041 
0042 /**
0043  * drm_scdc_read - read a block of data from SCDC
0044  * @adapter: I2C controller
0045  * @offset: start offset of block to read
0046  * @buffer: return location for the block to read
0047  * @size: size of the block to read
0048  *
0049  * Reads a block of data from SCDC, starting at a given offset.
0050  *
0051  * Returns:
0052  * 0 on success, negative error code on failure.
0053  */
0054 ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
0055               size_t size)
0056 {
0057     int ret;
0058     struct i2c_msg msgs[2] = {
0059         {
0060             .addr = SCDC_I2C_SLAVE_ADDRESS,
0061             .flags = 0,
0062             .len = 1,
0063             .buf = &offset,
0064         }, {
0065             .addr = SCDC_I2C_SLAVE_ADDRESS,
0066             .flags = I2C_M_RD,
0067             .len = size,
0068             .buf = buffer,
0069         }
0070     };
0071 
0072     ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
0073     if (ret < 0)
0074         return ret;
0075     if (ret != ARRAY_SIZE(msgs))
0076         return -EPROTO;
0077 
0078     return 0;
0079 }
0080 EXPORT_SYMBOL(drm_scdc_read);
0081 
0082 /**
0083  * drm_scdc_write - write a block of data to SCDC
0084  * @adapter: I2C controller
0085  * @offset: start offset of block to write
0086  * @buffer: block of data to write
0087  * @size: size of the block to write
0088  *
0089  * Writes a block of data to SCDC, starting at a given offset.
0090  *
0091  * Returns:
0092  * 0 on success, negative error code on failure.
0093  */
0094 ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
0095                const void *buffer, size_t size)
0096 {
0097     struct i2c_msg msg = {
0098         .addr = SCDC_I2C_SLAVE_ADDRESS,
0099         .flags = 0,
0100         .len = 1 + size,
0101         .buf = NULL,
0102     };
0103     void *data;
0104     int err;
0105 
0106     data = kmalloc(1 + size, GFP_KERNEL);
0107     if (!data)
0108         return -ENOMEM;
0109 
0110     msg.buf = data;
0111 
0112     memcpy(data, &offset, sizeof(offset));
0113     memcpy(data + 1, buffer, size);
0114 
0115     err = i2c_transfer(adapter, &msg, 1);
0116 
0117     kfree(data);
0118 
0119     if (err < 0)
0120         return err;
0121     if (err != 1)
0122         return -EPROTO;
0123 
0124     return 0;
0125 }
0126 EXPORT_SYMBOL(drm_scdc_write);
0127 
0128 /**
0129  * drm_scdc_get_scrambling_status - what is status of scrambling?
0130  * @adapter: I2C adapter for DDC channel
0131  *
0132  * Reads the scrambler status over SCDC, and checks the
0133  * scrambling status.
0134  *
0135  * Returns:
0136  * True if the scrambling is enabled, false otherwise.
0137  */
0138 bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
0139 {
0140     u8 status;
0141     int ret;
0142 
0143     ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
0144     if (ret < 0) {
0145         DRM_DEBUG_KMS("Failed to read scrambling status: %d\n", ret);
0146         return false;
0147     }
0148 
0149     return status & SCDC_SCRAMBLING_STATUS;
0150 }
0151 EXPORT_SYMBOL(drm_scdc_get_scrambling_status);
0152 
0153 /**
0154  * drm_scdc_set_scrambling - enable scrambling
0155  * @adapter: I2C adapter for DDC channel
0156  * @enable: bool to indicate if scrambling is to be enabled/disabled
0157  *
0158  * Writes the TMDS config register over SCDC channel, and:
0159  * enables scrambling when enable = 1
0160  * disables scrambling when enable = 0
0161  *
0162  * Returns:
0163  * True if scrambling is set/reset successfully, false otherwise.
0164  */
0165 bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
0166 {
0167     u8 config;
0168     int ret;
0169 
0170     ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
0171     if (ret < 0) {
0172         DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
0173         return false;
0174     }
0175 
0176     if (enable)
0177         config |= SCDC_SCRAMBLING_ENABLE;
0178     else
0179         config &= ~SCDC_SCRAMBLING_ENABLE;
0180 
0181     ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
0182     if (ret < 0) {
0183         DRM_DEBUG_KMS("Failed to enable scrambling: %d\n", ret);
0184         return false;
0185     }
0186 
0187     return true;
0188 }
0189 EXPORT_SYMBOL(drm_scdc_set_scrambling);
0190 
0191 /**
0192  * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio
0193  * @adapter: I2C adapter for DDC channel
0194  * @set: ret or reset the high clock ratio
0195  *
0196  *
0197  *  TMDS clock ratio calculations go like this:
0198  *      TMDS character = 10 bit TMDS encoded value
0199  *
0200  *      TMDS character rate = The rate at which TMDS characters are
0201  *      transmitted (Mcsc)
0202  *
0203  *      TMDS bit rate = 10x TMDS character rate
0204  *
0205  *  As per the spec:
0206  *      TMDS clock rate for pixel clock < 340 MHz = 1x the character
0207  *      rate = 1/10 pixel clock rate
0208  *
0209  *      TMDS clock rate for pixel clock > 340 MHz = 0.25x the character
0210  *      rate = 1/40 pixel clock rate
0211  *
0212  *  Writes to the TMDS config register over SCDC channel, and:
0213  *      sets TMDS clock ratio to 1/40 when set = 1
0214  *
0215  *      sets TMDS clock ratio to 1/10 when set = 0
0216  *
0217  * Returns:
0218  * True if write is successful, false otherwise.
0219  */
0220 bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
0221 {
0222     u8 config;
0223     int ret;
0224 
0225     ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
0226     if (ret < 0) {
0227         DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
0228         return false;
0229     }
0230 
0231     if (set)
0232         config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
0233     else
0234         config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
0235 
0236     ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
0237     if (ret < 0) {
0238         DRM_DEBUG_KMS("Failed to set TMDS clock ratio: %d\n", ret);
0239         return false;
0240     }
0241 
0242     /*
0243      * The spec says that a source should wait minimum 1ms and maximum
0244      * 100ms after writing the TMDS config for clock ratio. Lets allow a
0245      * wait of up to 2ms here.
0246      */
0247     usleep_range(1000, 2000);
0248     return true;
0249 }
0250 EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);