317 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>4 * Copyright (C) 2017 Jonathan Liu <net147@gmail.com>5 */6 7#include <linux/clk.h>8#include <linux/i2c.h>9#include <linux/iopoll.h>10 11#include "sun4i_hdmi.h"12 13#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \14 SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \15 SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \16 SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \17 SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \18 SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \19 SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \20)21 22/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */23#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX24 25static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read)26{27 /*28 * 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz29 * clock. As clock rate is fixed, just round it up to 100 us.30 */31 const unsigned long byte_time_ns = 100;32 const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK |33 SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST |34 SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE;35 u32 reg;36 /*37 * If threshold is inclusive, then the FIFO may only have38 * RX_THRESHOLD number of bytes, instead of RX_THRESHOLD + 1.39 */40 int read_len = RX_THRESHOLD +41 (hdmi->variant->ddc_fifo_thres_incl ? 0 : 1);42 43 /*44 * Limit transfer length by FIFO threshold or FIFO size.45 * For TX the threshold is for an empty FIFO.46 */47 len = min_t(int, len, read ? read_len : SUN4I_HDMI_DDC_FIFO_SIZE);48 49 /* Wait until error, FIFO request bit set or transfer complete */50 if (regmap_field_read_poll_timeout(hdmi->field_ddc_int_status, reg,51 reg & mask, len * byte_time_ns,52 100000))53 return -ETIMEDOUT;54 55 if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK)56 return -EIO;57 58 if (read)59 ioread8_rep(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);60 else61 iowrite8_rep(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len);62 63 /* Clear FIFO request bit by forcing a write to that bit */64 regmap_field_force_write(hdmi->field_ddc_int_status,65 SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST);66 67 return len;68}69 70static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg)71{72 int i, len;73 u32 reg;74 75 /* Set FIFO direction */76 if (hdmi->variant->ddc_fifo_has_dir) {77 reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);78 reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;79 reg |= (msg->flags & I2C_M_RD) ?80 SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ :81 SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE;82 writel(reg, hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);83 }84 85 /* Clear address register (not cleared by soft reset) */86 regmap_field_write(hdmi->field_ddc_addr_reg, 0);87 88 /* Set I2C address */89 regmap_field_write(hdmi->field_ddc_slave_addr, msg->addr);90 91 /*92 * Set FIFO RX/TX thresholds and clear FIFO93 *94 * If threshold is inclusive, we can set the TX threshold to95 * 0 instead of 1.96 */97 regmap_field_write(hdmi->field_ddc_fifo_tx_thres,98 hdmi->variant->ddc_fifo_thres_incl ? 0 : 1);99 regmap_field_write(hdmi->field_ddc_fifo_rx_thres, RX_THRESHOLD);100 regmap_field_write(hdmi->field_ddc_fifo_clear, 1);101 if (regmap_field_read_poll_timeout(hdmi->field_ddc_fifo_clear,102 reg, !reg, 100, 2000))103 return -EIO;104 105 /* Set transfer length */106 regmap_field_write(hdmi->field_ddc_byte_count, msg->len);107 108 /* Set command */109 regmap_field_write(hdmi->field_ddc_cmd,110 msg->flags & I2C_M_RD ?111 SUN4I_HDMI_DDC_CMD_IMPLICIT_READ :112 SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE);113 114 /* Clear interrupt status bits by forcing a write */115 regmap_field_force_write(hdmi->field_ddc_int_status,116 SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK |117 SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST |118 SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE);119 120 /* Start command */121 regmap_field_write(hdmi->field_ddc_start, 1);122 123 /* Transfer bytes */124 for (i = 0; i < msg->len; i += len) {125 len = fifo_transfer(hdmi, msg->buf + i, msg->len - i,126 msg->flags & I2C_M_RD);127 if (len <= 0)128 return len;129 }130 131 /* Wait for command to finish */132 if (regmap_field_read_poll_timeout(hdmi->field_ddc_start,133 reg, !reg, 100, 100000))134 return -EIO;135 136 /* Check for errors */137 regmap_field_read(hdmi->field_ddc_int_status, ®);138 if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) ||139 !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) {140 return -EIO;141 }142 143 return 0;144}145 146static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap,147 struct i2c_msg *msgs, int num)148{149 struct sun4i_hdmi *hdmi = i2c_get_adapdata(adap);150 u32 reg;151 int err, i, ret = num;152 153 for (i = 0; i < num; i++) {154 if (!msgs[i].len)155 return -EINVAL;156 if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX)157 return -EINVAL;158 }159 160 /* DDC clock needs to be enabled for the module to work */161 clk_prepare_enable(hdmi->ddc_clk);162 clk_set_rate(hdmi->ddc_clk, 100000);163 164 /* Reset I2C controller */165 regmap_field_write(hdmi->field_ddc_en, 1);166 regmap_field_write(hdmi->field_ddc_reset, 1);167 if (regmap_field_read_poll_timeout(hdmi->field_ddc_reset,168 reg, !reg, 100, 2000)) {169 clk_disable_unprepare(hdmi->ddc_clk);170 return -EIO;171 }172 173 regmap_field_write(hdmi->field_ddc_sck_en, 1);174 regmap_field_write(hdmi->field_ddc_sda_en, 1);175 176 for (i = 0; i < num; i++) {177 err = xfer_msg(hdmi, &msgs[i]);178 if (err) {179 ret = err;180 break;181 }182 }183 184 clk_disable_unprepare(hdmi->ddc_clk);185 return ret;186}187 188static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap)189{190 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;191}192 193static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = {194 .master_xfer = sun4i_hdmi_i2c_xfer,195 .functionality = sun4i_hdmi_i2c_func,196};197 198static int sun4i_hdmi_init_regmap_fields(struct sun4i_hdmi *hdmi)199{200 hdmi->field_ddc_en =201 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,202 hdmi->variant->field_ddc_en);203 if (IS_ERR(hdmi->field_ddc_en))204 return PTR_ERR(hdmi->field_ddc_en);205 206 hdmi->field_ddc_start =207 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,208 hdmi->variant->field_ddc_start);209 if (IS_ERR(hdmi->field_ddc_start))210 return PTR_ERR(hdmi->field_ddc_start);211 212 hdmi->field_ddc_reset =213 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,214 hdmi->variant->field_ddc_reset);215 if (IS_ERR(hdmi->field_ddc_reset))216 return PTR_ERR(hdmi->field_ddc_reset);217 218 hdmi->field_ddc_addr_reg =219 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,220 hdmi->variant->field_ddc_addr_reg);221 if (IS_ERR(hdmi->field_ddc_addr_reg))222 return PTR_ERR(hdmi->field_ddc_addr_reg);223 224 hdmi->field_ddc_slave_addr =225 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,226 hdmi->variant->field_ddc_slave_addr);227 if (IS_ERR(hdmi->field_ddc_slave_addr))228 return PTR_ERR(hdmi->field_ddc_slave_addr);229 230 hdmi->field_ddc_int_mask =231 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,232 hdmi->variant->field_ddc_int_mask);233 if (IS_ERR(hdmi->field_ddc_int_mask))234 return PTR_ERR(hdmi->field_ddc_int_mask);235 236 hdmi->field_ddc_int_status =237 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,238 hdmi->variant->field_ddc_int_status);239 if (IS_ERR(hdmi->field_ddc_int_status))240 return PTR_ERR(hdmi->field_ddc_int_status);241 242 hdmi->field_ddc_fifo_clear =243 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,244 hdmi->variant->field_ddc_fifo_clear);245 if (IS_ERR(hdmi->field_ddc_fifo_clear))246 return PTR_ERR(hdmi->field_ddc_fifo_clear);247 248 hdmi->field_ddc_fifo_rx_thres =249 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,250 hdmi->variant->field_ddc_fifo_rx_thres);251 if (IS_ERR(hdmi->field_ddc_fifo_rx_thres))252 return PTR_ERR(hdmi->field_ddc_fifo_rx_thres);253 254 hdmi->field_ddc_fifo_tx_thres =255 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,256 hdmi->variant->field_ddc_fifo_tx_thres);257 if (IS_ERR(hdmi->field_ddc_fifo_tx_thres))258 return PTR_ERR(hdmi->field_ddc_fifo_tx_thres);259 260 hdmi->field_ddc_byte_count =261 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,262 hdmi->variant->field_ddc_byte_count);263 if (IS_ERR(hdmi->field_ddc_byte_count))264 return PTR_ERR(hdmi->field_ddc_byte_count);265 266 hdmi->field_ddc_cmd =267 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,268 hdmi->variant->field_ddc_cmd);269 if (IS_ERR(hdmi->field_ddc_cmd))270 return PTR_ERR(hdmi->field_ddc_cmd);271 272 hdmi->field_ddc_sda_en =273 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,274 hdmi->variant->field_ddc_sda_en);275 if (IS_ERR(hdmi->field_ddc_sda_en))276 return PTR_ERR(hdmi->field_ddc_sda_en);277 278 hdmi->field_ddc_sck_en =279 devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,280 hdmi->variant->field_ddc_sck_en);281 if (IS_ERR(hdmi->field_ddc_sck_en))282 return PTR_ERR(hdmi->field_ddc_sck_en);283 284 return 0;285}286 287int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi)288{289 struct i2c_adapter *adap;290 int ret = 0;291 292 ret = sun4i_ddc_create(hdmi, hdmi->ddc_parent_clk);293 if (ret)294 return ret;295 296 ret = sun4i_hdmi_init_regmap_fields(hdmi);297 if (ret)298 return ret;299 300 adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);301 if (!adap)302 return -ENOMEM;303 304 adap->owner = THIS_MODULE;305 adap->algo = &sun4i_hdmi_i2c_algorithm;306 strscpy(adap->name, "sun4i_hdmi_i2c adapter", sizeof(adap->name));307 i2c_set_adapdata(adap, hdmi);308 309 ret = i2c_add_adapter(adap);310 if (ret)311 return ret;312 313 hdmi->i2c = adap;314 315 return ret;316}317