xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_scdc_helper.c (revision a64b88a360d57611952e7966d37d58bada49ff56)
1*a64b88a3Sriastradh /*	$NetBSD: drm_scdc_helper.c,v 1.3 2021/12/19 01:15:07 riastradh Exp $	*/
24e390cabSriastradh 
34e390cabSriastradh /*
44e390cabSriastradh  * Copyright (c) 2015 NVIDIA Corporation. All rights reserved.
54e390cabSriastradh  *
64e390cabSriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
74e390cabSriastradh  * copy of this software and associated documentation files (the "Software"),
84e390cabSriastradh  * to deal in the Software without restriction, including without limitation
94e390cabSriastradh  * the rights to use, copy, modify, merge, publish, distribute, sub license,
104e390cabSriastradh  * and/or sell copies of the Software, and to permit persons to whom the
114e390cabSriastradh  * Software is furnished to do so, subject to the following conditions:
124e390cabSriastradh  *
134e390cabSriastradh  * The above copyright notice and this permission notice (including the
144e390cabSriastradh  * next paragraph) shall be included in all copies or substantial portions
154e390cabSriastradh  * of the Software.
164e390cabSriastradh  *
174e390cabSriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
184e390cabSriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
194e390cabSriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
204e390cabSriastradh  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
214e390cabSriastradh  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
224e390cabSriastradh  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
234e390cabSriastradh  * DEALINGS IN THE SOFTWARE.
244e390cabSriastradh  */
254e390cabSriastradh 
264e390cabSriastradh #include <sys/cdefs.h>
27*a64b88a3Sriastradh __KERNEL_RCSID(0, "$NetBSD: drm_scdc_helper.c,v 1.3 2021/12/19 01:15:07 riastradh Exp $");
284e390cabSriastradh 
294e390cabSriastradh #include <linux/slab.h>
304e390cabSriastradh #include <linux/delay.h>
314e390cabSriastradh 
324e390cabSriastradh #include <drm/drm_print.h>
334e390cabSriastradh #include <drm/drm_scdc_helper.h>
344e390cabSriastradh 
354e390cabSriastradh /**
364e390cabSriastradh  * DOC: scdc helpers
374e390cabSriastradh  *
384e390cabSriastradh  * Status and Control Data Channel (SCDC) is a mechanism introduced by the
394e390cabSriastradh  * HDMI 2.0 specification. It is a point-to-point protocol that allows the
404e390cabSriastradh  * HDMI source and HDMI sink to exchange data. The same I2C interface that
414e390cabSriastradh  * is used to access EDID serves as the transport mechanism for SCDC.
424e390cabSriastradh  */
434e390cabSriastradh 
444e390cabSriastradh #define SCDC_I2C_SLAVE_ADDRESS 0x54
454e390cabSriastradh 
464e390cabSriastradh /**
474e390cabSriastradh  * drm_scdc_read - read a block of data from SCDC
484e390cabSriastradh  * @adapter: I2C controller
494e390cabSriastradh  * @offset: start offset of block to read
504e390cabSriastradh  * @buffer: return location for the block to read
514e390cabSriastradh  * @size: size of the block to read
524e390cabSriastradh  *
534e390cabSriastradh  * Reads a block of data from SCDC, starting at a given offset.
544e390cabSriastradh  *
554e390cabSriastradh  * Returns:
564e390cabSriastradh  * 0 on success, negative error code on failure.
574e390cabSriastradh  */
drm_scdc_read(struct i2c_adapter * adapter,u8 offset,void * buffer,size_t size)584e390cabSriastradh ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer,
594e390cabSriastradh 		      size_t size)
604e390cabSriastradh {
614e390cabSriastradh 	int ret;
624e390cabSriastradh 	struct i2c_msg msgs[2] = {
634e390cabSriastradh 		{
644e390cabSriastradh 			.addr = SCDC_I2C_SLAVE_ADDRESS,
654e390cabSriastradh 			.flags = 0,
664e390cabSriastradh 			.len = 1,
674e390cabSriastradh 			.buf = &offset,
684e390cabSriastradh 		}, {
694e390cabSriastradh 			.addr = SCDC_I2C_SLAVE_ADDRESS,
704e390cabSriastradh 			.flags = I2C_M_RD,
714e390cabSriastradh 			.len = size,
724e390cabSriastradh 			.buf = buffer,
734e390cabSriastradh 		}
744e390cabSriastradh 	};
754e390cabSriastradh 
764e390cabSriastradh 	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
774e390cabSriastradh 	if (ret < 0)
784e390cabSriastradh 		return ret;
794e390cabSriastradh 	if (ret != ARRAY_SIZE(msgs))
804e390cabSriastradh 		return -EPROTO;
814e390cabSriastradh 
824e390cabSriastradh 	return 0;
834e390cabSriastradh }
844e390cabSriastradh EXPORT_SYMBOL(drm_scdc_read);
854e390cabSriastradh 
864e390cabSriastradh /**
874e390cabSriastradh  * drm_scdc_write - write a block of data to SCDC
884e390cabSriastradh  * @adapter: I2C controller
894e390cabSriastradh  * @offset: start offset of block to write
904e390cabSriastradh  * @buffer: block of data to write
914e390cabSriastradh  * @size: size of the block to write
924e390cabSriastradh  *
934e390cabSriastradh  * Writes a block of data to SCDC, starting at a given offset.
944e390cabSriastradh  *
954e390cabSriastradh  * Returns:
964e390cabSriastradh  * 0 on success, negative error code on failure.
974e390cabSriastradh  */
drm_scdc_write(struct i2c_adapter * adapter,u8 offset,const void * buffer,size_t size)984e390cabSriastradh ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset,
994e390cabSriastradh 		       const void *buffer, size_t size)
1004e390cabSriastradh {
1014e390cabSriastradh 	struct i2c_msg msg = {
1024e390cabSriastradh 		.addr = SCDC_I2C_SLAVE_ADDRESS,
1034e390cabSriastradh 		.flags = 0,
1044e390cabSriastradh 		.len = 1 + size,
1054e390cabSriastradh 		.buf = NULL,
1064e390cabSriastradh 	};
1074e390cabSriastradh 	void *data;
1084e390cabSriastradh 	int err;
1094e390cabSriastradh 
1104e390cabSriastradh 	data = kmalloc(1 + size, GFP_KERNEL);
1114e390cabSriastradh 	if (!data)
1124e390cabSriastradh 		return -ENOMEM;
1134e390cabSriastradh 
1144e390cabSriastradh 	msg.buf = data;
1154e390cabSriastradh 
1164e390cabSriastradh 	memcpy(data, &offset, sizeof(offset));
117*a64b88a3Sriastradh 	memcpy((char *)data + 1, buffer, size);
1184e390cabSriastradh 
1194e390cabSriastradh 	err = i2c_transfer(adapter, &msg, 1);
1204e390cabSriastradh 
1214e390cabSriastradh 	kfree(data);
1224e390cabSriastradh 
1234e390cabSriastradh 	if (err < 0)
1244e390cabSriastradh 		return err;
1254e390cabSriastradh 	if (err != 1)
1264e390cabSriastradh 		return -EPROTO;
1274e390cabSriastradh 
1284e390cabSriastradh 	return 0;
1294e390cabSriastradh }
1304e390cabSriastradh EXPORT_SYMBOL(drm_scdc_write);
1314e390cabSriastradh 
1324e390cabSriastradh /**
1334e390cabSriastradh  * drm_scdc_check_scrambling_status - what is status of scrambling?
1344e390cabSriastradh  * @adapter: I2C adapter for DDC channel
1354e390cabSriastradh  *
1364e390cabSriastradh  * Reads the scrambler status over SCDC, and checks the
1374e390cabSriastradh  * scrambling status.
1384e390cabSriastradh  *
1394e390cabSriastradh  * Returns:
1404e390cabSriastradh  * True if the scrambling is enabled, false otherwise.
1414e390cabSriastradh  */
drm_scdc_get_scrambling_status(struct i2c_adapter * adapter)1424e390cabSriastradh bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter)
1434e390cabSriastradh {
1444e390cabSriastradh 	u8 status;
1454e390cabSriastradh 	int ret;
1464e390cabSriastradh 
1474e390cabSriastradh 	ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status);
1484e390cabSriastradh 	if (ret < 0) {
1494e390cabSriastradh 		DRM_DEBUG_KMS("Failed to read scrambling status: %d\n", ret);
1504e390cabSriastradh 		return false;
1514e390cabSriastradh 	}
1524e390cabSriastradh 
1534e390cabSriastradh 	return status & SCDC_SCRAMBLING_STATUS;
1544e390cabSriastradh }
1554e390cabSriastradh EXPORT_SYMBOL(drm_scdc_get_scrambling_status);
1564e390cabSriastradh 
1574e390cabSriastradh /**
1584e390cabSriastradh  * drm_scdc_set_scrambling - enable scrambling
1594e390cabSriastradh  * @adapter: I2C adapter for DDC channel
1604e390cabSriastradh  * @enable: bool to indicate if scrambling is to be enabled/disabled
1614e390cabSriastradh  *
1624e390cabSriastradh  * Writes the TMDS config register over SCDC channel, and:
1634e390cabSriastradh  * enables scrambling when enable = 1
1644e390cabSriastradh  * disables scrambling when enable = 0
1654e390cabSriastradh  *
1664e390cabSriastradh  * Returns:
1674e390cabSriastradh  * True if scrambling is set/reset successfully, false otherwise.
1684e390cabSriastradh  */
drm_scdc_set_scrambling(struct i2c_adapter * adapter,bool enable)1694e390cabSriastradh bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable)
1704e390cabSriastradh {
1714e390cabSriastradh 	u8 config;
1724e390cabSriastradh 	int ret;
1734e390cabSriastradh 
1744e390cabSriastradh 	ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
1754e390cabSriastradh 	if (ret < 0) {
1764e390cabSriastradh 		DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
1774e390cabSriastradh 		return false;
1784e390cabSriastradh 	}
1794e390cabSriastradh 
1804e390cabSriastradh 	if (enable)
1814e390cabSriastradh 		config |= SCDC_SCRAMBLING_ENABLE;
1824e390cabSriastradh 	else
1834e390cabSriastradh 		config &= ~SCDC_SCRAMBLING_ENABLE;
1844e390cabSriastradh 
1854e390cabSriastradh 	ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
1864e390cabSriastradh 	if (ret < 0) {
1874e390cabSriastradh 		DRM_DEBUG_KMS("Failed to enable scrambling: %d\n", ret);
1884e390cabSriastradh 		return false;
1894e390cabSriastradh 	}
1904e390cabSriastradh 
1914e390cabSriastradh 	return true;
1924e390cabSriastradh }
1934e390cabSriastradh EXPORT_SYMBOL(drm_scdc_set_scrambling);
1944e390cabSriastradh 
1954e390cabSriastradh /**
1964e390cabSriastradh  * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio
1974e390cabSriastradh  * @adapter: I2C adapter for DDC channel
1984e390cabSriastradh  * @set: ret or reset the high clock ratio
1994e390cabSriastradh  *
2004e390cabSriastradh  *
2014e390cabSriastradh  *	TMDS clock ratio calculations go like this:
2024e390cabSriastradh  *		TMDS character = 10 bit TMDS encoded value
2034e390cabSriastradh  *
2044e390cabSriastradh  *		TMDS character rate = The rate at which TMDS characters are
2054e390cabSriastradh  *		transmitted (Mcsc)
2064e390cabSriastradh  *
2074e390cabSriastradh  *		TMDS bit rate = 10x TMDS character rate
2084e390cabSriastradh  *
2094e390cabSriastradh  *	As per the spec:
2104e390cabSriastradh  *		TMDS clock rate for pixel clock < 340 MHz = 1x the character
2114e390cabSriastradh  *		rate = 1/10 pixel clock rate
2124e390cabSriastradh  *
2134e390cabSriastradh  *		TMDS clock rate for pixel clock > 340 MHz = 0.25x the character
2144e390cabSriastradh  *		rate = 1/40 pixel clock rate
2154e390cabSriastradh  *
2164e390cabSriastradh  *	Writes to the TMDS config register over SCDC channel, and:
2174e390cabSriastradh  *		sets TMDS clock ratio to 1/40 when set = 1
2184e390cabSriastradh  *
2194e390cabSriastradh  *		sets TMDS clock ratio to 1/10 when set = 0
2204e390cabSriastradh  *
2214e390cabSriastradh  * Returns:
2224e390cabSriastradh  * True if write is successful, false otherwise.
2234e390cabSriastradh  */
drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter * adapter,bool set)2244e390cabSriastradh bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set)
2254e390cabSriastradh {
2264e390cabSriastradh 	u8 config;
2274e390cabSriastradh 	int ret;
2284e390cabSriastradh 
2294e390cabSriastradh 	ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config);
2304e390cabSriastradh 	if (ret < 0) {
2314e390cabSriastradh 		DRM_DEBUG_KMS("Failed to read TMDS config: %d\n", ret);
2324e390cabSriastradh 		return false;
2334e390cabSriastradh 	}
2344e390cabSriastradh 
2354e390cabSriastradh 	if (set)
2364e390cabSriastradh 		config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
2374e390cabSriastradh 	else
2384e390cabSriastradh 		config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40;
2394e390cabSriastradh 
2404e390cabSriastradh 	ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config);
2414e390cabSriastradh 	if (ret < 0) {
2424e390cabSriastradh 		DRM_DEBUG_KMS("Failed to set TMDS clock ratio: %d\n", ret);
2434e390cabSriastradh 		return false;
2444e390cabSriastradh 	}
2454e390cabSriastradh 
2464e390cabSriastradh 	/*
2474e390cabSriastradh 	 * The spec says that a source should wait minimum 1ms and maximum
2484e390cabSriastradh 	 * 100ms after writing the TMDS config for clock ratio. Lets allow a
2494e390cabSriastradh 	 * wait of upto 2ms here.
2504e390cabSriastradh 	 */
2514e390cabSriastradh 	usleep_range(1000, 2000);
2524e390cabSriastradh 	return true;
2534e390cabSriastradh }
2544e390cabSriastradh EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio);
255