* commit 'b65bb3bb00':
  media: i2c: rk628: add private interface, compatible with hdmirx.
  media: i2c: rk628: fix display error caused by read resolution error
  media: i2c: rk628: fix display error in plugout
  mfd: display-serdes: add error detection and recovery function

Change-Id: I51b5cc47db20be0b80a7107eda6cd5d7d1e1e176
This commit is contained in:
Tao Huang 2024-02-05 19:09:12 +08:00
commit f9ab55a3df
8 changed files with 432 additions and 55 deletions

View file

@ -294,6 +294,7 @@ struct rk628 {
struct mutex rst_lock;
int tx_mode;
struct dentry *debug_dir;
struct gpio_desc *hdmirx_det_gpio;
};
int rk628_media_i2c_write(struct rk628 *rk628, u32 reg, u32 val);

View file

@ -234,7 +234,7 @@ static int rk628_bt1120_s_edid(struct v4l2_subdev *sd,
static int rk628_hdmirx_inno_phy_power_on(struct v4l2_subdev *sd);
static int rk628_hdmirx_inno_phy_power_off(struct v4l2_subdev *sd);
static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd);
static void rk628_bt1120_format_change(struct v4l2_subdev *sd);
static int rk628_bt1120_format_change(struct v4l2_subdev *sd);
static void enable_stream(struct v4l2_subdev *sd, bool enable);
static void rk628_hdmirx_vid_enable(struct v4l2_subdev *sd, bool en);
static void rk628_hdmirx_hpd_ctrl(struct v4l2_subdev *sd, bool en);
@ -248,22 +248,9 @@ static inline struct rk628_bt1120 *to_bt1120(struct v4l2_subdev *sd)
static bool tx_5v_power_present(struct v4l2_subdev *sd)
{
bool ret;
int val, i, cnt;
struct rk628_bt1120 *bt1120 = to_bt1120(sd);
/* Direct Mode */
if (!bt1120->plugin_det_gpio)
return true;
cnt = 0;
for (i = 0; i < 5; i++) {
val = gpiod_get_value(bt1120->plugin_det_gpio);
if (val > 0)
cnt++;
usleep_range(500, 600);
}
ret = (cnt >= 3) ? true : false;
ret = rk628_hdmirx_tx_5v_power_detect(bt1120->plugin_det_gpio);
v4l2_dbg(1, debug, sd, "%s: %d\n", __func__, ret);
return ret;
@ -355,9 +342,11 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd)
ret = rk628_hdmirx_phy_setup(sd);
if (ret >= 0 && !rk628_hdmirx_scdc_ced_err(bt1120->rk628)) {
rk628_bt1120_format_change(sd);
bt1120->nosignal = false;
return;
ret = rk628_bt1120_format_change(sd);
if (!ret) {
bt1120->nosignal = false;
return;
}
}
if (ret < 0 || rk628_hdmirx_scdc_ced_err(bt1120->rk628)) {
@ -889,7 +878,7 @@ static void rk628_bt1120_initial_setup(struct v4l2_subdev *sd)
schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, 4000);
}
static void rk628_bt1120_format_change(struct v4l2_subdev *sd)
static int rk628_bt1120_format_change(struct v4l2_subdev *sd)
{
struct rk628_bt1120 *bt1120 = to_bt1120(sd);
struct v4l2_dv_timings timings;
@ -897,8 +886,13 @@ static void rk628_bt1120_format_change(struct v4l2_subdev *sd)
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
};
int ret;
rk628_bt1120_get_detected_timings(sd, &timings);
ret = rk628_bt1120_get_detected_timings(sd, &timings);
if (ret) {
v4l2_dbg(1, debug, sd, "%s: get timing fail\n", __func__);
return ret;
}
if (!v4l2_match_dv_timings(&bt1120->timings, &timings, 0, false)) {
/* automatically set timing rather than set by userspace */
rk628_bt1120_s_dv_timings(sd, &timings);
@ -909,6 +903,8 @@ static void rk628_bt1120_format_change(struct v4l2_subdev *sd)
if (sd->devnode)
v4l2_subdev_notify_event(sd, &rk628_bt1120_ev_fmt);
return 0;
}
static void rk628_bt1120_enable_interrupts(struct v4l2_subdev *sd, bool en)
@ -1736,6 +1732,7 @@ static int rk628_bt1120_probe_of(struct rk628_bt1120 *bt1120)
ret = PTR_ERR(bt1120->plugin_det_gpio);
goto clk_put;
}
bt1120->rk628->hdmirx_det_gpio = bt1120->plugin_det_gpio;
if (bt1120->enable_gpio) {
gpiod_set_value(bt1120->enable_gpio, 1);

View file

@ -105,6 +105,7 @@ struct rk628_csi {
u32 stream_state;
int hdmirx_irq;
int plugin_irq;
int avi_rdy;
bool nosignal;
bool rxphy_pwron;
bool txphy_pwron;
@ -116,6 +117,7 @@ struct rk628_csi {
bool continues_clk;
bool cec_enable;
struct rk628_hdmirx_cec *cec;
bool is_dvi;
struct rk628_hdcp hdcp;
bool i2s_enable_default;
HAUDINFO audio_info;
@ -342,7 +344,7 @@ static void mipi_dphy_power_off(struct rk628_csi *csi);
static int rk628_hdmirx_inno_phy_power_on(struct v4l2_subdev *sd);
static int rk628_hdmirx_inno_phy_power_off(struct v4l2_subdev *sd);
static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd);
static void rk628_csi_format_change(struct v4l2_subdev *sd);
static int rk628_csi_format_change(struct v4l2_subdev *sd);
static void enable_stream(struct v4l2_subdev *sd, bool enable);
static void rk628_hdmirx_vid_enable(struct v4l2_subdev *sd, bool en);
static void rk628_csi_set_csi(struct v4l2_subdev *sd);
@ -361,22 +363,9 @@ static inline struct rk628_csi *to_csi(struct v4l2_subdev *sd)
static bool tx_5v_power_present(struct v4l2_subdev *sd)
{
bool ret;
int val, i, cnt;
struct rk628_csi *csi = to_csi(sd);
/* Direct Mode */
if (!csi->plugin_det_gpio)
return true;
cnt = 0;
for (i = 0; i < 5; i++) {
val = gpiod_get_value(csi->plugin_det_gpio);
if (val > 0)
cnt++;
usleep_range(500, 600);
}
ret = (cnt >= 3) ? true : false;
ret = rk628_hdmirx_tx_5v_power_detect(csi->plugin_det_gpio);
v4l2_dbg(1, debug, sd, "%s: %d\n", __func__, ret);
return ret;
@ -479,9 +468,11 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd)
ret = rk628_hdmirx_phy_setup(sd);
if (ret >= 0 && !rk628_hdmirx_scdc_ced_err(csi->rk628)) {
rk628_csi_format_change(sd);
csi->nosignal = false;
return;
ret = rk628_csi_format_change(sd);
if (!ret) {
csi->nosignal = false;
return;
}
}
if (ret < 0 || rk628_hdmirx_scdc_ced_err(csi->rk628)) {
@ -730,10 +721,9 @@ static void rk628_dsi_set_scs(struct rk628_csi *csi)
{
u8 video_fmt;
u32 val;
int avi_rdy;
mutex_lock(&csi->confctl_mutex);
avi_rdy = rk628_is_avi_ready(csi->rk628, csi->avi_rcv_rdy);
csi->avi_rdy = rk628_is_avi_ready(csi->rk628, csi->avi_rcv_rdy);
mutex_unlock(&csi->confctl_mutex);
rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_AVI_PB, &val);
@ -762,7 +752,7 @@ static void rk628_dsi_set_scs(struct rk628_csi *csi)
}
/* if avi packet is not stable, reset ctrl*/
if (!avi_rdy) {
if (!csi->avi_rdy) {
csi->nosignal = true;
schedule_delayed_work(&csi->delayed_work_enable_hotplug, HZ / 20);
}
@ -930,7 +920,6 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
u8 lanes = csi->csi_lanes_in_use;
u8 lane_num;
u32 wc_usrdef, val;
int avi_rdy;
lane_num = lanes - 1;
csi->rk628->dphy_lane_en = (1 << (lanes + 1)) - 1;
@ -1088,7 +1077,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
}
mutex_lock(&csi->confctl_mutex);
avi_rdy = rk628_is_avi_ready(csi->rk628, csi->avi_rcv_rdy);
csi->avi_rdy = rk628_is_avi_ready(csi->rk628, csi->avi_rcv_rdy);
mutex_unlock(&csi->confctl_mutex);
rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_AVI_PB, &val);
@ -1112,7 +1101,7 @@ static void rk628_csi_set_csi(struct v4l2_subdev *sd)
rk628_post_process_csc_en(csi->rk628);
}
/* if avi packet is not stable, reset ctrl*/
if (!avi_rdy) {
if (!csi->avi_rdy) {
csi->nosignal = true;
schedule_delayed_work(&csi->delayed_work_enable_hotplug, HZ / 20);
}
@ -1253,8 +1242,12 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd)
__func__, width, height, frame_width, frame_height, status, cnt);
rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_STS, &val);
if (csi->rk628->version < RK628F_VERSION && (val & DVI_DET))
if (csi->rk628->version < RK628F_VERSION && (val & DVI_DET)) {
csi->is_dvi = true;
dev_info(csi->dev, "DVI mode detected\n");
} else {
csi->is_dvi = false;
}
if (!tx_5v_power_present(sd)) {
v4l2_info(sd, "HDMI pull out, return!\n");
@ -1371,7 +1364,7 @@ static void rk628_csi_initial_setup(struct v4l2_subdev *sd)
schedule_delayed_work(&csi->delayed_work_enable_hotplug, msecs_to_jiffies(4000));
}
static void rk628_csi_format_change(struct v4l2_subdev *sd)
static int rk628_csi_format_change(struct v4l2_subdev *sd)
{
struct rk628_csi *csi = to_csi(sd);
struct v4l2_dv_timings timings;
@ -1379,8 +1372,13 @@ static void rk628_csi_format_change(struct v4l2_subdev *sd)
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
};
int ret;
rk628_csi_get_detected_timings(sd, &timings);
ret = rk628_csi_get_detected_timings(sd, &timings);
if (ret) {
v4l2_dbg(1, debug, sd, "%s: get timing fail\n", __func__);
return ret;
}
if (!v4l2_match_dv_timings(&csi->timings, &timings, 0, false)) {
/* automatically set timing rather than set by userspace */
rk628_csi_s_dv_timings(sd, &timings);
@ -1391,6 +1389,8 @@ static void rk628_csi_format_change(struct v4l2_subdev *sd)
if (sd->devnode)
v4l2_subdev_notify_event(sd, &rk628_csi_ev_fmt);
return 0;
}
static void rk628_csi_enable_csi_interrupts(struct v4l2_subdev *sd, bool en)
@ -2213,6 +2213,32 @@ static long rk628_csi_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
else
*(int *)arg = RKMODULE_CSI_INPUT;
break;
case RK_HDMIRX_CMD_GET_FPS:
*(int *)arg = fps_calc(&csi->timings.bt);
break;
case RK_HDMIRX_CMD_GET_HDCP_ENC_STATUS:
*(int *)arg = rk628_hdmirx_get_hdcp_enc_status(csi->rk628);
break;
case RK_HDMIRX_CMD_GET_INPUT_MODE:
*(int *)arg = csi->is_dvi;
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
*(int *)arg = csi->avi_rdy;
break;
case RK_HDMIRX_CMD_GET_SCAN_MODE:
if (csi->timings.bt.interlaced == V4L2_DV_INTERLACED)
*(int *)arg = HDMIRX_INTERLACED;
else
*(int *)arg = HDMIRX_PROGRESSIVE;
break;
case RK_HDMIRX_CMD_GET_EDID_MODE:
*(int *)arg = HDMIRX_EDID_4K60HZ_YUV444;
break;
case RK_HDMIRX_CMD_SET_EDID_MODE:
break;
case RK_HDMIRX_CMD_GET_COLOR_RANGE:
*(int *)arg = rk628_hdmirx_get_range(csi->rk628);
break;
default:
ret = -ENOIOCTLCMD;
break;
@ -2385,6 +2411,118 @@ static long rk628_csi_compat_ioctl32(struct v4l2_subdev *sd,
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_FPS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_HDCP_ENC_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_INPUT_MODE:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_SIGNAL_STABLE_STATUS:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_SCAN_MODE:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_EDID_MODE:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_SET_EDID_MODE:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
case RK_HDMIRX_CMD_GET_COLOR_RANGE:
seq = kzalloc(sizeof(*seq), GFP_KERNEL);
if (!seq) {
ret = -ENOMEM;
return ret;
}
ret = rk628_csi_ioctl(sd, cmd, seq);
if (!ret) {
ret = copy_to_user(up, seq, sizeof(*seq));
if (ret)
ret = -EFAULT;
}
kfree(seq);
break;
default:
ret = -ENOIOCTLCMD;
break;
@ -2537,6 +2675,7 @@ static int rk628_csi_probe_of(struct rk628_csi *csi)
ret = PTR_ERR(csi->plugin_det_gpio);
goto clk_put;
}
csi->rk628->hdmirx_det_gpio = csi->plugin_det_gpio;
if (csi->enable_gpio) {
gpiod_set_value(csi->enable_gpio, 1);

View file

@ -213,6 +213,17 @@ void rk628_hdmirx_controller_setup(struct rk628 *rk628)
}
EXPORT_SYMBOL(rk628_hdmirx_controller_setup);
int rk628_hdmirx_get_hdcp_enc_status(struct rk628 *rk628)
{
u32 val;
rk628_i2c_read(rk628, HDMI_RX_HDCP_STS, &val);
val &= HDCP_ENC_STATE;
return val ? 1 : 0;
}
EXPORT_SYMBOL(rk628_hdmirx_get_hdcp_enc_status);
static bool is_validfs(int fs)
{
int i = 0;
@ -1375,6 +1386,29 @@ TIMING_ERR:
return -ENOLCK;
}
bool rk628_hdmirx_tx_5v_power_detect(struct gpio_desc *det_gpio)
{
bool ret;
int val, i, cnt;
/* Direct Mode */
if (!det_gpio)
return true;
cnt = 0;
for (i = 0; i < 5; i++) {
val = gpiod_get_value(det_gpio);
if (val > 0)
cnt++;
usleep_range(500, 600);
}
ret = (cnt >= 3) ? true : false;
return ret;
}
EXPORT_SYMBOL(rk628_hdmirx_tx_5v_power_detect);
static int rk628_hdmirx_try_to_get_timing(struct rk628 *rk628,
struct v4l2_dv_timings *timings)
{
@ -1404,6 +1438,11 @@ int rk628_hdmirx_get_timings(struct rk628 *rk628,
last_fmt = BUS_FMT_RGB;
for (i = 0; i < HDMIRX_GET_TIMING_CNT; i++) {
if (!rk628_hdmirx_tx_5v_power_detect(rk628->hdmirx_det_gpio)) {
dev_info(rk628->dev, "%s: hdmi plug out!\n", __func__);
return -EINVAL;
}
ret = rk628_hdmirx_try_to_get_timing(rk628, timings);
if ((last_w == 0) && (last_h == 0)) {
last_w = bt->width;
@ -1411,6 +1450,9 @@ int rk628_hdmirx_get_timings(struct rk628 *rk628,
last_fmt = rk628_hdmirx_get_format(rk628);
}
if (ret && i > 2)
return -EINVAL;
if (ret || (last_w != bt->width) || (last_h != bt->height)
|| (last_fmt != rk628_hdmirx_get_format(rk628)))
cnt = 0;

View file

@ -8,9 +8,11 @@
#ifndef __RK628_HDMIRX_H
#define __RK628_HDMIRX_H
#include <linux/gpio/consumer.h>
#include <media/cec.h>
#include <media/cec-notifier.h>
#include <media/v4l2-dv-timings.h>
#include "rk628.h"
/* --------- EDID and HDCP KEY ------- */
@ -119,6 +121,7 @@
#define HDMI_RX_HDCP_DBG (HDMI_RX_BASE + 0x00e0)
#define HDMI_RX_HDCP_AN0 (HDMI_RX_BASE + 0x00f0)
#define HDMI_RX_HDCP_STS (HDMI_RX_BASE + 0x00fc)
#define HDCP_ENC_STATE BIT(9)
#define HDMI_RX_MD_HCTRL1 (HDMI_RX_BASE + 0x0140)
#define HACT_PIX_ITH(x) UPDATE(x, 10, 8)
#define HACT_PIX_SRC(x) UPDATE(x, 5, 5)
@ -438,7 +441,7 @@
#define MIPI_DATARATE_MBPS_HIGH 1300
#define POLL_INTERVAL_MS 1000
#define RXPHY_CFG_MAX_TIMES 10
#define RXPHY_CFG_MAX_TIMES 5
#define CSITX_ERR_RETRY_TIMES 3
#define USE_4_LANES 4
@ -509,10 +512,12 @@ void rk628_hdmirx_phy_prepclk_cfg(struct rk628 *rk628);
int rk628_hdmirx_verisyno_phy_init(struct rk628 *rk628);
u8 rk628_hdmirx_get_format(struct rk628 *rk628);
void rk628_set_bg_enable(struct rk628 *rk628, bool en);
bool rk628_hdmirx_tx_5v_power_detect(struct gpio_desc *det_gpio);
u32 rk628_hdmirx_get_tmdsclk_cnt(struct rk628 *rk628);
int rk628_hdmirx_get_timings(struct rk628 *rk628,
struct v4l2_dv_timings *timings);
u8 rk628_hdmirx_get_range(struct rk628 *rk628);
int rk628_hdmirx_get_hdcp_enc_status(struct rk628 *rk628);
void rk628_hdmirx_controller_reset(struct rk628 *rk628);
bool rk628_hdmirx_scdc_ced_err(struct rk628 *rk628);
bool rk628_hdmirx_is_signal_change_ists(struct rk628 *rk628);

View file

@ -28,6 +28,7 @@
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/random.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
@ -351,6 +352,7 @@ struct serdes {
struct mutex io_lock;
struct mutex irq_lock;
struct mutex wq_lock;
struct mutex reg_check_lock;
struct device *dev;
enum serdes_type type;
struct regmap *regmap;
@ -377,6 +379,10 @@ struct serdes {
bool route_enable;
bool use_delay_work;
struct kthread_worker *kworker;
struct kthread_delayed_work reg_check_work;
bool use_reg_check_work;
bool split_mode_enable;
unsigned int reg_hw;
unsigned int reg_use;
@ -391,6 +397,7 @@ struct serdes {
struct pinctrl_state *pins_sleep;
struct serdes_init_seq *serdes_init_seq;
struct serdes_init_seq *serdes_backup_seq;
struct serdes_bridge *serdes_bridge;
struct serdes_bridge_split *serdes_bridge_split;
struct serdes_panel *serdes_panel;

View file

@ -14,7 +14,7 @@ static struct serdes *g_serdes_ser_split[MAX_NUM_SERDES_SPLIT];
int serdes_i2c_set_sequence(struct serdes *serdes)
{
struct device *dev = serdes->dev;
int i, ret = 0;
int i, num = 0, ret = 0;
unsigned int def = 0;
for (i = 0; i < serdes->serdes_init_seq->reg_seq_cnt; i++) {
@ -30,9 +30,9 @@ int serdes_i2c_set_sequence(struct serdes *serdes)
serdes->serdes_init_seq->reg_sequence[i].def);
if (ret < 0) {
dev_err(serdes->dev,
"failed to write register %04x, ret %d, write again now\n",
serdes->serdes_init_seq->reg_sequence[i].reg, ret);
SERDES_DBG_MFD("%s failed to write reg %04x, ret %d, again now\n",
serdes->dev,
serdes->serdes_init_seq->reg_sequence[i].reg, ret);
ret = serdes_reg_write(serdes,
serdes->serdes_init_seq->reg_sequence[i].reg,
serdes->serdes_init_seq->reg_sequence[i].def);
@ -40,9 +40,10 @@ int serdes_i2c_set_sequence(struct serdes *serdes)
serdes_reg_read(serdes, serdes->serdes_init_seq->reg_sequence[i].reg, &def);
if ((def != serdes->serdes_init_seq->reg_sequence[i].def) || (ret < 0)) {
/* if read value != write value then write again */
dev_err(dev, "read %04x %04x != %04x\n",
serdes->serdes_init_seq->reg_sequence[i].reg,
def, serdes->serdes_init_seq->reg_sequence[i].def);
if (num++ < 1)
dev_err(dev, "read %04x %04x != %04x\n",
serdes->serdes_init_seq->reg_sequence[i].reg,
def, serdes->serdes_init_seq->reg_sequence[i].def);
serdes_reg_write(serdes,
serdes->serdes_init_seq->reg_sequence[i].reg,
serdes->serdes_init_seq->reg_sequence[i].def);
@ -91,6 +92,133 @@ static int serdes_set_i2c_address(struct serdes *serdes, u32 reg_hw, u32 reg_use
return ret;
}
static int serdes_i2c_set_sequence_backup(struct serdes *serdes)
{
struct device *dev = serdes->dev;
int i, num = 0, ret = 0;
unsigned int def = 0;
for (i = 0; i < serdes->serdes_backup_seq->reg_seq_cnt; i++) {
if (serdes->serdes_backup_seq->reg_sequence[i].reg == 0xffff) {
SERDES_DBG_MFD("%s: delay 0x%04x us\n", __func__,
serdes->serdes_backup_seq->reg_sequence[i].def);
udelay(serdes->serdes_backup_seq->reg_sequence[i].def);
continue;
}
ret = serdes_reg_write(serdes,
serdes->serdes_backup_seq->reg_sequence[i].reg,
serdes->serdes_backup_seq->reg_sequence[i].def);
if (ret < 0) {
SERDES_DBG_MFD("%s failed to write reg %04x, ret %d, again now\n",
serdes->dev,
serdes->serdes_backup_seq->reg_sequence[i].reg, ret);
ret = serdes_reg_write(serdes,
serdes->serdes_backup_seq->reg_sequence[i].reg,
serdes->serdes_backup_seq->reg_sequence[i].def);
}
serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[i].reg, &def);
if ((def != serdes->serdes_backup_seq->reg_sequence[i].def) || (ret < 0)) {
/* if read value != write value then write again */
if (num++ < 1)
dev_err(dev, "%s read %04x %04x != %04x\n", __func__,
serdes->serdes_backup_seq->reg_sequence[i].reg,
def, serdes->serdes_backup_seq->reg_sequence[i].def);
serdes_reg_write(serdes,
serdes->serdes_backup_seq->reg_sequence[i].reg,
serdes->serdes_backup_seq->reg_sequence[i].def);
}
}
return ret;
}
static int serdes_i2c_backup_register(struct serdes *serdes)
{
int i, ret = 0;
for (i = 0; i < serdes->serdes_backup_seq->reg_seq_cnt; i++) {
if (serdes->serdes_backup_seq->reg_sequence[i].reg == 0xffff)
continue;
serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[i].reg,
&serdes->serdes_backup_seq->reg_sequence[i].def);
}
return ret;
}
static int serdes_i2c_check_register(struct serdes *serdes, int *flag)
{
struct device *dev = serdes->dev;
int ret = 0;
unsigned int def = 0;
unsigned int num = 0;
get_random_bytes(&num, 1);
if (num > serdes->serdes_backup_seq->reg_seq_cnt - 1)
num = 0;
if (serdes->serdes_backup_seq->reg_sequence[num].reg == 0xffff)
return 0;
ret = serdes_reg_read(serdes, serdes->serdes_backup_seq->reg_sequence[num].reg, &def);
if ((def != serdes->serdes_backup_seq->reg_sequence[num].def) || (ret < 0)) {
/* if read value != write value then write again */
dev_err(dev, "%s read %04x %04x != %04x\n", __func__,
serdes->serdes_backup_seq->reg_sequence[num].reg,
def, serdes->serdes_backup_seq->reg_sequence[num].def);
*flag = 1;
return ret;
}
return ret;
}
static void serdes_reg_check_work(struct kthread_work *work)
{
int flag = 0;
struct serdes *serdes = container_of(work, struct serdes,
reg_check_work.work);
if (atomic_read(&serdes->flag_ser_init)) {
serdes_i2c_backup_register(serdes);
atomic_set(&serdes->flag_ser_init, 0);
}
serdes_i2c_check_register(serdes, &flag);
if (flag) {
if (serdes->chip_data->chip_init)
serdes->chip_data->chip_init(serdes);
serdes_i2c_set_sequence_backup(serdes);
msleep(500);
SERDES_DBG_MFD("%s %s\n", __func__, serdes->chip_data->name);
}
kthread_queue_delayed_work(serdes->kworker, &serdes->reg_check_work,
msecs_to_jiffies(2000));
}
static int serdes_reg_check_work_setup(struct serdes *serdes)
{
kthread_init_delayed_work(&serdes->reg_check_work,
serdes_reg_check_work);
serdes->kworker = kthread_create_worker(0, "%s", dev_name(serdes->dev));
if (IS_ERR(serdes->kworker))
return PTR_ERR(serdes->kworker);
mutex_init(&serdes->reg_check_lock);
atomic_set(&serdes->flag_ser_init, 1);
kthread_queue_delayed_work(serdes->kworker, &serdes->reg_check_work,
msecs_to_jiffies(20000));
return 0;
}
static void serdes_reg_check_work_free(struct serdes *serdes)
{
kthread_cancel_delayed_work_sync(&serdes->reg_check_work);
kthread_destroy_worker(serdes->kworker);
}
static void serdes_mfd_work(struct work_struct *work)
{
struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work);
@ -161,6 +289,17 @@ static int serdes_get_init_seq(struct serdes *serdes)
return err;
}
serdes->serdes_backup_seq = devm_kzalloc(dev, sizeof(*serdes->serdes_backup_seq),
GFP_KERNEL);
if (!serdes->serdes_backup_seq)
return -ENOMEM;
err = serdes_parse_init_seq(dev, data, len, serdes->serdes_backup_seq);
if (err) {
dev_err(dev, "failed to parse serdes-init-sequence\n");
return err;
}
/* init ser register(not des register) more early if uboot logo disabled */
serdes->route_enable = of_property_read_bool(dev->of_node, "route-enable");
if ((!serdes->route_enable) && (serdes->chip_data->serdes_type == TYPE_SER)) {
@ -284,6 +423,13 @@ static int serdes_i2c_probe(struct i2c_client *client,
SERDES_DBG_MFD("%s: use_delay_work=%d\n", __func__, serdes->use_delay_work);
}
serdes->use_reg_check_work = of_property_read_bool(dev->of_node, "use-reg-check-work");
if (serdes->use_reg_check_work) {
serdes_reg_check_work_setup(serdes);
SERDES_DBG_MFD("%s: use_reg_check_work=%d\n", __func__, serdes->use_reg_check_work);
}
dev_info(dev, "serdes %s serdes_i2c_probe successful version %s\n",
serdes->chip_data->name, MFD_SERDES_DISPLAY_VERSION);
@ -298,6 +444,20 @@ static void serdes_i2c_shutdown(struct i2c_client *client)
serdes_device_shutdown(serdes);
}
static void serdes_i2c_remove(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct serdes *serdes = dev_get_drvdata(dev);
if (serdes->use_reg_check_work)
serdes_reg_check_work_free(serdes);
if (serdes->use_delay_work) {
cancel_delayed_work_sync(&serdes->mfd_delay_work);
destroy_workqueue(serdes->mfd_wq);
}
}
static int serdes_i2c_prepare(struct device *dev)
{
return 0;
@ -394,6 +554,7 @@ static struct i2c_driver serdes_i2c_driver = {
},
.probe = serdes_i2c_probe,
.shutdown = serdes_i2c_shutdown,
.remove = (void *)serdes_i2c_remove,
};
static int __init serdes_i2c_init(void)

View file

@ -44,6 +44,19 @@ enum hdmirx_color_space {
HDMIRX_BT2020_RGB_OR_YCC = 6,
};
/* hdmirx scan mode */
enum hdmirx_scan_mode {
HDMIRX_PROGRESSIVE = 0,
HDMIRX_INTERLACED = 1,
};
enum hdmirx_edid_mode {
HDMIRX_EDID_2K60HZ_YUV444,
HDMIRX_EDID_4K30HZ_YUV444,
HDMIRX_EDID_4K60HZ_YUV444,
HDMIRX_EDID_4K60HZ_YUV420,
};
/* Private v4l2 ioctl */
#define RK_HDMIRX_CMD_GET_FPS \
_IOR('V', BASE_VIDIOC_PRIVATE + 0, int)
@ -78,6 +91,18 @@ enum hdmirx_color_space {
#define RK_HDMIRX_CMD_GET_COLOR_SPACE \
_IOR('V', BASE_VIDIOC_PRIVATE + 10, int)
#define RK_HDMIRX_CMD_GET_SCAN_MODE \
_IOR('V', BASE_VIDIOC_PRIVATE + 11, __u8)
#define RK_HDMIRX_CMD_GET_EDID_MODE \
_IOR('V', BASE_VIDIOC_PRIVATE + 12, __u8)
#define RK_HDMIRX_CMD_SET_EDID_MODE \
_IOW('V', BASE_VIDIOC_PRIVATE + 13, __u8)
#define RK_HDMIRX_CMD_GET_HDCP_ENC_STATUS \
_IOR('V', BASE_VIDIOC_PRIVATE + 14, __u8)
/* Private v4l2 event */
#define RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST \
(V4L2_EVENT_PRIVATE_START + 1)