Skip to content

Commit ab593e0

Browse files
Fabrizio CastroCodrin Ciubotariu
authored andcommitted
drm/bridge/sii902x: Fix EDID readback
While adding SiI9022A support to the iwg23s board, it came up that when the HDMI transmitter is in pass through mode the device is not compliant with the I2C specification anymore, as it requires a far bigger tbuf, due to a delay the HDMI transmitter is adding when relaying the STOP condition on the monitor i2c side of things. When not providing an appropriate delay after the STOP condition the i2c bus would get stuck. Also, any other traffic on the bus while talking to the monitor may cause the transaction to fail or even cause issues with the i2c bus as well. I2c-gates seemed to reach consent as a possible way to address these issues, and as such this patch is implementing a solution based on that. Since others are clearly relying on the current implementation of the driver, this patch won't require any DT changes. Since we don't want any interference during the DDC Bus Request/Grant procedure and while talking to the monitor, we have to use the adapter locking primitives rather than the i2c-mux locking primitives. Signed-off-by: Fabrizio Castro <fabrizio.castro@bp.renesas.com> Reviewed-by: Peter Rosin <peda@axentia.se> Acked-by: Linus Walleij <linus.walleij@linaro.org> Tested-by: Yannick Fertré <yannick.fertre@st.com> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> Link: https://patchwork.freedesktop.org/patch/msgid/1541505156-8097-1-git-send-email-fabrizio.castro@bp.renesas.com
1 parent 5b0fe7a commit ab593e0

1 file changed

Lines changed: 178 additions & 69 deletions

File tree

drivers/gpu/drm/bridge/sii902x.c

Lines changed: 178 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/*
2+
* Copyright (C) 2018 Renesas Electronics
3+
*
24
* Copyright (C) 2016 Atmel
35
* Bo Shen <voice.shen@atmel.com>
46
*
@@ -21,6 +23,7 @@
2123
*/
2224

2325
#include <linux/gpio/consumer.h>
26+
#include <linux/i2c-mux.h>
2427
#include <linux/i2c.h>
2528
#include <linux/module.h>
2629
#include <linux/regmap.h>
@@ -86,8 +89,49 @@ struct sii902x {
8689
struct drm_bridge bridge;
8790
struct drm_connector connector;
8891
struct gpio_desc *reset_gpio;
92+
struct i2c_mux_core *i2cmux;
8993
};
9094

95+
static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val)
96+
{
97+
union i2c_smbus_data data;
98+
int ret;
99+
100+
ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
101+
I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data);
102+
103+
if (ret < 0)
104+
return ret;
105+
106+
*val = data.byte;
107+
return 0;
108+
}
109+
110+
static int sii902x_write_unlocked(struct i2c_client *i2c, u8 reg, u8 val)
111+
{
112+
union i2c_smbus_data data;
113+
114+
data.byte = val;
115+
116+
return __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags,
117+
I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA,
118+
&data);
119+
}
120+
121+
static int sii902x_update_bits_unlocked(struct i2c_client *i2c, u8 reg, u8 mask,
122+
u8 val)
123+
{
124+
int ret;
125+
u8 status;
126+
127+
ret = sii902x_read_unlocked(i2c, reg, &status);
128+
if (ret)
129+
return ret;
130+
status &= ~mask;
131+
status |= val & mask;
132+
return sii902x_write_unlocked(i2c, reg, status);
133+
}
134+
91135
static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge)
92136
{
93137
return container_of(bridge, struct sii902x, bridge);
@@ -135,41 +179,11 @@ static const struct drm_connector_funcs sii902x_connector_funcs = {
135179
static int sii902x_get_modes(struct drm_connector *connector)
136180
{
137181
struct sii902x *sii902x = connector_to_sii902x(connector);
138-
struct regmap *regmap = sii902x->regmap;
139182
u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
140-
struct device *dev = &sii902x->i2c->dev;
141-
unsigned long timeout;
142-
unsigned int retries;
143-
unsigned int status;
144183
struct edid *edid;
145-
int num = 0;
146-
int ret;
147-
148-
ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
149-
SII902X_SYS_CTRL_DDC_BUS_REQ,
150-
SII902X_SYS_CTRL_DDC_BUS_REQ);
151-
if (ret)
152-
return ret;
153-
154-
timeout = jiffies +
155-
msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
156-
do {
157-
ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
158-
if (ret)
159-
return ret;
160-
} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
161-
time_before(jiffies, timeout));
184+
int num = 0, ret;
162185

163-
if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
164-
dev_err(dev, "failed to acquire the i2c bus\n");
165-
return -ETIMEDOUT;
166-
}
167-
168-
ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status);
169-
if (ret)
170-
return ret;
171-
172-
edid = drm_get_edid(connector, sii902x->i2c->adapter);
186+
edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]);
173187
drm_connector_update_edid_property(connector, edid);
174188
if (edid) {
175189
num = drm_add_edid_modes(connector, edid);
@@ -181,42 +195,6 @@ static int sii902x_get_modes(struct drm_connector *connector)
181195
if (ret)
182196
return ret;
183197

184-
/*
185-
* Sometimes the I2C bus can stall after failure to use the
186-
* EDID channel. Retry a few times to see if things clear
187-
* up, else continue anyway.
188-
*/
189-
retries = 5;
190-
do {
191-
ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA,
192-
&status);
193-
retries--;
194-
} while (ret && retries);
195-
if (ret)
196-
dev_err(dev, "failed to read status (%d)\n", ret);
197-
198-
ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
199-
SII902X_SYS_CTRL_DDC_BUS_REQ |
200-
SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
201-
if (ret)
202-
return ret;
203-
204-
timeout = jiffies +
205-
msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
206-
do {
207-
ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
208-
if (ret)
209-
return ret;
210-
} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
211-
SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
212-
time_before(jiffies, timeout));
213-
214-
if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
215-
SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
216-
dev_err(dev, "failed to release the i2c bus\n");
217-
return -ETIMEDOUT;
218-
}
219-
220198
return num;
221199
}
222200

