Newer
Older
/*
* Silicon Image SiI8620 HDMI/MHL bridge driver
*
* Copyright (C) 2015, Samsung Electronics Co., Ltd.
* Andrzej Hajda <a.hajda@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <asm/unaligned.h>
#include <drm/bridge/mhl.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include "sil-sii8620.h"
#define SII8620_BURST_BUF_LEN 288
#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3)
#define MHL1_MAX_LCLK 225000
#define MHL3_MAX_LCLK 600000
enum sii8620_mode {
CM_DISCONNECTED,
CM_DISCOVERY,
CM_MHL1,
CM_MHL3,
CM_ECBUS_S
};
enum sii8620_sink_type {
SINK_NONE,
SINK_HDMI,
SINK_DVI
};
enum sii8620_mt_state {
MT_STATE_READY,
MT_STATE_BUSY,
MT_STATE_DONE
};
struct sii8620 {
struct drm_bridge bridge;
struct device *dev;
struct clk *clk_xtal;
struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_int;
struct regulator_bulk_data supplies[2];
struct mutex lock; /* context lock, protects fields below */
int error;
int pixel_clock;
unsigned int use_packed_pixel:1;
int video_code;
enum sii8620_mode mode;
enum sii8620_sink_type sink_type;
u8 cbus_status;
u8 stat[MHL_DST_SIZE];
u8 xstat[MHL_XDS_SIZE];
u8 devcap[MHL_DCAP_SIZE];
u8 xdevcap[MHL_XDC_SIZE];
u8 avif[HDMI_INFOFRAME_SIZE(AVI)];
struct edid *edid;
unsigned int gen2_write_burst:1;
enum sii8620_mt_state mt_state;
struct list_head mt_queue;
struct {
int r_size;
int r_count;
int rx_ack;
int rx_count;
u8 rx_buf[32];
int tx_count;
u8 tx_buf[32];
} burst;
};
struct sii8620_mt_msg;
typedef void (*sii8620_mt_msg_cb)(struct sii8620 *ctx,
struct sii8620_mt_msg *msg);
typedef void (*sii8620_cb)(struct sii8620 *ctx, int ret);
struct sii8620_mt_msg {
struct list_head node;
u8 reg[4];
u8 ret;
sii8620_mt_msg_cb send;
sii8620_mt_msg_cb recv;
sii8620_cb continuation;
};
static const u8 sii8620_i2c_page[] = {
0x39, /* Main System */
0x3d, /* TDM and HSIC */
0x49, /* TMDS Receiver, MHL EDID */
0x4d, /* eMSC, HDCP, HSIC */
0x5d, /* MHL Spec */
0x64, /* MHL CBUS */
0x59, /* Hardware TPI (Transmitter Programming Interface) */
0x61, /* eCBUS-S, eCBUS-D */
};
static void sii8620_fetch_edid(struct sii8620 *ctx);
static void sii8620_set_upstream_edid(struct sii8620 *ctx);
static void sii8620_enable_hpd(struct sii8620 *ctx);
static void sii8620_mhl_disconnected(struct sii8620 *ctx);
static void sii8620_disconnect(struct sii8620 *ctx);
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
static int sii8620_clear_error(struct sii8620 *ctx)
{
int ret = ctx->error;
ctx->error = 0;
return ret;
}
static void sii8620_read_buf(struct sii8620 *ctx, u16 addr, u8 *buf, int len)
{
struct device *dev = ctx->dev;
struct i2c_client *client = to_i2c_client(dev);
u8 data = addr;
struct i2c_msg msg[] = {
{
.addr = sii8620_i2c_page[addr >> 8],
.flags = client->flags,
.len = 1,
.buf = &data
},
{
.addr = sii8620_i2c_page[addr >> 8],
.flags = client->flags | I2C_M_RD,
.len = len,
.buf = buf
},
};
int ret;
if (ctx->error)
return;
ret = i2c_transfer(client->adapter, msg, 2);
dev_dbg(dev, "read at %04x: %*ph, %d\n", addr, len, buf, ret);
if (ret != 2) {
dev_err(dev, "Read at %#06x of %d bytes failed with code %d.\n",
addr, len, ret);
ctx->error = ret < 0 ? ret : -EIO;
}
}
static u8 sii8620_readb(struct sii8620 *ctx, u16 addr)
{
u8 ret;
sii8620_read_buf(ctx, addr, &ret, 1);
return ret;
}
static void sii8620_write_buf(struct sii8620 *ctx, u16 addr, const u8 *buf,
int len)
{
struct device *dev = ctx->dev;
struct i2c_client *client = to_i2c_client(dev);
u8 data[2];
struct i2c_msg msg = {
.addr = sii8620_i2c_page[addr >> 8],
.flags = client->flags,
.len = len + 1,
};
int ret;
if (ctx->error)
return;
if (len > 1) {
msg.buf = kmalloc(len + 1, GFP_KERNEL);
if (!msg.buf) {
ctx->error = -ENOMEM;
return;
}
memcpy(msg.buf + 1, buf, len);
} else {
msg.buf = data;
Loading
Loading full blame...