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