@@ -366,6 +344,121 @@ static irqreturn_t sii902x_interrupt(int irq, void *data)
366344
return IRQ_HANDLED;
367345
}
368346

347+
/*
348+
* The purpose of sii902x_i2c_bypass_select is to enable the pass through
349+
* mode of the HDMI transmitter. Do not use regmap from within this function,
350+
* only use sii902x_*_unlocked functions to read/modify/write registers.
351+
* We are holding the parent adapter lock here, keep this in mind before
352+
* adding more i2c transactions.
353+
*
354+
* Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere
355+
* in this driver, we need to make sure that we only touch 0x1A[2:1] from
356+
* within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that
357+
* we leave the remaining bits as we have found them.
358+
*/
359+
static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
360+
{
361+
struct sii902x *sii902x = i2c_mux_priv(mux);
362+
struct device *dev = &sii902x->i2c->dev;
363+
unsigned long timeout;
364+
u8 status;
365+
int ret;
366+
367+
ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
368+
SII902X_SYS_CTRL_DDC_BUS_REQ,
369+
SII902X_SYS_CTRL_DDC_BUS_REQ);
370+
if (ret)
371+
return ret;
372+
373+
timeout = jiffies +
374+
msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
375+
do {
376+
ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
377+
&status);
378+
if (ret)
379+
return ret;
380+
} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
381+
time_before(jiffies, timeout));
382+
383+
if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
384+
dev_err(dev, "Failed to acquire the i2c bus\n");
385+
return -ETIMEDOUT;
386+
}
387+
388+
return sii902x_write_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
389+
status);
390+
}
391+
392+
/*
393+
* The purpose of sii902x_i2c_bypass_deselect is to disable the pass through
394+
* mode of the HDMI transmitter. Do not use regmap from within this function,
395+
* only use sii902x_*_unlocked functions to read/modify/write registers.
396+
* We are holding the parent adapter lock here, keep this in mind before
397+
* adding more i2c transactions.
398+
*
399+
* Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere
400+
* in this driver, we need to make sure that we only touch 0x1A[2:1] from
401+
* within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that
402+
* we leave the remaining bits as we have found them.
403+
*/
404+
static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
405+
{
406+
struct sii902x *sii902x = i2c_mux_priv(mux);
407+
struct device *dev = &sii902x->i2c->dev;
408+
unsigned long timeout;
409+
unsigned int retries;
410+
u8 status;
411+
int ret;
412+
413+
/*
414+
* When the HDMI transmitter is in pass through mode, we need an
415+
* (undocumented) additional delay between STOP and START conditions
416+
* to guarantee the bus won't get stuck.
417+
*/
418+
udelay(30);
419+
420+
/*
421+
* Sometimes the I2C bus can stall after failure to use the
422+
* EDID channel. Retry a few times to see if things clear
423+
* up, else continue anyway.
424+
*/
425+
retries = 5;
426+
do {
427+
ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
428+
&status);
429+
retries--;
430+
} while (ret && retries);
431+
if (ret) {
432+
dev_err(dev, "failed to read status (%d)\n", ret);
433+
return ret;
434+
}
435+
436+
ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
437+
SII902X_SYS_CTRL_DDC_BUS_REQ |
438+
SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
439+
if (ret)
440+
return ret;
441+
442+
timeout = jiffies +
443+
msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
444+
do {
445+
ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
446+
&status);
447+
if (ret)
448+
return ret;
449+
} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
450+
SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
451+
time_before(jiffies, timeout));
452+
453+
if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
454+
SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
455+
dev_err(dev, "failed to release the i2c bus\n");
456+
return -ETIMEDOUT;
457+
}
458+
459+
return 0;
460+
}
461+
369462
static int sii902x_probe(struct i2c_client *client,
370463
const struct i2c_device_id *id)
371464
{
@@ -375,6 +468,13 @@ static int sii902x_probe(struct i2c_client *client,
375468
u8 chipid[4];
376469
int ret;
377470

471+
ret = i2c_check_functionality(client->adapter,
472+
I2C_FUNC_SMBUS_BYTE_DATA);
473+
if (!ret) {
474+
dev_err(dev, "I2C adapter not suitable\n");
475+
return -EIO;
476+
}
477+
378478
sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL);
379479
if (!sii902x)
380480
return -ENOMEM;
@@ -433,14 +533,23 @@ static int sii902x_probe(struct i2c_client *client,
433533

434534
i2c_set_clientdata(client, sii902x);
435535

436-
return 0;
536+
sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev,
537+
1, 0, I2C_MUX_GATE,
538+
sii902x_i2c_bypass_select,
539+
sii902x_i2c_bypass_deselect);
540+
if (!sii902x->i2cmux)
541+
return -ENOMEM;
542+
543+
sii902x->i2cmux->priv = sii902x;
544+
return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0);
437545
}
438546

439547
static int sii902x_remove(struct i2c_client *client)
440548

441549
{
442550
struct sii902x *sii902x = i2c_get_clientdata(client);
443551

552+
i2c_mux_del_adapters(sii902x->i2cmux);
444553
drm_bridge_remove(&sii902x->bridge);
445554

446555
return 0;

0 commit comments

Comments
 (0)