Home | History | Annotate | Line # | Download | only in drm
      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