162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Silicon Image SiI8620 HDMI/MHL bridge driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015, Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Andrzej Hajda <a.hajda@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <asm/unaligned.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <drm/bridge/mhl.h> 1262306a36Sopenharmony_ci#include <drm/drm_bridge.h> 1362306a36Sopenharmony_ci#include <drm/drm_crtc.h> 1462306a36Sopenharmony_ci#include <drm/drm_edid.h> 1562306a36Sopenharmony_ci#include <drm/drm_encoder.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/extcon.h> 2062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2162306a36Sopenharmony_ci#include <linux/i2c.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/irq.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/list.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/mutex.h> 2862306a36Sopenharmony_ci#include <linux/of_graph.h> 2962306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <media/rc-core.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "sil-sii8620.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SII8620_BURST_BUF_LEN 288 3762306a36Sopenharmony_ci#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define MHL1_MAX_PCLK 75000 4062306a36Sopenharmony_ci#define MHL1_MAX_PCLK_PP_MODE 150000 4162306a36Sopenharmony_ci#define MHL3_MAX_PCLK 200000 4262306a36Sopenharmony_ci#define MHL3_MAX_PCLK_PP_MODE 300000 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cienum sii8620_mode { 4562306a36Sopenharmony_ci CM_DISCONNECTED, 4662306a36Sopenharmony_ci CM_DISCOVERY, 4762306a36Sopenharmony_ci CM_MHL1, 4862306a36Sopenharmony_ci CM_MHL3, 4962306a36Sopenharmony_ci CM_ECBUS_S 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cienum sii8620_sink_type { 5362306a36Sopenharmony_ci SINK_NONE, 5462306a36Sopenharmony_ci SINK_HDMI, 5562306a36Sopenharmony_ci SINK_DVI 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cienum sii8620_mt_state { 5962306a36Sopenharmony_ci MT_STATE_READY, 6062306a36Sopenharmony_ci MT_STATE_BUSY, 6162306a36Sopenharmony_ci MT_STATE_DONE 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct sii8620 { 6562306a36Sopenharmony_ci struct drm_bridge bridge; 6662306a36Sopenharmony_ci struct device *dev; 6762306a36Sopenharmony_ci struct rc_dev *rc_dev; 6862306a36Sopenharmony_ci struct clk *clk_xtal; 6962306a36Sopenharmony_ci struct gpio_desc *gpio_reset; 7062306a36Sopenharmony_ci struct gpio_desc *gpio_int; 7162306a36Sopenharmony_ci struct regulator_bulk_data supplies[2]; 7262306a36Sopenharmony_ci struct mutex lock; /* context lock, protects fields below */ 7362306a36Sopenharmony_ci int error; 7462306a36Sopenharmony_ci unsigned int use_packed_pixel:1; 7562306a36Sopenharmony_ci enum sii8620_mode mode; 7662306a36Sopenharmony_ci enum sii8620_sink_type sink_type; 7762306a36Sopenharmony_ci u8 cbus_status; 7862306a36Sopenharmony_ci u8 stat[MHL_DST_SIZE]; 7962306a36Sopenharmony_ci u8 xstat[MHL_XDS_SIZE]; 8062306a36Sopenharmony_ci u8 devcap[MHL_DCAP_SIZE]; 8162306a36Sopenharmony_ci u8 xdevcap[MHL_XDC_SIZE]; 8262306a36Sopenharmony_ci bool feature_complete; 8362306a36Sopenharmony_ci bool devcap_read; 8462306a36Sopenharmony_ci bool sink_detected; 8562306a36Sopenharmony_ci struct edid *edid; 8662306a36Sopenharmony_ci unsigned int gen2_write_burst:1; 8762306a36Sopenharmony_ci enum sii8620_mt_state mt_state; 8862306a36Sopenharmony_ci struct extcon_dev *extcon; 8962306a36Sopenharmony_ci struct notifier_block extcon_nb; 9062306a36Sopenharmony_ci struct work_struct extcon_wq; 9162306a36Sopenharmony_ci int cable_state; 9262306a36Sopenharmony_ci struct list_head mt_queue; 9362306a36Sopenharmony_ci struct { 9462306a36Sopenharmony_ci int r_size; 9562306a36Sopenharmony_ci int r_count; 9662306a36Sopenharmony_ci int rx_ack; 9762306a36Sopenharmony_ci int rx_count; 9862306a36Sopenharmony_ci u8 rx_buf[32]; 9962306a36Sopenharmony_ci int tx_count; 10062306a36Sopenharmony_ci u8 tx_buf[32]; 10162306a36Sopenharmony_ci } burst; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct sii8620_mt_msg; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_citypedef void (*sii8620_mt_msg_cb)(struct sii8620 *ctx, 10762306a36Sopenharmony_ci struct sii8620_mt_msg *msg); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_citypedef void (*sii8620_cb)(struct sii8620 *ctx, int ret); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct sii8620_mt_msg { 11262306a36Sopenharmony_ci struct list_head node; 11362306a36Sopenharmony_ci u8 reg[4]; 11462306a36Sopenharmony_ci u8 ret; 11562306a36Sopenharmony_ci sii8620_mt_msg_cb send; 11662306a36Sopenharmony_ci sii8620_mt_msg_cb recv; 11762306a36Sopenharmony_ci sii8620_cb continuation; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic const u8 sii8620_i2c_page[] = { 12162306a36Sopenharmony_ci 0x39, /* Main System */ 12262306a36Sopenharmony_ci 0x3d, /* TDM and HSIC */ 12362306a36Sopenharmony_ci 0x49, /* TMDS Receiver, MHL EDID */ 12462306a36Sopenharmony_ci 0x4d, /* eMSC, HDCP, HSIC */ 12562306a36Sopenharmony_ci 0x5d, /* MHL Spec */ 12662306a36Sopenharmony_ci 0x64, /* MHL CBUS */ 12762306a36Sopenharmony_ci 0x59, /* Hardware TPI (Transmitter Programming Interface) */ 12862306a36Sopenharmony_ci 0x61, /* eCBUS-S, eCBUS-D */ 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void sii8620_fetch_edid(struct sii8620 *ctx); 13262306a36Sopenharmony_cistatic void sii8620_set_upstream_edid(struct sii8620 *ctx); 13362306a36Sopenharmony_cistatic void sii8620_enable_hpd(struct sii8620 *ctx); 13462306a36Sopenharmony_cistatic void sii8620_mhl_disconnected(struct sii8620 *ctx); 13562306a36Sopenharmony_cistatic void sii8620_disconnect(struct sii8620 *ctx); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int sii8620_clear_error(struct sii8620 *ctx) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int ret = ctx->error; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ctx->error = 0; 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void sii8620_read_buf(struct sii8620 *ctx, u16 addr, u8 *buf, int len) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct device *dev = ctx->dev; 14862306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 14962306a36Sopenharmony_ci u8 data = addr; 15062306a36Sopenharmony_ci struct i2c_msg msg[] = { 15162306a36Sopenharmony_ci { 15262306a36Sopenharmony_ci .addr = sii8620_i2c_page[addr >> 8], 15362306a36Sopenharmony_ci .flags = client->flags, 15462306a36Sopenharmony_ci .len = 1, 15562306a36Sopenharmony_ci .buf = &data 15662306a36Sopenharmony_ci }, 15762306a36Sopenharmony_ci { 15862306a36Sopenharmony_ci .addr = sii8620_i2c_page[addr >> 8], 15962306a36Sopenharmony_ci .flags = client->flags | I2C_M_RD, 16062306a36Sopenharmony_ci .len = len, 16162306a36Sopenharmony_ci .buf = buf 16262306a36Sopenharmony_ci }, 16362306a36Sopenharmony_ci }; 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (ctx->error) 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ret = i2c_transfer(client->adapter, msg, 2); 17062306a36Sopenharmony_ci dev_dbg(dev, "read at %04x: %*ph, %d\n", addr, len, buf, ret); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (ret != 2) { 17362306a36Sopenharmony_ci dev_err(dev, "Read at %#06x of %d bytes failed with code %d.\n", 17462306a36Sopenharmony_ci addr, len, ret); 17562306a36Sopenharmony_ci ctx->error = ret < 0 ? ret : -EIO; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic u8 sii8620_readb(struct sii8620 *ctx, u16 addr) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u8 ret = 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci sii8620_read_buf(ctx, addr, &ret, 1); 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void sii8620_write_buf(struct sii8620 *ctx, u16 addr, const u8 *buf, 18862306a36Sopenharmony_ci int len) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct device *dev = ctx->dev; 19162306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 19262306a36Sopenharmony_ci u8 data[2]; 19362306a36Sopenharmony_ci struct i2c_msg msg = { 19462306a36Sopenharmony_ci .addr = sii8620_i2c_page[addr >> 8], 19562306a36Sopenharmony_ci .flags = client->flags, 19662306a36Sopenharmony_ci .len = len + 1, 19762306a36Sopenharmony_ci }; 19862306a36Sopenharmony_ci int ret; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (ctx->error) 20162306a36Sopenharmony_ci return; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (len > 1) { 20462306a36Sopenharmony_ci msg.buf = kmalloc(len + 1, GFP_KERNEL); 20562306a36Sopenharmony_ci if (!msg.buf) { 20662306a36Sopenharmony_ci ctx->error = -ENOMEM; 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci memcpy(msg.buf + 1, buf, len); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci msg.buf = data; 21262306a36Sopenharmony_ci msg.buf[1] = *buf; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci msg.buf[0] = addr; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = i2c_transfer(client->adapter, &msg, 1); 21862306a36Sopenharmony_ci dev_dbg(dev, "write at %04x: %*ph, %d\n", addr, len, buf, ret); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (ret != 1) { 22162306a36Sopenharmony_ci dev_err(dev, "Write at %#06x of %*ph failed with code %d.\n", 22262306a36Sopenharmony_ci addr, len, buf, ret); 22362306a36Sopenharmony_ci ctx->error = ret ?: -EIO; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (len > 1) 22762306a36Sopenharmony_ci kfree(msg.buf); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#define sii8620_write(ctx, addr, arr...) \ 23162306a36Sopenharmony_ci({\ 23262306a36Sopenharmony_ci u8 d[] = { arr }; \ 23362306a36Sopenharmony_ci sii8620_write_buf(ctx, addr, d, ARRAY_SIZE(d)); \ 23462306a36Sopenharmony_ci}) 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void __sii8620_write_seq(struct sii8620 *ctx, const u16 *seq, int len) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci int i; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci for (i = 0; i < len; i += 2) 24162306a36Sopenharmony_ci sii8620_write(ctx, seq[i], seq[i + 1]); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#define sii8620_write_seq(ctx, seq...) \ 24562306a36Sopenharmony_ci({\ 24662306a36Sopenharmony_ci const u16 d[] = { seq }; \ 24762306a36Sopenharmony_ci __sii8620_write_seq(ctx, d, ARRAY_SIZE(d)); \ 24862306a36Sopenharmony_ci}) 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci#define sii8620_write_seq_static(ctx, seq...) \ 25162306a36Sopenharmony_ci({\ 25262306a36Sopenharmony_ci static const u16 d[] = { seq }; \ 25362306a36Sopenharmony_ci __sii8620_write_seq(ctx, d, ARRAY_SIZE(d)); \ 25462306a36Sopenharmony_ci}) 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void sii8620_setbits(struct sii8620 *ctx, u16 addr, u8 mask, u8 val) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci val = (val & mask) | (sii8620_readb(ctx, addr) & ~mask); 25962306a36Sopenharmony_ci sii8620_write(ctx, addr, val); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic inline bool sii8620_is_mhl3(struct sii8620 *ctx) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci return ctx->mode >= CM_MHL3; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void sii8620_mt_cleanup(struct sii8620 *ctx) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct sii8620_mt_msg *msg, *n; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci list_for_each_entry_safe(msg, n, &ctx->mt_queue, node) { 27262306a36Sopenharmony_ci list_del(&msg->node); 27362306a36Sopenharmony_ci kfree(msg); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci ctx->mt_state = MT_STATE_READY; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void sii8620_mt_work(struct sii8620 *ctx) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct sii8620_mt_msg *msg; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (ctx->error) 28362306a36Sopenharmony_ci return; 28462306a36Sopenharmony_ci if (ctx->mt_state == MT_STATE_BUSY || list_empty(&ctx->mt_queue)) 28562306a36Sopenharmony_ci return; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (ctx->mt_state == MT_STATE_DONE) { 28862306a36Sopenharmony_ci ctx->mt_state = MT_STATE_READY; 28962306a36Sopenharmony_ci msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, 29062306a36Sopenharmony_ci node); 29162306a36Sopenharmony_ci list_del(&msg->node); 29262306a36Sopenharmony_ci if (msg->recv) 29362306a36Sopenharmony_ci msg->recv(ctx, msg); 29462306a36Sopenharmony_ci if (msg->continuation) 29562306a36Sopenharmony_ci msg->continuation(ctx, msg->ret); 29662306a36Sopenharmony_ci kfree(msg); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (ctx->mt_state != MT_STATE_READY || list_empty(&ctx->mt_queue)) 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci ctx->mt_state = MT_STATE_BUSY; 30362306a36Sopenharmony_ci msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); 30462306a36Sopenharmony_ci if (msg->send) 30562306a36Sopenharmony_ci msg->send(ctx, msg); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci u8 ctrl = BIT_MDT_RCV_CTRL_MDT_RCV_EN; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (ctx->gen2_write_burst) 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (ctx->mode >= CM_MHL1) 31662306a36Sopenharmony_ci ctrl |= BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci sii8620_write_seq(ctx, 31962306a36Sopenharmony_ci REG_MDT_RCV_TIMEOUT, 100, 32062306a36Sopenharmony_ci REG_MDT_RCV_CTRL, ctrl 32162306a36Sopenharmony_ci ); 32262306a36Sopenharmony_ci ctx->gen2_write_burst = 1; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci if (!ctx->gen2_write_burst) 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 33162306a36Sopenharmony_ci REG_MDT_XMIT_CTRL, 0, 33262306a36Sopenharmony_ci REG_MDT_RCV_CTRL, 0 33362306a36Sopenharmony_ci ); 33462306a36Sopenharmony_ci ctx->gen2_write_burst = 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void sii8620_start_gen2_write_burst(struct sii8620 *ctx) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 34062306a36Sopenharmony_ci REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT 34162306a36Sopenharmony_ci | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR 34262306a36Sopenharmony_ci | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD 34362306a36Sopenharmony_ci | BIT_MDT_XMIT_SM_ERROR, 34462306a36Sopenharmony_ci REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY 34562306a36Sopenharmony_ci | BIT_MDT_IDLE_AFTER_HAWB_DISABLE 34662306a36Sopenharmony_ci | BIT_MDT_RFIFO_DATA_RDY 34762306a36Sopenharmony_ci ); 34862306a36Sopenharmony_ci sii8620_enable_gen2_write_burst(ctx); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, 35262306a36Sopenharmony_ci struct sii8620_mt_msg *msg) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci if (msg->reg[0] == MHL_SET_INT && 35562306a36Sopenharmony_ci msg->reg[1] == MHL_INT_REG(RCHANGE) && 35662306a36Sopenharmony_ci msg->reg[2] == MHL_INT_RC_FEAT_REQ) 35762306a36Sopenharmony_ci sii8620_enable_gen2_write_burst(ctx); 35862306a36Sopenharmony_ci else 35962306a36Sopenharmony_ci sii8620_disable_gen2_write_burst(ctx); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (msg->reg[0]) { 36262306a36Sopenharmony_ci case MHL_WRITE_STAT: 36362306a36Sopenharmony_ci case MHL_SET_INT: 36462306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg + 1, 2); 36562306a36Sopenharmony_ci sii8620_write(ctx, REG_MSC_COMMAND_START, 36662306a36Sopenharmony_ci BIT_MSC_COMMAND_START_WRITE_STAT); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case MHL_MSC_MSG: 36962306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg, 3); 37062306a36Sopenharmony_ci sii8620_write(ctx, REG_MSC_COMMAND_START, 37162306a36Sopenharmony_ci BIT_MSC_COMMAND_START_MSC_MSG); 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci case MHL_READ_DEVCAP_REG: 37462306a36Sopenharmony_ci case MHL_READ_XDEVCAP_REG: 37562306a36Sopenharmony_ci sii8620_write(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg[1]); 37662306a36Sopenharmony_ci sii8620_write(ctx, REG_MSC_COMMAND_START, 37762306a36Sopenharmony_ci BIT_MSC_COMMAND_START_READ_DEVCAP); 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci default: 38062306a36Sopenharmony_ci dev_err(ctx->dev, "%s: command %#x not supported\n", __func__, 38162306a36Sopenharmony_ci msg->reg[0]); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic struct sii8620_mt_msg *sii8620_mt_msg_new(struct sii8620 *ctx) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct sii8620_mt_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!msg) 39062306a36Sopenharmony_ci ctx->error = -ENOMEM; 39162306a36Sopenharmony_ci else 39262306a36Sopenharmony_ci list_add_tail(&msg->node, &ctx->mt_queue); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return msg; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void sii8620_mt_set_cont(struct sii8620 *ctx, sii8620_cb cont) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct sii8620_mt_msg *msg; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (ctx->error) 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (list_empty(&ctx->mt_queue)) { 40562306a36Sopenharmony_ci ctx->error = -EINVAL; 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci msg = list_last_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); 40962306a36Sopenharmony_ci msg->continuation = cont; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void sii8620_mt_msc_cmd(struct sii8620 *ctx, u8 cmd, u8 arg1, u8 arg2) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!msg) 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci msg->reg[0] = cmd; 42062306a36Sopenharmony_ci msg->reg[1] = arg1; 42162306a36Sopenharmony_ci msg->reg[2] = arg2; 42262306a36Sopenharmony_ci msg->send = sii8620_mt_msc_cmd_send; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void sii8620_mt_write_stat(struct sii8620 *ctx, u8 reg, u8 val) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci sii8620_mt_msc_cmd(ctx, MHL_WRITE_STAT, reg, val); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic inline void sii8620_mt_set_int(struct sii8620 *ctx, u8 irq, u8 mask) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci sii8620_mt_msc_cmd(ctx, MHL_SET_INT, irq, mask); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void sii8620_mt_msc_msg(struct sii8620 *ctx, u8 cmd, u8 data) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci sii8620_mt_msc_cmd(ctx, MHL_MSC_MSG, cmd, data); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void sii8620_mt_rap(struct sii8620 *ctx, u8 code) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void sii8620_mt_read_devcap_send(struct sii8620 *ctx, 45662306a36Sopenharmony_ci struct sii8620_mt_msg *msg) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci u8 ctrl = BIT_EDID_CTRL_DEVCAP_SELECT_DEVCAP 45962306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO 46062306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_MODE_EN; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (msg->reg[0] == MHL_READ_XDEVCAP) 46362306a36Sopenharmony_ci ctrl |= BIT_EDID_CTRL_XDEVCAP_EN; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci sii8620_write_seq(ctx, 46662306a36Sopenharmony_ci REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE, 46762306a36Sopenharmony_ci REG_EDID_CTRL, ctrl, 46862306a36Sopenharmony_ci REG_TPI_CBUS_START, BIT_TPI_CBUS_START_GET_DEVCAP_START 46962306a36Sopenharmony_ci ); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* copy src to dst and set changed bits in src */ 47362306a36Sopenharmony_cistatic void sii8620_update_array(u8 *dst, u8 *src, int count) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci while (--count >= 0) { 47662306a36Sopenharmony_ci *src ^= *dst; 47762306a36Sopenharmony_ci *dst++ ^= *src++; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void sii8620_identify_sink(struct sii8620 *ctx) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci static const char * const sink_str[] = { 48462306a36Sopenharmony_ci [SINK_NONE] = "NONE", 48562306a36Sopenharmony_ci [SINK_HDMI] = "HDMI", 48662306a36Sopenharmony_ci [SINK_DVI] = "DVI" 48762306a36Sopenharmony_ci }; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci char sink_name[20]; 49062306a36Sopenharmony_ci struct device *dev = ctx->dev; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (!ctx->sink_detected || !ctx->devcap_read) 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci sii8620_fetch_edid(ctx); 49662306a36Sopenharmony_ci if (!ctx->edid) { 49762306a36Sopenharmony_ci dev_err(ctx->dev, "Cannot fetch EDID\n"); 49862306a36Sopenharmony_ci sii8620_mhl_disconnected(ctx); 49962306a36Sopenharmony_ci return; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci sii8620_set_upstream_edid(ctx); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (drm_detect_hdmi_monitor(ctx->edid)) 50462306a36Sopenharmony_ci ctx->sink_type = SINK_HDMI; 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci ctx->sink_type = SINK_DVI; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci drm_edid_get_monitor_name(ctx->edid, sink_name, ARRAY_SIZE(sink_name)); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev_info(dev, "detected sink(type: %s): %s\n", 51162306a36Sopenharmony_ci sink_str[ctx->sink_type], sink_name); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void sii8620_mr_devcap(struct sii8620 *ctx) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci u8 dcap[MHL_DCAP_SIZE]; 51762306a36Sopenharmony_ci struct device *dev = ctx->dev; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); 52062306a36Sopenharmony_ci if (ctx->error < 0) 52162306a36Sopenharmony_ci return; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", 52462306a36Sopenharmony_ci dcap[MHL_DCAP_MHL_VERSION] / 16, 52562306a36Sopenharmony_ci dcap[MHL_DCAP_MHL_VERSION] % 16, 52662306a36Sopenharmony_ci dcap[MHL_DCAP_ADOPTER_ID_H], dcap[MHL_DCAP_ADOPTER_ID_L], 52762306a36Sopenharmony_ci dcap[MHL_DCAP_DEVICE_ID_H], dcap[MHL_DCAP_DEVICE_ID_L]); 52862306a36Sopenharmony_ci sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); 52962306a36Sopenharmony_ci ctx->devcap_read = true; 53062306a36Sopenharmony_ci sii8620_identify_sink(ctx); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void sii8620_mr_xdevcap(struct sii8620 *ctx) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, ctx->xdevcap, 53662306a36Sopenharmony_ci MHL_XDC_SIZE); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic void sii8620_mt_read_devcap_recv(struct sii8620 *ctx, 54062306a36Sopenharmony_ci struct sii8620_mt_msg *msg) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci u8 ctrl = BIT_EDID_CTRL_DEVCAP_SELECT_DEVCAP 54362306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO 54462306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_MODE_EN; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (msg->reg[0] == MHL_READ_XDEVCAP) 54762306a36Sopenharmony_ci ctrl |= BIT_EDID_CTRL_XDEVCAP_EN; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci sii8620_write_seq(ctx, 55062306a36Sopenharmony_ci REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE | BIT_INTR9_EDID_DONE 55162306a36Sopenharmony_ci | BIT_INTR9_EDID_ERROR, 55262306a36Sopenharmony_ci REG_EDID_CTRL, ctrl, 55362306a36Sopenharmony_ci REG_EDID_FIFO_ADDR, 0 55462306a36Sopenharmony_ci ); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (msg->reg[0] == MHL_READ_XDEVCAP) 55762306a36Sopenharmony_ci sii8620_mr_xdevcap(ctx); 55862306a36Sopenharmony_ci else 55962306a36Sopenharmony_ci sii8620_mr_devcap(ctx); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void sii8620_mt_read_devcap(struct sii8620 *ctx, bool xdevcap) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!msg) 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci msg->reg[0] = xdevcap ? MHL_READ_XDEVCAP : MHL_READ_DEVCAP; 57062306a36Sopenharmony_ci msg->send = sii8620_mt_read_devcap_send; 57162306a36Sopenharmony_ci msg->recv = sii8620_mt_read_devcap_recv; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic void sii8620_mt_read_devcap_reg_recv(struct sii8620 *ctx, 57562306a36Sopenharmony_ci struct sii8620_mt_msg *msg) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci u8 reg = msg->reg[1] & 0x7f; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (msg->reg[1] & 0x80) 58062306a36Sopenharmony_ci ctx->xdevcap[reg] = msg->ret; 58162306a36Sopenharmony_ci else 58262306a36Sopenharmony_ci ctx->devcap[reg] = msg->ret; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic void sii8620_mt_read_devcap_reg(struct sii8620 *ctx, u8 reg) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!msg) 59062306a36Sopenharmony_ci return; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci msg->reg[0] = (reg & 0x80) ? MHL_READ_XDEVCAP_REG : MHL_READ_DEVCAP_REG; 59362306a36Sopenharmony_ci msg->reg[1] = reg; 59462306a36Sopenharmony_ci msg->send = sii8620_mt_msc_cmd_send; 59562306a36Sopenharmony_ci msg->recv = sii8620_mt_read_devcap_reg_recv; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic inline void sii8620_mt_read_xdevcap_reg(struct sii8620 *ctx, u8 reg) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci sii8620_mt_read_devcap_reg(ctx, reg | 0x80); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void *sii8620_burst_get_tx_buf(struct sii8620 *ctx, int len) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci u8 *buf = &ctx->burst.tx_buf[ctx->burst.tx_count]; 60662306a36Sopenharmony_ci int size = len + 2; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (ctx->burst.tx_count + size >= ARRAY_SIZE(ctx->burst.tx_buf)) { 60962306a36Sopenharmony_ci dev_err(ctx->dev, "TX-BLK buffer exhausted\n"); 61062306a36Sopenharmony_ci ctx->error = -EINVAL; 61162306a36Sopenharmony_ci return NULL; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci ctx->burst.tx_count += size; 61562306a36Sopenharmony_ci buf[1] = len; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return buf + 2; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic u8 *sii8620_burst_get_rx_buf(struct sii8620 *ctx, int len) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci u8 *buf = &ctx->burst.rx_buf[ctx->burst.rx_count]; 62362306a36Sopenharmony_ci int size = len + 1; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (ctx->burst.rx_count + size >= ARRAY_SIZE(ctx->burst.rx_buf)) { 62662306a36Sopenharmony_ci dev_err(ctx->dev, "RX-BLK buffer exhausted\n"); 62762306a36Sopenharmony_ci ctx->error = -EINVAL; 62862306a36Sopenharmony_ci return NULL; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ctx->burst.rx_count += size; 63262306a36Sopenharmony_ci buf[0] = len; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return buf + 1; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic void sii8620_burst_send(struct sii8620 *ctx) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci int tx_left = ctx->burst.tx_count; 64062306a36Sopenharmony_ci u8 *d = ctx->burst.tx_buf; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci while (tx_left > 0) { 64362306a36Sopenharmony_ci int len = d[1] + 2; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (ctx->burst.r_count + len > ctx->burst.r_size) 64662306a36Sopenharmony_ci break; 64762306a36Sopenharmony_ci d[0] = min(ctx->burst.rx_ack, 255); 64862306a36Sopenharmony_ci ctx->burst.rx_ack -= d[0]; 64962306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, d, len); 65062306a36Sopenharmony_ci ctx->burst.r_count += len; 65162306a36Sopenharmony_ci tx_left -= len; 65262306a36Sopenharmony_ci d += len; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ctx->burst.tx_count = tx_left; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci while (ctx->burst.rx_ack > 0) { 65862306a36Sopenharmony_ci u8 b[2] = { min(ctx->burst.rx_ack, 255), 0 }; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (ctx->burst.r_count + 2 > ctx->burst.r_size) 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci ctx->burst.rx_ack -= b[0]; 66362306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, b, 2); 66462306a36Sopenharmony_ci ctx->burst.r_count += 2; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic void sii8620_burst_receive(struct sii8620 *ctx) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u8 buf[3], *d; 67162306a36Sopenharmony_ci int count; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_EMSCRFIFOBCNTL, buf, 2); 67462306a36Sopenharmony_ci count = get_unaligned_le16(buf); 67562306a36Sopenharmony_ci while (count > 0) { 67662306a36Sopenharmony_ci int len = min(count, 3); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, buf, len); 67962306a36Sopenharmony_ci count -= len; 68062306a36Sopenharmony_ci ctx->burst.rx_ack += len - 1; 68162306a36Sopenharmony_ci ctx->burst.r_count -= buf[1]; 68262306a36Sopenharmony_ci if (ctx->burst.r_count < 0) 68362306a36Sopenharmony_ci ctx->burst.r_count = 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (len < 3 || !buf[2]) 68662306a36Sopenharmony_ci continue; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci len = buf[2]; 68962306a36Sopenharmony_ci d = sii8620_burst_get_rx_buf(ctx, len); 69062306a36Sopenharmony_ci if (!d) 69162306a36Sopenharmony_ci continue; 69262306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, d, len); 69362306a36Sopenharmony_ci count -= len; 69462306a36Sopenharmony_ci ctx->burst.rx_ack += len; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct mhl_burst_blk_rcv_buffer_info *d = 70162306a36Sopenharmony_ci sii8620_burst_get_tx_buf(ctx, sizeof(*d)); 70262306a36Sopenharmony_ci if (!d) 70362306a36Sopenharmony_ci return; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci d->id = cpu_to_be16(MHL_BURST_ID_BLK_RCV_BUFFER_INFO); 70662306a36Sopenharmony_ci d->size = cpu_to_le16(size); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic u8 sii8620_checksum(void *ptr, int size) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci u8 *d = ptr, sum = 0; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci while (size--) 71462306a36Sopenharmony_ci sum += *d++; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return sum; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, 72062306a36Sopenharmony_ci enum mhl_burst_id id) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci h->id = cpu_to_be16(id); 72362306a36Sopenharmony_ci h->total_entries = 1; 72462306a36Sopenharmony_ci h->sequence_index = 1; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct mhl_burst_bits_per_pixel_fmt *d; 73062306a36Sopenharmony_ci const int size = sizeof(*d) + sizeof(d->desc[0]); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci d = sii8620_burst_get_tx_buf(ctx, size); 73362306a36Sopenharmony_ci if (!d) 73462306a36Sopenharmony_ci return; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); 73762306a36Sopenharmony_ci d->num_entries = 1; 73862306a36Sopenharmony_ci d->desc[0].stream_id = 0; 73962306a36Sopenharmony_ci d->desc[0].pixel_format = fmt; 74062306a36Sopenharmony_ci d->hdr.checksum -= sii8620_checksum(d, size); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic void sii8620_burst_rx_all(struct sii8620 *ctx) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci u8 *d = ctx->burst.rx_buf; 74662306a36Sopenharmony_ci int count = ctx->burst.rx_count; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci while (count-- > 0) { 74962306a36Sopenharmony_ci int len = *d++; 75062306a36Sopenharmony_ci int id = get_unaligned_be16(&d[0]); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci switch (id) { 75362306a36Sopenharmony_ci case MHL_BURST_ID_BLK_RCV_BUFFER_INFO: 75462306a36Sopenharmony_ci ctx->burst.r_size = get_unaligned_le16(&d[2]); 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci default: 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci count -= len; 76062306a36Sopenharmony_ci d += len; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci ctx->burst.rx_count = 0; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void sii8620_fetch_edid(struct sii8620 *ctx) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci u8 lm_ddc, ddc_cmd, int3, cbus; 76862306a36Sopenharmony_ci unsigned long timeout; 76962306a36Sopenharmony_ci int fetched, i; 77062306a36Sopenharmony_ci int edid_len = EDID_LENGTH; 77162306a36Sopenharmony_ci u8 *edid; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci sii8620_readb(ctx, REG_CBUS_STATUS); 77462306a36Sopenharmony_ci lm_ddc = sii8620_readb(ctx, REG_LM_DDC); 77562306a36Sopenharmony_ci ddc_cmd = sii8620_readb(ctx, REG_DDC_CMD); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci sii8620_write_seq(ctx, 77862306a36Sopenharmony_ci REG_INTR9_MASK, 0, 77962306a36Sopenharmony_ci REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO, 78062306a36Sopenharmony_ci REG_HDCP2X_POLL_CS, 0x71, 78162306a36Sopenharmony_ci REG_HDCP2X_CTRL_0, BIT_HDCP2X_CTRL_0_HDCP2X_HDCPTX, 78262306a36Sopenharmony_ci REG_LM_DDC, lm_ddc | BIT_LM_DDC_SW_TPI_EN_DISABLED, 78362306a36Sopenharmony_ci ); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci for (i = 0; i < 256; ++i) { 78662306a36Sopenharmony_ci u8 ddc_stat = sii8620_readb(ctx, REG_DDC_STATUS); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!(ddc_stat & BIT_DDC_STATUS_DDC_I2C_IN_PROG)) 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci sii8620_write(ctx, REG_DDC_STATUS, 79162306a36Sopenharmony_ci BIT_DDC_STATUS_DDC_FIFO_EMPTY); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci sii8620_write(ctx, REG_DDC_ADDR, 0x50 << 1); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci edid = kmalloc(EDID_LENGTH, GFP_KERNEL); 79762306a36Sopenharmony_ci if (!edid) { 79862306a36Sopenharmony_ci ctx->error = -ENOMEM; 79962306a36Sopenharmony_ci return; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci#define FETCH_SIZE 16 80362306a36Sopenharmony_ci for (fetched = 0; fetched < edid_len; fetched += FETCH_SIZE) { 80462306a36Sopenharmony_ci sii8620_readb(ctx, REG_DDC_STATUS); 80562306a36Sopenharmony_ci sii8620_write_seq(ctx, 80662306a36Sopenharmony_ci REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_DDC_CMD_ABORT, 80762306a36Sopenharmony_ci REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_DDC_CMD_CLEAR_FIFO, 80862306a36Sopenharmony_ci REG_DDC_STATUS, BIT_DDC_STATUS_DDC_FIFO_EMPTY 80962306a36Sopenharmony_ci ); 81062306a36Sopenharmony_ci sii8620_write_seq(ctx, 81162306a36Sopenharmony_ci REG_DDC_SEGM, fetched >> 8, 81262306a36Sopenharmony_ci REG_DDC_OFFSET, fetched & 0xff, 81362306a36Sopenharmony_ci REG_DDC_DIN_CNT1, FETCH_SIZE, 81462306a36Sopenharmony_ci REG_DDC_DIN_CNT2, 0, 81562306a36Sopenharmony_ci REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_ENH_DDC_READ_NO_ACK 81662306a36Sopenharmony_ci ); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci int3 = 0; 81962306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(200); 82062306a36Sopenharmony_ci for (;;) { 82162306a36Sopenharmony_ci cbus = sii8620_readb(ctx, REG_CBUS_STATUS); 82262306a36Sopenharmony_ci if (~cbus & BIT_CBUS_STATUS_CBUS_CONNECTED) { 82362306a36Sopenharmony_ci kfree(edid); 82462306a36Sopenharmony_ci edid = NULL; 82562306a36Sopenharmony_ci goto end; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci if (int3 & BIT_DDC_CMD_DONE) { 82862306a36Sopenharmony_ci if (sii8620_readb(ctx, REG_DDC_DOUT_CNT) 82962306a36Sopenharmony_ci >= FETCH_SIZE) 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci } else { 83262306a36Sopenharmony_ci int3 = sii8620_readb(ctx, REG_INTR3); 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci if (time_is_before_jiffies(timeout)) { 83562306a36Sopenharmony_ci ctx->error = -ETIMEDOUT; 83662306a36Sopenharmony_ci dev_err(ctx->dev, "timeout during EDID read\n"); 83762306a36Sopenharmony_ci kfree(edid); 83862306a36Sopenharmony_ci edid = NULL; 83962306a36Sopenharmony_ci goto end; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci usleep_range(10, 20); 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_DDC_DATA, edid + fetched, FETCH_SIZE); 84562306a36Sopenharmony_ci if (fetched + FETCH_SIZE == EDID_LENGTH) { 84662306a36Sopenharmony_ci u8 ext = ((struct edid *)edid)->extensions; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (ext) { 84962306a36Sopenharmony_ci u8 *new_edid; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci edid_len += ext * EDID_LENGTH; 85262306a36Sopenharmony_ci new_edid = krealloc(edid, edid_len, GFP_KERNEL); 85362306a36Sopenharmony_ci if (!new_edid) { 85462306a36Sopenharmony_ci kfree(edid); 85562306a36Sopenharmony_ci ctx->error = -ENOMEM; 85662306a36Sopenharmony_ci return; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci edid = new_edid; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci sii8620_write_seq(ctx, 86462306a36Sopenharmony_ci REG_INTR3_MASK, BIT_DDC_CMD_DONE, 86562306a36Sopenharmony_ci REG_LM_DDC, lm_ddc 86662306a36Sopenharmony_ci ); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ciend: 86962306a36Sopenharmony_ci kfree(ctx->edid); 87062306a36Sopenharmony_ci ctx->edid = (struct edid *)edid; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic void sii8620_set_upstream_edid(struct sii8620 *ctx) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci sii8620_setbits(ctx, REG_DPD, BIT_DPD_PDNRX12 | BIT_DPD_PDIDCK_N 87662306a36Sopenharmony_ci | BIT_DPD_PD_MHL_CLK_N, 0xff); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 87962306a36Sopenharmony_ci REG_RX_HDMI_CTRL3, 0x00, 88062306a36Sopenharmony_ci REG_PKT_FILTER_0, 0xFF, 88162306a36Sopenharmony_ci REG_PKT_FILTER_1, 0xFF, 88262306a36Sopenharmony_ci REG_ALICE0_BW_I2C, 0x06 88362306a36Sopenharmony_ci ); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci sii8620_setbits(ctx, REG_RX_HDMI_CLR_BUFFER, 88662306a36Sopenharmony_ci BIT_RX_HDMI_CLR_BUFFER_VSI_CLR_EN, 0xff); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 88962306a36Sopenharmony_ci REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO 89062306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_MODE_EN, 89162306a36Sopenharmony_ci REG_EDID_FIFO_ADDR, 0, 89262306a36Sopenharmony_ci ); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_EDID_FIFO_WR_DATA, (u8 *)ctx->edid, 89562306a36Sopenharmony_ci (ctx->edid->extensions + 1) * EDID_LENGTH); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 89862306a36Sopenharmony_ci REG_EDID_CTRL, BIT_EDID_CTRL_EDID_PRIME_VALID 89962306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO 90062306a36Sopenharmony_ci | BIT_EDID_CTRL_EDID_MODE_EN, 90162306a36Sopenharmony_ci REG_INTR5_MASK, BIT_INTR_SCDT_CHANGE, 90262306a36Sopenharmony_ci REG_INTR9_MASK, 0 90362306a36Sopenharmony_ci ); 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic void sii8620_xtal_set_rate(struct sii8620 *ctx) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci static const struct { 90962306a36Sopenharmony_ci unsigned int rate; 91062306a36Sopenharmony_ci u8 div; 91162306a36Sopenharmony_ci u8 tp1; 91262306a36Sopenharmony_ci } rates[] = { 91362306a36Sopenharmony_ci { 19200, 0x04, 0x53 }, 91462306a36Sopenharmony_ci { 20000, 0x04, 0x62 }, 91562306a36Sopenharmony_ci { 24000, 0x05, 0x75 }, 91662306a36Sopenharmony_ci { 30000, 0x06, 0x92 }, 91762306a36Sopenharmony_ci { 38400, 0x0c, 0xbc }, 91862306a36Sopenharmony_ci }; 91962306a36Sopenharmony_ci unsigned long rate = clk_get_rate(ctx->clk_xtal) / 1000; 92062306a36Sopenharmony_ci int i; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rates) - 1; ++i) 92362306a36Sopenharmony_ci if (rate <= rates[i].rate) 92462306a36Sopenharmony_ci break; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (rate != rates[i].rate) 92762306a36Sopenharmony_ci dev_err(ctx->dev, "xtal clock rate(%lukHz) not supported, setting MHL for %ukHz.\n", 92862306a36Sopenharmony_ci rate, rates[i].rate); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci sii8620_write(ctx, REG_DIV_CTL_MAIN, rates[i].div); 93162306a36Sopenharmony_ci sii8620_write(ctx, REG_HDCP2X_TP1, rates[i].tp1); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int sii8620_hw_on(struct sii8620 *ctx) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci int ret; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 93962306a36Sopenharmony_ci if (ret) 94062306a36Sopenharmony_ci return ret; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci usleep_range(10000, 20000); 94362306a36Sopenharmony_ci ret = clk_prepare_enable(ctx->clk_xtal); 94462306a36Sopenharmony_ci if (ret) 94562306a36Sopenharmony_ci return ret; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci msleep(100); 94862306a36Sopenharmony_ci gpiod_set_value(ctx->gpio_reset, 0); 94962306a36Sopenharmony_ci msleep(100); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return 0; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic int sii8620_hw_off(struct sii8620 *ctx) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci clk_disable_unprepare(ctx->clk_xtal); 95762306a36Sopenharmony_ci gpiod_set_value(ctx->gpio_reset, 1); 95862306a36Sopenharmony_ci return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic void sii8620_cbus_reset(struct sii8620 *ctx) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST 96462306a36Sopenharmony_ci | BIT_PWD_SRST_CBUS_RST_SW_EN); 96562306a36Sopenharmony_ci usleep_range(10000, 20000); 96662306a36Sopenharmony_ci sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN); 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic void sii8620_set_auto_zone(struct sii8620 *ctx) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci if (ctx->mode != CM_MHL1) { 97262306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 97362306a36Sopenharmony_ci REG_TX_ZONE_CTL1, 0x0, 97462306a36Sopenharmony_ci REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X 97562306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL 97662306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_ZONE_MASK_OE 97762306a36Sopenharmony_ci ); 97862306a36Sopenharmony_ci } else { 97962306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 98062306a36Sopenharmony_ci REG_TX_ZONE_CTL1, VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE, 98162306a36Sopenharmony_ci REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X 98262306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_ZONE_MASK_OE 98362306a36Sopenharmony_ci ); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic void sii8620_stop_video(struct sii8620 *ctx) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci u8 val; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 99262306a36Sopenharmony_ci REG_TPI_INTR_EN, 0, 99362306a36Sopenharmony_ci REG_HDCP2X_INTR0_MASK, 0, 99462306a36Sopenharmony_ci REG_TPI_COPP_DATA2, 0, 99562306a36Sopenharmony_ci REG_TPI_INTR_ST0, ~0, 99662306a36Sopenharmony_ci ); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci switch (ctx->sink_type) { 99962306a36Sopenharmony_ci case SINK_DVI: 100062306a36Sopenharmony_ci val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN 100162306a36Sopenharmony_ci | BIT_TPI_SC_TPI_AV_MUTE; 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci case SINK_HDMI: 100462306a36Sopenharmony_ci default: 100562306a36Sopenharmony_ci val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN 100662306a36Sopenharmony_ci | BIT_TPI_SC_TPI_AV_MUTE 100762306a36Sopenharmony_ci | BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI; 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci sii8620_write(ctx, REG_TPI_SC, val); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic void sii8620_set_format(struct sii8620 *ctx) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci u8 out_fmt; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (sii8620_is_mhl3(ctx)) { 101962306a36Sopenharmony_ci sii8620_setbits(ctx, REG_M3_P0CTRL, 102062306a36Sopenharmony_ci BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, 102162306a36Sopenharmony_ci ctx->use_packed_pixel ? ~0 : 0); 102262306a36Sopenharmony_ci } else { 102362306a36Sopenharmony_ci if (ctx->use_packed_pixel) { 102462306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 102562306a36Sopenharmony_ci REG_VID_MODE, BIT_VID_MODE_M1080P, 102662306a36Sopenharmony_ci REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, 102762306a36Sopenharmony_ci REG_MHLTX_CTL6, 0x60 102862306a36Sopenharmony_ci ); 102962306a36Sopenharmony_ci } else { 103062306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 103162306a36Sopenharmony_ci REG_VID_MODE, 0, 103262306a36Sopenharmony_ci REG_MHL_TOP_CTL, 1, 103362306a36Sopenharmony_ci REG_MHLTX_CTL6, 0xa0 103462306a36Sopenharmony_ci ); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (ctx->use_packed_pixel) 103962306a36Sopenharmony_ci out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL); 104062306a36Sopenharmony_ci else 104162306a36Sopenharmony_ci out_fmt = VAL_TPI_FORMAT(RGB, FULL); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci sii8620_write_seq(ctx, 104462306a36Sopenharmony_ci REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), 104562306a36Sopenharmony_ci REG_TPI_OUTPUT, out_fmt, 104662306a36Sopenharmony_ci ); 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic int mhl3_infoframe_init(struct mhl3_infoframe *frame) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci frame->version = 3; 105462306a36Sopenharmony_ci frame->hev_format = -1; 105562306a36Sopenharmony_ci return 0; 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic ssize_t mhl3_infoframe_pack(struct mhl3_infoframe *frame, 105962306a36Sopenharmony_ci void *buffer, size_t size) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci const int frm_len = HDMI_INFOFRAME_HEADER_SIZE + MHL3_INFOFRAME_SIZE; 106262306a36Sopenharmony_ci u8 *ptr = buffer; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (size < frm_len) 106562306a36Sopenharmony_ci return -ENOSPC; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci memset(buffer, 0, size); 106862306a36Sopenharmony_ci ptr[0] = HDMI_INFOFRAME_TYPE_VENDOR; 106962306a36Sopenharmony_ci ptr[1] = frame->version; 107062306a36Sopenharmony_ci ptr[2] = MHL3_INFOFRAME_SIZE; 107162306a36Sopenharmony_ci ptr[4] = MHL3_IEEE_OUI & 0xff; 107262306a36Sopenharmony_ci ptr[5] = (MHL3_IEEE_OUI >> 8) & 0xff; 107362306a36Sopenharmony_ci ptr[6] = (MHL3_IEEE_OUI >> 16) & 0xff; 107462306a36Sopenharmony_ci ptr[7] = frame->video_format & 0x3; 107562306a36Sopenharmony_ci ptr[7] |= (frame->format_type & 0x7) << 2; 107662306a36Sopenharmony_ci ptr[7] |= frame->sep_audio ? BIT(5) : 0; 107762306a36Sopenharmony_ci if (frame->hev_format >= 0) { 107862306a36Sopenharmony_ci ptr[9] = 1; 107962306a36Sopenharmony_ci ptr[10] = (frame->hev_format >> 8) & 0xff; 108062306a36Sopenharmony_ci ptr[11] = frame->hev_format & 0xff; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci if (frame->av_delay) { 108362306a36Sopenharmony_ci bool sign = frame->av_delay < 0; 108462306a36Sopenharmony_ci int delay = sign ? -frame->av_delay : frame->av_delay; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci ptr[12] = (delay >> 16) & 0xf; 108762306a36Sopenharmony_ci if (sign) 108862306a36Sopenharmony_ci ptr[12] |= BIT(4); 108962306a36Sopenharmony_ci ptr[13] = (delay >> 8) & 0xff; 109062306a36Sopenharmony_ci ptr[14] = delay & 0xff; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci ptr[3] -= sii8620_checksum(buffer, frm_len); 109362306a36Sopenharmony_ci return frm_len; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic void sii8620_set_infoframes(struct sii8620 *ctx, 109762306a36Sopenharmony_ci struct drm_display_mode *mode) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct mhl3_infoframe mhl_frm; 110062306a36Sopenharmony_ci union hdmi_infoframe frm; 110162306a36Sopenharmony_ci u8 buf[31]; 110262306a36Sopenharmony_ci int ret; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci ret = drm_hdmi_avi_infoframe_from_display_mode(&frm.avi, 110562306a36Sopenharmony_ci NULL, mode); 110662306a36Sopenharmony_ci if (ctx->use_packed_pixel) 110762306a36Sopenharmony_ci frm.avi.colorspace = HDMI_COLORSPACE_YUV422; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (!ret) 111062306a36Sopenharmony_ci ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf)); 111162306a36Sopenharmony_ci if (ret > 0) 111262306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) { 111562306a36Sopenharmony_ci sii8620_write(ctx, REG_TPI_SC, 111662306a36Sopenharmony_ci BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); 111762306a36Sopenharmony_ci sii8620_write(ctx, REG_PKT_FILTER_0, 111862306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | 111962306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_MPEG_PKT | 112062306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_GCP_PKT, 112162306a36Sopenharmony_ci BIT_PKT_FILTER_1_DROP_GEN_PKT); 112262306a36Sopenharmony_ci return; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci sii8620_write(ctx, REG_PKT_FILTER_0, 112662306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | 112762306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_MPEG_PKT | 112862306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_AVI_PKT | 112962306a36Sopenharmony_ci BIT_PKT_FILTER_0_DROP_GCP_PKT, 113062306a36Sopenharmony_ci BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | 113162306a36Sopenharmony_ci BIT_PKT_FILTER_1_DROP_GEN_PKT | 113262306a36Sopenharmony_ci BIT_PKT_FILTER_1_DROP_VSIF_PKT); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci sii8620_write(ctx, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_EN 113562306a36Sopenharmony_ci | BIT_TPI_INFO_FSEL_RPT | VAL_TPI_INFO_FSEL_VSI); 113662306a36Sopenharmony_ci ret = mhl3_infoframe_init(&mhl_frm); 113762306a36Sopenharmony_ci if (!ret) 113862306a36Sopenharmony_ci ret = mhl3_infoframe_pack(&mhl_frm, buf, ARRAY_SIZE(buf)); 113962306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_TPI_INFO_B0, buf, ret); 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic void sii8620_start_video(struct sii8620 *ctx) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci struct drm_display_mode *mode = 114562306a36Sopenharmony_ci &ctx->bridge.encoder->crtc->state->adjusted_mode; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (!sii8620_is_mhl3(ctx)) 114862306a36Sopenharmony_ci sii8620_stop_video(ctx); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (ctx->sink_type == SINK_DVI && !sii8620_is_mhl3(ctx)) { 115162306a36Sopenharmony_ci sii8620_write(ctx, REG_RX_HDMI_CTRL2, 115262306a36Sopenharmony_ci VAL_RX_HDMI_CTRL2_DEFVAL); 115362306a36Sopenharmony_ci sii8620_write(ctx, REG_TPI_SC, 0); 115462306a36Sopenharmony_ci return; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 115862306a36Sopenharmony_ci REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL 115962306a36Sopenharmony_ci | BIT_RX_HDMI_CTRL2_USE_AV_MUTE, 116062306a36Sopenharmony_ci REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE 116162306a36Sopenharmony_ci | BIT_VID_OVRRD_M1080P_OVRRD); 116262306a36Sopenharmony_ci sii8620_set_format(ctx); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (!sii8620_is_mhl3(ctx)) { 116562306a36Sopenharmony_ci u8 link_mode = MHL_DST_LM_PATH_ENABLED; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (ctx->use_packed_pixel) 116862306a36Sopenharmony_ci link_mode |= MHL_DST_LM_CLK_MODE_PACKED_PIXEL; 116962306a36Sopenharmony_ci else 117062306a36Sopenharmony_ci link_mode |= MHL_DST_LM_CLK_MODE_NORMAL; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), link_mode); 117362306a36Sopenharmony_ci sii8620_set_auto_zone(ctx); 117462306a36Sopenharmony_ci } else { 117562306a36Sopenharmony_ci static const struct { 117662306a36Sopenharmony_ci int max_clk; 117762306a36Sopenharmony_ci u8 zone; 117862306a36Sopenharmony_ci u8 link_rate; 117962306a36Sopenharmony_ci u8 rrp_decode; 118062306a36Sopenharmony_ci } clk_spec[] = { 118162306a36Sopenharmony_ci { 150000, VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS, 118262306a36Sopenharmony_ci MHL_XDS_LINK_RATE_1_5_GBPS, 0x38 }, 118362306a36Sopenharmony_ci { 300000, VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS, 118462306a36Sopenharmony_ci MHL_XDS_LINK_RATE_3_0_GBPS, 0x40 }, 118562306a36Sopenharmony_ci { 600000, VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS, 118662306a36Sopenharmony_ci MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 }, 118762306a36Sopenharmony_ci }; 118862306a36Sopenharmony_ci u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN; 118962306a36Sopenharmony_ci int clk = mode->clock * (ctx->use_packed_pixel ? 2 : 3); 119062306a36Sopenharmony_ci int i; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(clk_spec) - 1; ++i) 119362306a36Sopenharmony_ci if (clk < clk_spec[i].max_clk) 119462306a36Sopenharmony_ci break; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (100 * clk >= 98 * clk_spec[i].max_clk) 119762306a36Sopenharmony_ci p0_ctrl |= BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci sii8620_burst_tx_bits_per_pixel_fmt(ctx, ctx->use_packed_pixel); 120062306a36Sopenharmony_ci sii8620_burst_send(ctx); 120162306a36Sopenharmony_ci sii8620_write_seq(ctx, 120262306a36Sopenharmony_ci REG_MHL_DP_CTL0, 0xf0, 120362306a36Sopenharmony_ci REG_MHL3_TX_ZONE_CTL, clk_spec[i].zone); 120462306a36Sopenharmony_ci sii8620_setbits(ctx, REG_M3_P0CTRL, 120562306a36Sopenharmony_ci BIT_M3_P0CTRL_MHL3_P0_PORT_EN 120662306a36Sopenharmony_ci | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, p0_ctrl); 120762306a36Sopenharmony_ci sii8620_setbits(ctx, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 120862306a36Sopenharmony_ci clk_spec[i].rrp_decode); 120962306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 121062306a36Sopenharmony_ci REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE 121162306a36Sopenharmony_ci | BIT_M3_CTRL_H2M_SWRST, 121262306a36Sopenharmony_ci REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE 121362306a36Sopenharmony_ci ); 121462306a36Sopenharmony_ci sii8620_mt_write_stat(ctx, MHL_XDS_REG(AVLINK_MODE_CONTROL), 121562306a36Sopenharmony_ci clk_spec[i].link_rate); 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci sii8620_set_infoframes(ctx, mode); 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic void sii8620_disable_hpd(struct sii8620 *ctx) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci sii8620_setbits(ctx, REG_EDID_CTRL, BIT_EDID_CTRL_EDID_PRIME_VALID, 0); 122462306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 122562306a36Sopenharmony_ci REG_HPD_CTRL, BIT_HPD_CTRL_HPD_OUT_OVR_EN, 122662306a36Sopenharmony_ci REG_INTR8_MASK, 0 122762306a36Sopenharmony_ci ); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void sii8620_enable_hpd(struct sii8620 *ctx) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci sii8620_setbits(ctx, REG_TMDS_CSTAT_P3, 123362306a36Sopenharmony_ci BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS 123462306a36Sopenharmony_ci | BIT_TMDS_CSTAT_P3_CLR_AVI, ~0); 123562306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 123662306a36Sopenharmony_ci REG_HPD_CTRL, BIT_HPD_CTRL_HPD_OUT_OVR_EN 123762306a36Sopenharmony_ci | BIT_HPD_CTRL_HPD_HIGH, 123862306a36Sopenharmony_ci ); 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic void sii8620_mhl_discover(struct sii8620 *ctx) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 124462306a36Sopenharmony_ci REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT 124562306a36Sopenharmony_ci | BIT_DISC_CTRL9_DISC_PULSE_PROCEED, 124662306a36Sopenharmony_ci REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_5K, VAL_PUP_20K), 124762306a36Sopenharmony_ci REG_CBUS_DISC_INTR0_MASK, BIT_MHL3_EST_INT 124862306a36Sopenharmony_ci | BIT_MHL_EST_INT 124962306a36Sopenharmony_ci | BIT_NOT_MHL_EST_INT 125062306a36Sopenharmony_ci | BIT_CBUS_MHL3_DISCON_INT 125162306a36Sopenharmony_ci | BIT_CBUS_MHL12_DISCON_INT 125262306a36Sopenharmony_ci | BIT_RGND_READY_INT, 125362306a36Sopenharmony_ci REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X 125462306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL 125562306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_ZONE_MASK_OE, 125662306a36Sopenharmony_ci REG_MHL_DP_CTL0, BIT_MHL_DP_CTL0_DP_OE 125762306a36Sopenharmony_ci | BIT_MHL_DP_CTL0_TX_OE_OVR, 125862306a36Sopenharmony_ci REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE, 125962306a36Sopenharmony_ci REG_MHL_DP_CTL1, 0xA2, 126062306a36Sopenharmony_ci REG_MHL_DP_CTL2, 0x03, 126162306a36Sopenharmony_ci REG_MHL_DP_CTL3, 0x35, 126262306a36Sopenharmony_ci REG_MHL_DP_CTL5, 0x02, 126362306a36Sopenharmony_ci REG_MHL_DP_CTL6, 0x02, 126462306a36Sopenharmony_ci REG_MHL_DP_CTL7, 0x03, 126562306a36Sopenharmony_ci REG_COC_CTLC, 0xFF, 126662306a36Sopenharmony_ci REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 126762306a36Sopenharmony_ci | BIT_DPD_OSC_EN | BIT_DPD_PWRON_HSIC, 126862306a36Sopenharmony_ci REG_COC_INTR_MASK, BIT_COC_PLL_LOCK_STATUS_CHANGE 126962306a36Sopenharmony_ci | BIT_COC_CALIBRATION_DONE, 127062306a36Sopenharmony_ci REG_CBUS_INT_1_MASK, BIT_CBUS_MSC_ABORT_RCVD 127162306a36Sopenharmony_ci | BIT_CBUS_CMD_ABORT, 127262306a36Sopenharmony_ci REG_CBUS_INT_0_MASK, BIT_CBUS_MSC_MT_DONE 127362306a36Sopenharmony_ci | BIT_CBUS_HPD_CHG 127462306a36Sopenharmony_ci | BIT_CBUS_MSC_MR_WRITE_STAT 127562306a36Sopenharmony_ci | BIT_CBUS_MSC_MR_MSC_MSG 127662306a36Sopenharmony_ci | BIT_CBUS_MSC_MR_WRITE_BURST 127762306a36Sopenharmony_ci | BIT_CBUS_MSC_MR_SET_INT 127862306a36Sopenharmony_ci | BIT_CBUS_MSC_MT_DONE_NACK 127962306a36Sopenharmony_ci ); 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cistatic void sii8620_peer_specific_init(struct sii8620 *ctx) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci if (sii8620_is_mhl3(ctx)) 128562306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 128662306a36Sopenharmony_ci REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD, 128762306a36Sopenharmony_ci REG_EMSCINTRMASK1, 128862306a36Sopenharmony_ci BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR 128962306a36Sopenharmony_ci ); 129062306a36Sopenharmony_ci else 129162306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 129262306a36Sopenharmony_ci REG_HDCP2X_INTR0_MASK, 0x00, 129362306a36Sopenharmony_ci REG_EMSCINTRMASK1, 0x00, 129462306a36Sopenharmony_ci REG_HDCP2X_INTR0, 0xFF, 129562306a36Sopenharmony_ci REG_INTR1, 0xFF, 129662306a36Sopenharmony_ci REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD 129762306a36Sopenharmony_ci | BIT_SYS_CTRL1_TX_CTRL_HDMI 129862306a36Sopenharmony_ci ); 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci#define SII8620_MHL_VERSION 0x32 130262306a36Sopenharmony_ci#define SII8620_SCRATCHPAD_SIZE 16 130362306a36Sopenharmony_ci#define SII8620_INT_STAT_SIZE 0x33 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic void sii8620_set_dev_cap(struct sii8620 *ctx) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci static const u8 devcap[MHL_DCAP_SIZE] = { 130862306a36Sopenharmony_ci [MHL_DCAP_MHL_VERSION] = SII8620_MHL_VERSION, 130962306a36Sopenharmony_ci [MHL_DCAP_CAT] = MHL_DCAP_CAT_SOURCE | MHL_DCAP_CAT_POWER, 131062306a36Sopenharmony_ci [MHL_DCAP_ADOPTER_ID_H] = 0x01, 131162306a36Sopenharmony_ci [MHL_DCAP_ADOPTER_ID_L] = 0x41, 131262306a36Sopenharmony_ci [MHL_DCAP_VID_LINK_MODE] = MHL_DCAP_VID_LINK_RGB444 131362306a36Sopenharmony_ci | MHL_DCAP_VID_LINK_PPIXEL 131462306a36Sopenharmony_ci | MHL_DCAP_VID_LINK_16BPP, 131562306a36Sopenharmony_ci [MHL_DCAP_AUD_LINK_MODE] = MHL_DCAP_AUD_LINK_2CH, 131662306a36Sopenharmony_ci [MHL_DCAP_VIDEO_TYPE] = MHL_DCAP_VT_GRAPHICS, 131762306a36Sopenharmony_ci [MHL_DCAP_LOG_DEV_MAP] = MHL_DCAP_LD_GUI, 131862306a36Sopenharmony_ci [MHL_DCAP_BANDWIDTH] = 0x0f, 131962306a36Sopenharmony_ci [MHL_DCAP_FEATURE_FLAG] = MHL_DCAP_FEATURE_RCP_SUPPORT 132062306a36Sopenharmony_ci | MHL_DCAP_FEATURE_RAP_SUPPORT 132162306a36Sopenharmony_ci | MHL_DCAP_FEATURE_SP_SUPPORT, 132262306a36Sopenharmony_ci [MHL_DCAP_SCRATCHPAD_SIZE] = SII8620_SCRATCHPAD_SIZE, 132362306a36Sopenharmony_ci [MHL_DCAP_INT_STAT_SIZE] = SII8620_INT_STAT_SIZE, 132462306a36Sopenharmony_ci }; 132562306a36Sopenharmony_ci static const u8 xdcap[MHL_XDC_SIZE] = { 132662306a36Sopenharmony_ci [MHL_XDC_ECBUS_SPEEDS] = MHL_XDC_ECBUS_S_075 132762306a36Sopenharmony_ci | MHL_XDC_ECBUS_S_8BIT, 132862306a36Sopenharmony_ci [MHL_XDC_TMDS_SPEEDS] = MHL_XDC_TMDS_150 132962306a36Sopenharmony_ci | MHL_XDC_TMDS_300 | MHL_XDC_TMDS_600, 133062306a36Sopenharmony_ci [MHL_XDC_ECBUS_ROLES] = MHL_XDC_DEV_HOST, 133162306a36Sopenharmony_ci [MHL_XDC_LOG_DEV_MAPX] = MHL_XDC_LD_PHONE, 133262306a36Sopenharmony_ci }; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_MHL_DEVCAP_0, devcap, ARRAY_SIZE(devcap)); 133562306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_MHL_EXTDEVCAP_0, xdcap, ARRAY_SIZE(xdcap)); 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic void sii8620_mhl_init(struct sii8620 *ctx) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 134162306a36Sopenharmony_ci REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_OFF, VAL_PUP_20K), 134262306a36Sopenharmony_ci REG_CBUS_MSC_COMPAT_CTRL, 134362306a36Sopenharmony_ci BIT_CBUS_MSC_COMPAT_CTRL_XDEVCAP_EN, 134462306a36Sopenharmony_ci ); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci sii8620_peer_specific_init(ctx); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci sii8620_disable_hpd(ctx); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 135162306a36Sopenharmony_ci REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO, 135262306a36Sopenharmony_ci REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT 135362306a36Sopenharmony_ci | BIT_DISC_CTRL9_WAKE_PULSE_BYPASS, 135462306a36Sopenharmony_ci REG_TMDS0_CCTRL1, 0x90, 135562306a36Sopenharmony_ci REG_TMDS_CLK_EN, 0x01, 135662306a36Sopenharmony_ci REG_TMDS_CH_EN, 0x11, 135762306a36Sopenharmony_ci REG_BGR_BIAS, 0x87, 135862306a36Sopenharmony_ci REG_ALICE0_ZONE_CTRL, 0xE8, 135962306a36Sopenharmony_ci REG_ALICE0_MODE_CTRL, 0x04, 136062306a36Sopenharmony_ci ); 136162306a36Sopenharmony_ci sii8620_setbits(ctx, REG_LM_DDC, BIT_LM_DDC_SW_TPI_EN_DISABLED, 0); 136262306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 136362306a36Sopenharmony_ci REG_TPI_HW_OPT3, 0x76, 136462306a36Sopenharmony_ci REG_TMDS_CCTRL, BIT_TMDS_CCTRL_TMDS_OE, 136562306a36Sopenharmony_ci REG_TPI_DTD_B2, 79, 136662306a36Sopenharmony_ci ); 136762306a36Sopenharmony_ci sii8620_set_dev_cap(ctx); 136862306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 136962306a36Sopenharmony_ci REG_MDT_XMIT_TIMEOUT, 100, 137062306a36Sopenharmony_ci REG_MDT_XMIT_CTRL, 0x03, 137162306a36Sopenharmony_ci REG_MDT_XFIFO_STAT, 0x00, 137262306a36Sopenharmony_ci REG_MDT_RCV_TIMEOUT, 100, 137362306a36Sopenharmony_ci REG_CBUS_LINK_CTRL_8, 0x1D, 137462306a36Sopenharmony_ci ); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci sii8620_start_gen2_write_burst(ctx); 137762306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 137862306a36Sopenharmony_ci REG_BIST_CTRL, 0x00, 137962306a36Sopenharmony_ci REG_COC_CTL1, 0x10, 138062306a36Sopenharmony_ci REG_COC_CTL2, 0x18, 138162306a36Sopenharmony_ci REG_COC_CTLF, 0x07, 138262306a36Sopenharmony_ci REG_COC_CTL11, 0xF8, 138362306a36Sopenharmony_ci REG_COC_CTL17, 0x61, 138462306a36Sopenharmony_ci REG_COC_CTL18, 0x46, 138562306a36Sopenharmony_ci REG_COC_CTL19, 0x15, 138662306a36Sopenharmony_ci REG_COC_CTL1A, 0x01, 138762306a36Sopenharmony_ci REG_MHL_COC_CTL3, BIT_MHL_COC_CTL3_COC_AECHO_EN, 138862306a36Sopenharmony_ci REG_MHL_COC_CTL4, 0x2D, 138962306a36Sopenharmony_ci REG_MHL_COC_CTL5, 0xF9, 139062306a36Sopenharmony_ci REG_MSC_HEARTBEAT_CTRL, 0x27, 139162306a36Sopenharmony_ci ); 139262306a36Sopenharmony_ci sii8620_disable_gen2_write_burst(ctx); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), SII8620_MHL_VERSION); 139562306a36Sopenharmony_ci sii8620_mt_write_stat(ctx, MHL_DST_REG(CONNECTED_RDY), 139662306a36Sopenharmony_ci MHL_DST_CONN_DCAP_RDY | MHL_DST_CONN_XDEVCAPP_SUPP 139762306a36Sopenharmony_ci | MHL_DST_CONN_POW_STAT); 139862306a36Sopenharmony_ci sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), MHL_INT_RC_DCAP_CHG); 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic void sii8620_emsc_enable(struct sii8620 *ctx) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci u8 reg; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_EMSC_EN 140662306a36Sopenharmony_ci | BIT_GENCTL_CLR_EMSC_RFIFO 140762306a36Sopenharmony_ci | BIT_GENCTL_CLR_EMSC_XFIFO, ~0); 140862306a36Sopenharmony_ci sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_CLR_EMSC_RFIFO 140962306a36Sopenharmony_ci | BIT_GENCTL_CLR_EMSC_XFIFO, 0); 141062306a36Sopenharmony_ci sii8620_setbits(ctx, REG_COMMECNT, BIT_COMMECNT_I2C_TO_EMSC_EN, ~0); 141162306a36Sopenharmony_ci reg = sii8620_readb(ctx, REG_EMSCINTR); 141262306a36Sopenharmony_ci sii8620_write(ctx, REG_EMSCINTR, reg); 141362306a36Sopenharmony_ci sii8620_write(ctx, REG_EMSCINTRMASK, BIT_EMSCINTR_SPI_DVLD); 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci int i; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci for (i = 0; i < 10; ++i) { 142162306a36Sopenharmony_ci u8 s = sii8620_readb(ctx, REG_COC_STAT_0); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if ((s & MSK_COC_STAT_0_FSM_STATE) == state) 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci if (!(s & BIT_COC_STAT_0_PLL_LOCKED)) 142662306a36Sopenharmony_ci return -EBUSY; 142762306a36Sopenharmony_ci usleep_range(4000, 6000); 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci return -ETIMEDOUT; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci int ret; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci if (ctx->mode == mode) 143762306a36Sopenharmony_ci return; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci switch (mode) { 144062306a36Sopenharmony_ci case CM_MHL1: 144162306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 144262306a36Sopenharmony_ci REG_CBUS_MSC_COMPAT_CTRL, 0x02, 144362306a36Sopenharmony_ci REG_M3_CTRL, VAL_M3_CTRL_MHL1_2_VALUE, 144462306a36Sopenharmony_ci REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 144562306a36Sopenharmony_ci | BIT_DPD_OSC_EN, 144662306a36Sopenharmony_ci REG_COC_INTR_MASK, 0 144762306a36Sopenharmony_ci ); 144862306a36Sopenharmony_ci ctx->mode = mode; 144962306a36Sopenharmony_ci break; 145062306a36Sopenharmony_ci case CM_MHL3: 145162306a36Sopenharmony_ci sii8620_write(ctx, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); 145262306a36Sopenharmony_ci ctx->mode = mode; 145362306a36Sopenharmony_ci return; 145462306a36Sopenharmony_ci case CM_ECBUS_S: 145562306a36Sopenharmony_ci sii8620_emsc_enable(ctx); 145662306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 145762306a36Sopenharmony_ci REG_TTXSPINUMS, 4, 145862306a36Sopenharmony_ci REG_TRXSPINUMS, 4, 145962306a36Sopenharmony_ci REG_TTXHSICNUMS, 0x14, 146062306a36Sopenharmony_ci REG_TRXHSICNUMS, 0x14, 146162306a36Sopenharmony_ci REG_TTXTOTNUMS, 0x18, 146262306a36Sopenharmony_ci REG_TRXTOTNUMS, 0x18, 146362306a36Sopenharmony_ci REG_PWD_SRST, BIT_PWD_SRST_COC_DOC_RST 146462306a36Sopenharmony_ci | BIT_PWD_SRST_CBUS_RST_SW_EN, 146562306a36Sopenharmony_ci REG_MHL_COC_CTL1, 0xbd, 146662306a36Sopenharmony_ci REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN, 146762306a36Sopenharmony_ci REG_COC_CTLB, 0x01, 146862306a36Sopenharmony_ci REG_COC_CTL0, 0x5c, 146962306a36Sopenharmony_ci REG_COC_CTL14, 0x03, 147062306a36Sopenharmony_ci REG_COC_CTL15, 0x80, 147162306a36Sopenharmony_ci REG_MHL_DP_CTL6, BIT_MHL_DP_CTL6_DP_TAP1_SGN 147262306a36Sopenharmony_ci | BIT_MHL_DP_CTL6_DP_TAP1_EN 147362306a36Sopenharmony_ci | BIT_MHL_DP_CTL6_DT_PREDRV_FEEDCAP_EN, 147462306a36Sopenharmony_ci REG_MHL_DP_CTL8, 0x03 147562306a36Sopenharmony_ci ); 147662306a36Sopenharmony_ci ret = sii8620_wait_for_fsm_state(ctx, 0x03); 147762306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 147862306a36Sopenharmony_ci REG_COC_CTL14, 0x00, 147962306a36Sopenharmony_ci REG_COC_CTL15, 0x80 148062306a36Sopenharmony_ci ); 148162306a36Sopenharmony_ci if (!ret) 148262306a36Sopenharmony_ci sii8620_write(ctx, REG_CBUS3_CNVT, 0x85); 148362306a36Sopenharmony_ci else 148462306a36Sopenharmony_ci sii8620_disconnect(ctx); 148562306a36Sopenharmony_ci return; 148662306a36Sopenharmony_ci case CM_DISCONNECTED: 148762306a36Sopenharmony_ci ctx->mode = mode; 148862306a36Sopenharmony_ci break; 148962306a36Sopenharmony_ci default: 149062306a36Sopenharmony_ci dev_err(ctx->dev, "%s mode %d not supported\n", __func__, mode); 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci sii8620_set_auto_zone(ctx); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (mode != CM_MHL1) 149762306a36Sopenharmony_ci return; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 150062306a36Sopenharmony_ci REG_MHL_DP_CTL0, 0xBC, 150162306a36Sopenharmony_ci REG_MHL_DP_CTL1, 0xBB, 150262306a36Sopenharmony_ci REG_MHL_DP_CTL3, 0x48, 150362306a36Sopenharmony_ci REG_MHL_DP_CTL5, 0x39, 150462306a36Sopenharmony_ci REG_MHL_DP_CTL2, 0x2A, 150562306a36Sopenharmony_ci REG_MHL_DP_CTL6, 0x2A, 150662306a36Sopenharmony_ci REG_MHL_DP_CTL7, 0x08 150762306a36Sopenharmony_ci ); 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic void sii8620_hpd_unplugged(struct sii8620 *ctx) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci sii8620_disable_hpd(ctx); 151362306a36Sopenharmony_ci ctx->sink_type = SINK_NONE; 151462306a36Sopenharmony_ci ctx->sink_detected = false; 151562306a36Sopenharmony_ci ctx->feature_complete = false; 151662306a36Sopenharmony_ci kfree(ctx->edid); 151762306a36Sopenharmony_ci ctx->edid = NULL; 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_cistatic void sii8620_disconnect(struct sii8620 *ctx) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci sii8620_disable_gen2_write_burst(ctx); 152362306a36Sopenharmony_ci sii8620_stop_video(ctx); 152462306a36Sopenharmony_ci msleep(100); 152562306a36Sopenharmony_ci sii8620_cbus_reset(ctx); 152662306a36Sopenharmony_ci sii8620_set_mode(ctx, CM_DISCONNECTED); 152762306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 152862306a36Sopenharmony_ci REG_TX_ZONE_CTL1, 0, 152962306a36Sopenharmony_ci REG_MHL_PLL_CTL0, 0x07, 153062306a36Sopenharmony_ci REG_COC_CTL0, 0x40, 153162306a36Sopenharmony_ci REG_CBUS3_CNVT, 0x84, 153262306a36Sopenharmony_ci REG_COC_CTL14, 0x00, 153362306a36Sopenharmony_ci REG_COC_CTL0, 0x40, 153462306a36Sopenharmony_ci REG_HRXCTRL3, 0x07, 153562306a36Sopenharmony_ci REG_MHL_PLL_CTL0, VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X 153662306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL 153762306a36Sopenharmony_ci | BIT_MHL_PLL_CTL0_ZONE_MASK_OE, 153862306a36Sopenharmony_ci REG_MHL_DP_CTL0, BIT_MHL_DP_CTL0_DP_OE 153962306a36Sopenharmony_ci | BIT_MHL_DP_CTL0_TX_OE_OVR, 154062306a36Sopenharmony_ci REG_MHL_DP_CTL1, 0xBB, 154162306a36Sopenharmony_ci REG_MHL_DP_CTL3, 0x48, 154262306a36Sopenharmony_ci REG_MHL_DP_CTL5, 0x3F, 154362306a36Sopenharmony_ci REG_MHL_DP_CTL2, 0x2F, 154462306a36Sopenharmony_ci REG_MHL_DP_CTL6, 0x2A, 154562306a36Sopenharmony_ci REG_MHL_DP_CTL7, 0x03 154662306a36Sopenharmony_ci ); 154762306a36Sopenharmony_ci sii8620_hpd_unplugged(ctx); 154862306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 154962306a36Sopenharmony_ci REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE, 155062306a36Sopenharmony_ci REG_MHL_COC_CTL1, 0x07, 155162306a36Sopenharmony_ci REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_OFF, VAL_PUP_20K), 155262306a36Sopenharmony_ci REG_DISC_CTRL8, 0x00, 155362306a36Sopenharmony_ci REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT 155462306a36Sopenharmony_ci | BIT_DISC_CTRL9_WAKE_PULSE_BYPASS, 155562306a36Sopenharmony_ci REG_INT_CTRL, 0x00, 155662306a36Sopenharmony_ci REG_MSC_HEARTBEAT_CTRL, 0x27, 155762306a36Sopenharmony_ci REG_DISC_CTRL1, 0x25, 155862306a36Sopenharmony_ci REG_CBUS_DISC_INTR0, (u8)~BIT_RGND_READY_INT, 155962306a36Sopenharmony_ci REG_CBUS_DISC_INTR0_MASK, BIT_RGND_READY_INT, 156062306a36Sopenharmony_ci REG_MDT_INT_1, 0xff, 156162306a36Sopenharmony_ci REG_MDT_INT_1_MASK, 0x00, 156262306a36Sopenharmony_ci REG_MDT_INT_0, 0xff, 156362306a36Sopenharmony_ci REG_MDT_INT_0_MASK, 0x00, 156462306a36Sopenharmony_ci REG_COC_INTR, 0xff, 156562306a36Sopenharmony_ci REG_COC_INTR_MASK, 0x00, 156662306a36Sopenharmony_ci REG_TRXINTH, 0xff, 156762306a36Sopenharmony_ci REG_TRXINTMH, 0x00, 156862306a36Sopenharmony_ci REG_CBUS_INT_0, 0xff, 156962306a36Sopenharmony_ci REG_CBUS_INT_0_MASK, 0x00, 157062306a36Sopenharmony_ci REG_CBUS_INT_1, 0xff, 157162306a36Sopenharmony_ci REG_CBUS_INT_1_MASK, 0x00, 157262306a36Sopenharmony_ci REG_EMSCINTR, 0xff, 157362306a36Sopenharmony_ci REG_EMSCINTRMASK, 0x00, 157462306a36Sopenharmony_ci REG_EMSCINTR1, 0xff, 157562306a36Sopenharmony_ci REG_EMSCINTRMASK1, 0x00, 157662306a36Sopenharmony_ci REG_INTR8, 0xff, 157762306a36Sopenharmony_ci REG_INTR8_MASK, 0x00, 157862306a36Sopenharmony_ci REG_TPI_INTR_ST0, 0xff, 157962306a36Sopenharmony_ci REG_TPI_INTR_EN, 0x00, 158062306a36Sopenharmony_ci REG_HDCP2X_INTR0, 0xff, 158162306a36Sopenharmony_ci REG_HDCP2X_INTR0_MASK, 0x00, 158262306a36Sopenharmony_ci REG_INTR9, 0xff, 158362306a36Sopenharmony_ci REG_INTR9_MASK, 0x00, 158462306a36Sopenharmony_ci REG_INTR3, 0xff, 158562306a36Sopenharmony_ci REG_INTR3_MASK, 0x00, 158662306a36Sopenharmony_ci REG_INTR5, 0xff, 158762306a36Sopenharmony_ci REG_INTR5_MASK, 0x00, 158862306a36Sopenharmony_ci REG_INTR2, 0xff, 158962306a36Sopenharmony_ci REG_INTR2_MASK, 0x00, 159062306a36Sopenharmony_ci ); 159162306a36Sopenharmony_ci memset(ctx->stat, 0, sizeof(ctx->stat)); 159262306a36Sopenharmony_ci memset(ctx->xstat, 0, sizeof(ctx->xstat)); 159362306a36Sopenharmony_ci memset(ctx->devcap, 0, sizeof(ctx->devcap)); 159462306a36Sopenharmony_ci memset(ctx->xdevcap, 0, sizeof(ctx->xdevcap)); 159562306a36Sopenharmony_ci ctx->devcap_read = false; 159662306a36Sopenharmony_ci ctx->cbus_status = 0; 159762306a36Sopenharmony_ci sii8620_mt_cleanup(ctx); 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic void sii8620_mhl_disconnected(struct sii8620 *ctx) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 160362306a36Sopenharmony_ci REG_DISC_CTRL4, VAL_DISC_CTRL4(VAL_PUP_OFF, VAL_PUP_20K), 160462306a36Sopenharmony_ci REG_CBUS_MSC_COMPAT_CTRL, 160562306a36Sopenharmony_ci BIT_CBUS_MSC_COMPAT_CTRL_XDEVCAP_EN 160662306a36Sopenharmony_ci ); 160762306a36Sopenharmony_ci sii8620_disconnect(ctx); 160862306a36Sopenharmony_ci} 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_cistatic void sii8620_irq_disc(struct sii8620 *ctx) 161162306a36Sopenharmony_ci{ 161262306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_CBUS_DISC_INTR0); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci if (stat & VAL_CBUS_MHL_DISCON) 161562306a36Sopenharmony_ci sii8620_mhl_disconnected(ctx); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (stat & BIT_RGND_READY_INT) { 161862306a36Sopenharmony_ci u8 stat2 = sii8620_readb(ctx, REG_DISC_STAT2); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if ((stat2 & MSK_DISC_STAT2_RGND) == VAL_RGND_1K) { 162162306a36Sopenharmony_ci sii8620_mhl_discover(ctx); 162262306a36Sopenharmony_ci } else { 162362306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 162462306a36Sopenharmony_ci REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT 162562306a36Sopenharmony_ci | BIT_DISC_CTRL9_NOMHL_EST 162662306a36Sopenharmony_ci | BIT_DISC_CTRL9_WAKE_PULSE_BYPASS, 162762306a36Sopenharmony_ci REG_CBUS_DISC_INTR0_MASK, BIT_RGND_READY_INT 162862306a36Sopenharmony_ci | BIT_CBUS_MHL3_DISCON_INT 162962306a36Sopenharmony_ci | BIT_CBUS_MHL12_DISCON_INT 163062306a36Sopenharmony_ci | BIT_NOT_MHL_EST_INT 163162306a36Sopenharmony_ci ); 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci if (stat & BIT_MHL_EST_INT) 163562306a36Sopenharmony_ci sii8620_mhl_init(ctx); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci sii8620_write(ctx, REG_CBUS_DISC_INTR0, stat); 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic void sii8620_read_burst(struct sii8620 *ctx) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci u8 buf[17]; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_MDT_RCV_READ_PORT, buf, ARRAY_SIZE(buf)); 164562306a36Sopenharmony_ci sii8620_write(ctx, REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN | 164662306a36Sopenharmony_ci BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN | 164762306a36Sopenharmony_ci BIT_MDT_RCV_CTRL_MDT_RFIFO_CLR_CUR); 164862306a36Sopenharmony_ci sii8620_readb(ctx, REG_MDT_RFIFO_STAT); 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic void sii8620_irq_g2wb(struct sii8620 *ctx) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_MDT_INT_0); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (stat & BIT_MDT_IDLE_AFTER_HAWB_DISABLE) 165662306a36Sopenharmony_ci if (sii8620_is_mhl3(ctx)) 165762306a36Sopenharmony_ci sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), 165862306a36Sopenharmony_ci MHL_INT_RC_FEAT_COMPLETE); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (stat & BIT_MDT_RFIFO_DATA_RDY) 166162306a36Sopenharmony_ci sii8620_read_burst(ctx); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if (stat & BIT_MDT_XFIFO_EMPTY) 166462306a36Sopenharmony_ci sii8620_write(ctx, REG_MDT_XMIT_CTRL, 0); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci sii8620_write(ctx, REG_MDT_INT_0, stat); 166762306a36Sopenharmony_ci} 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_cistatic void sii8620_status_dcap_ready(struct sii8620 *ctx) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci enum sii8620_mode mode; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci mode = ctx->stat[MHL_DST_VERSION] >= 0x30 ? CM_MHL3 : CM_MHL1; 167462306a36Sopenharmony_ci if (mode > ctx->mode) 167562306a36Sopenharmony_ci sii8620_set_mode(ctx, mode); 167662306a36Sopenharmony_ci sii8620_peer_specific_init(ctx); 167762306a36Sopenharmony_ci sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE 167862306a36Sopenharmony_ci | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic void sii8620_status_changed_path(struct sii8620 *ctx) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci u8 link_mode; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci if (ctx->use_packed_pixel) 168662306a36Sopenharmony_ci link_mode = MHL_DST_LM_CLK_MODE_PACKED_PIXEL; 168762306a36Sopenharmony_ci else 168862306a36Sopenharmony_ci link_mode = MHL_DST_LM_CLK_MODE_NORMAL; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci if (ctx->stat[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) 169162306a36Sopenharmony_ci link_mode |= MHL_DST_LM_PATH_ENABLED; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), 169462306a36Sopenharmony_ci link_mode); 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic void sii8620_msc_mr_write_stat(struct sii8620 *ctx) 169862306a36Sopenharmony_ci{ 169962306a36Sopenharmony_ci u8 st[MHL_DST_SIZE], xst[MHL_XDS_SIZE]; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_MHL_STAT_0, st, MHL_DST_SIZE); 170262306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_MHL_EXTSTAT_0, xst, MHL_XDS_SIZE); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci sii8620_update_array(ctx->stat, st, MHL_DST_SIZE); 170562306a36Sopenharmony_ci sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci if (ctx->stat[MHL_DST_CONNECTED_RDY] & st[MHL_DST_CONNECTED_RDY] & 170862306a36Sopenharmony_ci MHL_DST_CONN_DCAP_RDY) { 170962306a36Sopenharmony_ci sii8620_status_dcap_ready(ctx); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (!sii8620_is_mhl3(ctx)) 171262306a36Sopenharmony_ci sii8620_mt_read_devcap(ctx, false); 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) 171662306a36Sopenharmony_ci sii8620_status_changed_path(ctx); 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic void sii8620_ecbus_up(struct sii8620 *ctx, int ret) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci if (ret < 0) 172262306a36Sopenharmony_ci return; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci sii8620_set_mode(ctx, CM_ECBUS_S); 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_cistatic void sii8620_got_ecbus_speed(struct sii8620 *ctx, int ret) 172862306a36Sopenharmony_ci{ 172962306a36Sopenharmony_ci if (ret < 0) 173062306a36Sopenharmony_ci return; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), 173362306a36Sopenharmony_ci MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); 173462306a36Sopenharmony_ci sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); 173562306a36Sopenharmony_ci sii8620_mt_set_cont(ctx, sii8620_ecbus_up); 173662306a36Sopenharmony_ci} 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cistatic void sii8620_mhl_burst_emsc_support_set(struct mhl_burst_emsc_support *d, 173962306a36Sopenharmony_ci enum mhl_burst_id id) 174062306a36Sopenharmony_ci{ 174162306a36Sopenharmony_ci sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_EMSC_SUPPORT); 174262306a36Sopenharmony_ci d->num_entries = 1; 174362306a36Sopenharmony_ci d->burst_id[0] = cpu_to_be16(id); 174462306a36Sopenharmony_ci} 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_cistatic void sii8620_send_features(struct sii8620 *ctx) 174762306a36Sopenharmony_ci{ 174862306a36Sopenharmony_ci u8 buf[16]; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci sii8620_write(ctx, REG_MDT_XMIT_CTRL, BIT_MDT_XMIT_CTRL_EN 175162306a36Sopenharmony_ci | BIT_MDT_XMIT_CTRL_FIXED_BURST_LEN); 175262306a36Sopenharmony_ci sii8620_mhl_burst_emsc_support_set((void *)buf, 175362306a36Sopenharmony_ci MHL_BURST_ID_HID_PAYLOAD); 175462306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf)); 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_cistatic bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci scancode &= MHL_RCP_KEY_ID_MASK; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_RC_CORE) || !ctx->rc_dev) 176462306a36Sopenharmony_ci return false; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci if (pressed) 176762306a36Sopenharmony_ci rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0); 176862306a36Sopenharmony_ci else 176962306a36Sopenharmony_ci rc_keyup(ctx->rc_dev); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci return true; 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cistatic void sii8620_msc_mr_set_int(struct sii8620 *ctx) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci u8 ints[MHL_INT_SIZE]; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); 177962306a36Sopenharmony_ci sii8620_write_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_DCAP_CHG) { 178262306a36Sopenharmony_ci switch (ctx->mode) { 178362306a36Sopenharmony_ci case CM_MHL3: 178462306a36Sopenharmony_ci sii8620_mt_read_xdevcap_reg(ctx, MHL_XDC_ECBUS_SPEEDS); 178562306a36Sopenharmony_ci sii8620_mt_set_cont(ctx, sii8620_got_ecbus_speed); 178662306a36Sopenharmony_ci break; 178762306a36Sopenharmony_ci case CM_ECBUS_S: 178862306a36Sopenharmony_ci sii8620_mt_read_devcap(ctx, true); 178962306a36Sopenharmony_ci break; 179062306a36Sopenharmony_ci default: 179162306a36Sopenharmony_ci break; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) 179562306a36Sopenharmony_ci sii8620_send_features(ctx); 179662306a36Sopenharmony_ci if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) { 179762306a36Sopenharmony_ci ctx->feature_complete = true; 179862306a36Sopenharmony_ci if (ctx->edid) 179962306a36Sopenharmony_ci sii8620_enable_hpd(ctx); 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci} 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci struct device *dev = ctx->dev; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (list_empty(&ctx->mt_queue)) { 180862306a36Sopenharmony_ci dev_err(dev, "unexpected MSC MT response\n"); 180962306a36Sopenharmony_ci return NULL; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci return list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic void sii8620_msc_mt_done(struct sii8620 *ctx) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx); 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci if (!msg) 182062306a36Sopenharmony_ci return; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci msg->ret = sii8620_readb(ctx, REG_MSC_MT_RCVD_DATA0); 182362306a36Sopenharmony_ci ctx->mt_state = MT_STATE_DONE; 182462306a36Sopenharmony_ci} 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_cistatic void sii8620_msc_mr_msc_msg(struct sii8620 *ctx) 182762306a36Sopenharmony_ci{ 182862306a36Sopenharmony_ci struct sii8620_mt_msg *msg; 182962306a36Sopenharmony_ci u8 buf[2]; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci switch (buf[0]) { 183462306a36Sopenharmony_ci case MHL_MSC_MSG_RAPK: 183562306a36Sopenharmony_ci msg = sii8620_msc_msg_first(ctx); 183662306a36Sopenharmony_ci if (!msg) 183762306a36Sopenharmony_ci return; 183862306a36Sopenharmony_ci msg->ret = buf[1]; 183962306a36Sopenharmony_ci ctx->mt_state = MT_STATE_DONE; 184062306a36Sopenharmony_ci break; 184162306a36Sopenharmony_ci case MHL_MSC_MSG_RCP: 184262306a36Sopenharmony_ci if (!sii8620_rcp_consume(ctx, buf[1])) 184362306a36Sopenharmony_ci sii8620_mt_rcpe(ctx, 184462306a36Sopenharmony_ci MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE); 184562306a36Sopenharmony_ci sii8620_mt_rcpk(ctx, buf[1]); 184662306a36Sopenharmony_ci break; 184762306a36Sopenharmony_ci default: 184862306a36Sopenharmony_ci dev_err(ctx->dev, "%s message type %d,%d not supported", 184962306a36Sopenharmony_ci __func__, buf[0], buf[1]); 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_cistatic void sii8620_irq_msc(struct sii8620 *ctx) 185462306a36Sopenharmony_ci{ 185562306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_CBUS_INT_0); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci if (stat & ~BIT_CBUS_HPD_CHG) 185862306a36Sopenharmony_ci sii8620_write(ctx, REG_CBUS_INT_0, stat & ~BIT_CBUS_HPD_CHG); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci if (stat & BIT_CBUS_HPD_CHG) { 186162306a36Sopenharmony_ci u8 cbus_stat = sii8620_readb(ctx, REG_CBUS_STATUS); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if ((cbus_stat ^ ctx->cbus_status) & BIT_CBUS_STATUS_CBUS_HPD) { 186462306a36Sopenharmony_ci sii8620_write(ctx, REG_CBUS_INT_0, BIT_CBUS_HPD_CHG); 186562306a36Sopenharmony_ci } else { 186662306a36Sopenharmony_ci stat ^= BIT_CBUS_STATUS_CBUS_HPD; 186762306a36Sopenharmony_ci cbus_stat ^= BIT_CBUS_STATUS_CBUS_HPD; 186862306a36Sopenharmony_ci } 186962306a36Sopenharmony_ci ctx->cbus_status = cbus_stat; 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci if (stat & BIT_CBUS_MSC_MR_WRITE_STAT) 187362306a36Sopenharmony_ci sii8620_msc_mr_write_stat(ctx); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (stat & BIT_CBUS_HPD_CHG) { 187662306a36Sopenharmony_ci if (ctx->cbus_status & BIT_CBUS_STATUS_CBUS_HPD) { 187762306a36Sopenharmony_ci ctx->sink_detected = true; 187862306a36Sopenharmony_ci sii8620_identify_sink(ctx); 187962306a36Sopenharmony_ci } else { 188062306a36Sopenharmony_ci sii8620_hpd_unplugged(ctx); 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (stat & BIT_CBUS_MSC_MR_SET_INT) 188562306a36Sopenharmony_ci sii8620_msc_mr_set_int(ctx); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci if (stat & BIT_CBUS_MSC_MT_DONE) 188862306a36Sopenharmony_ci sii8620_msc_mt_done(ctx); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci if (stat & BIT_CBUS_MSC_MR_MSC_MSG) 189162306a36Sopenharmony_ci sii8620_msc_mr_msc_msg(ctx); 189262306a36Sopenharmony_ci} 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_cistatic void sii8620_irq_coc(struct sii8620 *ctx) 189562306a36Sopenharmony_ci{ 189662306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_COC_INTR); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (stat & BIT_COC_CALIBRATION_DONE) { 189962306a36Sopenharmony_ci u8 cstat = sii8620_readb(ctx, REG_COC_STAT_0); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci cstat &= BIT_COC_STAT_0_PLL_LOCKED | MSK_COC_STAT_0_FSM_STATE; 190262306a36Sopenharmony_ci if (cstat == (BIT_COC_STAT_0_PLL_LOCKED | 0x02)) { 190362306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 190462306a36Sopenharmony_ci REG_COC_CTLB, 0, 190562306a36Sopenharmony_ci REG_TRXINTMH, BIT_TDM_INTR_SYNC_DATA 190662306a36Sopenharmony_ci | BIT_TDM_INTR_SYNC_WAIT 190762306a36Sopenharmony_ci ); 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci sii8620_write(ctx, REG_COC_INTR, stat); 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic void sii8620_irq_merr(struct sii8620 *ctx) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_CBUS_INT_1); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci sii8620_write(ctx, REG_CBUS_INT_1, stat); 191962306a36Sopenharmony_ci} 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_cistatic void sii8620_irq_edid(struct sii8620 *ctx) 192262306a36Sopenharmony_ci{ 192362306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_INTR9); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci sii8620_write(ctx, REG_INTR9, stat); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (stat & BIT_INTR9_DEVCAP_DONE) 192862306a36Sopenharmony_ci ctx->mt_state = MT_STATE_DONE; 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_cistatic void sii8620_irq_scdt(struct sii8620 *ctx) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_INTR5); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci if (stat & BIT_INTR_SCDT_CHANGE) { 193662306a36Sopenharmony_ci u8 cstat = sii8620_readb(ctx, REG_TMDS_CSTAT_P3); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci if (cstat & BIT_TMDS_CSTAT_P3_SCDT) 193962306a36Sopenharmony_ci sii8620_start_video(ctx); 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci sii8620_write(ctx, REG_INTR5, stat); 194362306a36Sopenharmony_ci} 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_cistatic void sii8620_got_xdevcap(struct sii8620 *ctx, int ret) 194662306a36Sopenharmony_ci{ 194762306a36Sopenharmony_ci if (ret < 0) 194862306a36Sopenharmony_ci return; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci sii8620_mt_read_devcap(ctx, false); 195162306a36Sopenharmony_ci} 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_cistatic void sii8620_irq_tdm(struct sii8620 *ctx) 195462306a36Sopenharmony_ci{ 195562306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_TRXINTH); 195662306a36Sopenharmony_ci u8 tdm = sii8620_readb(ctx, REG_TRXSTA2); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci if ((tdm & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) { 195962306a36Sopenharmony_ci ctx->mode = CM_ECBUS_S; 196062306a36Sopenharmony_ci ctx->burst.rx_ack = 0; 196162306a36Sopenharmony_ci ctx->burst.r_size = SII8620_BURST_BUF_LEN; 196262306a36Sopenharmony_ci sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN); 196362306a36Sopenharmony_ci sii8620_mt_read_devcap(ctx, true); 196462306a36Sopenharmony_ci sii8620_mt_set_cont(ctx, sii8620_got_xdevcap); 196562306a36Sopenharmony_ci } else { 196662306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 196762306a36Sopenharmony_ci REG_MHL_PLL_CTL2, 0, 196862306a36Sopenharmony_ci REG_MHL_PLL_CTL2, BIT_MHL_PLL_CTL2_CLKDETECT_EN 196962306a36Sopenharmony_ci ); 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci sii8620_write(ctx, REG_TRXINTH, stat); 197362306a36Sopenharmony_ci} 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_cistatic void sii8620_irq_block(struct sii8620 *ctx) 197662306a36Sopenharmony_ci{ 197762306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_EMSCINTR); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci if (stat & BIT_EMSCINTR_SPI_DVLD) { 198062306a36Sopenharmony_ci u8 bstat = sii8620_readb(ctx, REG_SPIBURSTSTAT); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci if (bstat & BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE) 198362306a36Sopenharmony_ci sii8620_burst_receive(ctx); 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci sii8620_write(ctx, REG_EMSCINTR, stat); 198762306a36Sopenharmony_ci} 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_cistatic void sii8620_irq_ddc(struct sii8620 *ctx) 199062306a36Sopenharmony_ci{ 199162306a36Sopenharmony_ci u8 stat = sii8620_readb(ctx, REG_INTR3); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci if (stat & BIT_DDC_CMD_DONE) { 199462306a36Sopenharmony_ci sii8620_write(ctx, REG_INTR3_MASK, 0); 199562306a36Sopenharmony_ci if (sii8620_is_mhl3(ctx) && !ctx->feature_complete) 199662306a36Sopenharmony_ci sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), 199762306a36Sopenharmony_ci MHL_INT_RC_FEAT_REQ); 199862306a36Sopenharmony_ci else 199962306a36Sopenharmony_ci sii8620_enable_hpd(ctx); 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci sii8620_write(ctx, REG_INTR3, stat); 200262306a36Sopenharmony_ci} 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci/* endian agnostic, non-volatile version of test_bit */ 200562306a36Sopenharmony_cistatic bool sii8620_test_bit(unsigned int nr, const u8 *addr) 200662306a36Sopenharmony_ci{ 200762306a36Sopenharmony_ci return 1 & (addr[nr / BITS_PER_BYTE] >> (nr % BITS_PER_BYTE)); 200862306a36Sopenharmony_ci} 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_cistatic irqreturn_t sii8620_irq_thread(int irq, void *data) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci static const struct { 201362306a36Sopenharmony_ci int bit; 201462306a36Sopenharmony_ci void (*handler)(struct sii8620 *ctx); 201562306a36Sopenharmony_ci } irq_vec[] = { 201662306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc }, 201762306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb }, 201862306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_COC, sii8620_irq_coc }, 201962306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_TDM, sii8620_irq_tdm }, 202062306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc }, 202162306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr }, 202262306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block }, 202362306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid }, 202462306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_DDC, sii8620_irq_ddc }, 202562306a36Sopenharmony_ci { BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt }, 202662306a36Sopenharmony_ci }; 202762306a36Sopenharmony_ci struct sii8620 *ctx = data; 202862306a36Sopenharmony_ci u8 stats[LEN_FAST_INTR_STAT]; 202962306a36Sopenharmony_ci int i, ret; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci mutex_lock(&ctx->lock); 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_FAST_INTR_STAT, stats, ARRAY_SIZE(stats)); 203462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(irq_vec); ++i) 203562306a36Sopenharmony_ci if (sii8620_test_bit(irq_vec[i].bit, stats)) 203662306a36Sopenharmony_ci irq_vec[i].handler(ctx); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci sii8620_burst_rx_all(ctx); 203962306a36Sopenharmony_ci sii8620_mt_work(ctx); 204062306a36Sopenharmony_ci sii8620_burst_send(ctx); 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci ret = sii8620_clear_error(ctx); 204362306a36Sopenharmony_ci if (ret) { 204462306a36Sopenharmony_ci dev_err(ctx->dev, "Error during IRQ handling, %d.\n", ret); 204562306a36Sopenharmony_ci sii8620_mhl_disconnected(ctx); 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci return IRQ_HANDLED; 205062306a36Sopenharmony_ci} 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_cistatic void sii8620_cable_in(struct sii8620 *ctx) 205362306a36Sopenharmony_ci{ 205462306a36Sopenharmony_ci struct device *dev = ctx->dev; 205562306a36Sopenharmony_ci u8 ver[5]; 205662306a36Sopenharmony_ci int ret; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci ret = sii8620_hw_on(ctx); 205962306a36Sopenharmony_ci if (ret) { 206062306a36Sopenharmony_ci dev_err(dev, "Error powering on, %d.\n", ret); 206162306a36Sopenharmony_ci return; 206262306a36Sopenharmony_ci } 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci sii8620_read_buf(ctx, REG_VND_IDL, ver, ARRAY_SIZE(ver)); 206562306a36Sopenharmony_ci ret = sii8620_clear_error(ctx); 206662306a36Sopenharmony_ci if (ret) { 206762306a36Sopenharmony_ci dev_err(dev, "Error accessing I2C bus, %d.\n", ret); 206862306a36Sopenharmony_ci return; 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci dev_info(dev, "ChipID %02x%02x:%02x%02x rev %02x.\n", ver[1], ver[0], 207262306a36Sopenharmony_ci ver[3], ver[2], ver[4]); 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci sii8620_write(ctx, REG_DPD, 207562306a36Sopenharmony_ci BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci sii8620_xtal_set_rate(ctx); 207862306a36Sopenharmony_ci sii8620_disconnect(ctx); 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci sii8620_write_seq_static(ctx, 208162306a36Sopenharmony_ci REG_MHL_CBUS_CTL0, VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_STRONG 208262306a36Sopenharmony_ci | VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_734, 208362306a36Sopenharmony_ci REG_MHL_CBUS_CTL1, VAL_MHL_CBUS_CTL1_1115_OHM, 208462306a36Sopenharmony_ci REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN, 208562306a36Sopenharmony_ci ); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci ret = sii8620_clear_error(ctx); 208862306a36Sopenharmony_ci if (ret) { 208962306a36Sopenharmony_ci dev_err(dev, "Error accessing I2C bus, %d.\n", ret); 209062306a36Sopenharmony_ci return; 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci enable_irq(to_i2c_client(ctx->dev)->irq); 209462306a36Sopenharmony_ci} 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_cistatic void sii8620_init_rcp_input_dev(struct sii8620 *ctx) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci struct rc_dev *rc_dev; 209962306a36Sopenharmony_ci int ret; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_RC_CORE)) 210262306a36Sopenharmony_ci return; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); 210562306a36Sopenharmony_ci if (!rc_dev) { 210662306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to allocate RC device\n"); 210762306a36Sopenharmony_ci ctx->error = -ENOMEM; 210862306a36Sopenharmony_ci return; 210962306a36Sopenharmony_ci } 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci rc_dev->input_phys = "sii8620/input0"; 211262306a36Sopenharmony_ci rc_dev->input_id.bustype = BUS_VIRTUAL; 211362306a36Sopenharmony_ci rc_dev->map_name = RC_MAP_CEC; 211462306a36Sopenharmony_ci rc_dev->allowed_protocols = RC_PROTO_BIT_CEC; 211562306a36Sopenharmony_ci rc_dev->driver_name = "sii8620"; 211662306a36Sopenharmony_ci rc_dev->device_name = "sii8620"; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci ret = rc_register_device(rc_dev); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci if (ret) { 212162306a36Sopenharmony_ci dev_err(ctx->dev, "Failed to register RC device\n"); 212262306a36Sopenharmony_ci ctx->error = ret; 212362306a36Sopenharmony_ci rc_free_device(rc_dev); 212462306a36Sopenharmony_ci return; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci ctx->rc_dev = rc_dev; 212762306a36Sopenharmony_ci} 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic void sii8620_cable_out(struct sii8620 *ctx) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci disable_irq(to_i2c_client(ctx->dev)->irq); 213262306a36Sopenharmony_ci sii8620_hw_off(ctx); 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cistatic void sii8620_extcon_work(struct work_struct *work) 213662306a36Sopenharmony_ci{ 213762306a36Sopenharmony_ci struct sii8620 *ctx = 213862306a36Sopenharmony_ci container_of(work, struct sii8620, extcon_wq); 213962306a36Sopenharmony_ci int state = extcon_get_state(ctx->extcon, EXTCON_DISP_MHL); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci if (state == ctx->cable_state) 214262306a36Sopenharmony_ci return; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci ctx->cable_state = state; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (state > 0) 214762306a36Sopenharmony_ci sii8620_cable_in(ctx); 214862306a36Sopenharmony_ci else 214962306a36Sopenharmony_ci sii8620_cable_out(ctx); 215062306a36Sopenharmony_ci} 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_cistatic int sii8620_extcon_notifier(struct notifier_block *self, 215362306a36Sopenharmony_ci unsigned long event, void *ptr) 215462306a36Sopenharmony_ci{ 215562306a36Sopenharmony_ci struct sii8620 *ctx = 215662306a36Sopenharmony_ci container_of(self, struct sii8620, extcon_nb); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci schedule_work(&ctx->extcon_wq); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci return NOTIFY_DONE; 216162306a36Sopenharmony_ci} 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic int sii8620_extcon_init(struct sii8620 *ctx) 216462306a36Sopenharmony_ci{ 216562306a36Sopenharmony_ci struct extcon_dev *edev; 216662306a36Sopenharmony_ci struct device_node *musb, *muic; 216762306a36Sopenharmony_ci int ret; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci /* get micro-USB connector node */ 217062306a36Sopenharmony_ci musb = of_graph_get_remote_node(ctx->dev->of_node, 1, -1); 217162306a36Sopenharmony_ci /* next get micro-USB Interface Controller node */ 217262306a36Sopenharmony_ci muic = of_get_next_parent(musb); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (!muic) { 217562306a36Sopenharmony_ci dev_info(ctx->dev, "no extcon found, switching to 'always on' mode\n"); 217662306a36Sopenharmony_ci return 0; 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci edev = extcon_find_edev_by_node(muic); 218062306a36Sopenharmony_ci of_node_put(muic); 218162306a36Sopenharmony_ci if (IS_ERR(edev)) { 218262306a36Sopenharmony_ci if (PTR_ERR(edev) == -EPROBE_DEFER) 218362306a36Sopenharmony_ci return -EPROBE_DEFER; 218462306a36Sopenharmony_ci dev_err(ctx->dev, "Invalid or missing extcon\n"); 218562306a36Sopenharmony_ci return PTR_ERR(edev); 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci ctx->extcon = edev; 218962306a36Sopenharmony_ci ctx->extcon_nb.notifier_call = sii8620_extcon_notifier; 219062306a36Sopenharmony_ci INIT_WORK(&ctx->extcon_wq, sii8620_extcon_work); 219162306a36Sopenharmony_ci ret = extcon_register_notifier(edev, EXTCON_DISP_MHL, &ctx->extcon_nb); 219262306a36Sopenharmony_ci if (ret) { 219362306a36Sopenharmony_ci dev_err(ctx->dev, "failed to register notifier for MHL\n"); 219462306a36Sopenharmony_ci return ret; 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci return 0; 219862306a36Sopenharmony_ci} 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_cistatic inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) 220162306a36Sopenharmony_ci{ 220262306a36Sopenharmony_ci return container_of(bridge, struct sii8620, bridge); 220362306a36Sopenharmony_ci} 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_cistatic int sii8620_attach(struct drm_bridge *bridge, 220662306a36Sopenharmony_ci enum drm_bridge_attach_flags flags) 220762306a36Sopenharmony_ci{ 220862306a36Sopenharmony_ci struct sii8620 *ctx = bridge_to_sii8620(bridge); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci sii8620_init_rcp_input_dev(ctx); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci return sii8620_clear_error(ctx); 221362306a36Sopenharmony_ci} 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_cistatic void sii8620_detach(struct drm_bridge *bridge) 221662306a36Sopenharmony_ci{ 221762306a36Sopenharmony_ci struct sii8620 *ctx = bridge_to_sii8620(bridge); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_RC_CORE)) 222062306a36Sopenharmony_ci return; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci rc_unregister_device(ctx->rc_dev); 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_cistatic int sii8620_is_packing_required(struct sii8620 *ctx, 222662306a36Sopenharmony_ci const struct drm_display_mode *mode) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci int max_pclk, max_pclk_pp_mode; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci if (sii8620_is_mhl3(ctx)) { 223162306a36Sopenharmony_ci max_pclk = MHL3_MAX_PCLK; 223262306a36Sopenharmony_ci max_pclk_pp_mode = MHL3_MAX_PCLK_PP_MODE; 223362306a36Sopenharmony_ci } else { 223462306a36Sopenharmony_ci max_pclk = MHL1_MAX_PCLK; 223562306a36Sopenharmony_ci max_pclk_pp_mode = MHL1_MAX_PCLK_PP_MODE; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci if (mode->clock < max_pclk) 223962306a36Sopenharmony_ci return 0; 224062306a36Sopenharmony_ci else if (mode->clock < max_pclk_pp_mode) 224162306a36Sopenharmony_ci return 1; 224262306a36Sopenharmony_ci else 224362306a36Sopenharmony_ci return -1; 224462306a36Sopenharmony_ci} 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_cistatic enum drm_mode_status sii8620_mode_valid(struct drm_bridge *bridge, 224762306a36Sopenharmony_ci const struct drm_display_info *info, 224862306a36Sopenharmony_ci const struct drm_display_mode *mode) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci struct sii8620 *ctx = bridge_to_sii8620(bridge); 225162306a36Sopenharmony_ci int pack_required = sii8620_is_packing_required(ctx, mode); 225262306a36Sopenharmony_ci bool can_pack = ctx->devcap[MHL_DCAP_VID_LINK_MODE] & 225362306a36Sopenharmony_ci MHL_DCAP_VID_LINK_PPIXEL; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci switch (pack_required) { 225662306a36Sopenharmony_ci case 0: 225762306a36Sopenharmony_ci return MODE_OK; 225862306a36Sopenharmony_ci case 1: 225962306a36Sopenharmony_ci return (can_pack) ? MODE_OK : MODE_CLOCK_HIGH; 226062306a36Sopenharmony_ci default: 226162306a36Sopenharmony_ci return MODE_CLOCK_HIGH; 226262306a36Sopenharmony_ci } 226362306a36Sopenharmony_ci} 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_cistatic bool sii8620_mode_fixup(struct drm_bridge *bridge, 226662306a36Sopenharmony_ci const struct drm_display_mode *mode, 226762306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct sii8620 *ctx = bridge_to_sii8620(bridge); 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci mutex_lock(&ctx->lock); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci ctx->use_packed_pixel = sii8620_is_packing_required(ctx, adjusted_mode); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci return true; 227862306a36Sopenharmony_ci} 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_cistatic const struct drm_bridge_funcs sii8620_bridge_funcs = { 228162306a36Sopenharmony_ci .attach = sii8620_attach, 228262306a36Sopenharmony_ci .detach = sii8620_detach, 228362306a36Sopenharmony_ci .mode_fixup = sii8620_mode_fixup, 228462306a36Sopenharmony_ci .mode_valid = sii8620_mode_valid, 228562306a36Sopenharmony_ci}; 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_cistatic int sii8620_probe(struct i2c_client *client) 228862306a36Sopenharmony_ci{ 228962306a36Sopenharmony_ci struct device *dev = &client->dev; 229062306a36Sopenharmony_ci struct sii8620 *ctx; 229162306a36Sopenharmony_ci int ret; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 229462306a36Sopenharmony_ci if (!ctx) 229562306a36Sopenharmony_ci return -ENOMEM; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci ctx->dev = dev; 229862306a36Sopenharmony_ci mutex_init(&ctx->lock); 229962306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->mt_queue); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci ctx->clk_xtal = devm_clk_get(dev, "xtal"); 230262306a36Sopenharmony_ci if (IS_ERR(ctx->clk_xtal)) 230362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ctx->clk_xtal), 230462306a36Sopenharmony_ci "failed to get xtal clock from DT\n"); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci if (!client->irq) { 230762306a36Sopenharmony_ci dev_err(dev, "no irq provided\n"); 230862306a36Sopenharmony_ci return -EINVAL; 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci irq_set_status_flags(client->irq, IRQ_NOAUTOEN); 231162306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, client->irq, NULL, 231262306a36Sopenharmony_ci sii8620_irq_thread, 231362306a36Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 231462306a36Sopenharmony_ci "sii8620", ctx); 231562306a36Sopenharmony_ci if (ret < 0) 231662306a36Sopenharmony_ci return dev_err_probe(dev, ret, 231762306a36Sopenharmony_ci "failed to install IRQ handler\n"); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 232062306a36Sopenharmony_ci if (IS_ERR(ctx->gpio_reset)) 232162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ctx->gpio_reset), 232262306a36Sopenharmony_ci "failed to get reset gpio from DT\n"); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci ctx->supplies[0].supply = "cvcc10"; 232562306a36Sopenharmony_ci ctx->supplies[1].supply = "iovcc18"; 232662306a36Sopenharmony_ci ret = devm_regulator_bulk_get(dev, 2, ctx->supplies); 232762306a36Sopenharmony_ci if (ret) 232862306a36Sopenharmony_ci return ret; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci ret = sii8620_extcon_init(ctx); 233162306a36Sopenharmony_ci if (ret < 0) { 233262306a36Sopenharmony_ci dev_err(ctx->dev, "failed to initialize EXTCON\n"); 233362306a36Sopenharmony_ci return ret; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci i2c_set_clientdata(client, ctx); 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci ctx->bridge.funcs = &sii8620_bridge_funcs; 233962306a36Sopenharmony_ci ctx->bridge.of_node = dev->of_node; 234062306a36Sopenharmony_ci drm_bridge_add(&ctx->bridge); 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci if (!ctx->extcon) 234362306a36Sopenharmony_ci sii8620_cable_in(ctx); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci return 0; 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_cistatic void sii8620_remove(struct i2c_client *client) 234962306a36Sopenharmony_ci{ 235062306a36Sopenharmony_ci struct sii8620 *ctx = i2c_get_clientdata(client); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci if (ctx->extcon) { 235362306a36Sopenharmony_ci extcon_unregister_notifier(ctx->extcon, EXTCON_DISP_MHL, 235462306a36Sopenharmony_ci &ctx->extcon_nb); 235562306a36Sopenharmony_ci flush_work(&ctx->extcon_wq); 235662306a36Sopenharmony_ci if (ctx->cable_state > 0) 235762306a36Sopenharmony_ci sii8620_cable_out(ctx); 235862306a36Sopenharmony_ci } else { 235962306a36Sopenharmony_ci sii8620_cable_out(ctx); 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci drm_bridge_remove(&ctx->bridge); 236262306a36Sopenharmony_ci} 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_cistatic const struct of_device_id sii8620_dt_match[] = { 236562306a36Sopenharmony_ci { .compatible = "sil,sii8620" }, 236662306a36Sopenharmony_ci { }, 236762306a36Sopenharmony_ci}; 236862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sii8620_dt_match); 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_cistatic const struct i2c_device_id sii8620_id[] = { 237162306a36Sopenharmony_ci { "sii8620", 0 }, 237262306a36Sopenharmony_ci { }, 237362306a36Sopenharmony_ci}; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sii8620_id); 237662306a36Sopenharmony_cistatic struct i2c_driver sii8620_driver = { 237762306a36Sopenharmony_ci .driver = { 237862306a36Sopenharmony_ci .name = "sii8620", 237962306a36Sopenharmony_ci .of_match_table = sii8620_dt_match, 238062306a36Sopenharmony_ci }, 238162306a36Sopenharmony_ci .probe = sii8620_probe, 238262306a36Sopenharmony_ci .remove = sii8620_remove, 238362306a36Sopenharmony_ci .id_table = sii8620_id, 238462306a36Sopenharmony_ci}; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_cimodule_i2c_driver(sii8620_driver); 238762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2388