Linux Audio

Check our new training course

Linux kernel drivers training

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