1d38ceaf9SAlex Deucher /*
2d38ceaf9SAlex Deucher * Copyright 2007-8 Advanced Micro Devices, Inc.
3d38ceaf9SAlex Deucher * Copyright 2008 Red Hat Inc.
4d38ceaf9SAlex Deucher *
5d38ceaf9SAlex Deucher * Permission is hereby granted, free of charge, to any person obtaining a
6d38ceaf9SAlex Deucher * copy of this software and associated documentation files (the "Software"),
7d38ceaf9SAlex Deucher * to deal in the Software without restriction, including without limitation
8d38ceaf9SAlex Deucher * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9d38ceaf9SAlex Deucher * and/or sell copies of the Software, and to permit persons to whom the
10d38ceaf9SAlex Deucher * Software is furnished to do so, subject to the following conditions:
11d38ceaf9SAlex Deucher *
12d38ceaf9SAlex Deucher * The above copyright notice and this permission notice shall be included in
13d38ceaf9SAlex Deucher * all copies or substantial portions of the Software.
14d38ceaf9SAlex Deucher *
15d38ceaf9SAlex Deucher * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d38ceaf9SAlex Deucher * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d38ceaf9SAlex Deucher * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18d38ceaf9SAlex Deucher * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19d38ceaf9SAlex Deucher * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20d38ceaf9SAlex Deucher * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21d38ceaf9SAlex Deucher * OTHER DEALINGS IN THE SOFTWARE.
22d38ceaf9SAlex Deucher *
23d38ceaf9SAlex Deucher * Authors: Dave Airlie
24d38ceaf9SAlex Deucher * Alex Deucher
25d38ceaf9SAlex Deucher */
26d38ceaf9SAlex Deucher
27fdf2f6c5SSam Ravnborg #include <linux/export.h>
28fdf2f6c5SSam Ravnborg #include <linux/pci.h>
29fdf2f6c5SSam Ravnborg
30d38ceaf9SAlex Deucher #include <drm/drm_edid.h>
31d38ceaf9SAlex Deucher #include <drm/amdgpu_drm.h>
32d38ceaf9SAlex Deucher #include "amdgpu.h"
33d38ceaf9SAlex Deucher #include "amdgpu_i2c.h"
34d38ceaf9SAlex Deucher #include "amdgpu_atombios.h"
35d38ceaf9SAlex Deucher #include "atom.h"
36d38ceaf9SAlex Deucher #include "atombios_dp.h"
37d38ceaf9SAlex Deucher #include "atombios_i2c.h"
38d38ceaf9SAlex Deucher
39d38ceaf9SAlex Deucher /* bit banging i2c */
amdgpu_i2c_pre_xfer(struct i2c_adapter * i2c_adap)40d38ceaf9SAlex Deucher static int amdgpu_i2c_pre_xfer(struct i2c_adapter *i2c_adap)
41d38ceaf9SAlex Deucher {
42d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
431348969aSLuben Tuikov struct amdgpu_device *adev = drm_to_adev(i2c->dev);
44d38ceaf9SAlex Deucher struct amdgpu_i2c_bus_rec *rec = &i2c->rec;
45d38ceaf9SAlex Deucher uint32_t temp;
46d38ceaf9SAlex Deucher
47d38ceaf9SAlex Deucher mutex_lock(&i2c->mutex);
48d38ceaf9SAlex Deucher
49d38ceaf9SAlex Deucher /* switch the pads to ddc mode */
50d38ceaf9SAlex Deucher if (rec->hw_capable) {
51d38ceaf9SAlex Deucher temp = RREG32(rec->mask_clk_reg);
52d38ceaf9SAlex Deucher temp &= ~(1 << 16);
53d38ceaf9SAlex Deucher WREG32(rec->mask_clk_reg, temp);
54d38ceaf9SAlex Deucher }
55d38ceaf9SAlex Deucher
56d38ceaf9SAlex Deucher /* clear the output pin values */
57d38ceaf9SAlex Deucher temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask;
58d38ceaf9SAlex Deucher WREG32(rec->a_clk_reg, temp);
59d38ceaf9SAlex Deucher
60d38ceaf9SAlex Deucher temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask;
61d38ceaf9SAlex Deucher WREG32(rec->a_data_reg, temp);
62d38ceaf9SAlex Deucher
63d38ceaf9SAlex Deucher /* set the pins to input */
64d38ceaf9SAlex Deucher temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
65d38ceaf9SAlex Deucher WREG32(rec->en_clk_reg, temp);
66d38ceaf9SAlex Deucher
67d38ceaf9SAlex Deucher temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
68d38ceaf9SAlex Deucher WREG32(rec->en_data_reg, temp);
69d38ceaf9SAlex Deucher
70d38ceaf9SAlex Deucher /* mask the gpio pins for software use */
71d38ceaf9SAlex Deucher temp = RREG32(rec->mask_clk_reg) | rec->mask_clk_mask;
72d38ceaf9SAlex Deucher WREG32(rec->mask_clk_reg, temp);
73d38ceaf9SAlex Deucher temp = RREG32(rec->mask_clk_reg);
74d38ceaf9SAlex Deucher
75d38ceaf9SAlex Deucher temp = RREG32(rec->mask_data_reg) | rec->mask_data_mask;
76d38ceaf9SAlex Deucher WREG32(rec->mask_data_reg, temp);
77d38ceaf9SAlex Deucher temp = RREG32(rec->mask_data_reg);
78d38ceaf9SAlex Deucher
79d38ceaf9SAlex Deucher return 0;
80d38ceaf9SAlex Deucher }
81d38ceaf9SAlex Deucher
amdgpu_i2c_post_xfer(struct i2c_adapter * i2c_adap)82d38ceaf9SAlex Deucher static void amdgpu_i2c_post_xfer(struct i2c_adapter *i2c_adap)
83d38ceaf9SAlex Deucher {
84d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
851348969aSLuben Tuikov struct amdgpu_device *adev = drm_to_adev(i2c->dev);
86d38ceaf9SAlex Deucher struct amdgpu_i2c_bus_rec *rec = &i2c->rec;
87d38ceaf9SAlex Deucher uint32_t temp;
88d38ceaf9SAlex Deucher
89d38ceaf9SAlex Deucher /* unmask the gpio pins for software use */
90d38ceaf9SAlex Deucher temp = RREG32(rec->mask_clk_reg) & ~rec->mask_clk_mask;
91d38ceaf9SAlex Deucher WREG32(rec->mask_clk_reg, temp);
92d38ceaf9SAlex Deucher temp = RREG32(rec->mask_clk_reg);
93d38ceaf9SAlex Deucher
94d38ceaf9SAlex Deucher temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask;
95d38ceaf9SAlex Deucher WREG32(rec->mask_data_reg, temp);
96d38ceaf9SAlex Deucher temp = RREG32(rec->mask_data_reg);
97d38ceaf9SAlex Deucher
98d38ceaf9SAlex Deucher mutex_unlock(&i2c->mutex);
99d38ceaf9SAlex Deucher }
100d38ceaf9SAlex Deucher
amdgpu_i2c_get_clock(void * i2c_priv)101d38ceaf9SAlex Deucher static int amdgpu_i2c_get_clock(void *i2c_priv)
102d38ceaf9SAlex Deucher {
103d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c = i2c_priv;
1041348969aSLuben Tuikov struct amdgpu_device *adev = drm_to_adev(i2c->dev);
105d38ceaf9SAlex Deucher struct amdgpu_i2c_bus_rec *rec = &i2c->rec;
106d38ceaf9SAlex Deucher uint32_t val;
107d38ceaf9SAlex Deucher
108d38ceaf9SAlex Deucher /* read the value off the pin */
109d38ceaf9SAlex Deucher val = RREG32(rec->y_clk_reg);
110d38ceaf9SAlex Deucher val &= rec->y_clk_mask;
111d38ceaf9SAlex Deucher
112d38ceaf9SAlex Deucher return (val != 0);
113d38ceaf9SAlex Deucher }
114d38ceaf9SAlex Deucher
115d38ceaf9SAlex Deucher
amdgpu_i2c_get_data(void * i2c_priv)116d38ceaf9SAlex Deucher static int amdgpu_i2c_get_data(void *i2c_priv)
117d38ceaf9SAlex Deucher {
118d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c = i2c_priv;
1191348969aSLuben Tuikov struct amdgpu_device *adev = drm_to_adev(i2c->dev);
120d38ceaf9SAlex Deucher struct amdgpu_i2c_bus_rec *rec = &i2c->rec;
121d38ceaf9SAlex Deucher uint32_t val;
122d38ceaf9SAlex Deucher
123d38ceaf9SAlex Deucher /* read the value off the pin */
124d38ceaf9SAlex Deucher val = RREG32(rec->y_data_reg);
125d38ceaf9SAlex Deucher val &= rec->y_data_mask;
126d38ceaf9SAlex Deucher
127d38ceaf9SAlex Deucher return (val != 0);
128d38ceaf9SAlex Deucher }
129d38ceaf9SAlex Deucher
amdgpu_i2c_set_clock(void * i2c_priv,int clock)130d38ceaf9SAlex Deucher static void amdgpu_i2c_set_clock(void *i2c_priv, int clock)
131d38ceaf9SAlex Deucher {
132d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c = i2c_priv;
1331348969aSLuben Tuikov struct amdgpu_device *adev = drm_to_adev(i2c->dev);
134d38ceaf9SAlex Deucher struct amdgpu_i2c_bus_rec *rec = &i2c->rec;
135d38ceaf9SAlex Deucher uint32_t val;
136d38ceaf9SAlex Deucher
137d38ceaf9SAlex Deucher /* set pin direction */
138d38ceaf9SAlex Deucher val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
139d38ceaf9SAlex Deucher val |= clock ? 0 : rec->en_clk_mask;
140d38ceaf9SAlex Deucher WREG32(rec->en_clk_reg, val);
141d38ceaf9SAlex Deucher }
142d38ceaf9SAlex Deucher
amdgpu_i2c_set_data(void * i2c_priv,int data)143d38ceaf9SAlex Deucher static void amdgpu_i2c_set_data(void *i2c_priv, int data)
144d38ceaf9SAlex Deucher {
145d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c = i2c_priv;
1461348969aSLuben Tuikov struct amdgpu_device *adev = drm_to_adev(i2c->dev);
147d38ceaf9SAlex Deucher struct amdgpu_i2c_bus_rec *rec = &i2c->rec;
148d38ceaf9SAlex Deucher uint32_t val;
149d38ceaf9SAlex Deucher
150d38ceaf9SAlex Deucher /* set pin direction */
151d38ceaf9SAlex Deucher val = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
152d38ceaf9SAlex Deucher val |= data ? 0 : rec->en_data_mask;
153d38ceaf9SAlex Deucher WREG32(rec->en_data_reg, val);
154d38ceaf9SAlex Deucher }
155d38ceaf9SAlex Deucher
156d38ceaf9SAlex Deucher static const struct i2c_algorithm amdgpu_atombios_i2c_algo = {
157d38ceaf9SAlex Deucher .master_xfer = amdgpu_atombios_i2c_xfer,
158d38ceaf9SAlex Deucher .functionality = amdgpu_atombios_i2c_func,
159d38ceaf9SAlex Deucher };
160d38ceaf9SAlex Deucher
amdgpu_i2c_create(struct drm_device * dev,const struct amdgpu_i2c_bus_rec * rec,const char * name)161d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev,
16244f9d7b3SGrazvydas Ignotas const struct amdgpu_i2c_bus_rec *rec,
163d38ceaf9SAlex Deucher const char *name)
164d38ceaf9SAlex Deucher {
165d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *i2c;
166d38ceaf9SAlex Deucher int ret;
167d38ceaf9SAlex Deucher
168d38ceaf9SAlex Deucher /* don't add the mm_i2c bus unless hw_i2c is enabled */
169d38ceaf9SAlex Deucher if (rec->mm_i2c && (amdgpu_hw_i2c == 0))
170d38ceaf9SAlex Deucher return NULL;
171d38ceaf9SAlex Deucher
172d38ceaf9SAlex Deucher i2c = kzalloc(sizeof(struct amdgpu_i2c_chan), GFP_KERNEL);
173d38ceaf9SAlex Deucher if (i2c == NULL)
174d38ceaf9SAlex Deucher return NULL;
175d38ceaf9SAlex Deucher
176d38ceaf9SAlex Deucher i2c->rec = *rec;
177d38ceaf9SAlex Deucher i2c->adapter.owner = THIS_MODULE;
1788f66090bSThomas Zimmermann i2c->adapter.dev.parent = dev->dev;
179d38ceaf9SAlex Deucher i2c->dev = dev;
180d38ceaf9SAlex Deucher i2c_set_adapdata(&i2c->adapter, i2c);
181d38ceaf9SAlex Deucher mutex_init(&i2c->mutex);
182d38ceaf9SAlex Deucher if (rec->hw_capable &&
183d38ceaf9SAlex Deucher amdgpu_hw_i2c) {
184d38ceaf9SAlex Deucher /* hw i2c using atom */
185d38ceaf9SAlex Deucher snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
186d38ceaf9SAlex Deucher "AMDGPU i2c hw bus %s", name);
187d38ceaf9SAlex Deucher i2c->adapter.algo = &amdgpu_atombios_i2c_algo;
188d38ceaf9SAlex Deucher ret = i2c_add_adapter(&i2c->adapter);
189e7b26d12SWolfram Sang if (ret)
190d38ceaf9SAlex Deucher goto out_free;
191d38ceaf9SAlex Deucher } else {
192d38ceaf9SAlex Deucher /* set the amdgpu bit adapter */
193d38ceaf9SAlex Deucher snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
194d38ceaf9SAlex Deucher "AMDGPU i2c bit bus %s", name);
195d38ceaf9SAlex Deucher i2c->adapter.algo_data = &i2c->bit;
196d38ceaf9SAlex Deucher i2c->bit.pre_xfer = amdgpu_i2c_pre_xfer;
197d38ceaf9SAlex Deucher i2c->bit.post_xfer = amdgpu_i2c_post_xfer;
198d38ceaf9SAlex Deucher i2c->bit.setsda = amdgpu_i2c_set_data;
199d38ceaf9SAlex Deucher i2c->bit.setscl = amdgpu_i2c_set_clock;
200d38ceaf9SAlex Deucher i2c->bit.getsda = amdgpu_i2c_get_data;
201d38ceaf9SAlex Deucher i2c->bit.getscl = amdgpu_i2c_get_clock;
202d38ceaf9SAlex Deucher i2c->bit.udelay = 10;
203d38ceaf9SAlex Deucher i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */
204d38ceaf9SAlex Deucher i2c->bit.data = i2c;
205d38ceaf9SAlex Deucher ret = i2c_bit_add_bus(&i2c->adapter);
206d38ceaf9SAlex Deucher if (ret) {
207d38ceaf9SAlex Deucher DRM_ERROR("Failed to register bit i2c %s\n", name);
208d38ceaf9SAlex Deucher goto out_free;
209d38ceaf9SAlex Deucher }
210d38ceaf9SAlex Deucher }
211d38ceaf9SAlex Deucher
212d38ceaf9SAlex Deucher return i2c;
213d38ceaf9SAlex Deucher out_free:
214d38ceaf9SAlex Deucher kfree(i2c);
215d38ceaf9SAlex Deucher return NULL;
216d38ceaf9SAlex Deucher
217d38ceaf9SAlex Deucher }
218d38ceaf9SAlex Deucher
amdgpu_i2c_destroy(struct amdgpu_i2c_chan * i2c)219d38ceaf9SAlex Deucher void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c)
220d38ceaf9SAlex Deucher {
221d38ceaf9SAlex Deucher if (!i2c)
222d38ceaf9SAlex Deucher return;
2232f9ba199SGrazvydas Ignotas WARN_ON(i2c->has_aux);
224d38ceaf9SAlex Deucher i2c_del_adapter(&i2c->adapter);
225d38ceaf9SAlex Deucher kfree(i2c);
226d38ceaf9SAlex Deucher }
227d38ceaf9SAlex Deucher
amdgpu_i2c_init(struct amdgpu_device * adev)2281c0b144bSAlex Deucher void amdgpu_i2c_init(struct amdgpu_device *adev)
2291c0b144bSAlex Deucher {
2301c0b144bSAlex Deucher if (!adev->is_atom_fw) {
231*20f48be6SAlex Deucher if (!amdgpu_device_has_dc_support(adev)) {
2321c0b144bSAlex Deucher amdgpu_atombios_i2c_init(adev);
233*20f48be6SAlex Deucher } else {
234*20f48be6SAlex Deucher switch (adev->asic_type) {
235*20f48be6SAlex Deucher case CHIP_POLARIS10:
236*20f48be6SAlex Deucher case CHIP_POLARIS11:
237*20f48be6SAlex Deucher case CHIP_POLARIS12:
238*20f48be6SAlex Deucher amdgpu_atombios_oem_i2c_init(adev, 0x97);
239*20f48be6SAlex Deucher break;
240*20f48be6SAlex Deucher default:
241*20f48be6SAlex Deucher break;
242*20f48be6SAlex Deucher }
243*20f48be6SAlex Deucher }
2441c0b144bSAlex Deucher }
2451c0b144bSAlex Deucher }
2461c0b144bSAlex Deucher
247d38ceaf9SAlex Deucher /* remove all the buses */
amdgpu_i2c_fini(struct amdgpu_device * adev)248d38ceaf9SAlex Deucher void amdgpu_i2c_fini(struct amdgpu_device *adev)
249d38ceaf9SAlex Deucher {
250d38ceaf9SAlex Deucher int i;
251d38ceaf9SAlex Deucher
252d38ceaf9SAlex Deucher for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) {
253d38ceaf9SAlex Deucher if (adev->i2c_bus[i]) {
254d38ceaf9SAlex Deucher amdgpu_i2c_destroy(adev->i2c_bus[i]);
255d38ceaf9SAlex Deucher adev->i2c_bus[i] = NULL;
256d38ceaf9SAlex Deucher }
257d38ceaf9SAlex Deucher }
258d38ceaf9SAlex Deucher }
259d38ceaf9SAlex Deucher
260d38ceaf9SAlex Deucher /* looks up bus based on id */
261d38ceaf9SAlex Deucher struct amdgpu_i2c_chan *
amdgpu_i2c_lookup(struct amdgpu_device * adev,const struct amdgpu_i2c_bus_rec * i2c_bus)262d38ceaf9SAlex Deucher amdgpu_i2c_lookup(struct amdgpu_device *adev,
26344f9d7b3SGrazvydas Ignotas const struct amdgpu_i2c_bus_rec *i2c_bus)
264d38ceaf9SAlex Deucher {
265d38ceaf9SAlex Deucher int i;
266d38ceaf9SAlex Deucher
267d38ceaf9SAlex Deucher for (i = 0; i < AMDGPU_MAX_I2C_BUS; i++) {
268d38ceaf9SAlex Deucher if (adev->i2c_bus[i] &&
269d38ceaf9SAlex Deucher (adev->i2c_bus[i]->rec.i2c_id == i2c_bus->i2c_id)) {
270d38ceaf9SAlex Deucher return adev->i2c_bus[i];
271d38ceaf9SAlex Deucher }
272d38ceaf9SAlex Deucher }
273d38ceaf9SAlex Deucher return NULL;
274d38ceaf9SAlex Deucher }
275d38ceaf9SAlex Deucher
amdgpu_i2c_get_byte(struct amdgpu_i2c_chan * i2c_bus,u8 slave_addr,u8 addr,u8 * val)276cd48b97cSBob Zhou static int amdgpu_i2c_get_byte(struct amdgpu_i2c_chan *i2c_bus,
277d38ceaf9SAlex Deucher u8 slave_addr,
278d38ceaf9SAlex Deucher u8 addr,
279d38ceaf9SAlex Deucher u8 *val)
280d38ceaf9SAlex Deucher {
281d38ceaf9SAlex Deucher u8 out_buf[2];
282d38ceaf9SAlex Deucher u8 in_buf[2];
283d38ceaf9SAlex Deucher struct i2c_msg msgs[] = {
284d38ceaf9SAlex Deucher {
285d38ceaf9SAlex Deucher .addr = slave_addr,
286d38ceaf9SAlex Deucher .flags = 0,
287d38ceaf9SAlex Deucher .len = 1,
288d38ceaf9SAlex Deucher .buf = out_buf,
289d38ceaf9SAlex Deucher },
290d38ceaf9SAlex Deucher {
291d38ceaf9SAlex Deucher .addr = slave_addr,
292d38ceaf9SAlex Deucher .flags = I2C_M_RD,
293d38ceaf9SAlex Deucher .len = 1,
294d38ceaf9SAlex Deucher .buf = in_buf,
295d38ceaf9SAlex Deucher }
296d38ceaf9SAlex Deucher };
297d38ceaf9SAlex Deucher
298d38ceaf9SAlex Deucher out_buf[0] = addr;
299d38ceaf9SAlex Deucher out_buf[1] = 0;
300d38ceaf9SAlex Deucher
301cd48b97cSBob Zhou if (i2c_transfer(&i2c_bus->adapter, msgs, 2) != 2) {
302cd48b97cSBob Zhou DRM_DEBUG("i2c 0x%02x read failed\n", addr);
303cd48b97cSBob Zhou return -EIO;
304d38ceaf9SAlex Deucher }
305d38ceaf9SAlex Deucher
306cd48b97cSBob Zhou *val = in_buf[0];
307cd48b97cSBob Zhou DRM_DEBUG("val = 0x%02x\n", *val);
308cd48b97cSBob Zhou
309cd48b97cSBob Zhou return 0;
310cd48b97cSBob Zhou }
311cd48b97cSBob Zhou
amdgpu_i2c_put_byte(struct amdgpu_i2c_chan * i2c_bus,u8 slave_addr,u8 addr,u8 val)312cd48b97cSBob Zhou static int amdgpu_i2c_put_byte(struct amdgpu_i2c_chan *i2c_bus,
313d38ceaf9SAlex Deucher u8 slave_addr,
314d38ceaf9SAlex Deucher u8 addr,
315d38ceaf9SAlex Deucher u8 val)
316d38ceaf9SAlex Deucher {
317d38ceaf9SAlex Deucher uint8_t out_buf[2];
318d38ceaf9SAlex Deucher struct i2c_msg msg = {
319d38ceaf9SAlex Deucher .addr = slave_addr,
320d38ceaf9SAlex Deucher .flags = 0,
321d38ceaf9SAlex Deucher .len = 2,
322d38ceaf9SAlex Deucher .buf = out_buf,
323d38ceaf9SAlex Deucher };
324d38ceaf9SAlex Deucher
325d38ceaf9SAlex Deucher out_buf[0] = addr;
326d38ceaf9SAlex Deucher out_buf[1] = val;
327d38ceaf9SAlex Deucher
328cd48b97cSBob Zhou if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1) {
329cd48b97cSBob Zhou DRM_DEBUG("i2c 0x%02x 0x%02x write failed\n", addr, val);
330cd48b97cSBob Zhou return -EIO;
331cd48b97cSBob Zhou }
332cd48b97cSBob Zhou
333cd48b97cSBob Zhou return 0;
334d38ceaf9SAlex Deucher }
335d38ceaf9SAlex Deucher
336d38ceaf9SAlex Deucher /* ddc router switching */
337d38ceaf9SAlex Deucher void
amdgpu_i2c_router_select_ddc_port(const struct amdgpu_connector * amdgpu_connector)33844f9d7b3SGrazvydas Ignotas amdgpu_i2c_router_select_ddc_port(const struct amdgpu_connector *amdgpu_connector)
339d38ceaf9SAlex Deucher {
340a211260cSTuo Li u8 val = 0;
341d38ceaf9SAlex Deucher
342d38ceaf9SAlex Deucher if (!amdgpu_connector->router.ddc_valid)
343d38ceaf9SAlex Deucher return;
344d38ceaf9SAlex Deucher
345d38ceaf9SAlex Deucher if (!amdgpu_connector->router_bus)
346d38ceaf9SAlex Deucher return;
347d38ceaf9SAlex Deucher
348cd48b97cSBob Zhou if (amdgpu_i2c_get_byte(amdgpu_connector->router_bus,
349d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
350cd48b97cSBob Zhou 0x3, &val))
351cd48b97cSBob Zhou return;
352d38ceaf9SAlex Deucher val &= ~amdgpu_connector->router.ddc_mux_control_pin;
353d38ceaf9SAlex Deucher amdgpu_i2c_put_byte(amdgpu_connector->router_bus,
354d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
355d38ceaf9SAlex Deucher 0x3, val);
356cd48b97cSBob Zhou if (amdgpu_i2c_get_byte(amdgpu_connector->router_bus,
357d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
358cd48b97cSBob Zhou 0x1, &val))
359cd48b97cSBob Zhou return;
360d38ceaf9SAlex Deucher val &= ~amdgpu_connector->router.ddc_mux_control_pin;
361d38ceaf9SAlex Deucher val |= amdgpu_connector->router.ddc_mux_state;
362d38ceaf9SAlex Deucher amdgpu_i2c_put_byte(amdgpu_connector->router_bus,
363d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
364d38ceaf9SAlex Deucher 0x1, val);
365d38ceaf9SAlex Deucher }
366d38ceaf9SAlex Deucher
367d38ceaf9SAlex Deucher /* clock/data router switching */
368d38ceaf9SAlex Deucher void
amdgpu_i2c_router_select_cd_port(const struct amdgpu_connector * amdgpu_connector)36944f9d7b3SGrazvydas Ignotas amdgpu_i2c_router_select_cd_port(const struct amdgpu_connector *amdgpu_connector)
370d38ceaf9SAlex Deucher {
371d38ceaf9SAlex Deucher u8 val;
372d38ceaf9SAlex Deucher
373d38ceaf9SAlex Deucher if (!amdgpu_connector->router.cd_valid)
374d38ceaf9SAlex Deucher return;
375d38ceaf9SAlex Deucher
376d38ceaf9SAlex Deucher if (!amdgpu_connector->router_bus)
377d38ceaf9SAlex Deucher return;
378d38ceaf9SAlex Deucher
379cd48b97cSBob Zhou if (amdgpu_i2c_get_byte(amdgpu_connector->router_bus,
380d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
381cd48b97cSBob Zhou 0x3, &val))
382cd48b97cSBob Zhou return;
383d38ceaf9SAlex Deucher val &= ~amdgpu_connector->router.cd_mux_control_pin;
384d38ceaf9SAlex Deucher amdgpu_i2c_put_byte(amdgpu_connector->router_bus,
385d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
386d38ceaf9SAlex Deucher 0x3, val);
387cd48b97cSBob Zhou if (amdgpu_i2c_get_byte(amdgpu_connector->router_bus,
388d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
389cd48b97cSBob Zhou 0x1, &val))
390cd48b97cSBob Zhou return;
391d38ceaf9SAlex Deucher val &= ~amdgpu_connector->router.cd_mux_control_pin;
392d38ceaf9SAlex Deucher val |= amdgpu_connector->router.cd_mux_state;
393d38ceaf9SAlex Deucher amdgpu_i2c_put_byte(amdgpu_connector->router_bus,
394d38ceaf9SAlex Deucher amdgpu_connector->router.i2c_addr,
395d38ceaf9SAlex Deucher 0x1, val);
396d38ceaf9SAlex Deucher }
397