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