Skip to content

Commit 9535c47

Browse files
dtorjnikula
authored andcommitted
drm/i915: cope with large i2c transfers
The hardware, according to the specs, is limited to 256 byte transfers, and current driver has no protections in case users attempt to do larger transfers. The code will just stomp over status register and mayhem ensues. Let's split larger transfers into digestable chunks. Doing this allows Atmel MXT driver on Pixel 1 function properly (it hasn't since commit 9d8dc3e "Input: atmel_mxt_ts - implement T44 message handling" which tries to consume multiple touchscreen/touchpad reports in a single transaction). Cc: [email protected] Reviewed-by: Chris Wilson <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]> Signed-off-by: Jani Nikula <[email protected]>
1 parent 37ef01a commit 9535c47

File tree

2 files changed

+57
-10
lines changed

2 files changed

+57
-10
lines changed

drivers/gpu/drm/i915/i915_reg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,7 @@ enum skl_disp_power_wells {
18071807
#define GMBUS_CYCLE_INDEX (2<<25)
18081808
#define GMBUS_CYCLE_STOP (4<<25)
18091809
#define GMBUS_BYTE_COUNT_SHIFT 16
1810+
#define GMBUS_BYTE_COUNT_MAX 256U
18101811
#define GMBUS_SLAVE_INDEX_SHIFT 8
18111812
#define GMBUS_SLAVE_ADDR_SHIFT 1
18121813
#define GMBUS_SLAVE_READ (1<<0)

drivers/gpu/drm/i915/intel_i2c.c

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,17 @@ gmbus_wait_idle(struct drm_i915_private *dev_priv)
270270
}
271271

272272
static int
273-
gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
274-
u32 gmbus1_index)
273+
gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv,
274+
unsigned short addr, u8 *buf, unsigned int len,
275+
u32 gmbus1_index)
275276
{
276277
int reg_offset = dev_priv->gpio_mmio_base;
277-
u16 len = msg->len;
278-
u8 *buf = msg->buf;
279278

280279
I915_WRITE(GMBUS1 + reg_offset,
281280
gmbus1_index |
282281
GMBUS_CYCLE_WAIT |
283282
(len << GMBUS_BYTE_COUNT_SHIFT) |
284-
(msg->addr << GMBUS_SLAVE_ADDR_SHIFT) |
283+
(addr << GMBUS_SLAVE_ADDR_SHIFT) |
285284
GMBUS_SLAVE_READ | GMBUS_SW_RDY);
286285
while (len) {
287286
int ret;
@@ -303,11 +302,35 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
303302
}
304303

305304
static int
306-
gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
305+
gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg,
306+
u32 gmbus1_index)
307307
{
308-
int reg_offset = dev_priv->gpio_mmio_base;
309-
u16 len = msg->len;
310308
u8 *buf = msg->buf;
309+
unsigned int rx_size = msg->len;
310+
unsigned int len;
311+
int ret;
312+
313+
do {
314+
len = min(rx_size, GMBUS_BYTE_COUNT_MAX);
315+
316+
ret = gmbus_xfer_read_chunk(dev_priv, msg->addr,
317+
buf, len, gmbus1_index);
318+
if (ret)
319+
return ret;
320+
321+
rx_size -= len;
322+
buf += len;
323+
} while (rx_size != 0);
324+
325+
return 0;
326+
}
327+
328+
static int
329+
gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
330+
unsigned short addr, u8 *buf, unsigned int len)
331+
{
332+
int reg_offset = dev_priv->gpio_mmio_base;
333+
unsigned int chunk_size = len;
311334
u32 val, loop;
312335

313336
val = loop = 0;
@@ -319,8 +342,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
319342
I915_WRITE(GMBUS3 + reg_offset, val);
320343
I915_WRITE(GMBUS1 + reg_offset,
321344
GMBUS_CYCLE_WAIT |
322-
(msg->len << GMBUS_BYTE_COUNT_SHIFT) |
323-
(msg->addr << GMBUS_SLAVE_ADDR_SHIFT) |
345+
(chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
346+
(addr << GMBUS_SLAVE_ADDR_SHIFT) |
324347
GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
325348
while (len) {
326349
int ret;
@@ -337,6 +360,29 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
337360
if (ret)
338361
return ret;
339362
}
363+
364+
return 0;
365+
}
366+
367+
static int
368+
gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)
369+
{
370+
u8 *buf = msg->buf;
371+
unsigned int tx_size = msg->len;
372+
unsigned int len;
373+
int ret;
374+
375+
do {
376+
len = min(tx_size, GMBUS_BYTE_COUNT_MAX);
377+
378+
ret = gmbus_xfer_write_chunk(dev_priv, msg->addr, buf, len);
379+
if (ret)
380+
return ret;
381+
382+
buf += len;
383+
tx_size -= len;
384+
} while (tx_size != 0);
385+
340386
return 0;
341387
}
342388

0 commit comments

Comments
 (0)