linux/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
<<
>>
Prefs
   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}
 268
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.