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