18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sub license, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the 128c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 138c2ecf20Sopenharmony_ci * of the Software. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 218c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_scdc_helper.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * DOC: scdc helpers 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Status and Control Data Channel (SCDC) is a mechanism introduced by the 348c2ecf20Sopenharmony_ci * HDMI 2.0 specification. It is a point-to-point protocol that allows the 358c2ecf20Sopenharmony_ci * HDMI source and HDMI sink to exchange data. The same I2C interface that 368c2ecf20Sopenharmony_ci * is used to access EDID serves as the transport mechanism for SCDC. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define SCDC_I2C_SLAVE_ADDRESS 0x54 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/** 428c2ecf20Sopenharmony_ci * drm_scdc_read - read a block of data from SCDC 438c2ecf20Sopenharmony_ci * @adapter: I2C controller 448c2ecf20Sopenharmony_ci * @offset: start offset of block to read 458c2ecf20Sopenharmony_ci * @buffer: return location for the block to read 468c2ecf20Sopenharmony_ci * @size: size of the block to read 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Reads a block of data from SCDC, starting at a given offset. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Returns: 518c2ecf20Sopenharmony_ci * 0 on success, negative error code on failure. 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cissize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, 548c2ecf20Sopenharmony_ci size_t size) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int ret; 578c2ecf20Sopenharmony_ci struct i2c_msg msgs[2] = { 588c2ecf20Sopenharmony_ci { 598c2ecf20Sopenharmony_ci .addr = SCDC_I2C_SLAVE_ADDRESS, 608c2ecf20Sopenharmony_ci .flags = 0, 618c2ecf20Sopenharmony_ci .len = 1, 628c2ecf20Sopenharmony_ci .buf = &offset, 638c2ecf20Sopenharmony_ci }, { 648c2ecf20Sopenharmony_ci .addr = SCDC_I2C_SLAVE_ADDRESS, 658c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 668c2ecf20Sopenharmony_ci .len = size, 678c2ecf20Sopenharmony_ci .buf = buffer, 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci }; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); 728c2ecf20Sopenharmony_ci if (ret < 0) 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci if (ret != ARRAY_SIZE(msgs)) 758c2ecf20Sopenharmony_ci return -EPROTO; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_scdc_read); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * drm_scdc_write - write a block of data to SCDC 838c2ecf20Sopenharmony_ci * @adapter: I2C controller 848c2ecf20Sopenharmony_ci * @offset: start offset of block to write 858c2ecf20Sopenharmony_ci * @buffer: block of data to write 868c2ecf20Sopenharmony_ci * @size: size of the block to write 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Writes a block of data to SCDC, starting at a given offset. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * Returns: 918c2ecf20Sopenharmony_ci * 0 on success, negative error code on failure. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cissize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, 948c2ecf20Sopenharmony_ci const void *buffer, size_t size) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct i2c_msg msg = { 978c2ecf20Sopenharmony_ci .addr = SCDC_I2C_SLAVE_ADDRESS, 988c2ecf20Sopenharmony_ci .flags = 0, 998c2ecf20Sopenharmony_ci .len = 1 + size, 1008c2ecf20Sopenharmony_ci .buf = NULL, 1018c2ecf20Sopenharmony_ci }; 1028c2ecf20Sopenharmony_ci void *data; 1038c2ecf20Sopenharmony_ci int err; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci data = kmalloc(1 + size, GFP_KERNEL); 1068c2ecf20Sopenharmony_ci if (!data) 1078c2ecf20Sopenharmony_ci return -ENOMEM; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci msg.buf = data; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci memcpy(data, &offset, sizeof(offset)); 1128c2ecf20Sopenharmony_ci memcpy(data + 1, buffer, size); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci err = i2c_transfer(adapter, &msg, 1); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci kfree(data); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (err < 0) 1198c2ecf20Sopenharmony_ci return err; 1208c2ecf20Sopenharmony_ci if (err != 1) 1218c2ecf20Sopenharmony_ci return -EPROTO; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_scdc_write); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/** 1288c2ecf20Sopenharmony_ci * drm_scdc_check_scrambling_status - what is status of scrambling? 1298c2ecf20Sopenharmony_ci * @adapter: I2C adapter for DDC channel 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Reads the scrambler status over SCDC, and checks the 1328c2ecf20Sopenharmony_ci * scrambling status. 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * Returns: 1358c2ecf20Sopenharmony_ci * True if the scrambling is enabled, false otherwise. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cibool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci u8 status; 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status); 1438c2ecf20Sopenharmony_ci if (ret < 0) { 1448c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to read scrambling status: %d\n", ret); 1458c2ecf20Sopenharmony_ci return false; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return status & SCDC_SCRAMBLING_STATUS; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_scdc_get_scrambling_status); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * drm_scdc_set_scrambling - enable scrambling 1548c2ecf20Sopenharmony_ci * @adapter: I2C adapter for DDC channel 1558c2ecf20Sopenharmony_ci * @enable: bool to indicate if scrambling is to be enabled/disabled 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Writes the TMDS config register over SCDC channel, and: 1588c2ecf20Sopenharmony_ci * enables scrambling when enable = 1 1598c2ecf20Sopenharmony_ci * disables scrambling when enable = 0 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * Returns: 1628c2ecf20Sopenharmony_ci * True if scrambling is set/reset successfully, false otherwise. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_cibool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci u8 config; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); 1708c2ecf20Sopenharmony_ci if (ret < 0) { 1718c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret); 1728c2ecf20Sopenharmony_ci return false; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (enable) 1768c2ecf20Sopenharmony_ci config |= SCDC_SCRAMBLING_ENABLE; 1778c2ecf20Sopenharmony_ci else 1788c2ecf20Sopenharmony_ci config &= ~SCDC_SCRAMBLING_ENABLE; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); 1818c2ecf20Sopenharmony_ci if (ret < 0) { 1828c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to enable scrambling: %d\n", ret); 1838c2ecf20Sopenharmony_ci return false; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return true; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_scdc_set_scrambling); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio 1928c2ecf20Sopenharmony_ci * @adapter: I2C adapter for DDC channel 1938c2ecf20Sopenharmony_ci * @set: ret or reset the high clock ratio 1948c2ecf20Sopenharmony_ci * 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * TMDS clock ratio calculations go like this: 1978c2ecf20Sopenharmony_ci * TMDS character = 10 bit TMDS encoded value 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * TMDS character rate = The rate at which TMDS characters are 2008c2ecf20Sopenharmony_ci * transmitted (Mcsc) 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * TMDS bit rate = 10x TMDS character rate 2038c2ecf20Sopenharmony_ci * 2048c2ecf20Sopenharmony_ci * As per the spec: 2058c2ecf20Sopenharmony_ci * TMDS clock rate for pixel clock < 340 MHz = 1x the character 2068c2ecf20Sopenharmony_ci * rate = 1/10 pixel clock rate 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * TMDS clock rate for pixel clock > 340 MHz = 0.25x the character 2098c2ecf20Sopenharmony_ci * rate = 1/40 pixel clock rate 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Writes to the TMDS config register over SCDC channel, and: 2128c2ecf20Sopenharmony_ci * sets TMDS clock ratio to 1/40 when set = 1 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * sets TMDS clock ratio to 1/10 when set = 0 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * Returns: 2178c2ecf20Sopenharmony_ci * True if write is successful, false otherwise. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cibool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci u8 config; 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); 2258c2ecf20Sopenharmony_ci if (ret < 0) { 2268c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret); 2278c2ecf20Sopenharmony_ci return false; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (set) 2318c2ecf20Sopenharmony_ci config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; 2328c2ecf20Sopenharmony_ci else 2338c2ecf20Sopenharmony_ci config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); 2368c2ecf20Sopenharmony_ci if (ret < 0) { 2378c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to set TMDS clock ratio: %d\n", ret); 2388c2ecf20Sopenharmony_ci return false; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * The spec says that a source should wait minimum 1ms and maximum 2438c2ecf20Sopenharmony_ci * 100ms after writing the TMDS config for clock ratio. Lets allow a 2448c2ecf20Sopenharmony_ci * wait of upto 2ms here. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 2478c2ecf20Sopenharmony_ci return true; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); 250