cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hdmi_i2c.c (6105B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2013 Red Hat
      4 * Author: Rob Clark <robdclark@gmail.com>
      5 */
      6
      7#include "hdmi.h"
      8
      9struct hdmi_i2c_adapter {
     10	struct i2c_adapter base;
     11	struct hdmi *hdmi;
     12	bool sw_done;
     13	wait_queue_head_t ddc_event;
     14};
     15#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base)
     16
     17static void init_ddc(struct hdmi_i2c_adapter *hdmi_i2c)
     18{
     19	struct hdmi *hdmi = hdmi_i2c->hdmi;
     20
     21	hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
     22			HDMI_DDC_CTRL_SW_STATUS_RESET);
     23	hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
     24			HDMI_DDC_CTRL_SOFT_RESET);
     25
     26	hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
     27			HDMI_DDC_SPEED_THRESHOLD(2) |
     28			HDMI_DDC_SPEED_PRESCALE(10));
     29
     30	hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
     31			HDMI_DDC_SETUP_TIMEOUT(0xff));
     32
     33	/* enable reference timer for 27us */
     34	hdmi_write(hdmi, REG_HDMI_DDC_REF,
     35			HDMI_DDC_REF_REFTIMER_ENABLE |
     36			HDMI_DDC_REF_REFTIMER(27));
     37}
     38
     39static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c)
     40{
     41	struct hdmi *hdmi = hdmi_i2c->hdmi;
     42	struct drm_device *dev = hdmi->dev;
     43	uint32_t retry = 0xffff;
     44	uint32_t ddc_int_ctrl;
     45
     46	do {
     47		--retry;
     48
     49		hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
     50				HDMI_DDC_INT_CTRL_SW_DONE_ACK |
     51				HDMI_DDC_INT_CTRL_SW_DONE_MASK);
     52
     53		ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
     54
     55	} while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry);
     56
     57	if (!retry) {
     58		DRM_DEV_ERROR(dev->dev, "timeout waiting for DDC\n");
     59		return -ETIMEDOUT;
     60	}
     61
     62	hdmi_i2c->sw_done = false;
     63
     64	return 0;
     65}
     66
     67#define MAX_TRANSACTIONS 4
     68
     69static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c)
     70{
     71	struct hdmi *hdmi = hdmi_i2c->hdmi;
     72
     73	if (!hdmi_i2c->sw_done) {
     74		uint32_t ddc_int_ctrl;
     75
     76		ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL);
     77
     78		if ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_MASK) &&
     79				(ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT)) {
     80			hdmi_i2c->sw_done = true;
     81			hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL,
     82					HDMI_DDC_INT_CTRL_SW_DONE_ACK);
     83		}
     84	}
     85
     86	return hdmi_i2c->sw_done;
     87}
     88
     89static int msm_hdmi_i2c_xfer(struct i2c_adapter *i2c,
     90		struct i2c_msg *msgs, int num)
     91{
     92	struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
     93	struct hdmi *hdmi = hdmi_i2c->hdmi;
     94	struct drm_device *dev = hdmi->dev;
     95	static const uint32_t nack[] = {
     96			HDMI_DDC_SW_STATUS_NACK0, HDMI_DDC_SW_STATUS_NACK1,
     97			HDMI_DDC_SW_STATUS_NACK2, HDMI_DDC_SW_STATUS_NACK3,
     98	};
     99	int indices[MAX_TRANSACTIONS];
    100	int ret, i, j, index = 0;
    101	uint32_t ddc_status, ddc_data, i2c_trans;
    102
    103	num = min(num, MAX_TRANSACTIONS);
    104
    105	WARN_ON(!(hdmi_read(hdmi, REG_HDMI_CTRL) & HDMI_CTRL_ENABLE));
    106
    107	if (num == 0)
    108		return num;
    109
    110	init_ddc(hdmi_i2c);
    111
    112	ret = ddc_clear_irq(hdmi_i2c);
    113	if (ret)
    114		return ret;
    115
    116	for (i = 0; i < num; i++) {
    117		struct i2c_msg *p = &msgs[i];
    118		uint32_t raw_addr = p->addr << 1;
    119
    120		if (p->flags & I2C_M_RD)
    121			raw_addr |= 1;
    122
    123		ddc_data = HDMI_DDC_DATA_DATA(raw_addr) |
    124				HDMI_DDC_DATA_DATA_RW(DDC_WRITE);
    125
    126		if (i == 0) {
    127			ddc_data |= HDMI_DDC_DATA_INDEX(0) |
    128					HDMI_DDC_DATA_INDEX_WRITE;
    129		}
    130
    131		hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
    132		index++;
    133
    134		indices[i] = index;
    135
    136		if (p->flags & I2C_M_RD) {
    137			index += p->len;
    138		} else {
    139			for (j = 0; j < p->len; j++) {
    140				ddc_data = HDMI_DDC_DATA_DATA(p->buf[j]) |
    141						HDMI_DDC_DATA_DATA_RW(DDC_WRITE);
    142				hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
    143				index++;
    144			}
    145		}
    146
    147		i2c_trans = HDMI_I2C_TRANSACTION_REG_CNT(p->len) |
    148				HDMI_I2C_TRANSACTION_REG_RW(
    149						(p->flags & I2C_M_RD) ? DDC_READ : DDC_WRITE) |
    150				HDMI_I2C_TRANSACTION_REG_START;
    151
    152		if (i == (num - 1))
    153			i2c_trans |= HDMI_I2C_TRANSACTION_REG_STOP;
    154
    155		hdmi_write(hdmi, REG_HDMI_I2C_TRANSACTION(i), i2c_trans);
    156	}
    157
    158	/* trigger the transfer: */
    159	hdmi_write(hdmi, REG_HDMI_DDC_CTRL,
    160			HDMI_DDC_CTRL_TRANSACTION_CNT(num - 1) |
    161			HDMI_DDC_CTRL_GO);
    162
    163	ret = wait_event_timeout(hdmi_i2c->ddc_event, sw_done(hdmi_i2c), HZ/4);
    164	if (ret <= 0) {
    165		if (ret == 0)
    166			ret = -ETIMEDOUT;
    167		dev_warn(dev->dev, "DDC timeout: %d\n", ret);
    168		DBG("sw_status=%08x, hw_status=%08x, int_ctrl=%08x",
    169				hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS),
    170				hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS),
    171				hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL));
    172		return ret;
    173	}
    174
    175	ddc_status = hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS);
    176
    177	/* read back results of any read transactions: */
    178	for (i = 0; i < num; i++) {
    179		struct i2c_msg *p = &msgs[i];
    180
    181		if (!(p->flags & I2C_M_RD))
    182			continue;
    183
    184		/* check for NACK: */
    185		if (ddc_status & nack[i]) {
    186			DBG("ddc_status=%08x", ddc_status);
    187			break;
    188		}
    189
    190		ddc_data = HDMI_DDC_DATA_DATA_RW(DDC_READ) |
    191				HDMI_DDC_DATA_INDEX(indices[i]) |
    192				HDMI_DDC_DATA_INDEX_WRITE;
    193
    194		hdmi_write(hdmi, REG_HDMI_DDC_DATA, ddc_data);
    195
    196		/* discard first byte: */
    197		hdmi_read(hdmi, REG_HDMI_DDC_DATA);
    198
    199		for (j = 0; j < p->len; j++) {
    200			ddc_data = hdmi_read(hdmi, REG_HDMI_DDC_DATA);
    201			p->buf[j] = FIELD(ddc_data, HDMI_DDC_DATA_DATA);
    202		}
    203	}
    204
    205	return i;
    206}
    207
    208static u32 msm_hdmi_i2c_func(struct i2c_adapter *adapter)
    209{
    210	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
    211}
    212
    213static const struct i2c_algorithm msm_hdmi_i2c_algorithm = {
    214	.master_xfer	= msm_hdmi_i2c_xfer,
    215	.functionality	= msm_hdmi_i2c_func,
    216};
    217
    218void msm_hdmi_i2c_irq(struct i2c_adapter *i2c)
    219{
    220	struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
    221
    222	if (sw_done(hdmi_i2c))
    223		wake_up_all(&hdmi_i2c->ddc_event);
    224}
    225
    226void msm_hdmi_i2c_destroy(struct i2c_adapter *i2c)
    227{
    228	struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c);
    229	i2c_del_adapter(i2c);
    230	kfree(hdmi_i2c);
    231}
    232
    233struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi)
    234{
    235	struct hdmi_i2c_adapter *hdmi_i2c;
    236	struct i2c_adapter *i2c = NULL;
    237	int ret;
    238
    239	hdmi_i2c = kzalloc(sizeof(*hdmi_i2c), GFP_KERNEL);
    240	if (!hdmi_i2c) {
    241		ret = -ENOMEM;
    242		goto fail;
    243	}
    244
    245	i2c = &hdmi_i2c->base;
    246
    247	hdmi_i2c->hdmi = hdmi;
    248	init_waitqueue_head(&hdmi_i2c->ddc_event);
    249
    250
    251	i2c->owner = THIS_MODULE;
    252	i2c->class = I2C_CLASS_DDC;
    253	snprintf(i2c->name, sizeof(i2c->name), "msm hdmi i2c");
    254	i2c->dev.parent = &hdmi->pdev->dev;
    255	i2c->algo = &msm_hdmi_i2c_algorithm;
    256
    257	ret = i2c_add_adapter(i2c);
    258	if (ret)
    259		goto fail;
    260
    261	return i2c;
    262
    263fail:
    264	if (i2c)
    265		msm_hdmi_i2c_destroy(i2c);
    266	return ERR_PTR(ret);
    267}