Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
   1/*
   2 * Silicon Image SiI8620 HDMI/MHL bridge driver
   3 *
   4 * Copyright (C) 2015, Samsung Electronics Co., Ltd.
   5 * Andrzej Hajda <a.hajda@samsung.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <drm/bridge/mhl.h>
  13#include <drm/drm_crtc.h>
  14#include <drm/drm_edid.h>
  15
  16#include <linux/clk.h>
  17#include <linux/delay.h>
  18#include <linux/gpio/consumer.h>
  19#include <linux/i2c.h>
  20#include <linux/interrupt.h>
  21#include <linux/irq.h>
  22#include <linux/kernel.h>
  23#include <linux/list.h>
  24#include <linux/module.h>
  25#include <linux/mutex.h>
  26#include <linux/regulator/consumer.h>
  27#include <linux/slab.h>
  28
  29#include "sil-sii8620.h"
  30
  31#define VAL_RX_HDMI_CTRL2_DEFVAL	VAL_RX_HDMI_CTRL2_IDLE_CNT(3)
  32
  33enum sii8620_mode {
  34	CM_DISCONNECTED,
  35	CM_DISCOVERY,
  36	CM_MHL1,
  37	CM_MHL3,
  38	CM_ECBUS_S
  39};
  40
  41enum sii8620_sink_type {
  42	SINK_NONE,
  43	SINK_HDMI,
  44	SINK_DVI
  45};
  46
  47enum sii8620_mt_state {
  48	MT_STATE_READY,
  49	MT_STATE_BUSY,
  50	MT_STATE_DONE
  51};
  52
  53struct sii8620 {
  54	struct drm_bridge bridge;
  55	struct device *dev;
  56	struct clk *clk_xtal;
  57	struct gpio_desc *gpio_reset;
  58	struct gpio_desc *gpio_int;
  59	struct regulator_bulk_data supplies[2];
  60	struct mutex lock; /* context lock, protects fields below */
  61	int error;
  62	enum sii8620_mode mode;
  63	enum sii8620_sink_type sink_type;
  64	u8 cbus_status;
  65	u8 stat[MHL_DST_SIZE];
  66	u8 xstat[MHL_XDS_SIZE];
  67	u8 devcap[MHL_DCAP_SIZE];
  68	u8 xdevcap[MHL_XDC_SIZE];
  69	u8 avif[19];
  70	struct edid *edid;
  71	unsigned int gen2_write_burst:1;
  72	enum sii8620_mt_state mt_state;
  73	struct list_head mt_queue;
  74};
  75
  76struct sii8620_mt_msg;
  77
  78typedef void (*sii8620_mt_msg_cb)(struct sii8620 *ctx,
  79				  struct sii8620_mt_msg *msg);
  80
  81struct sii8620_mt_msg {
  82	struct list_head node;
  83	u8 reg[4];
  84	u8 ret;
  85	sii8620_mt_msg_cb send;
  86	sii8620_mt_msg_cb recv;
  87};
  88
  89static const u8 sii8620_i2c_page[] = {
  90	0x39, /* Main System */
  91	0x3d, /* TDM and HSIC */
  92	0x49, /* TMDS Receiver, MHL EDID */
  93	0x4d, /* eMSC, HDCP, HSIC */
  94	0x5d, /* MHL Spec */
  95	0x64, /* MHL CBUS */
  96	0x59, /* Hardware TPI (Transmitter Programming Interface) */
  97	0x61, /* eCBUS-S, eCBUS-D */
  98};
  99
 100static void sii8620_fetch_edid(struct sii8620 *ctx);
 101static void sii8620_set_upstream_edid(struct sii8620 *ctx);
 102static void sii8620_enable_hpd(struct sii8620 *ctx);
 103static void sii8620_mhl_disconnected(struct sii8620 *ctx);
 104
 105static int sii8620_clear_error(struct sii8620 *ctx)
 106{
 107	int ret = ctx->error;
 108
 109	ctx->error = 0;
 110	return ret;
 111}
 112
 113static void sii8620_read_buf(struct sii8620 *ctx, u16 addr, u8 *buf, int len)
 114{
 115	struct device *dev = ctx->dev;
 116	struct i2c_client *client = to_i2c_client(dev);
 117	u8 data = addr;
 118	struct i2c_msg msg[] = {
 119		{
 120			.addr = sii8620_i2c_page[addr >> 8],
 121			.flags = client->flags,
 122			.len = 1,
 123			.buf = &data
 124		},
 125		{
 126			.addr = sii8620_i2c_page[addr >> 8],
 127			.flags = client->flags | I2C_M_RD,
 128			.len = len,
 129			.buf = buf
 130		},
 131	};
 132	int ret;
 133
 134	if (ctx->error)
 135		return;
 136
 137	ret = i2c_transfer(client->adapter, msg, 2);
 138	dev_dbg(dev, "read at %04x: %*ph, %d\n", addr, len, buf, ret);
 139
 140	if (ret != 2) {
 141		dev_err(dev, "Read at %#06x of %d bytes failed with code %d.\n",
 142			addr, len, ret);
 143		ctx->error = ret < 0 ? ret : -EIO;
 144	}
 145}
 146
 147static u8 sii8620_readb(struct sii8620 *ctx, u16 addr)
 148{
 149	u8 ret;
 150
 151	sii8620_read_buf(ctx, addr, &ret, 1);
 152	return ret;
 153}
 154
 155static void sii8620_write_buf(struct sii8620 *ctx, u16 addr, const u8 *buf,
 156			      int len)
 157{
 158	struct device *dev = ctx->dev;
 159	struct i2c_client *client = to_i2c_client(dev);
 160	u8 data[2];
 161	struct i2c_msg msg = {
 162		.addr = sii8620_i2c_page[addr >> 8],
 163		.flags = client->flags,
 164		.len = len + 1,
 165	};
 166	int ret;
 167
 168	if (ctx->error)
 169		return;
 170
 171	if (len > 1) {
 172		msg.buf = kmalloc(len + 1, GFP_KERNEL);
 173		if (!msg.buf) {
 174			ctx->error = -ENOMEM;
 175			return;
 176		}
 177		memcpy(msg.buf + 1, buf, len);
 178	} else {
 179		msg.buf = data;
 180		msg.buf[1] = *buf;
 181	}
 182
 183	msg.buf[0] = addr;
 184
 185	ret = i2c_transfer(client->adapter, &msg, 1);
 186	dev_dbg(dev, "write at %04x: %*ph, %d\n", addr, len, buf, ret);
 187
 188	if (ret != 1) {
 189		dev_err(dev, "Write at %#06x of %*ph failed with code %d.\n",
 190			addr, len, buf, ret);
 191		ctx->error = ret ?: -EIO;
 192	}
 193
 194	if (len > 1)
 195		kfree(msg.buf);
 196}
 197
 198#define sii8620_write(ctx, addr, arr...) \
 199({\
 200	u8 d[] = { arr }; \
 201	sii8620_write_buf(ctx, addr, d, ARRAY_SIZE(d)); \
 202})
 203
 204static void __sii8620_write_seq(struct sii8620 *ctx, const u16 *seq, int len)
 205{
 206	int i;
 207
 208	for (i = 0; i < len; i += 2)
 209		sii8620_write(ctx, seq[i], seq[i + 1]);
 210}
 211
 212#define sii8620_write_seq(ctx, seq...) \
 213({\
 214	const u16 d[] = { seq }; \
 215	__sii8620_write_seq(ctx, d, ARRAY_SIZE(d)); \
 216})
 217
 218#define sii8620_write_seq_static(ctx, seq...) \
 219({\
 220	static const u16 d[] = { seq }; \
 221	__sii8620_write_seq(ctx, d, ARRAY_SIZE(d)); \
 222})
 223
 224static void sii8620_setbits(struct sii8620 *ctx, u16 addr, u8 mask, u8 val)
 225{
 226	val = (val & mask) | (sii8620_readb(ctx, addr) & ~mask);
 227	sii8620_write(ctx, addr, val);
 228}
 229
 230static void sii8620_mt_cleanup(struct sii8620 *ctx)
 231{
 232	struct sii8620_mt_msg *msg, *n;
 233
 234	list_for_each_entry_safe(msg, n, &ctx->mt_queue, node) {
 235		list_del(&msg->node);
 236		kfree(msg);
 237	}
 238	ctx->mt_state = MT_STATE_READY;
 239}
 240
 241static void sii8620_mt_work(struct sii8620 *ctx)
 242{
 243	struct sii8620_mt_msg *msg;
 244
 245	if (ctx->error)
 246		return;
 247	if (ctx->mt_state == MT_STATE_BUSY || list_empty(&ctx->mt_queue))
 248		return;
 249
 250	if (ctx->mt_state == MT_STATE_DONE) {
 251		ctx->mt_state = MT_STATE_READY;
 252		msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg,
 253				       node);
 254		if (msg->recv)
 255			msg->recv(ctx, msg);
 256		list_del(&msg->node);
 257		kfree(msg);
 258	}
 259
 260	if (ctx->mt_state != MT_STATE_READY || list_empty(&ctx->mt_queue))
 261		return;
 262
 263	ctx->mt_state = MT_STATE_BUSY;
 264	msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node);
 265	if (msg->send)
 266		msg->send(ctx, msg);
 267}
 268
 269static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx,
 270				    struct sii8620_mt_msg *msg)
 271{
 272	switch (msg->reg[0]) {
 273	case MHL_WRITE_STAT:
 274	case MHL_SET_INT:
 275		sii8620_write_buf(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg + 1, 2);
 276		sii8620_write(ctx, REG_MSC_COMMAND_START,
 277			      BIT_MSC_COMMAND_START_WRITE_STAT);
 278		break;
 279	case MHL_MSC_MSG:
 280		sii8620_write_buf(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg, 3);
 281		sii8620_write(ctx, REG_MSC_COMMAND_START,
 282			      BIT_MSC_COMMAND_START_MSC_MSG);
 283		break;
 284	default:
 285		dev_err(ctx->dev, "%s: command %#x not supported\n", __func__,
 286			msg->reg[0]);
 287	}
 288}
 289
 290static struct sii8620_mt_msg *sii8620_mt_msg_new(struct sii8620 *ctx)
 291{
 292	struct sii8620_mt_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
 293
 294	if (!msg)
 295		ctx->error = -ENOMEM;
 296	else
 297		list_add_tail(&msg->node, &ctx->mt_queue);
 298
 299	return msg;
 300}
 301
 302static void sii8620_mt_msc_cmd(struct sii8620 *ctx, u8 cmd, u8 arg1, u8 arg2)
 303{
 304	struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx);
 305
 306	if (!msg)
 307		return;
 308
 309	msg->reg[0] = cmd;
 310	msg->reg[1] = arg1;
 311	msg->reg[2] = arg2;
 312	msg->send = sii8620_mt_msc_cmd_send;
 313}
 314
 315static void sii8620_mt_write_stat(struct sii8620 *ctx, u8 reg, u8 val)
 316{
 317	sii8620_mt_msc_cmd(ctx, MHL_WRITE_STAT, reg, val);
 318}
 319
 320static inline void sii8620_mt_set_int(struct sii8620 *ctx, u8 irq, u8 mask)
 321{
 322	sii8620_mt_msc_cmd(ctx, MHL_SET_INT, irq, mask);
 323}
 324
 325static void sii8620_mt_msc_msg(struct sii8620 *ctx, u8 cmd, u8 data)
 326{
 327	sii8620_mt_msc_cmd(ctx, MHL_MSC_MSG, cmd, data);
 328}
 329
 330static void sii8620_mt_rap(struct sii8620 *ctx, u8 code)
 331{
 332	sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code);
 333}
 334
 335static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
 336					struct sii8620_mt_msg *msg)
 337{
 338	u8 ctrl = BIT_EDID_CTRL_DEVCAP_SELECT_DEVCAP
 339			| BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO
 340			| BIT_EDID_CTRL_EDID_MODE_EN;
 341
 342	if (msg->reg[0] == MHL_READ_XDEVCAP)
 343		ctrl |= BIT_EDID_CTRL_XDEVCAP_EN;
 344
 345	sii8620_write_seq(ctx,
 346		REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE,
 347		REG_EDID_CTRL, ctrl,
 348		REG_TPI_CBUS_START, BIT_TPI_CBUS_START_GET_DEVCAP_START
 349	);
 350}
 351
 352/* copy src to dst and set changed bits in src */
 353static void sii8620_update_array(u8 *dst, u8 *src, int count)
 354{
 355	while (--count >= 0) {
 356		*src ^= *dst;
 357		*dst++ ^= *src++;
 358	}
 359}
 360
 361static void sii8620_mr_devcap(struct sii8620 *ctx)
 362{
 363	static const char * const sink_str[] = {
 364		[SINK_NONE] = "NONE",
 365		[SINK_HDMI] = "HDMI",
 366		[SINK_DVI] = "DVI"
 367	};
 368
 369	u8 dcap[MHL_DCAP_SIZE];
 370	char sink_name[20];
 371	struct device *dev = ctx->dev;
 372
 373	sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE);
 374	if (ctx->error < 0)
 375		return;
 376
 377	dev_info(dev, "dcap: %*ph\n", MHL_DCAP_SIZE, dcap);
 378	dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n",
 379		 dcap[MHL_DCAP_MHL_VERSION] / 16,
 380		 dcap[MHL_DCAP_MHL_VERSION] % 16, dcap[MHL_DCAP_ADOPTER_ID_H],
 381		 dcap[MHL_DCAP_ADOPTER_ID_L], dcap[MHL_DCAP_DEVICE_ID_H],
 382		 dcap[MHL_DCAP_DEVICE_ID_L]);
 383	sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE);
 384
 385	if (!(dcap[MHL_DCAP_CAT] & MHL_DCAP_CAT_SINK))
 386		return;
 387
 388	sii8620_fetch_edid(ctx);
 389	if (!ctx->edid) {
 390		dev_err(ctx->dev, "Cannot fetch EDID\n");
 391		sii8620_mhl_disconnected(ctx);
 392		return;
 393	}
 394
 395	if (drm_detect_hdmi_monitor(ctx->edid))
 396		ctx->sink_type = SINK_HDMI;
 397	else
 398		ctx->sink_type = SINK_DVI;
 399
 400	drm_edid_get_monitor_name(ctx->edid, sink_name, ARRAY_SIZE(sink_name));
 401
 402	dev_info(dev, "detected sink(type: %s): %s\n",
 403		 sink_str[ctx->sink_type], sink_name);
 404	sii8620_set_upstream_edid(ctx);
 405	sii8620_enable_hpd(ctx);
 406}
 407
 408static void sii8620_mr_xdevcap(struct sii8620 *ctx)
 409{
 410	sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, ctx->xdevcap,
 411			 MHL_XDC_SIZE);
 412
 413	sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE),
 414			      MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT);
 415	sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP);
 416}
 417
 418static void sii8620_mt_read_devcap_recv(struct sii8620 *ctx,
 419					struct sii8620_mt_msg *msg)
 420{
 421	u8 ctrl = BIT_EDID_CTRL_DEVCAP_SELECT_DEVCAP
 422		| BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO
 423		| BIT_EDID_CTRL_EDID_MODE_EN;
 424
 425	if (msg->reg[0] == MHL_READ_XDEVCAP)
 426		ctrl |= BIT_EDID_CTRL_XDEVCAP_EN;
 427
 428	sii8620_write_seq(ctx,
 429		REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE | BIT_INTR9_EDID_DONE
 430			| BIT_INTR9_EDID_ERROR,
 431		REG_EDID_CTRL, ctrl,
 432		REG_EDID_FIFO_ADDR, 0
 433	);
 434
 435	if (msg->reg[0] == MHL_READ_XDEVCAP)
 436		sii8620_mr_xdevcap(ctx);
 437	else
 438		sii8620_mr_devcap(ctx);
 439}
 440
 441static void sii8620_mt_read_devcap(struct sii8620 *ctx, bool xdevcap)
 442{
 443	struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx);
 444
 445	if (!msg)
 446		return;
 447
 448	msg->reg[0] = xdevcap ? MHL_READ_XDEVCAP : MHL_READ_DEVCAP;
 449	msg->send = sii8620_mt_read_devcap_send;
 450	msg->recv = sii8620_mt_read_devcap_recv;
 451}
 452
 453static void sii8620_fetch_edid(struct sii8620 *ctx)
 454{
 455	u8 lm_ddc, ddc_cmd, int3, cbus;
 456	int fetched, i;
 457	int edid_len = EDID_LENGTH;
 458	u8 *edid;
 459
 460	sii8620_readb(ctx, REG_CBUS_STATUS);
 461	lm_ddc = sii8620_readb(ctx, REG_LM_DDC);
 462	ddc_cmd = sii8620_readb(ctx, REG_DDC_CMD);
 463
 464	sii8620_write_seq(ctx,
 465		REG_INTR9_MASK, 0,
 466		REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO,
 467		REG_HDCP2X_POLL_CS, 0x71,
 468		REG_HDCP2X_CTRL_0, BIT_HDCP2X_CTRL_0_HDCP2X_HDCPTX,
 469		REG_LM_DDC, lm_ddc | BIT_LM_DDC_SW_TPI_EN_DISABLED,
 470	);
 471
 472	for (i = 0; i < 256; ++i) {
 473		u8 ddc_stat = sii8620_readb(ctx, REG_DDC_STATUS);
 474
 475		if (!(ddc_stat & BIT_DDC_STATUS_DDC_I2C_IN_PROG))
 476			break;
 477		sii8620_write(ctx, REG_DDC_STATUS,
 478			      BIT_DDC_STATUS_DDC_FIFO_EMPTY);
 479	}
 480
 481	sii8620_write(ctx, REG_DDC_ADDR, 0x50 << 1);
 482
 483	edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
 484	if (!edid) {
 485		ctx->error = -ENOMEM;
 486		return;
 487	}
 488
 489#define FETCH_SIZE 16
 490	for (fetched = 0; fetched < edid_len; fetched += FETCH_SIZE) {
 491		sii8620_readb(ctx, REG_DDC_STATUS);
 492		sii8620_write_seq(ctx,
 493			REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_DDC_CMD_ABORT,
 494			REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_DDC_CMD_CLEAR_FIFO,
 495			REG_DDC_STATUS, BIT_DDC_STATUS_DDC_FIFO_EMPTY
 496		);
 497		sii8620_write_seq(ctx,
 498			REG_DDC_SEGM, fetched >> 8,
 499			REG_DDC_OFFSET, fetched & 0xff,
 500			REG_DDC_DIN_CNT1, FETCH_SIZE,
 501			REG_DDC_DIN_CNT2, 0,
 502			REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_ENH_DDC_READ_NO_ACK
 503		);
 504
 505		do {
 506			int3 = sii8620_readb(ctx, REG_INTR3);
 507			cbus = sii8620_readb(ctx, REG_CBUS_STATUS);
 508
 509			if (int3 & BIT_DDC_CMD_DONE)
 510				break;
 511
 512			if (!(cbus & BIT_CBUS_STATUS_CBUS_CONNECTED)) {
 513				kfree(edid);
 514				edid = NULL;
 515				goto end;
 516			}
 517		} while (1);
 518
 519		sii8620_readb(ctx, REG_DDC_STATUS);
 520		while (sii8620_readb(ctx, REG_DDC_DOUT_CNT) < FETCH_SIZE)
 521			usleep_range(10, 20);
 522
 523		sii8620_read_buf(ctx, REG_DDC_DATA, edid + fetched, FETCH_SIZE);
 524		if (fetched + FETCH_SIZE == EDID_LENGTH) {
 525			u8 ext = ((struct edid *)edid)->extensions;
 526
 527			if (ext) {
 528				u8 *new_edid;
 529
 530				edid_len += ext * EDID_LENGTH;
 531				new_edid = krealloc(edid, edid_len, GFP_KERNEL);
 532				if (!new_edid) {
 533					kfree(edid);
 534					ctx->error = -ENOMEM;
 535					return;
 536				}
 537				edid = new_edid;
 538			}
 539		}
 540
 541		if (fetched + FETCH_SIZE == edid_len)
 542			sii8620_write(ctx, REG_INTR3, int3);
 543	}
 544
 545	sii8620_write(ctx, REG_LM_DDC, lm_ddc);
 546
 547end:
 548	kfree(ctx->edid);
 549	ctx->edid = (struct edid *)edid;
 550}
 551
 552static void sii8620_set_upstream_edid(struct sii8620 *ctx)
 553{
 554	sii8620_setbits(ctx, REG_DPD, BIT_DPD_PDNRX12 | BIT_DPD_PDIDCK_N
 555			| BIT_DPD_PD_MHL_CLK_N, 0xff);
 556
 557	sii8620_write_seq_static(ctx,
 558		REG_RX_HDMI_CTRL3, 0x00,
 559		REG_PKT_FILTER_0, 0xFF,
 560		REG_PKT_FILTER_1, 0xFF,
 561		REG_ALICE0_BW_I2C, 0x06
 562	);
 563
 564	sii8620_setbits(ctx, REG_RX_HDMI_CLR_BUFFER,
 565			BIT_RX_HDMI_CLR_BUFFER_VSI_CLR_EN, 0xff);
 566
 567	sii8620_write_seq_static(ctx,
 568		REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO
 569			| BIT_EDID_CTRL_EDID_MODE_EN,
 570		REG_EDID_FIFO_ADDR, 0,
 571	);
 572
 573	sii8620_write_buf(ctx, REG_EDID_FIFO_WR_DATA, (u8 *)ctx->edid,
 574			  (ctx->edid->extensions + 1) * EDID_LENGTH);
 575
 576	sii8620_write_seq_static(ctx,
 577		REG_EDID_CTRL, BIT_EDID_CTRL_EDID_PRIME_VALID
 578			| BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO
 579			| BIT_EDID_CTRL_EDID_MODE_EN,
 580		REG_INTR5_MASK, BIT_INTR_SCDT_CHANGE,
 581		REG_INTR9_MASK, 0
 582	);
 583}
 584
 585static void sii8620_xtal_set_rate(struct sii8620 *ctx)
 586{
 587	static const struct {
 588		unsigned int rate;
 589		u8 div;
 590		u8 tp1;
 591	} rates[] = {
 592		{ 19200, 0x04, 0x53 },
 593		{ 20000, 0x04, 0x62 },
 594		{ 24000, 0x05, 0x75 },
 595		{ 30000, 0x06, 0x92 },
 596		{ 38400, 0x0c, 0xbc },
 597	};
 598	unsigned long rate = clk_get_rate(ctx->clk_xtal) / 1000;
 599	int i;
 600
 601	for (i = 0; i < ARRAY_SIZE(rates) - 1; ++i)
 602		if (rate <= rates[i].rate)
 603			break;
 604
 605	if (rate != rates[i].rate)
 606		dev_err(ctx->dev, "xtal clock rate(%lukHz) not supported, setting MHL for %ukHz.\n",
 607			rate, rates[i].rate);
 608
 609	sii8620_write(ctx, REG_DIV_CTL_MAIN, rates[i].div);
 610	sii8620_write(ctx, REG_HDCP2X_TP1, rates[i].tp1);
 611}
 612
 613static int sii8620_hw_on(struct sii8620 *ctx)
 614{
 615	int ret;
 616
 617	ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 618	if (ret)
 619		return ret;
 620	usleep_range(10000, 20000);
 621	return clk_prepare_enable(ctx->clk_xtal);
 622}
 623
 624static int sii8620_hw_off(struct sii8620 *ctx)
 625{
 626	clk_disable_unprepare(ctx->clk_xtal);
 627	gpiod_set_value(ctx->gpio_reset, 1);
 628	return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
 629}
 630
 631static void sii8620_hw_reset(struct sii8620 *ctx)
 632{
 633	usleep_range(10000, 20000);
 634	gpiod_set_value(ctx->gpio_reset, 0);
 635	usleep_range(5000, 20000);
 636	gpiod_set_value(ctx->gpio_reset, 1);
 637	usleep_range(10000, 20000);
 638	gpiod_set_value(ctx->gpio_reset, 0);
 639	msleep(300);
 640}
 641
 642static void sii8620_cbus_reset(struct sii8620 *ctx)
 643{
 644	sii8620_write_seq_static(ctx,
 645		REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST
 646			| BIT_PWD_SRST_CBUS_RST_SW_EN,
 647		REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN
 648	);
 649}
 650
 651static void sii8620_set_auto_zone(struct sii8620 *ctx)
 652{
 653	if (ctx->mode != CM_MHL1) {
 654		sii8620_write_seq_static(ctx,
 655			REG_TX_ZONE_CTL1, 0x0,
 656			REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X
 657				| BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL
 658				| BIT_MHL_PLL_CTL0_ZONE_MASK_OE
 659		);
 660	} else {
 661		sii8620_write_seq_static(ctx,
 662			REG_TX_ZONE_CTL1, VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE,
 663			REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X
 664				| BIT_MHL_PLL_CTL0_ZONE_MASK_OE
 665		);
 666	}
 667}
 668
 669static void sii8620_stop_video(struct sii8620 *ctx)
 670{
 671	u8 uninitialized_var(val);
 672
 673	sii8620_write_seq_static(ctx,
 674		REG_TPI_INTR_EN, 0,
 675		REG_HDCP2X_INTR0_MASK, 0,
 676		REG_TPI_COPP_DATA2, 0,
 677		REG_TPI_INTR_ST0, ~0,
 678	);
 679
 680	switch (ctx->sink_type) {
 681	case SINK_DVI:
 682		val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN
 683			| BIT_TPI_SC_TPI_AV_MUTE;
 684		break;
 685	case SINK_HDMI:
 686		val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN
 687			| BIT_TPI_SC_TPI_AV_MUTE
 688			| BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI;
 689		break;
 690	default:
 691		return;
 692	}
 693
 694	sii8620_write(ctx, REG_TPI_SC, val);
 695}
 696
 697static void sii8620_start_hdmi(struct sii8620 *ctx)
 698{
 699	sii8620_write_seq_static(ctx,
 700		REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL
 701			| BIT_RX_HDMI_CTRL2_USE_AV_MUTE,
 702		REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE
 703			| BIT_VID_OVRRD_M1080P_OVRRD,
 704		REG_VID_MODE, 0,
 705		REG_MHL_TOP_CTL, 0x1,
 706		REG_MHLTX_CTL6, 0xa0,
 707		REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL),
 708		REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL),
 709	);
 710
 711	sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
 712			      MHL_DST_LM_CLK_MODE_NORMAL |
 713			      MHL_DST_LM_PATH_ENABLED);
 714
 715	sii8620_set_auto_zone(ctx);
 716
 717	sii8620_write(ctx, REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI);
 718
 719	sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif,
 720			  ARRAY_SIZE(ctx->avif));
 721
 722	sii8620_write(ctx, REG_PKT_FILTER_0, 0xa1, 0x2);
 723}
 724
 725static void sii8620_start_video(struct sii8620 *ctx)
 726{
 727	if (ctx->mode < CM_MHL3)
 728		sii8620_stop_video(ctx);
 729
 730	switch (ctx->sink_type) {
 731	case SINK_HDMI:
 732		sii8620_start_hdmi(ctx);
 733		break;
 734	case SINK_DVI:
 735	default:
 736		break;
 737	}
 738}
 739
 740static void sii8620_disable_hpd(struct sii8620 *ctx)
 741{
 742	sii8620_setbits(ctx, REG_EDID_CTRL, BIT_EDID_CTRL_EDID_PRIME_VALID, 0);
 743	sii8620_write_seq_static(ctx,
 744		REG_HPD_CTRL, BIT_HPD_CTRL_HPD_OUT_OVR_EN,
 745		REG_INTR8_MASK, 0
 746	);
 747}
 748
 749static void sii8620_enable_hpd(struct sii8620 *ctx)
 750{
 751	sii8620_setbits(ctx, REG_TMDS_CSTAT_P3,
 752			BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS
 753			| BIT_TMDS_CSTAT_P3_CLR_AVI, ~0);
 754	sii8620_write_seq_static(ctx,
 755		REG_HPD_CTRL, BIT_HPD_CTRL_HPD_OUT_OVR_EN
 756			| BIT_HPD_CTRL_HPD_HIGH,
 757	);
 758}
 759
 760static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx)
 761{
 762	if (ctx->gen2_write_burst)
 763		return;
 764
 765	sii8620_write_seq_static(ctx,
 766		REG_MDT_RCV_TIMEOUT, 100,
 767		REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN
 768	);
 769	ctx->gen2_write_burst = 1;
 770}
 771
 772static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx)
 773{
 774	if (!ctx->gen2_write_burst)
 775		return;
 776
 777	sii8620_write_seq_static(ctx,
 778		REG_MDT_XMIT_CTRL, 0,
 779		REG_MDT_RCV_CTRL, 0
 780	);
 781	ctx->gen2_write_burst = 0;
 782}
 783
 784static void sii8620_start_gen2_write_burst(struct sii8620 *ctx)
 785{
 786	sii8620_write_seq_static(ctx,
 787		REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT
 788			| BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR
 789			| BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD
 790			| BIT_MDT_XMIT_SM_ERROR,
 791		REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY
 792			| BIT_MDT_IDLE_AFTER_HAWB_DISABLE
 793			| BIT_MDT_RFIFO_DATA_RDY
 794	);
 795	sii8620_enable_gen2_write_burst(ctx);
 796}
 797
 798static void sii8620_mhl_discover(struct sii8620 *ctx)
 799{
 800	sii8620_write_seq_static(ctx,
 801		REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT
 802			| BIT_DISC_CTRL9_DISC_PULSE_PROCEED,
 803		REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_5K, VAL_PUP_20K),
 804		REG_CBUS_DISC_INTR0_MASK, BIT_MHL3_EST_INT
 805			| BIT_MHL_EST_INT
 806			| BIT_NOT_MHL_EST_INT
 807			| BIT_CBUS_MHL3_DISCON_INT
 808			| BIT_CBUS_MHL12_DISCON_INT
 809			| BIT_RGND_READY_INT,
 810		REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X
 811			| BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL
 812			| BIT_MHL_PLL_CTL0_ZONE_MASK_OE,
 813		REG_MHL_DP_CTL0, BIT_MHL_DP_CTL0_DP_OE
 814			| BIT_MHL_DP_CTL0_TX_OE_OVR,
 815		REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE,
 816		REG_MHL_DP_CTL1, 0xA2,
 817		REG_MHL_DP_CTL2, 0x03,
 818		REG_MHL_DP_CTL3, 0x35,
 819		REG_MHL_DP_CTL5, 0x02,
 820		REG_MHL_DP_CTL6, 0x02,
 821		REG_MHL_DP_CTL7, 0x03,
 822		REG_COC_CTLC, 0xFF,
 823		REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12
 824			| BIT_DPD_OSC_EN | BIT_DPD_PWRON_HSIC,
 825		REG_COC_INTR_MASK, BIT_COC_PLL_LOCK_STATUS_CHANGE
 826			| BIT_COC_CALIBRATION_DONE,
 827		REG_CBUS_INT_1_MASK, BIT_CBUS_MSC_ABORT_RCVD
 828			| BIT_CBUS_CMD_ABORT,
 829		REG_CBUS_INT_0_MASK, BIT_CBUS_MSC_MT_DONE
 830			| BIT_CBUS_HPD_CHG
 831			| BIT_CBUS_MSC_MR_WRITE_STAT
 832			| BIT_CBUS_MSC_MR_MSC_MSG
 833			| BIT_CBUS_MSC_MR_WRITE_BURST
 834			| BIT_CBUS_MSC_MR_SET_INT
 835			| BIT_CBUS_MSC_MT_DONE_NACK
 836	);
 837}
 838
 839static void sii8620_peer_specific_init(struct sii8620 *ctx)
 840{
 841	if (ctx->mode == CM_MHL3)
 842		sii8620_write_seq_static(ctx,
 843			REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD,
 844			REG_EMSCINTRMASK1,
 845				BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR
 846		);
 847	else
 848		sii8620_write_seq_static(ctx,
 849			REG_HDCP2X_INTR0_MASK, 0x00,
 850			REG_EMSCINTRMASK1, 0x00,
 851			REG_HDCP2X_INTR0, 0xFF,
 852			REG_INTR1, 0xFF,
 853			REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD
 854				| BIT_SYS_CTRL1_TX_CTRL_HDMI
 855		);
 856}
 857
 858#define SII8620_MHL_VERSION			0x32
 859#define SII8620_SCRATCHPAD_SIZE			16
 860#define SII8620_INT_STAT_SIZE			0x33
 861
 862static void sii8620_set_dev_cap(struct sii8620 *ctx)
 863{
 864	static const u8 devcap[MHL_DCAP_SIZE] = {
 865		[MHL_DCAP_MHL_VERSION] = SII8620_MHL_VERSION,
 866		[MHL_DCAP_CAT] = MHL_DCAP_CAT_SOURCE | MHL_DCAP_CAT_POWER,
 867		[MHL_DCAP_ADOPTER_ID_H] = 0x01,
 868		[MHL_DCAP_ADOPTER_ID_L] = 0x41,
 869		[MHL_DCAP_VID_LINK_MODE] = MHL_DCAP_VID_LINK_RGB444
 870			| MHL_DCAP_VID_LINK_PPIXEL
 871			| MHL_DCAP_VID_LINK_16BPP,
 872		[MHL_DCAP_AUD_LINK_MODE] = MHL_DCAP_AUD_LINK_2CH,
 873		[MHL_DCAP_VIDEO_TYPE] = MHL_DCAP_VT_GRAPHICS,
 874		[MHL_DCAP_LOG_DEV_MAP] = MHL_DCAP_LD_GUI,
 875		[MHL_DCAP_BANDWIDTH] = 0x0f,
 876		[MHL_DCAP_FEATURE_FLAG] = MHL_DCAP_FEATURE_RCP_SUPPORT
 877			| MHL_DCAP_FEATURE_RAP_SUPPORT
 878			| MHL_DCAP_FEATURE_SP_SUPPORT,
 879		[MHL_DCAP_SCRATCHPAD_SIZE] = SII8620_SCRATCHPAD_SIZE,
 880		[MHL_DCAP_INT_STAT_SIZE] = SII8620_INT_STAT_SIZE,
 881	};
 882	static const u8 xdcap[MHL_XDC_SIZE] = {
 883		[MHL_XDC_ECBUS_SPEEDS] = MHL_XDC_ECBUS_S_075
 884			| MHL_XDC_ECBUS_S_8BIT,
 885		[MHL_XDC_TMDS_SPEEDS] = MHL_XDC_TMDS_150
 886			| MHL_XDC_TMDS_300 | MHL_XDC_TMDS_600,
 887		[MHL_XDC_ECBUS_ROLES] = MHL_XDC_DEV_HOST,
 888		[MHL_XDC_LOG_DEV_MAPX] = MHL_XDC_LD_PHONE,
 889	};
 890
 891	sii8620_write_buf(ctx, REG_MHL_DEVCAP_0, devcap, ARRAY_SIZE(devcap));
 892	sii8620_write_buf(ctx, REG_MHL_EXTDEVCAP_0, xdcap, ARRAY_SIZE(xdcap));
 893}
 894
 895static void sii8620_mhl_init(struct sii8620 *ctx)
 896{
 897	sii8620_write_seq_static(ctx,
 898		REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_OFF, VAL_PUP_20K),
 899		REG_CBUS_MSC_COMPAT_CTRL,
 900			BIT_CBUS_MSC_COMPAT_CTRL_XDEVCAP_EN,
 901	);
 902
 903	sii8620_peer_specific_init(ctx);
 904
 905	sii8620_disable_hpd(ctx);
 906
 907	sii8620_write_seq_static(ctx,
 908		REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO,
 909		REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT
 910			| BIT_DISC_CTRL9_WAKE_PULSE_BYPASS,
 911		REG_TMDS0_CCTRL1, 0x90,
 912		REG_TMDS_CLK_EN, 0x01,
 913		REG_TMDS_CH_EN, 0x11,
 914		REG_BGR_BIAS, 0x87,
 915		REG_ALICE0_ZONE_CTRL, 0xE8,
 916		REG_ALICE0_MODE_CTRL, 0x04,
 917	);
 918	sii8620_setbits(ctx, REG_LM_DDC, BIT_LM_DDC_SW_TPI_EN_DISABLED, 0);
 919	sii8620_write_seq_static(ctx,
 920		REG_TPI_HW_OPT3, 0x76,
 921		REG_TMDS_CCTRL, BIT_TMDS_CCTRL_TMDS_OE,
 922		REG_TPI_DTD_B2, 79,
 923	);
 924	sii8620_set_dev_cap(ctx);
 925	sii8620_write_seq_static(ctx,
 926		REG_MDT_XMIT_TIMEOUT, 100,
 927		REG_MDT_XMIT_CTRL, 0x03,
 928		REG_MDT_XFIFO_STAT, 0x00,
 929		REG_MDT_RCV_TIMEOUT, 100,
 930		REG_CBUS_LINK_CTRL_8, 0x1D,
 931	);
 932
 933	sii8620_start_gen2_write_burst(ctx);
 934	sii8620_write_seq_static(ctx,
 935		REG_BIST_CTRL, 0x00,
 936		REG_COC_CTL1, 0x10,
 937		REG_COC_CTL2, 0x18,
 938		REG_COC_CTLF, 0x07,
 939		REG_COC_CTL11, 0xF8,
 940		REG_COC_CTL17, 0x61,
 941		REG_COC_CTL18, 0x46,
 942		REG_COC_CTL19, 0x15,
 943		REG_COC_CTL1A, 0x01,
 944		REG_MHL_COC_CTL3, BIT_MHL_COC_CTL3_COC_AECHO_EN,
 945		REG_MHL_COC_CTL4, 0x2D,
 946		REG_MHL_COC_CTL5, 0xF9,
 947		REG_MSC_HEARTBEAT_CTRL, 0x27,
 948	);
 949	sii8620_disable_gen2_write_burst(ctx);
 950
 951	/* currently MHL3 is not supported, so we force version to 0 */
 952	sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), 0);
 953	sii8620_mt_write_stat(ctx, MHL_DST_REG(CONNECTED_RDY),
 954			      MHL_DST_CONN_DCAP_RDY | MHL_DST_CONN_XDEVCAPP_SUPP
 955			      | MHL_DST_CONN_POW_STAT);
 956	sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), MHL_INT_RC_DCAP_CHG);
 957}
 958
 959static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode)
 960{
 961	if (ctx->mode == mode)
 962		return;
 963
 964	ctx->mode = mode;
 965
 966	switch (mode) {
 967	case CM_MHL1:
 968		sii8620_write_seq_static(ctx,
 969			REG_CBUS_MSC_COMPAT_CTRL, 0x02,
 970			REG_M3_CTRL, VAL_M3_CTRL_MHL1_2_VALUE,
 971			REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12
 972				| BIT_DPD_OSC_EN,
 973			REG_COC_INTR_MASK, 0
 974		);
 975		break;
 976	case CM_MHL3:
 977		sii8620_write_seq_static(ctx,
 978			REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE,
 979			REG_COC_CTL0, 0x40,
 980			REG_MHL_COC_CTL1, 0x07
 981		);
 982		break;
 983	case CM_DISCONNECTED:
 984		break;
 985	default:
 986		dev_err(ctx->dev, "%s mode %d not supported\n", __func__, mode);
 987		break;
 988	}
 989
 990	sii8620_set_auto_zone(ctx);
 991
 992	if (mode != CM_MHL1)
 993		return;
 994
 995	sii8620_write_seq_static(ctx,
 996		REG_MHL_DP_CTL0, 0xBC,
 997		REG_MHL_DP_CTL1, 0xBB,
 998		REG_MHL_DP_CTL3, 0x48,
 999		REG_MHL_DP_CTL5, 0x39,
1000		REG_MHL_DP_CTL2, 0x2A,
1001		REG_MHL_DP_CTL6, 0x2A,
1002		REG_MHL_DP_CTL7, 0x08
1003	);
1004}
1005
1006static void sii8620_disconnect(struct sii8620 *ctx)
1007{
1008	sii8620_disable_gen2_write_burst(ctx);
1009	sii8620_stop_video(ctx);
1010	msleep(50);
1011	sii8620_cbus_reset(ctx);
1012	sii8620_set_mode(ctx, CM_DISCONNECTED);
1013	sii8620_write_seq_static(ctx,
1014		REG_COC_CTL0, 0x40,
1015		REG_CBUS3_CNVT, 0x84,
1016		REG_COC_CTL14, 0x00,
1017		REG_COC_CTL0, 0x40,
1018		REG_HRXCTRL3, 0x07,
1019		REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X
1020			| BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL
1021			| BIT_MHL_PLL_CTL0_ZONE_MASK_OE,
1022		REG_MHL_DP_CTL0, BIT_MHL_DP_CTL0_DP_OE
1023			| BIT_MHL_DP_CTL0_TX_OE_OVR,
1024		REG_MHL_DP_CTL1, 0xBB,
1025		REG_MHL_DP_CTL3, 0x48,
1026		REG_MHL_DP_CTL5, 0x3F,
1027		REG_MHL_DP_CTL2, 0x2F,
1028		REG_MHL_DP_CTL6, 0x2A,
1029		REG_MHL_DP_CTL7, 0x03
1030	);
1031	sii8620_disable_hpd(ctx);
1032	sii8620_write_seq_static(ctx,
1033		REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE,
1034		REG_MHL_COC_CTL1, 0x07,
1035		REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_OFF, VAL_PUP_20K),
1036		REG_DISC_CTRL8, 0x00,
1037		REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT
1038			| BIT_DISC_CTRL9_WAKE_PULSE_BYPASS,
1039		REG_INT_CTRL, 0x00,
1040		REG_MSC_HEARTBEAT_CTRL, 0x27,
1041		REG_DISC_CTRL1, 0x25,
1042		REG_CBUS_DISC_INTR0, (u8)~BIT_RGND_READY_INT,
1043		REG_CBUS_DISC_INTR0_MASK, BIT_RGND_READY_INT,
1044		REG_MDT_INT_1, 0xff,
1045		REG_MDT_INT_1_MASK, 0x00,
1046		REG_MDT_INT_0, 0xff,
1047		REG_MDT_INT_0_MASK, 0x00,
1048		REG_COC_INTR, 0xff,
1049		REG_COC_INTR_MASK, 0x00,
1050		REG_TRXINTH, 0xff,
1051		REG_TRXINTMH, 0x00,
1052		REG_CBUS_INT_0, 0xff,
1053		REG_CBUS_INT_0_MASK, 0x00,
1054		REG_CBUS_INT_1, 0xff,
1055		REG_CBUS_INT_1_MASK, 0x00,
1056		REG_EMSCINTR, 0xff,
1057		REG_EMSCINTRMASK, 0x00,
1058		REG_EMSCINTR1, 0xff,
1059		REG_EMSCINTRMASK1, 0x00,
1060		REG_INTR8, 0xff,
1061		REG_INTR8_MASK, 0x00,
1062		REG_TPI_INTR_ST0, 0xff,
1063		REG_TPI_INTR_EN, 0x00,
1064		REG_HDCP2X_INTR0, 0xff,
1065		REG_HDCP2X_INTR0_MASK, 0x00,
1066		REG_INTR9, 0xff,
1067		REG_INTR9_MASK, 0x00,
1068		REG_INTR3, 0xff,
1069		REG_INTR3_MASK, 0x00,
1070		REG_INTR5, 0xff,
1071		REG_INTR5_MASK, 0x00,
1072		REG_INTR2, 0xff,
1073		REG_INTR2_MASK, 0x00,
1074	);
1075	memset(ctx->stat, 0, sizeof(ctx->stat));
1076	memset(ctx->xstat, 0, sizeof(ctx->xstat));
1077	memset(ctx->devcap, 0, sizeof(ctx->devcap));
1078	memset(ctx->xdevcap, 0, sizeof(ctx->xdevcap));
1079	ctx->cbus_status = 0;
1080	ctx->sink_type = SINK_NONE;
1081	kfree(ctx->edid);
1082	ctx->edid = NULL;
1083	sii8620_mt_cleanup(ctx);
1084}
1085
1086static void sii8620_mhl_disconnected(struct sii8620 *ctx)
1087{
1088	sii8620_write_seq_static(ctx,
1089		REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_OFF, VAL_PUP_20K),
1090		REG_CBUS_MSC_COMPAT_CTRL,
1091			BIT_CBUS_MSC_COMPAT_CTRL_XDEVCAP_EN
1092	);
1093	sii8620_disconnect(ctx);
1094}
1095
1096static void sii8620_irq_disc(struct sii8620 *ctx)
1097{
1098	u8 stat = sii8620_readb(ctx, REG_CBUS_DISC_INTR0);
1099
1100	if (stat & VAL_CBUS_MHL_DISCON)
1101		sii8620_mhl_disconnected(ctx);
1102
1103	if (stat & BIT_RGND_READY_INT) {
1104		u8 stat2 = sii8620_readb(ctx, REG_DISC_STAT2);
1105
1106		if ((stat2 & MSK_DISC_STAT2_RGND) == VAL_RGND_1K) {
1107			sii8620_mhl_discover(ctx);
1108		} else {
1109			sii8620_write_seq_static(ctx,
1110				REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT
1111					| BIT_DISC_CTRL9_NOMHL_EST
1112					| BIT_DISC_CTRL9_WAKE_PULSE_BYPASS,
1113				REG_CBUS_DISC_INTR0_MASK, BIT_RGND_READY_INT
1114					| BIT_CBUS_MHL3_DISCON_INT
1115					| BIT_CBUS_MHL12_DISCON_INT
1116					| BIT_NOT_MHL_EST_INT
1117			);
1118		}
1119	}
1120	if (stat & BIT_MHL_EST_INT)
1121		sii8620_mhl_init(ctx);
1122
1123	sii8620_write(ctx, REG_CBUS_DISC_INTR0, stat);
1124}
1125
1126static void sii8620_irq_g2wb(struct sii8620 *ctx)
1127{
1128	u8 stat = sii8620_readb(ctx, REG_MDT_INT_0);
1129
1130	if (stat & BIT_MDT_IDLE_AFTER_HAWB_DISABLE)
1131		dev_dbg(ctx->dev, "HAWB idle\n");
1132
1133	sii8620_write(ctx, REG_MDT_INT_0, stat);
1134}
1135
1136static void sii8620_status_changed_dcap(struct sii8620 *ctx)
1137{
1138	if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) {
1139		sii8620_set_mode(ctx, CM_MHL1);
1140		sii8620_peer_specific_init(ctx);
1141		sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE
1142			       | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR);
1143	}
1144}
1145
1146static void sii8620_status_changed_path(struct sii8620 *ctx)
1147{
1148	if (ctx->stat[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) {
1149		sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
1150				      MHL_DST_LM_CLK_MODE_NORMAL
1151				      | MHL_DST_LM_PATH_ENABLED);
1152		sii8620_mt_read_devcap(ctx, false);
1153	} else {
1154		sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE),
1155				      MHL_DST_LM_CLK_MODE_NORMAL);
1156	}
1157}
1158
1159static void sii8620_msc_mr_write_stat(struct sii8620 *ctx)
1160{
1161	u8 st[MHL_DST_SIZE], xst[MHL_XDS_SIZE];
1162
1163	sii8620_read_buf(ctx, REG_MHL_STAT_0, st, MHL_DST_SIZE);
1164	sii8620_read_buf(ctx, REG_MHL_EXTSTAT_0, xst, MHL_XDS_SIZE);
1165
1166	sii8620_update_array(ctx->stat, st, MHL_DST_SIZE);
1167	sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE);
1168
1169	if (st[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY)
1170		sii8620_status_changed_dcap(ctx);
1171
1172	if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED)
1173		sii8620_status_changed_path(ctx);
1174}
1175
1176static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
1177{
1178	u8 ints[MHL_INT_SIZE];
1179
1180	sii8620_read_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE);
1181	sii8620_write_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE);
1182}
1183
1184static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx)
1185{
1186	struct device *dev = ctx->dev;
1187
1188	if (list_empty(&ctx->mt_queue)) {
1189		dev_err(dev, "unexpected MSC MT response\n");
1190		return NULL;
1191	}
1192
1193	return list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node);
1194}
1195
1196static void sii8620_msc_mt_done(struct sii8620 *ctx)
1197{
1198	struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx);
1199
1200	if (!msg)
1201		return;
1202
1203	msg->ret = sii8620_readb(ctx, REG_MSC_MT_RCVD_DATA0);
1204	ctx->mt_state = MT_STATE_DONE;
1205}
1206
1207static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx)
1208{
1209	struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx);
1210	u8 buf[2];
1211
1212	if (!msg)
1213		return;
1214
1215	sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
1216
1217	switch (buf[0]) {
1218	case MHL_MSC_MSG_RAPK:
1219		msg->ret = buf[1];
1220		ctx->mt_state = MT_STATE_DONE;
1221		break;
1222	default:
1223		dev_err(ctx->dev, "%s message type %d,%d not supported",
1224			__func__, buf[0], buf[1]);
1225	}
1226}
1227
1228static void sii8620_irq_msc(struct sii8620 *ctx)
1229{
1230	u8 stat = sii8620_readb(ctx, REG_CBUS_INT_0);
1231
1232	if (stat & ~BIT_CBUS_HPD_CHG)
1233		sii8620_write(ctx, REG_CBUS_INT_0, stat & ~BIT_CBUS_HPD_CHG);
1234
1235	if (stat & BIT_CBUS_HPD_CHG) {
1236		u8 cbus_stat = sii8620_readb(ctx, REG_CBUS_STATUS);
1237
1238		if ((cbus_stat ^ ctx->cbus_status) & BIT_CBUS_STATUS_CBUS_HPD) {
1239			sii8620_write(ctx, REG_CBUS_INT_0, BIT_CBUS_HPD_CHG);
1240		} else {
1241			stat ^= BIT_CBUS_STATUS_CBUS_HPD;
1242			cbus_stat ^= BIT_CBUS_STATUS_CBUS_HPD;
1243		}
1244		ctx->cbus_status = cbus_stat;
1245	}
1246
1247	if (stat & BIT_CBUS_MSC_MR_WRITE_STAT)
1248		sii8620_msc_mr_write_stat(ctx);
1249
1250	if (stat & BIT_CBUS_MSC_MR_SET_INT)
1251		sii8620_msc_mr_set_int(ctx);
1252
1253	if (stat & BIT_CBUS_MSC_MT_DONE)
1254		sii8620_msc_mt_done(ctx);
1255
1256	if (stat & BIT_CBUS_MSC_MR_MSC_MSG)
1257		sii8620_msc_mr_msc_msg(ctx);
1258}
1259
1260static void sii8620_irq_coc(struct sii8620 *ctx)
1261{
1262	u8 stat = sii8620_readb(ctx, REG_COC_INTR);
1263
1264	sii8620_write(ctx, REG_COC_INTR, stat);
1265}
1266
1267static void sii8620_irq_merr(struct sii8620 *ctx)
1268{
1269	u8 stat = sii8620_readb(ctx, REG_CBUS_INT_1);
1270
1271	sii8620_write(ctx, REG_CBUS_INT_1, stat);
1272}
1273
1274static void sii8620_irq_edid(struct sii8620 *ctx)
1275{
1276	u8 stat = sii8620_readb(ctx, REG_INTR9);
1277
1278	sii8620_write(ctx, REG_INTR9, stat);
1279
1280	if (stat & BIT_INTR9_DEVCAP_DONE)
1281		ctx->mt_state = MT_STATE_DONE;
1282}
1283
1284static void sii8620_scdt_high(struct sii8620 *ctx)
1285{
1286	sii8620_write_seq_static(ctx,
1287		REG_INTR8_MASK, BIT_CEA_NEW_AVI | BIT_CEA_NEW_VSI,
1288		REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI,
1289	);
1290}
1291
1292static void sii8620_scdt_low(struct sii8620 *ctx)
1293{
1294	sii8620_write(ctx, REG_TMDS_CSTAT_P3,
1295		      BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS |
1296		      BIT_TMDS_CSTAT_P3_CLR_AVI);
1297
1298	sii8620_stop_video(ctx);
1299
1300	sii8620_write(ctx, REG_INTR8_MASK, 0);
1301}
1302
1303static void sii8620_irq_scdt(struct sii8620 *ctx)
1304{
1305	u8 stat = sii8620_readb(ctx, REG_INTR5);
1306
1307	if (stat & BIT_INTR_SCDT_CHANGE) {
1308		u8 cstat = sii8620_readb(ctx, REG_TMDS_CSTAT_P3);
1309
1310		if (cstat & BIT_TMDS_CSTAT_P3_SCDT)
1311			sii8620_scdt_high(ctx);
1312		else
1313			sii8620_scdt_low(ctx);
1314	}
1315
1316	sii8620_write(ctx, REG_INTR5, stat);
1317}
1318
1319static void sii8620_new_vsi(struct sii8620 *ctx)
1320{
1321	u8 vsif[11];
1322
1323	sii8620_write(ctx, REG_RX_HDMI_CTRL2,
1324		      VAL_RX_HDMI_CTRL2_DEFVAL |
1325		      BIT_RX_HDMI_CTRL2_VSI_MON_SEL_VSI);
1326	sii8620_read_buf(ctx, REG_RX_HDMI_MON_PKT_HEADER1, vsif,
1327			 ARRAY_SIZE(vsif));
1328}
1329
1330static void sii8620_new_avi(struct sii8620 *ctx)
1331{
1332	sii8620_write(ctx, REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL);
1333	sii8620_read_buf(ctx, REG_RX_HDMI_MON_PKT_HEADER1, ctx->avif,
1334			 ARRAY_SIZE(ctx->avif));
1335}
1336
1337static void sii8620_irq_infr(struct sii8620 *ctx)
1338{
1339	u8 stat = sii8620_readb(ctx, REG_INTR8)
1340		& (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI);
1341
1342	sii8620_write(ctx, REG_INTR8, stat);
1343
1344	if (stat & BIT_CEA_NEW_VSI)
1345		sii8620_new_vsi(ctx);
1346
1347	if (stat & BIT_CEA_NEW_AVI)
1348		sii8620_new_avi(ctx);
1349
1350	if (stat & (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI))
1351		sii8620_start_video(ctx);
1352}
1353
1354/* endian agnostic, non-volatile version of test_bit */
1355static bool sii8620_test_bit(unsigned int nr, const u8 *addr)
1356{
1357	return 1 & (addr[nr / BITS_PER_BYTE] >> (nr % BITS_PER_BYTE));
1358}
1359
1360static irqreturn_t sii8620_irq_thread(int irq, void *data)
1361{
1362	static const struct {
1363		int bit;
1364		void (*handler)(struct sii8620 *ctx);
1365	} irq_vec[] = {
1366		{ BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc },
1367		{ BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb },
1368		{ BIT_FAST_INTR_STAT_COC, sii8620_irq_coc },
1369		{ BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc },
1370		{ BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr },
1371		{ BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid },
1372		{ BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt },
1373		{ BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr },
1374	};
1375	struct sii8620 *ctx = data;
1376	u8 stats[LEN_FAST_INTR_STAT];
1377	int i, ret;
1378
1379	mutex_lock(&ctx->lock);
1380
1381	sii8620_read_buf(ctx, REG_FAST_INTR_STAT, stats, ARRAY_SIZE(stats));
1382	for (i = 0; i < ARRAY_SIZE(irq_vec); ++i)
1383		if (sii8620_test_bit(irq_vec[i].bit, stats))
1384			irq_vec[i].handler(ctx);
1385
1386	sii8620_mt_work(ctx);
1387
1388	ret = sii8620_clear_error(ctx);
1389	if (ret) {
1390		dev_err(ctx->dev, "Error during IRQ handling, %d.\n", ret);
1391		sii8620_mhl_disconnected(ctx);
1392	}
1393	mutex_unlock(&ctx->lock);
1394
1395	return IRQ_HANDLED;
1396}
1397
1398static void sii8620_cable_in(struct sii8620 *ctx)
1399{
1400	struct device *dev = ctx->dev;
1401	u8 ver[5];
1402	int ret;
1403
1404	ret = sii8620_hw_on(ctx);
1405	if (ret) {
1406		dev_err(dev, "Error powering on, %d.\n", ret);
1407		return;
1408	}
1409	sii8620_hw_reset(ctx);
1410
1411	sii8620_read_buf(ctx, REG_VND_IDL, ver, ARRAY_SIZE(ver));
1412	ret = sii8620_clear_error(ctx);
1413	if (ret) {
1414		dev_err(dev, "Error accessing I2C bus, %d.\n", ret);
1415		return;
1416	}
1417
1418	dev_info(dev, "ChipID %02x%02x:%02x%02x rev %02x.\n", ver[1], ver[0],
1419		 ver[3], ver[2], ver[4]);
1420
1421	sii8620_write(ctx, REG_DPD,
1422		      BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN);
1423
1424	sii8620_xtal_set_rate(ctx);
1425	sii8620_disconnect(ctx);
1426
1427	sii8620_write_seq_static(ctx,
1428		REG_MHL_CBUS_CTL0, VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_STRONG
1429			| VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_734,
1430		REG_MHL_CBUS_CTL1, VAL_MHL_CBUS_CTL1_1115_OHM,
1431		REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN,
1432	);
1433
1434	ret = sii8620_clear_error(ctx);
1435	if (ret) {
1436		dev_err(dev, "Error accessing I2C bus, %d.\n", ret);
1437		return;
1438	}
1439
1440	enable_irq(to_i2c_client(ctx->dev)->irq);
1441}
1442
1443static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
1444{
1445	return container_of(bridge, struct sii8620, bridge);
1446}
1447
1448static bool sii8620_mode_fixup(struct drm_bridge *bridge,
1449			       const struct drm_display_mode *mode,
1450			       struct drm_display_mode *adjusted_mode)
1451{
1452	struct sii8620 *ctx = bridge_to_sii8620(bridge);
1453	bool ret = false;
1454	int max_clock = 74250;
1455
1456	mutex_lock(&ctx->lock);
1457
1458	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1459		goto out;
1460
1461	if (ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL)
1462		max_clock = 300000;
1463
1464	ret = mode->clock <= max_clock;
1465
1466out:
1467	mutex_unlock(&ctx->lock);
1468
1469	return ret;
1470}
1471
1472static const struct drm_bridge_funcs sii8620_bridge_funcs = {
1473	.mode_fixup = sii8620_mode_fixup,
1474};
1475
1476static int sii8620_probe(struct i2c_client *client,
1477			 const struct i2c_device_id *id)
1478{
1479	struct device *dev = &client->dev;
1480	struct sii8620 *ctx;
1481	int ret;
1482
1483	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
1484	if (!ctx)
1485		return -ENOMEM;
1486
1487	ctx->dev = dev;
1488	mutex_init(&ctx->lock);
1489	INIT_LIST_HEAD(&ctx->mt_queue);
1490
1491	ctx->clk_xtal = devm_clk_get(dev, "xtal");
1492	if (IS_ERR(ctx->clk_xtal)) {
1493		dev_err(dev, "failed to get xtal clock from DT\n");
1494		return PTR_ERR(ctx->clk_xtal);
1495	}
1496
1497	if (!client->irq) {
1498		dev_err(dev, "no irq provided\n");
1499		return -EINVAL;
1500	}
1501	irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
1502	ret = devm_request_threaded_irq(dev, client->irq, NULL,
1503					sii8620_irq_thread,
1504					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
1505					"sii8620", ctx);
1506
1507	ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
1508	if (IS_ERR(ctx->gpio_reset)) {
1509		dev_err(dev, "failed to get reset gpio from DT\n");
1510		return PTR_ERR(ctx->gpio_reset);
1511	}
1512
1513	ctx->supplies[0].supply = "cvcc10";
1514	ctx->supplies[1].supply = "iovcc18";
1515	ret = devm_regulator_bulk_get(dev, 2, ctx->supplies);
1516	if (ret)
1517		return ret;
1518
1519	i2c_set_clientdata(client, ctx);
1520
1521	ctx->bridge.funcs = &sii8620_bridge_funcs;
1522	ctx->bridge.of_node = dev->of_node;
1523	drm_bridge_add(&ctx->bridge);
1524
1525	sii8620_cable_in(ctx);
1526
1527	return 0;
1528}
1529
1530static int sii8620_remove(struct i2c_client *client)
1531{
1532	struct sii8620 *ctx = i2c_get_clientdata(client);
1533
1534	disable_irq(to_i2c_client(ctx->dev)->irq);
1535	drm_bridge_remove(&ctx->bridge);
1536	sii8620_hw_off(ctx);
1537
1538	return 0;
1539}
1540
1541static const struct of_device_id sii8620_dt_match[] = {
1542	{ .compatible = "sil,sii8620" },
1543	{ },
1544};
1545MODULE_DEVICE_TABLE(of, sii8620_dt_match);
1546
1547static const struct i2c_device_id sii8620_id[] = {
1548	{ "sii8620", 0 },
1549	{ },
1550};
1551
1552MODULE_DEVICE_TABLE(i2c, sii8620_id);
1553static struct i2c_driver sii8620_driver = {
1554	.driver = {
1555		.name	= "sii8620",
1556		.of_match_table = of_match_ptr(sii8620_dt_match),
1557	},
1558	.probe		= sii8620_probe,
1559	.remove		= sii8620_remove,
1560	.id_table = sii8620_id,
1561};
1562
1563module_i2c_driver(sii8620_driver);
1564MODULE_LICENSE("GPL v2");