2228 lines
61 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Rockchip Electronics Co., Ltd.
*
* Author: Shunqing Chen <csq@rock-chips.com>
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/soc/rockchip/rk_vendor_storage.h>
#include <linux/slab.h>
#include <linux/rk_hdmirx_config.h>
#include <sound/hdmi-codec.h>
#include "rk628.h"
#include "rk628_combrxphy.h"
#include "rk628_cru.h"
#include "rk628_hdmirx.h"
#define INIT_FIFO_STATE 128
#define DEFAULT_AUDIO_CLK 5644800
struct rk628_audiostate {
u32 hdmirx_aud_clkrate;
u32 fs_audio;
u32 ctsn_flag;
u32 fifo_flag;
int init_state;
int pre_state;
bool fifo_int;
bool audio_enable;
};
struct rk628_audioinfo {
struct delayed_work delayed_work_audio_rate_change;
struct delayed_work delayed_work_audio;
struct mutex *confctl_mutex;
struct rk628 *rk628;
struct rk628_audiostate audio_state;
bool i2s_enabled_default;
bool i2s_enabled;
int debug;
bool sample_flat;
bool fifo_ints_en;
bool ctsn_ints_en;
bool audio_present;
bool arc_en;
bool underflow;
bool overflow;
bool startthreshold;
int stablelimit;
int stablecount;
struct device *dev;
struct platform_device *pdev;
hdmi_codec_plugged_cb plugged_cb;
rk628_audio_info_cb info_cb;
struct device *codec_dev;
};
struct hdmirx_tmdsclk_cnt {
u32 tmds_cnt;
u8 cnt;
};
enum hdmirx_pix_fmt {
HDMIRX_RGB888 = 0,
HDMIRX_YUV422 = 1,
HDMIRX_YUV444 = 2,
HDMIRX_YUV420 = 3,
};
enum hdmirx_ycc_range {
HDMIRX_YCC_LIMIT,
HDMIRX_YCC_FULL,
};
static const char * const bus_format_str[] = {
"RGB",
"YUV422",
"YUV444",
"YUV420",
"UNKNOWN",
};
static const char *bus_color_range_str[3] = {
"Default", "Limited", "Full"
};
static const char *bus_color_space_str[8] = {
"xvYCC601", "xvYCC709", "sYCC601", "Adobe_YCC601",
"Adobe_RGB", "BT2020_YcCbcCrc", "BT2020_RGB_OR_YCbCr", "RGB"
};
#define HDMIRX_GET_TMDSCLK_TIME 21
static int supported_fs[] = {
32000,
44100,
48000,
88200,
96000,
176400,
192000,
768000,
-1
};
static int hdcp_load_keys_cb(struct rk628 *rk628, struct rk628_hdcp *hdcp)
{
int size;
u8 hdcp_vendor_data[320];
hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL);
if (!hdcp->keys)
return -ENOMEM;
hdcp->seeds = kmalloc(HDCP_KEY_SEED_SIZE, GFP_KERNEL);
if (!hdcp->seeds) {
kfree(hdcp->keys);
hdcp->keys = NULL;
return -ENOMEM;
}
size = rk_vendor_read(HDMIRX_HDCP1X_ID, hdcp_vendor_data, 314);
if (size < (HDCP_KEY_SIZE + HDCP_KEY_SEED_SIZE)) {
rk628_dbg(rk628, "HDCP: read size %d\n", size);
kfree(hdcp->keys);
hdcp->keys = NULL;
kfree(hdcp->seeds);
hdcp->seeds = NULL;
return -EINVAL;
}
memcpy(hdcp->keys, hdcp_vendor_data, HDCP_KEY_SIZE);
memcpy(hdcp->seeds, hdcp_vendor_data + HDCP_KEY_SIZE,
HDCP_KEY_SEED_SIZE);
return 0;
}
static int rk628_hdmi_hdcp_load_key(struct rk628 *rk628, struct rk628_hdcp *hdcp)
{
int i;
int ret;
struct hdcp_keys *hdcp_keys;
u32 seeds = 0;
if (!hdcp->keys) {
ret = hdcp_load_keys_cb(rk628, hdcp);
if (ret) {
dev_err(rk628->dev, "HDCP: load key failed\n");
return ret;
}
}
hdcp_keys = hdcp->keys;
rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_CTRL,
HDCP_ENABLE_MASK |
HDCP_ENC_EN_MASK,
HDCP_ENABLE(0) |
HDCP_ENC_EN(0));
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
SW_ADAPTER_I2CSLADR_MASK |
SW_EFUSE_HDCP_EN_MASK,
SW_ADAPTER_I2CSLADR(0) |
SW_EFUSE_HDCP_EN(1));
/* The useful data in ksv should be 5 byte */
for (i = 0; i < KSV_LEN; i++)
rk628_i2c_write(rk628, HDCP_KEY_KSV0 + i * 4,
hdcp_keys->KSV[i]);
for (i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++)
rk628_i2c_write(rk628, HDCP_KEY_DPK0 + i * 4,
hdcp_keys->devicekey[i]);
rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
SW_ADAPTER_I2CSLADR_MASK |
SW_EFUSE_HDCP_EN_MASK,
SW_ADAPTER_I2CSLADR(0) |
SW_EFUSE_HDCP_EN(0));
rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_CTRL,
HDCP_ENABLE_MASK |
HDCP_ENC_EN_MASK,
HDCP_ENABLE(1) |
HDCP_ENC_EN(1));
/* Enable decryption logic */
if (hdcp->seeds) {
seeds = (hdcp->seeds[0] & 0xff) << 8;
seeds |= (hdcp->seeds[1] & 0xff);
}
if (seeds) {
rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_CTRL,
KEY_DECRIPT_ENABLE_MASK,
KEY_DECRIPT_ENABLE(1));
rk628_i2c_write(rk628, HDMI_RX_HDCP_SEED, seeds);
} else {
rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_CTRL,
KEY_DECRIPT_ENABLE_MASK,
KEY_DECRIPT_ENABLE(0));
}
hdcp->hdcp_start = true;
return 0;
}
void rk628_hdmirx_set_hdcp(struct rk628 *rk628, struct rk628_hdcp *hdcp, bool en)
{
rk628_dbg(rk628, "%s: %sable\n", __func__, en ? "en" : "dis");
hdcp->rk628 = rk628;
hdcp->enable = en;
if (en) {
if (hdcp->hdcp_start && rk628->version >= RK628F_VERSION)
return;
rk628_hdmi_hdcp_load_key(rk628, hdcp);
} else {
rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_CTRL,
HDCP_ENABLE_MASK |
HDCP_ENC_EN_MASK,
HDCP_ENABLE(0) |
HDCP_ENC_EN(0));
hdcp->hdcp_start = false;
}
}
EXPORT_SYMBOL(rk628_hdmirx_set_hdcp);
void rk628_hdmirx_controller_setup(struct rk628 *rk628)
{
rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10000011);
rk628_i2c_write(rk628, HDMI_RX_HDMI_MODE_RECOVER, 0x00000021);
rk628_i2c_write(rk628, HDMI_RX_PDEC_CTRL, 0xbfff8011);
rk628_i2c_write(rk628, HDMI_RX_PDEC_ASP_CTRL, 0x00000040);
rk628_i2c_write(rk628, HDMI_RX_HDMI_RESMPL_CTRL, 0x00000000);
rk628_i2c_write(rk628, HDMI_RX_HDMI_SYNC_CTRL, 0x00000014);
rk628_i2c_write(rk628, HDMI_RX_PDEC_ERR_FILTER, 0x00000008);
rk628_i2c_write(rk628, HDMI_RX_SCDC_I2CCONFIG, 0x01000000);
rk628_i2c_write(rk628, HDMI_RX_SCDC_CONFIG, 0x00000001);
rk628_i2c_write(rk628, HDMI_RX_SCDC_WRDATA0, 0xabcdef01);
rk628_i2c_write(rk628, HDMI_RX_CHLOCK_CONFIG, 0x0030c15c);
rk628_i2c_write(rk628, HDMI_RX_HDMI_ERROR_PROTECT, 0x000d0c98);
rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL1, 0x00000010);
rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x0000173a);
rk628_i2c_write(rk628, HDMI_RX_MD_VCTRL, 0x00000002);
rk628_i2c_write(rk628, HDMI_RX_MD_VTH, 0x0000073a);
rk628_i2c_write(rk628, HDMI_RX_MD_IL_POL, 0x00000004);
rk628_i2c_write(rk628, HDMI_RX_PDEC_ACRM_CTRL, 0x00000000);
rk628_i2c_write(rk628, HDMI_RX_HDMI_DCM_CTRL, 0x00040414);
rk628_i2c_write(rk628, HDMI_RX_HDMI_CKM_EVLTM, 0x00103e70);
rk628_i2c_write(rk628, HDMI_RX_HDMI_CKM_F, 0x0c1c0b54);
rk628_i2c_update_bits(rk628, HDMI_RX_HDMI_TIMER_CTRL, VIDEO_PERIOD_MASK, VIDEO_PERIOD(1));
rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_SETTINGS,
HDMI_RESERVED_MASK |
FAST_I2C_MASK |
ONE_DOT_ONE_MASK |
FAST_REAUTH_MASK,
HDMI_RESERVED(1) |
FAST_I2C(0) |
ONE_DOT_ONE(0) |
FAST_REAUTH(0));
}
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;
int fs_t;
fs_t = supported_fs[i++];
while (fs_t > 0) {
if (fs == fs_t)
return true;
fs_t = supported_fs[i++];
};
return false;
}
static int rk628_hdmirx_audio_find_closest_fs(struct rk628_audioinfo *aif, int fs)
{
int i = 0;
int fs_t;
int difference;
fs_t = supported_fs[i++];
while (fs_t > 0) {
difference = abs(fs - fs_t);
if (difference <= 2000) {
if (fs != fs_t)
dev_dbg(aif->dev, "%s fix fs from %u to %u", __func__, fs, fs_t);
return fs_t;
}
fs_t = supported_fs[i++];
};
return fs_t;
}
static void rk628_hdmirx_audio_fifo_init(struct rk628_audioinfo *aif)
{
dev_dbg(aif->dev, "%s initial fifo\n", __func__);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_ICLR, 0x1f);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10001);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10000);
aif->audio_state.pre_state = aif->audio_state.init_state = INIT_FIFO_STATE*4;
aif->underflow = false;
aif->overflow = false;
aif->startthreshold = false;
}
static void rk628_hdmirx_audio_fifo_initd(struct rk628_audioinfo *aif)
{
dev_dbg(aif->dev, "%s double initial fifo\n", __func__);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_ICLR, 0x1f);
rk628_i2c_update_bits(aif->rk628, HDMI_RX_AUD_FIFO_TH,
AFIF_TH_START_MASK,
AFIF_TH_START(192));
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10001);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10000);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10001);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10000);
rk628_i2c_update_bits(aif->rk628, HDMI_RX_AUD_FIFO_TH,
AFIF_TH_START_MASK,
AFIF_TH_START(INIT_FIFO_STATE));
aif->audio_state.pre_state = aif->audio_state.init_state = INIT_FIFO_STATE*4;
}
static u32 _rk628_hdmirx_audio_fs(struct rk628_audioinfo *aif)
{
u64 tmdsclk = 0;
u32 clkrate = 0, cts_decoded = 0, n_decoded = 0, fs_audio = 0;
/* fout=128*fs=ftmds*N/CTS */
rk628_i2c_read(aif->rk628, HDMI_RX_HDMI_CKM_RESULT, &clkrate);
clkrate = clkrate & 0xffff;
/* tmdsclk = (clkrate/1000) * 49500000 */
tmdsclk = clkrate * (49500000 / 1000);
rk628_i2c_read(aif->rk628, HDMI_RX_PDEC_ACR_CTS, &cts_decoded);
rk628_i2c_read(aif->rk628, HDMI_RX_PDEC_ACR_N, &n_decoded);
if (cts_decoded != 0) {
fs_audio = div_u64((tmdsclk * n_decoded), cts_decoded);
fs_audio /= 128;
fs_audio = rk628_hdmirx_audio_find_closest_fs(aif, fs_audio);
}
dev_dbg(aif->dev,
"%s: clkrate:%u tmdsclk:%llu, n_decoded:%u, cts_decoded:%u, fs_audio:%u\n",
__func__, clkrate, tmdsclk, n_decoded, cts_decoded, fs_audio);
if (!is_validfs(fs_audio))
fs_audio = 0;
return fs_audio;
}
static void rk628_hdmirx_audio_clk_set_rate(struct rk628_audioinfo *aif, u32 rate)
{
dev_dbg(aif->dev, "%s: %u to %u\n",
__func__, aif->audio_state.hdmirx_aud_clkrate, rate);
rk628_clk_set_rate(aif->rk628, CGU_CLK_HDMIRX_AUD, rate);
aif->audio_state.hdmirx_aud_clkrate = rate;
}
static void rk628_hdmirx_audio_clk_inc_rate(struct rk628_audioinfo *aif, int dis)
{
u32 hdmirx_aud_clkrate = aif->audio_state.hdmirx_aud_clkrate + dis;
dev_dbg(aif->dev, "%s: %u to %u\n",
__func__, aif->audio_state.hdmirx_aud_clkrate, hdmirx_aud_clkrate);
rk628_clk_set_rate(aif->rk628, CGU_CLK_HDMIRX_AUD, hdmirx_aud_clkrate);
aif->audio_state.hdmirx_aud_clkrate = hdmirx_aud_clkrate;
}
static void rk628_hdmirx_audio_clk_ppm_inc(struct rk628_audioinfo *aif, int ppm)
{
int delta, rate, inc;
rate = aif->audio_state.hdmirx_aud_clkrate;
if (ppm < 0) {
ppm = -ppm;
inc = -1;
} else
inc = 1;
delta = div_u64(((uint64_t)rate * ppm + 500000), 1000000);
delta *= inc;
rate += delta;
dev_dbg(aif->dev, "%s: %u to %u(delta:%d ppm:%d)\n",
__func__, aif->audio_state.hdmirx_aud_clkrate, rate, delta, ppm);
rk628_clk_set_rate(aif->rk628, CGU_CLK_HDMIRX_AUD, rate);
aif->audio_state.hdmirx_aud_clkrate = rate;
}
static void rk628_hdmirx_audio_set_fs(struct rk628_audioinfo *aif, u32 fs_audio)
{
u32 hdmirx_aud_clkrate_t = fs_audio*128;
dev_dbg(aif->dev, "%s: %u to %u with fs %u\n", __func__,
aif->audio_state.hdmirx_aud_clkrate, hdmirx_aud_clkrate_t,
fs_audio);
rk628_clk_set_rate(aif->rk628, CGU_CLK_HDMIRX_AUD, hdmirx_aud_clkrate_t);
aif->audio_state.hdmirx_aud_clkrate = hdmirx_aud_clkrate_t;
aif->audio_state.fs_audio = fs_audio;
}
static void rk628_hdmirx_audio_enable(struct rk628_audioinfo *aif)
{
u32 fifo_ints;
rk628_i2c_read(aif->rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints);
dev_dbg(aif->dev, "%s fifo ints %#x\n", __func__, fifo_ints);
if ((fifo_ints & 0x18) == 0x18)
rk628_hdmirx_audio_fifo_initd(aif);
else if (fifo_ints & 0x18)
rk628_hdmirx_audio_fifo_init(aif);
rk628_i2c_update_bits(aif->rk628, HDMI_RX_DMI_DISABLE_IF,
AUD_ENABLE_MASK, AUD_ENABLE(1));
aif->audio_state.audio_enable = true;
aif->fifo_ints_en = true;
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_IEN_SET,
AFIF_OVERFL_ISTS | AFIF_UNDERFL_ISTS);
}
static const char *audio_fifo_err(u32 fifo_status)
{
switch (fifo_status & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) {
case AFIF_UNDERFL_ISTS:
return "underflow";
case AFIF_OVERFL_ISTS:
return "overflow";
case AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS:
return "underflow and overflow";
}
return "underflow or overflow";
}
static int rk628_hdmirx_audio_clk_adjust(struct rk628_audioinfo *aif,
int total_offset, int single_offset)
{
int shedule_time = 500;
int ppm = 10;
uint32_t offset_abs;
offset_abs = abs(total_offset);
if (offset_abs > 200) {
ppm += 200;
shedule_time -= 100;
}
if (offset_abs > 100) {
ppm += 200;
shedule_time -= 100;
}
if (offset_abs > 32) {
ppm += 20;
shedule_time -= 100;
}
if (offset_abs > 16)
ppm += 20;
if (total_offset > 16 && single_offset > 0) {
rk628_hdmirx_audio_clk_ppm_inc(aif, ppm);
} else if (total_offset < -16 && single_offset < 0) {
rk628_hdmirx_audio_clk_ppm_inc(aif, -ppm);
}
if (!aif->audio_present)
shedule_time = 50;
return shedule_time;
}
static void rk628_hdmirx_audio_state_change(struct rk628_audioinfo *aif, bool on)
{
struct device *dev = aif->rk628->dev;
if (on) {
if (aif->stablecount < aif->stablelimit) {
aif->stablecount++;
dev_info(dev, "wait for audio stable count %d\n", aif->stablecount);
return;
}
if (!aif->audio_present) {
aif->audio_present = true;
dev_info(dev, "audio on\n");
rk628_hdmirx_audio_handle_plugged_change(aif, aif->audio_present);
}
} else {
if (aif->audio_present) {
aif->stablecount = 0;
aif->audio_present = false;
dev_info(dev, "audio off\n");
rk628_hdmirx_audio_handle_plugged_change(aif, aif->audio_present);
}
}
}
static void rk628_csi_delayed_work_audio_v2(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct rk628_audioinfo *aif = container_of(dwork, struct rk628_audioinfo,
delayed_work_audio);
struct rk628_audiostate *audio_state = &aif->audio_state;
struct rk628 *rk628 = aif->rk628;
u32 fs_audio, sample_flat;
int init_state, pre_state, fifo_status, fifo_ints;
int single_offset, total_offset;
unsigned long delay = 500;
fs_audio = _rk628_hdmirx_audio_fs(aif);
/* read fifo init status */
rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints);
dev_dbg(rk628->dev, "%s: HDMI_RX_AUD_FIFO_ISTS:%#x\r\n", __func__, fifo_ints);
if (fifo_ints & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) {
dev_warn(rk628->dev, "%s: audio %s %#x, with fs %svalid %d\n",
__func__, audio_fifo_err(fifo_ints), fifo_ints,
is_validfs(fs_audio) ? "" : "in", fs_audio);
if (is_validfs(fs_audio))
rk628_hdmirx_audio_set_fs(aif, fs_audio);
rk628_hdmirx_audio_fifo_init(aif);
rk628_hdmirx_audio_state_change(aif, 0);
goto exit;
}
/* read fifo fill status */
init_state = audio_state->init_state;
pre_state = audio_state->pre_state;
rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &fifo_status);
single_offset = fifo_status - pre_state;
total_offset = fifo_status - init_state;
dev_dbg(rk628->dev,
"%s: HDMI_RX_AUD_FIFO_FILLSTS1:%#x, single offset:%d, total offset:%d\n",
__func__, fifo_status, single_offset, total_offset);
if (!is_validfs(fs_audio)) {
dev_dbg(rk628->dev, "%s: no supported fs(%u), fifo_status %d\n",
__func__, fs_audio, fifo_status);
delay = 1000;
} else if (abs(fs_audio - audio_state->fs_audio) > 1000) {
dev_info(rk628->dev, "%s: restart audio fs(%d -> %d)\n",
__func__, audio_state->fs_audio, fs_audio);
rk628_hdmirx_audio_set_fs(aif, fs_audio);
rk628_hdmirx_audio_fifo_init(aif);
rk628_hdmirx_audio_state_change(aif, 0);
goto exit;
}
if (fifo_status != 0) {
rk628_hdmirx_audio_state_change(aif, 1);
delay = rk628_hdmirx_audio_clk_adjust(aif, total_offset, single_offset);
} else {
rk628_hdmirx_audio_state_change(aif, 0);
}
audio_state->pre_state = fifo_status;
if (aif->i2s_enabled) {
rk628_i2c_read(rk628, HDMI_RX_AUD_SPARE, &sample_flat);
sample_flat = !!(sample_flat & AUDS_MAS_SAMPLE_FLAT);
if (sample_flat != aif->sample_flat) {
dev_info(rk628->dev, "audio sample flat change to %d\n", sample_flat);
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL, I2S_LPCM_BPCUV(0) | I2S_32_16(1) |
(sample_flat ? I2S_DATA_ENABLE_BITS(0xf) : I2S_DATA_ENABLE_BITS(0)));
aif->sample_flat = sample_flat;
}
}
exit:
schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(delay));
}
static void rk628_csi_delayed_work_audio(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct rk628_audioinfo *aif = container_of(dwork, struct rk628_audioinfo,
delayed_work_audio);
struct rk628_audiostate *audio_state = &aif->audio_state;
u32 fs_audio;
int cur_state, init_state, pre_state;
init_state = audio_state->init_state;
pre_state = audio_state->pre_state;
fs_audio = _rk628_hdmirx_audio_fs(aif);
if (!is_validfs(fs_audio)) {
dev_dbg(aif->dev, "%s: no supported fs(%u)\n", __func__, fs_audio);
goto exit;
}
if (!audio_state->audio_enable) {
rk628_hdmirx_audio_set_fs(aif, fs_audio);
rk628_hdmirx_audio_enable(aif);
goto exit;
}
if (abs(fs_audio - audio_state->fs_audio) > 1000)
rk628_hdmirx_audio_set_fs(aif, fs_audio);
rk628_i2c_read(aif->rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &cur_state);
dev_dbg(aif->dev, "%s: HDMI_RX_AUD_FIFO_FILLSTS1:%#x, single offset:%d, total offset:%d\n",
__func__, cur_state, cur_state - pre_state, cur_state - init_state);
if (cur_state != 0) {
if (!aif->audio_present) {
dev_dbg(aif->dev, "audio on\n");
aif->audio_present = true;
rk628_hdmirx_audio_handle_plugged_change(aif, 1);
}
} else {
if (aif->audio_present) {
dev_dbg(aif->dev, "audio off\n");
aif->audio_present = false;
rk628_hdmirx_audio_handle_plugged_change(aif, 0);
}
}
if ((cur_state - init_state) > 16 && (cur_state - pre_state) > 0)
rk628_hdmirx_audio_clk_inc_rate(aif, 10);
else if ((cur_state != 0) && (cur_state - init_state) < -16 && (cur_state - pre_state) < 0)
rk628_hdmirx_audio_clk_inc_rate(aif, -10);
audio_state->pre_state = cur_state;
exit:
schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(1000));
}
static void rk628_csi_delayed_work_audio_rate_change(struct work_struct *work)
{
u32 fifo_fillsts;
u32 fs_audio;
struct delayed_work *dwork = to_delayed_work(work);
struct rk628_audioinfo *aif = container_of(dwork, struct rk628_audioinfo,
delayed_work_audio_rate_change);
mutex_lock(aif->confctl_mutex);
fs_audio = _rk628_hdmirx_audio_fs(aif);
dev_dbg(aif->dev, "%s get audio fs %u\n", __func__, fs_audio);
if (aif->audio_state.ctsn_flag == (ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR)) {
aif->audio_state.ctsn_flag = 0;
if (is_validfs(fs_audio)) {
rk628_hdmirx_audio_set_fs(aif, fs_audio);
/* We start audio work after recieveing cts n interrupt */
rk628_hdmirx_audio_enable(aif);
} else {
dev_dbg(aif->dev, "%s invalid fs when ctsn updating\n", __func__);
}
schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(1000));
}
if (aif->audio_state.fifo_int) {
aif->audio_state.fifo_int = false;
if (is_validfs(fs_audio))
rk628_hdmirx_audio_set_fs(aif, fs_audio);
rk628_i2c_read(aif->rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &fifo_fillsts);
if (!fifo_fillsts)
dev_dbg(aif->dev, "%s underflow after overflow\n", __func__);
else
dev_dbg(aif->dev, "%s overflow after underflow\n", __func__);
rk628_hdmirx_audio_fifo_initd(aif);
aif->audio_present = false;
rk628_hdmirx_audio_handle_plugged_change(aif, 0);
}
mutex_unlock(aif->confctl_mutex);
}
static int rk628_hdmirx_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
{
dev_dbg(dev, "%s\n", __func__);
return 0;
}
static int rk628_hdmirx_audio_startup(struct device *dev, void *data)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)data;
dev_info(dev, "%s: %d\n", __func__, aif->audio_present);
if (aif->audio_present)
return 0;
dev_err(dev, "%s: device is no connected\n", __func__);
return -ENODEV;
}
static void rk628_hdmirx_audio_shutdown(struct device *dev, void *data)
{
dev_dbg(dev, "%s\n", __func__);
}
static int rk628_hdmirx_audio_get_dai_id(struct snd_soc_component *comment,
struct device_node *endpoint)
{
dev_dbg(comment->dev, "%s\n", __func__);
return 0;
}
void rk628_hdmirx_audio_handle_plugged_change(HAUDINFO info, bool plugged)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (aif->plugged_cb && aif->codec_dev)
aif->plugged_cb(aif->codec_dev, plugged);
if (aif->info_cb)
aif->info_cb(aif->rk628, plugged);
}
static int rk628_hdmirx_audio_hook_plugged_cb(struct device *dev, void *data,
hdmi_codec_plugged_cb fn,
struct device *codec_dev)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)data;
dev_dbg(dev, "%s\n", __func__);
if (aif->confctl_mutex)
mutex_lock(aif->confctl_mutex);
aif->plugged_cb = fn;
aif->codec_dev = codec_dev;
rk628_hdmirx_audio_handle_plugged_change(aif, aif->audio_present);
if (aif->confctl_mutex)
mutex_unlock(aif->confctl_mutex);
return 0;
}
static const struct hdmi_codec_ops rk628_hdmirx_audio_codec_ops = {
.hw_params = rk628_hdmirx_audio_hw_params,
.audio_startup = rk628_hdmirx_audio_startup,
.audio_shutdown = rk628_hdmirx_audio_shutdown,
.get_dai_id = rk628_hdmirx_audio_get_dai_id,
.hook_plugged_cb = rk628_hdmirx_audio_hook_plugged_cb
};
static int rk628_hdmirx_register_audio_device(struct rk628_audioinfo *aif)
{
struct hdmi_codec_pdata codec_data = {
.ops = &rk628_hdmirx_audio_codec_ops,
.spdif = 1,
.i2s = 1,
.max_i2s_channels = 8,
.data = aif,
};
aif->pdev = platform_device_register_data(aif->dev,
HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_AUTO,
&codec_data,
sizeof(codec_data));
return PTR_ERR_OR_ZERO(aif->pdev);
}
HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev,
struct mutex *confctl_mutex,
struct rk628 *rk628,
bool en,
rk628_audio_info_cb info_cb)
{
struct rk628_audioinfo *aif;
int ret;
aif = devm_kzalloc(dev, sizeof(*aif), GFP_KERNEL);
if (!aif)
return NULL;
if (rk628->version >= RK628F_VERSION) {
INIT_DELAYED_WORK(&aif->delayed_work_audio, rk628_csi_delayed_work_audio_v2);
} else {
INIT_DELAYED_WORK(&aif->delayed_work_audio, rk628_csi_delayed_work_audio);
INIT_DELAYED_WORK(&aif->delayed_work_audio_rate_change,
rk628_csi_delayed_work_audio_rate_change);
}
aif->confctl_mutex = confctl_mutex;
aif->rk628 = rk628;
aif->i2s_enabled_default = en;
aif->dev = dev;
aif->audio_present = false;
aif->info_cb = info_cb;
ret = rk628_hdmirx_register_audio_device(aif);
if (ret) {
dev_err(dev, "register audio_driver failed!\n");
return NULL;
}
return aif;
}
EXPORT_SYMBOL(rk628_hdmirx_audioinfo_alloc);
void rk628_hdmirx_audio_cancel_work_audio(HAUDINFO info, bool sync)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (sync)
cancel_delayed_work_sync(&aif->delayed_work_audio);
else
cancel_delayed_work(&aif->delayed_work_audio);
}
EXPORT_SYMBOL(rk628_hdmirx_audio_cancel_work_audio);
void rk628_hdmirx_audio_cancel_work_rate_change(HAUDINFO info, bool sync)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (sync)
cancel_delayed_work_sync(&aif->delayed_work_audio_rate_change);
else
cancel_delayed_work(&aif->delayed_work_audio_rate_change);
}
EXPORT_SYMBOL(rk628_hdmirx_audio_cancel_work_rate_change);
void rk628_hdmirx_audio_destroy(HAUDINFO info)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
struct rk628 *rk628;
if (!aif)
return;
rk628 = aif->rk628;
rk628_hdmirx_audio_cancel_work_audio(aif, true);
if (rk628->version < RK628F_VERSION)
rk628_hdmirx_audio_cancel_work_rate_change(aif, true);
if (aif->pdev)
platform_device_unregister(aif->pdev);
aif->confctl_mutex = NULL;
aif->rk628 = NULL;
}
EXPORT_SYMBOL(rk628_hdmirx_audio_destroy);
bool rk628_hdmirx_audio_present(HAUDINFO info)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (!aif)
return false;
return aif->audio_present;
}
EXPORT_SYMBOL(rk628_hdmirx_audio_present);
int rk628_hdmirx_audio_fs(HAUDINFO info)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (!aif)
return 0;
return aif->audio_state.fs_audio;
}
EXPORT_SYMBOL(rk628_hdmirx_audio_fs);
bool rk628_hdmirx_get_arc_enable(HAUDINFO info)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (!aif)
return false;
return aif->arc_en;
}
EXPORT_SYMBOL(rk628_hdmirx_get_arc_enable);
int rk628_hdmirx_set_arc_enable(HAUDINFO info, bool enabled)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (!aif)
return false;
return aif->arc_en = enabled;
}
EXPORT_SYMBOL(rk628_hdmirx_set_arc_enable);
void rk628_hdmirx_audio_i2s_ctrl(HAUDINFO info, bool enable)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
if (enable == aif->i2s_enabled || aif->i2s_enabled_default)
return;
if (enable && !aif->sample_flat) {
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL,
I2S_LPCM_BPCUV(0) | I2S_32_16(1) |
I2S_DATA_ENABLE_BITS(0));
} else {
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL,
I2S_LPCM_BPCUV(0) | I2S_32_16(1) |
I2S_DATA_ENABLE_BITS(0xf));
}
aif->i2s_enabled = enable;
}
EXPORT_SYMBOL(rk628_hdmirx_audio_i2s_ctrl);
void rk628_hdmirx_audio_setup(HAUDINFO info)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
struct rk628 *rk628 = aif->rk628;
u32 audio_pll_n, audio_pll_cts;
dev_dbg(aif->dev, "%s: setup audio\n", __func__);
audio_pll_n = 5644;
audio_pll_cts = 148500;
aif->audio_state.ctsn_flag = 0;
aif->audio_state.fs_audio = 0;
aif->audio_state.pre_state = 0;
aif->audio_state.init_state = INIT_FIFO_STATE*4;
aif->audio_state.fifo_int = false;
aif->audio_state.audio_enable = false;
aif->sample_flat = false;
aif->fifo_ints_en = false;
aif->ctsn_ints_en = false;
aif->i2s_enabled = false;
aif->underflow = false;
aif->overflow = false;
aif->startthreshold = false;
aif->stablelimit = 0;
if (rk628->version >= RK628F_VERSION)
rk628_i2c_write(rk628, CRU_MODE_CON00, HIWORD_UPDATE(1, 4, 4));
rk628_hdmirx_audio_clk_set_rate(aif, DEFAULT_AUDIO_CLK);
/* manual aud CTS */
rk628_i2c_write(aif->rk628, HDMI_RX_AUDPLL_GEN_CTS, audio_pll_cts);
/* manual aud N */
rk628_i2c_write(aif->rk628, HDMI_RX_AUDPLL_GEN_N, audio_pll_n);
/* aud CTS N en manual */
rk628_i2c_update_bits(aif->rk628, HDMI_RX_AUD_CLK_CTRL,
CTS_N_REF_MASK, CTS_N_REF(1));
/* aud pll ctrl */
rk628_i2c_update_bits(aif->rk628, HDMI_RX_AUD_PLL_CTRL,
PLL_LOCK_TOGGLE_DIV_MASK, PLL_LOCK_TOGGLE_DIV(0));
rk628_i2c_update_bits(aif->rk628, HDMI_RX_AUD_FIFO_TH,
AFIF_TH_START_MASK |
AFIF_TH_MAX_MASK |
AFIF_TH_MIN_MASK,
AFIF_TH_START(INIT_FIFO_STATE) |
AFIF_TH_MAX(INIT_FIFO_STATE*2) |
AFIF_TH_MIN(8));
/* AUTO_VMUTE */
rk628_i2c_update_bits(aif->rk628, HDMI_RX_AUD_FIFO_CTRL,
AFIF_SUBPACKET_DESEL_MASK |
AFIF_SUBPACKETS_MASK,
AFIF_SUBPACKET_DESEL(0) |
AFIF_SUBPACKETS(1));
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL,
I2S_LPCM_BPCUV(0) |
I2S_32_16(1) |
(aif->i2s_enabled_default ? 0 : I2S_DATA_ENABLE_BITS(0xf)));
aif->i2s_enabled = aif->i2s_enabled_default;
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_MUTE_CTRL,
APPLY_INT_MUTE(0) |
APORT_SHDW_CTRL(3) |
AUTO_ACLK_MUTE(2) |
AUD_MUTE_SPEED(1) |
AUD_AVMUTE_EN(1) |
AUD_MUTE_SEL(0) |
AUD_MUTE_MODE(1));
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_PAO_CTRL,
PAO_RATE(0));
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_CHEXTR_CTRL,
AUD_LAYOUT_CTRL(1));
if (rk628->version >= RK628F_VERSION) {
schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(1000));
} else {
aif->ctsn_ints_en = true;
rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_IEN_SET,
ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR);
/* audio detect */
rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_AUDIODET_CTRL, AUDIODET_THRESHOLD(0));
}
}
EXPORT_SYMBOL(rk628_hdmirx_audio_setup);
bool rk628_audio_fifoints_enabled(HAUDINFO info)
{
return ((struct rk628_audioinfo *)info)->fifo_ints_en;
}
EXPORT_SYMBOL(rk628_audio_fifoints_enabled);
bool rk628_audio_ctsnints_enabled(HAUDINFO info)
{
return ((struct rk628_audioinfo *)info)->ctsn_ints_en;
}
EXPORT_SYMBOL(rk628_audio_ctsnints_enabled);
void rk628_csi_isr_ctsn(HAUDINFO info, u32 pdec_ints)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
u32 ctsn_mask = ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR;
dev_dbg(aif->dev, "%s: pdec_ints:%#x\n", __func__, pdec_ints);
/* cts & n both need update but maybe come diff int */
if (pdec_ints & ACR_N_CHG_ICLR)
aif->audio_state.ctsn_flag |= ACR_N_CHG_ICLR;
if (pdec_ints & ACR_CTS_CHG_ICLR)
aif->audio_state.ctsn_flag |= ACR_CTS_CHG_ICLR;
if (aif->audio_state.ctsn_flag == ctsn_mask) {
dev_dbg(aif->dev, "%s: ctsn updated, disable ctsn int\n", __func__);
rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_IEN_CLR, ctsn_mask);
aif->ctsn_ints_en = false;
schedule_delayed_work(&aif->delayed_work_audio_rate_change, 0);
}
rk628_i2c_write(aif->rk628, HDMI_RX_PDEC_ICLR, pdec_ints & ctsn_mask);
}
EXPORT_SYMBOL(rk628_csi_isr_ctsn);
void rk628_csi_isr_fifoints(HAUDINFO info, u32 fifo_ints)
{
struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info;
u32 fifo_mask = AFIF_OVERFL_ISTS | AFIF_UNDERFL_ISTS;
dev_dbg(aif->dev, "%s: fifo_ints:%#x\n", __func__, fifo_ints);
/* cts & n both need update but maybe come diff int */
if (fifo_ints & AFIF_OVERFL_ISTS) {
dev_dbg(aif->dev, "%s: Audio FIFO overflow\n", __func__);
aif->audio_state.fifo_flag |= AFIF_OVERFL_ISTS;
}
if (fifo_ints & AFIF_UNDERFL_ISTS) {
dev_dbg(aif->dev, "%s: Audio FIFO underflow\n", __func__);
aif->audio_state.fifo_flag |= AFIF_UNDERFL_ISTS;
}
if (aif->audio_state.fifo_flag == fifo_mask) {
aif->audio_state.fifo_int = true;
aif->audio_state.fifo_flag = 0;
schedule_delayed_work(&aif->delayed_work_audio_rate_change, 0);
}
rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_ICLR, fifo_ints & fifo_mask);
}
EXPORT_SYMBOL(rk628_csi_isr_fifoints);
int rk628_is_avi_ready(struct rk628 *rk628, bool avi_rcv_rdy)
{
u8 i;
u32 val, avi_pb = 0;
u8 cnt = 0, max_cnt = 2;
u32 hdcp_ctrl_val = 0;
if (rk628->version >= RK628F_VERSION)
return 1;
rk628_i2c_read(rk628, HDMI_RX_HDCP_CTRL, &val);
if ((val & HDCP_ENABLE_MASK))
max_cnt = 5;
for (i = 0; i < 100; i++) {
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val);
dev_info(rk628->dev, "%s PDEC_AVI_PB:%#x, avi_rcv_rdy:%d\n",
__func__, val, avi_rcv_rdy);
if (i > 30 && !(hdcp_ctrl_val & 0x400)) {
rk628_i2c_read(rk628, HDMI_RX_HDCP_CTRL, &hdcp_ctrl_val);
/* force hdcp avmute */
hdcp_ctrl_val |= 0x400;
rk628_i2c_write(rk628, HDMI_RX_HDCP_CTRL, hdcp_ctrl_val);
}
if (val && val == avi_pb && avi_rcv_rdy) {
if (++cnt >= max_cnt)
break;
} else {
cnt = 0;
avi_pb = val;
}
msleep(30);
}
if (cnt < max_cnt)
return 0;
return 1;
}
EXPORT_SYMBOL(rk628_is_avi_ready);
static void hdmirxphy_write(struct rk628 *rk628, u32 offset, u32 val)
{
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_ADDRESS, offset);
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_DATAO, val);
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_OPERATION, 1);
}
static __maybe_unused u32 hdmirxphy_read(struct rk628 *rk628, u32 offset)
{
u32 val;
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_ADDRESS, offset);
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_OPERATION, 2);
rk628_i2c_read(rk628, HDMI_RX_I2CM_PHYG3_DATAI, &val);
return val;
}
static void rk628_hdmirxphy_enable(struct rk628 *rk628, bool is_hdmi2, bool scramble_en)
{
hdmirxphy_write(rk628, 0x3e, 0x0);
hdmirxphy_write(rk628, 0x5e, 0x0);
hdmirxphy_write(rk628, 0x7e, 0x0);
hdmirxphy_write(rk628, 0x02, 0x1860);
hdmirxphy_write(rk628, 0x03, 0x0060);
if (!is_hdmi2 && scramble_en)
hdmirxphy_write(rk628, 0x0d, 0x00c0);
else
hdmirxphy_write(rk628, 0x0d, 0x0);
hdmirxphy_write(rk628, 0x27, 0x1c94);
hdmirxphy_write(rk628, 0x28, 0x3713);
hdmirxphy_write(rk628, 0x29, 0x24da);
hdmirxphy_write(rk628, 0x2a, 0x5492);
hdmirxphy_write(rk628, 0x2b, 0x4b0d);
hdmirxphy_write(rk628, 0x2d, 0x008c);
hdmirxphy_write(rk628, 0x2e, 0x0001);
if (is_hdmi2) {
hdmirxphy_write(rk628, 0x0e, 0x0108);
hdmirxphy_write(rk628, 0x3e, 0x610);
hdmirxphy_write(rk628, 0x5e, 0x610);
hdmirxphy_write(rk628, 0x7e, 0x610);
} else {
hdmirxphy_write(rk628, 0x0e, 0x0008);
}
}
static void rk628_hdmirxphy_set_clrdpt(struct rk628 *rk628, bool is_8bit)
{
if (is_8bit)
hdmirxphy_write(rk628, 0x03, 0x0000);
else
hdmirxphy_write(rk628, 0x03, 0x0060);
}
static int rk628_hdmirx_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
{
struct rk628_hdmirx_cec *cec = cec_get_drvdata(adap);
struct rk628 *rk628 = cec->rk628;
if (logical_addr == CEC_LOG_ADDR_INVALID)
cec->addresses = 0;
else
cec->addresses |= BIT(logical_addr) | BIT(15);
mutex_lock(&rk628->rst_lock);
rk628_i2c_write(rk628, HDMI_RX_CEC_ADDR_L, cec->addresses & 0xff);
rk628_i2c_write(rk628, HDMI_RX_CEC_ADDR_H, (cec->addresses >> 8) & 0xff);
mutex_unlock(&rk628->rst_lock);
return 0;
}
static int rk628_hdmirx_cec_enable(struct cec_adapter *adap, bool enable)
{
struct rk628_hdmirx_cec *cec = cec_get_drvdata(adap);
struct rk628 *rk628 = cec->rk628;
mutex_lock(&rk628->rst_lock);
if (!enable) {
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_IEN_CLR, ~0);
rk628_i2c_update_bits(rk628, HDMI_RX_DMI_DISABLE_IF, CEC_ENABLE_MASK, 0);
} else {
unsigned int irqs;
rk628_i2c_update_bits(rk628, HDMI_RX_DMI_DISABLE_IF, CEC_ENABLE_MASK,
CEC_ENABLE_MASK);
rk628_i2c_write(rk628, HDMI_RX_CEC_CTRL, 0);
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_ICLR, ~0);
rk628_i2c_write(rk628, HDMI_RX_CEC_LOCK, 0);
irqs = ERROR_INIT_ENSET | NACK_ENSET | EOM_ENSET | DONE_ENSET;
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_IEN_SET, irqs);
}
mutex_unlock(&rk628->rst_lock);
return 0;
}
static int rk628_hdmirx_cec_transmit(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
struct rk628_hdmirx_cec *cec = cec_get_drvdata(adap);
struct rk628 *rk628 = cec->rk628;
int i, msg_len;
unsigned int ctrl;
switch (signal_free_time) {
case CEC_SIGNAL_FREE_TIME_RETRY:
ctrl = CEC_CTRL_RETRY;
break;
case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:
default:
ctrl = CEC_CTRL_NORMAL;
break;
case CEC_SIGNAL_FREE_TIME_NEXT_XFER:
ctrl = CEC_CTRL_IMMED;
break;
}
msg_len = msg->len;
if (msg->len > 16)
msg_len = 16;
if (msg_len <= 0)
return 0;
mutex_lock(&rk628->rst_lock);
for (i = 0; i < msg_len; i++)
rk628_i2c_write(rk628, HDMI_RX_CEC_TX_DATA_0 + i * 4, msg->msg[i]);
rk628_i2c_write(rk628, HDMI_RX_CEC_TX_CNT, msg_len);
rk628_i2c_write(rk628, HDMI_RX_CEC_CTRL, ctrl | CEC_SEND);
mutex_unlock(&rk628->rst_lock);
return 0;
}
static const struct cec_adap_ops rk628_hdmirx_cec_ops = {
.adap_enable = rk628_hdmirx_cec_enable,
.adap_log_addr = rk628_hdmirx_cec_log_addr,
.adap_transmit = rk628_hdmirx_cec_transmit,
};
static void rk628_hdmirx_cec_del(void *data)
{
struct rk628_hdmirx_cec *cec = data;
cec_delete_adapter(cec->adap);
}
void rk628_hdmirx_cec_irq(struct rk628 *rk628, struct rk628_hdmirx_cec *cec)
{
u32 stat, val;
rk628_i2c_read(rk628, HDMI_RX_AUD_CEC_ISTS, &stat);
if (stat == 0)
return;
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_ICLR, stat);
if (stat & ERROR_INIT) {
cec->tx_status = CEC_TX_STATUS_ERROR;
cec->tx_done = true;
} else if (stat & DONE) {
cec->tx_status = CEC_TX_STATUS_OK;
cec->tx_done = true;
} else if (stat & NACK) {
cec->tx_status = CEC_TX_STATUS_NACK;
cec->tx_done = true;
}
if (stat & EOM) {
unsigned int len, i;
rk628_i2c_read(rk628, HDMI_RX_CEC_RX_CNT, &val);
len = val & 0x1f;
if (len > sizeof(cec->rx_msg.msg))
len = sizeof(cec->rx_msg.msg);
for (i = 0; i < len; i++) {
rk628_i2c_read(rk628, HDMI_RX_CEC_RX_DATA_0 + i * 4, &val);
cec->rx_msg.msg[i] = val & 0xff;
}
rk628_i2c_write(rk628, HDMI_RX_CEC_LOCK, 0);
cec->rx_msg.len = len;
cec->rx_done = true;
}
if (cec->tx_done) {
cec->tx_done = false;
cec_transmit_attempt_done(cec->adap, cec->tx_status);
}
if (cec->rx_done) {
cec->rx_done = false;
cec_received_msg(cec->adap, &cec->rx_msg);
}
}
EXPORT_SYMBOL(rk628_hdmirx_cec_irq);
static void rk628_delayed_work_cec(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct rk628_hdmirx_cec *cec = container_of(dwork, struct rk628_hdmirx_cec,
delayed_work_cec);
bool en = rk628_hdmirx_tx_5v_power_detect(cec->rk628->hdmirx_det_gpio);
cec->cec_hpd = en;
cec_queue_pin_hpd_event(cec->adap, en, ktime_get());
}
struct rk628_hdmirx_cec *rk628_hdmirx_cec_register(struct rk628 *rk628)
{
struct rk628_hdmirx_cec *cec;
int ret;
unsigned int irqs;
if (!rk628)
return NULL;
/*
* Our device is just a convenience - we want to link to the real
* hardware device here, so that userspace can see the association
* between the HDMI hardware and its associated CEC chardev.
*/
cec = devm_kzalloc(rk628->dev, sizeof(*cec), GFP_KERNEL);
if (!cec)
return NULL;
cec->rk628 = rk628;
cec->dev = rk628->dev;
rk628_i2c_write(rk628, HDMI_RX_CEC_MASK, 0);
rk628_i2c_update_bits(rk628, HDMI_RX_DMI_DISABLE_IF, CEC_ENABLE_MASK, CEC_ENABLE_MASK);
rk628_i2c_write(rk628, HDMI_RX_CEC_TX_CNT, 0);
rk628_i2c_write(rk628, HDMI_RX_CEC_RX_CNT, 0);
/* clk_hdmirx_cec = 32.768k */
rk628_clk_set_rate(rk628, CGU_CLK_HDMIRX_CEC, 32768);
cec->adap = cec_allocate_adapter(&rk628_hdmirx_cec_ops, cec, "rk628-hdmirx",
CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
CEC_CAP_RC | CEC_CAP_PASSTHROUGH,
CEC_MAX_LOG_ADDRS);
if (IS_ERR(cec->adap)) {
dev_err(cec->dev, "cec adap allocate failed!\n");
return NULL;
}
/* override the module pointer */
cec->adap->owner = THIS_MODULE;
INIT_DELAYED_WORK(&cec->delayed_work_cec, rk628_delayed_work_cec);
ret = devm_add_action(cec->dev, rk628_hdmirx_cec_del, cec);
if (ret) {
cec_delete_adapter(cec->adap);
return NULL;
}
cec->notify = cec_notifier_cec_adap_register(cec->dev,
NULL, cec->adap);
if (!cec->notify) {
dev_err(cec->dev, "cec notify register failed!\n");
return NULL;
}
ret = cec_register_adapter(cec->adap, cec->dev);
if (ret < 0) {
dev_err(cec->dev, "cec register adapter failed!\n");
cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
return NULL;
}
/* The TV functionality can only map to physical address 0 */
cec_s_phys_addr(cec->adap, 0, false);
rk628_i2c_update_bits(rk628, HDMI_RX_DMI_DISABLE_IF, CEC_ENABLE_MASK, CEC_ENABLE_MASK);
irqs = ERROR_INIT_ENSET | NACK_ENSET | EOM_ENSET | DONE_ENSET;
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_IEN_SET, irqs);
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_ICLR, ~0);
/*
* CEC documentation says we must not call cec_delete_adapter
* after a successful call to cec_register_adapter().
*/
devm_remove_action(cec->dev, rk628_hdmirx_cec_del, cec);
schedule_delayed_work(&cec->delayed_work_cec, msecs_to_jiffies(10000));
return cec;
}
EXPORT_SYMBOL(rk628_hdmirx_cec_register);
void rk628_hdmirx_cec_unregister(struct rk628_hdmirx_cec *cec)
{
if (!cec)
return;
cec_notifier_cec_adap_unregister(cec->notify, cec->adap);
cec_unregister_adapter(cec->adap);
}
EXPORT_SYMBOL(rk628_hdmirx_cec_unregister);
void rk628_hdmirx_cec_hpd(struct rk628_hdmirx_cec *cec, bool en)
{
if (!cec || !cec->adap)
return;
rk628_dbg(cec->rk628, "%s: cec_hpd:%d, en:%d\n", __func__, cec->cec_hpd, en);
if (cec->cec_hpd != en) {
cec->cec_hpd = en;
cec_queue_pin_hpd_event(cec->adap, en, ktime_get());
}
}
EXPORT_SYMBOL(rk628_hdmirx_cec_hpd);
void rk628_hdmirx_cec_state_reconfiguration(struct rk628 *rk628,
struct rk628_hdmirx_cec *cec)
{
unsigned int irqs;
u32 val;
/* clk_hdmirx_cec = 32.768k */
rk628_clk_set_rate(rk628, CGU_CLK_HDMIRX_CEC, 32768);
rk628_i2c_write(rk628, HDMI_RX_CEC_ADDR_L, cec->addresses & 0xff);
rk628_i2c_write(rk628, HDMI_RX_CEC_ADDR_H, (cec->addresses >> 8) & 0xff);
rk628_i2c_write(rk628, HDMI_RX_CEC_MASK, 0);
rk628_i2c_write(rk628, HDMI_RX_CEC_TX_CNT, 0);
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_IEN_CLR, ~0);
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_ICLR, ~0);
rk628_i2c_write(rk628, HDMI_RX_CEC_CTRL, 0);
rk628_i2c_write(rk628, HDMI_RX_CEC_LOCK, 0);
irqs = ERROR_INIT_ENSET | NACK_ENSET | EOM_ENSET | DONE_ENSET;
rk628_i2c_read(rk628, HDMI_RX_AUD_CEC_IEN, &val);
if (!(val & irqs))
rk628_i2c_write(rk628, HDMI_RX_AUD_CEC_IEN_SET, irqs);
rk628_i2c_update_bits(rk628, HDMI_RX_DMI_DISABLE_IF, CEC_ENABLE_MASK, CEC_ENABLE(1));
}
EXPORT_SYMBOL(rk628_hdmirx_cec_state_reconfiguration);
void rk628_hdmirx_verisyno_phy_power_on(struct rk628 *rk628)
{
bool is_hdmi2 = false;
u32 val;
int i;
bool scramble = false;
/* wait tx to write scdc tmds ratio */
for (i = 0; i < 50; i++) {
rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS0, &val);
if (val & SCDC_TMDSBITCLKRATIO)
break;
msleep(20);
}
if (val & SCDC_TMDSBITCLKRATIO)
is_hdmi2 = true;
rk628_i2c_read(rk628, HDMI_RX_HDMI20_STATUS, &val);
scramble = (val & SCRAMBDET_MASK) ? true : false;
rk628_dbg(rk628, "%s: %s, %s\n", __func__, is_hdmi2 ? "hdmi2.0" : "hdmi1.4",
scramble ? "Scramble" : "Descramble");
/* power down phy */
rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x17);
usleep_range(20, 30);
rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x15);
/* init phy i2c */
rk628_i2c_write(rk628, HDMI_RX_SNPS_PHYG3_CTRL, 0);
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_SS_CNTS, 0x018c01d2);
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_FS_HCNT, 0x003c0081);
rk628_i2c_write(rk628, HDMI_RX_I2CM_PHYG3_MODE, 1);
rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x11);
/* enable rx phy */
rk628_hdmirxphy_enable(rk628, is_hdmi2, scramble);
rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x14);
msleep(20);
}
EXPORT_SYMBOL(rk628_hdmirx_verisyno_phy_power_on);
void rk628_hdmirx_verisyno_phy_power_off(struct rk628 *rk628)
{
if (rk628->version < RK628F_VERSION)
return;
rk628_i2c_write(rk628, GRF_SW_HDMIRXPHY_CRTL, 0x07);
}
EXPORT_SYMBOL(rk628_hdmirx_verisyno_phy_power_off);
void rk628_hdmirx_phy_prepclk_cfg(struct rk628 *rk628)
{
u32 format;
bool is_clrdpt_8bit = false;
usleep_range(20 * 1000, 30 * 1000);
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &format);
format = (format & VIDEO_FORMAT_MASK) >> 5;
rk628_dbg(rk628, "%s: format = %d from AVI\n", __func__, format);
/* yuv420 should set phy color depth 8bit */
if (format == 3)
is_clrdpt_8bit = true;
rk628_i2c_read(rk628, HDMI_RX_PDEC_GCP_AVMUTE, &format);
format = (format & PKTDEC_GCP_CD_MASK) >> 4;
rk628_dbg(rk628, "%s: format = %d from GCP\n", __func__, format);
/* 10bit color depth should set phy color depth 8bit */
if (format == 5)
is_clrdpt_8bit = true;
rk628_hdmirxphy_set_clrdpt(rk628, is_clrdpt_8bit);
}
EXPORT_SYMBOL(rk628_hdmirx_phy_prepclk_cfg);
u8 rk628_hdmirx_get_format(struct rk628 *rk628)
{
u32 val;
u8 video_fmt;
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val);
video_fmt = (val & VIDEO_FORMAT_MASK) >> 5;
if (video_fmt > BUS_FMT_UNKNOWN)
video_fmt = BUS_FMT_UNKNOWN;
rk628_dbg(rk628, "%s: format = %s\n", __func__, bus_format_str[video_fmt]);
/*
* set avmute value to black
* RGB: R: CH2[15:0], G:CH0_1[31:16], B: CH0_1[15:0]
* YUV: Cr:CH2[15:0], Y:CH0_1[31:16], Cb:CH0_1[15:0]
*/
if (video_fmt == BUS_FMT_RGB) {
rk628_i2c_write(rk628, HDMI_VM_CFG_CH0_1, 0x0);
rk628_i2c_write(rk628, HDMI_VM_CFG_CH2, 0x0);
} else {
rk628_i2c_write(rk628, HDMI_VM_CFG_CH0_1, 0x00008000);
rk628_i2c_write(rk628, HDMI_VM_CFG_CH2, 0x8000);
}
return video_fmt;
}
EXPORT_SYMBOL(rk628_hdmirx_get_format);
void rk628_set_bg_enable(struct rk628 *rk628, bool en)
{
if (en) {
if (rk628->tx_mode)
rk628_i2c_write(rk628, GRF_BG_CTRL,
BG_R_OR_V(0) | BG_B_OR_U(0) | BG_G_OR_Y(0) | BG_ENABLE(1));
else
rk628_i2c_write(rk628, GRF_BG_CTRL,
BG_R_OR_V(512) | BG_B_OR_U(512) | BG_G_OR_Y(64) | BG_ENABLE(1));
return;
}
rk628_i2c_write(rk628, GRF_BG_CTRL, BG_ENABLE(0));
}
EXPORT_SYMBOL(rk628_set_bg_enable);
u32 rk628_hdmirx_get_tmdsclk_cnt(struct rk628 *rk628)
{
int i, j;
u32 val, tmdsclk_cnt = 0;
struct hdmirx_tmdsclk_cnt tmdsclk[HDMIRX_GET_TMDSCLK_TIME] = {0};
for (i = 0; i < HDMIRX_GET_TMDSCLK_TIME; i++) {
rk628_i2c_read(rk628, HDMI_RX_HDMI_CKM_RESULT, &val);
tmdsclk_cnt = val & 0xffff;
for (j = 0; j < HDMIRX_GET_TMDSCLK_TIME; j++) {
if (tmdsclk_cnt == tmdsclk[j].tmds_cnt || !tmdsclk[j].tmds_cnt) {
tmdsclk[j].tmds_cnt = tmdsclk_cnt;
tmdsclk[j].cnt++;
break;
}
}
}
for (i = 0; i < HDMIRX_GET_TMDSCLK_TIME; i++) {
if (!tmdsclk[i].tmds_cnt)
return tmdsclk_cnt;
rk628_dbg(rk628, "tmdsclk_cnt: %d, cnt: %d\n",
tmdsclk[i].tmds_cnt, tmdsclk[i].cnt);
if (!i)
tmdsclk_cnt = tmdsclk[i].tmds_cnt;
else if (tmdsclk[i].cnt > tmdsclk[i - 1].cnt)
tmdsclk_cnt = tmdsclk[i].tmds_cnt;
}
return tmdsclk_cnt;
}
EXPORT_SYMBOL(rk628_hdmirx_get_tmdsclk_cnt);
static int rk628_hdmirx_read_timing(struct rk628 *rk628,
struct v4l2_dv_timings *timings)
{
struct v4l2_bt_timings *bt = &timings->bt;
u32 hact, vact, htotal, vtotal, fps, status;
u32 val;
u32 modetclk_cnt_hs, modetclk_cnt_vs, hs, vs;
u32 hofs_pix, hbp, hfp, vbp, vfp;
u32 tmds_clk, tmdsclk_cnt;
u64 tmp_data;
u8 video_fmt, vic, color_range, color_space;
u32 format;
memset(timings, 0, sizeof(struct v4l2_dv_timings));
timings->type = V4L2_DV_BT_656_1120;
rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val);
status = val;
rk628_i2c_read(rk628, HDMI_RX_MD_STS, &val);
bt->interlaced = val & ILACE_STS ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val);
hact = val & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_MD_VAL, &val);
vact = val & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val);
htotal = (val >> 16) & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_MD_VTL, &val);
vtotal = val & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val);
hofs_pix = val & 0xffff;
rk628_i2c_read(rk628, HDMI_RX_MD_VOL, &val);
vbp = (val & 0xffff) + 1;
tmdsclk_cnt = rk628_hdmirx_get_tmdsclk_cnt(rk628);
tmp_data = tmdsclk_cnt;
tmp_data = ((tmp_data * HDMIRX_MODETCLK_HZ) + HDMIRX_MODETCLK_CNT_NUM / 2);
do_div(tmp_data, HDMIRX_MODETCLK_CNT_NUM);
tmds_clk = tmp_data;
if (!htotal || !vtotal || bt->interlaced || vtotal > 3000) {
dev_err(rk628->dev, "timing err, %s htotal:%d, vtotal:%d\n",
bt->interlaced ? "interlaced is not supported," : "",
htotal, vtotal);
goto TIMING_ERR;
}
if (rk628->version >= RK628F_VERSION)
fps = tmds_clk / (htotal * vtotal);
else
fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal);
rk628_i2c_read(rk628, HDMI_RX_MD_HT0, &val);
modetclk_cnt_hs = val & 0xffff;
hs = (tmdsclk_cnt * modetclk_cnt_hs + HDMIRX_MODETCLK_CNT_NUM / 2) /
HDMIRX_MODETCLK_CNT_NUM;
rk628_i2c_read(rk628, HDMI_RX_MD_VSC, &val);
modetclk_cnt_vs = val & 0xffff;
vs = (tmdsclk_cnt * modetclk_cnt_vs + HDMIRX_MODETCLK_CNT_NUM / 2) /
HDMIRX_MODETCLK_CNT_NUM;
vs = (vs + htotal / 2) / htotal;
if ((hofs_pix < hs) || (htotal < (hact + hofs_pix)) ||
(vtotal < (vact + vs + vbp)) || !vs) {
dev_err(rk628->dev, "timing err, total:%dx%d, act:%dx%d, hofs:%d, hs:%d, vs:%d, vbp:%d\n",
htotal, vtotal, hact, vact, hofs_pix, hs, vs, vbp);
goto TIMING_ERR;
}
hbp = hofs_pix - hs;
hfp = htotal - hact - hofs_pix;
vfp = vtotal - vact - vs - vbp;
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val);
vic = (val & VID_IDENT_CODE_MASK) >> 24;
rk628_i2c_read(rk628, HDMI_RX_PDEC_GCP_AVMUTE, &format);
format = (format & PKTDEC_GCP_CD_MASK) >> 4;
video_fmt = rk628_hdmirx_get_format(rk628);
rk628->color_format = video_fmt;
color_range = rk628_hdmirx_get_range(rk628);
rk628->color_range = color_range;
color_space = rk628_hdmirx_get_color_space(rk628);
rk628->color_space = color_space;
if (video_fmt == BUS_FMT_YUV420) {
//format:color depth, 5: 10bit, 4: 8bit
if (format == 5) {
htotal = htotal * 2 * 8 / 10;
hact = hact * 2 * 8 / 10;
hfp = hfp * 2 * 8 / 10;
hbp = hbp * 2 * 8 / 10;
hs = hs * 2 * 8 / 10;
} else {
htotal *= 2;
hact *= 2;
hfp *= 2;
hbp *= 2;
hs *= 2;
}
}
rk628_dbg(rk628, "cnt_num:%d, tmds_cnt:%d, hs_cnt:%d, vs_cnt:%d, hofs:%d\n",
HDMIRX_MODETCLK_CNT_NUM, tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs, hofs_pix);
rk628_dbg(rk628, "get current aviif: vic:%d, color_range: %s, color_space %s",
vic, bus_color_range_str[color_range], bus_color_space_str[color_space]);
bt->width = hact;
bt->height = vact;
bt->hfrontporch = hfp;
bt->hsync = hs;
bt->hbackporch = hbp;
bt->vfrontporch = vfp;
bt->vsync = vs;
bt->vbackporch = vbp;
if (rk628->version >= RK628F_VERSION)
bt->pixelclock = tmds_clk;
else
bt->pixelclock = htotal * vtotal * fps;
if (bt->interlaced == V4L2_DV_INTERLACED) {
bt->height *= 2;
bt->il_vsync = bt->vsync + 1;
bt->pixelclock /= 2;
}
if (video_fmt == BUS_FMT_YUV420) {
if (format == 5) {
bt->pixelclock = bt->pixelclock * 8 * 2;
do_div(bt->pixelclock, 10);
} else {
bt->pixelclock *= 2;
}
}
if (vact == 1080 && vtotal > 1500)
goto TIMING_ERR;
rk628_dbg(rk628, "SCDC_REGS1:%#x, act:%dx%d, total:%dx%d, fps:%d, pixclk:%llu\n",
status, hact, vact, htotal, vtotal, fps, bt->pixelclock);
return 0;
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)
{
int ret, i;
for (i = 0; i < 5; i++) {
ret = rk628_hdmirx_read_timing(rk628, timings);
if (!ret)
return ret;
msleep(20);
}
return ret;
}
int rk628_hdmirx_get_timings(struct rk628 *rk628,
struct v4l2_dv_timings *timings)
{
int i, cnt = 0, ret = 0;
u32 last_w, last_h;
u8 last_fmt;
struct v4l2_bt_timings *bt = &timings->bt;
last_w = 0;
last_h = 0;
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;
last_h = bt->height;
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;
else
cnt++;
if (cnt >= 8)
break;
last_w = bt->width;
last_h = bt->height;
last_fmt = rk628_hdmirx_get_format(rk628);
usleep_range(10*1000, 10*1100);
}
if (cnt < 8) {
dev_info(rk628->dev, "%s: res not stable!\n", __func__);
ret = -EINVAL;
}
return ret;
}
EXPORT_SYMBOL(rk628_hdmirx_get_timings);
u8 rk628_hdmirx_get_range(struct rk628 *rk628)
{
u8 color_range, yuv_range;
u32 val, vic, fmt, avi_hb;
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val);
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_HB, &avi_hb);
color_range = (val & RGB_COLORRANGE_MASK) >> 18;
yuv_range = (avi_hb & YUV_COLORRANGE_MASK) >> 30;
vic = (val & VID_IDENT_CODE_MASK) >> 24;
fmt = (val & VIDEO_FORMAT_MASK) >> 5;
if (fmt != HDMIRX_RGB888) {
if (yuv_range == HDMIRX_YCC_LIMIT)
color_range = HDMIRX_LIMIT_RANGE;
else if (yuv_range == HDMIRX_YCC_FULL)
color_range = HDMIRX_FULL_RANGE;
else
color_range = HDMIRX_DEFAULT_RANGE;
}
if (fmt == HDMIRX_RGB888 && color_range == HDMIRX_DEFAULT_RANGE) {
(vic) ?
(color_range = HDMIRX_LIMIT_RANGE) :
(color_range = HDMIRX_FULL_RANGE);
}
return color_range;
}
EXPORT_SYMBOL(rk628_hdmirx_get_range);
u8 rk628_hdmirx_get_color_space(struct rk628 *rk628)
{
u32 val, EC2_0, C1_C0, fmt;
u8 color_space;
rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &val);
EC2_0 = (val & EXT_COLORIMETRY_MASK) >> 20;
C1_C0 = (val & COLORIMETRY_MASK) >> 14;
fmt = (val & VIDEO_FORMAT_MASK) >> 5;
if (HDMIRX_RGB888 == fmt) {
if (HDMIRX_ADOBE_RGB == EC2_0 ||
HDMIRX_BT2020_RGB_OR_YCC == EC2_0)
color_space = EC2_0;
else
color_space = HDMIRX_RGB;
} else {
switch (C1_C0) {
case 0:
color_space = HDMIRX_XVYCC709;
break;
case 1:
color_space = HDMIRX_XVYCC601;
break;
case 2:
color_space = HDMIRX_XVYCC709;
break;
default:
color_space = EC2_0;
break;
}
}
return color_space;
}
EXPORT_SYMBOL(rk628_hdmirx_get_color_space);
void rk628_hdmirx_controller_reset(struct rk628 *rk628)
{
mutex_lock(&rk628->rst_lock);
rk628_control_assert(rk628, RGU_HDMIRX);
rk628_control_assert(rk628, RGU_HDMIRX_PON);
udelay(10);
rk628_control_deassert(rk628, RGU_HDMIRX);
rk628_control_deassert(rk628, RGU_HDMIRX_PON);
udelay(10);
rk628_i2c_write(rk628, HDMI_RX_DMI_SW_RST, 0x000101ff);
rk628_i2c_write(rk628, HDMI_RX_DMI_DISABLE_IF, 0x00000000);
rk628_i2c_write(rk628, HDMI_RX_DMI_DISABLE_IF, 0x0000017f);
rk628_i2c_write(rk628, HDMI_RX_DMI_DISABLE_IF, 0x0001017f);
mutex_unlock(&rk628->rst_lock);
}
EXPORT_SYMBOL(rk628_hdmirx_controller_reset);
bool rk628_hdmirx_scdc_ced_err(struct rk628 *rk628)
{
u32 val, val1;
if (rk628->version < RK628F_VERSION)
return false;
rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val);
rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS2, &val1);
if (((val >> 15) & SCDC_ERRDET_MASK) < SCDC_CED_ERR_CNT &&
((val1 >> 15) & SCDC_ERRDET_MASK) < SCDC_CED_ERR_CNT &&
(val1 & SCDC_ERRDET_MASK) < SCDC_CED_ERR_CNT)
return false;
dev_info(rk628->dev, "%s: Character Error(0x%x 0x%x)!\n", __func__, val, val1);
return true;
}
EXPORT_SYMBOL(rk628_hdmirx_scdc_ced_err);
bool rk628_hdmirx_is_locked(struct rk628 *rk628)
{
u32 val;
rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val);
if (!(val & 0x100))
return false;
if (!(val & 0x200))
return false;
if (!(val & 0x400))
return false;
if (!(val & 0x800))
return false;
return true;
}
EXPORT_SYMBOL(rk628_hdmirx_is_locked);
bool rk628_hdmirx_is_signal_change_ists(struct rk628 *rk628, u32 md_ints, u32 pdec_ints)
{
u32 md_mask, pded_madk;
u8 video_fmt, color_range, color_space;
md_mask = VACT_LIN_ISTS | HACT_PIX_ISTS |
HS_CLK_ISTS | DE_ACTIVITY_ISTS |
VS_ACT_ISTS | HS_ACT_ISTS | VS_CLK_ISTS;
if (md_ints & md_mask)
return true;
pded_madk = AVI_CKS_CHG_ISTS;
if (pdec_ints & pded_madk) {
video_fmt = rk628_hdmirx_get_format(rk628);
if (rk628->color_format != video_fmt)
return true;
color_range = rk628_hdmirx_get_range(rk628);
if (rk628->color_range != color_range)
return true;
color_space = rk628_hdmirx_get_color_space(rk628);
if (rk628->color_space != color_space)
return true;
}
return false;
}
EXPORT_SYMBOL(rk628_hdmirx_is_signal_change_ists);
static int rk628_hdmirx_phy_reg_show(struct seq_file *s, void *v)
{
struct rk628 *rk628 = s->private;
unsigned int i;
seq_printf(s, "rk628_%s:\n", file_dentry(s->file)->d_iname);
for (i = 0; i <= 0xb7; i++)
seq_printf(s, "0x%02x: %08x\n", i, hdmirxphy_read(rk628, i));
return 0;
}
static ssize_t rk628_hdmirx_phy_reg_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct rk628 *rk628 = file->f_path.dentry->d_inode->i_private;
u32 addr;
u32 val;
char kbuf[25];
int ret;
if (count >= sizeof(kbuf))
return -ENOSPC;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
kbuf[count] = '\0';
ret = sscanf(kbuf, "%x%x", &addr, &val);
if (ret != 2)
return -EINVAL;
if (addr > 0xb7)
return -EINVAL;
hdmirxphy_write(rk628, addr, val);
return count;
}
static int rk628_hdmirx_phy_reg_open(struct inode *inode, struct file *file)
{
struct rk628 *rk628 = inode->i_private;
return single_open(file, rk628_hdmirx_phy_reg_show, rk628);
}
static const struct file_operations rk628_hdmirx_phy_reg_fops = {
.owner = THIS_MODULE,
.open = rk628_hdmirx_phy_reg_open,
.read = seq_read,
.write = rk628_hdmirx_phy_reg_write,
.llseek = seq_lseek,
.release = single_release,
};
void rk628_hdmirx_phy_debugfs_register_create(struct rk628 *rk628, struct dentry *dir)
{
if (rk628->version < RK628F_VERSION)
return;
if (IS_ERR(dir))
return;
debugfs_create_file("hdmirxphy", 0600, dir, rk628, &rk628_hdmirx_phy_reg_fops);
}
EXPORT_SYMBOL(rk628_hdmirx_phy_debugfs_register_create);
static int rk628_hdmirx_hdcp_enable_show(struct seq_file *s, void *v)
{
struct rk628_hdcp *hdcp = s->private;
seq_printf(s, "%d\n", hdcp->enable);
return 0;
}
static ssize_t rk628_hdmirx_hdcp_enable_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct rk628_hdcp *hdcp = file->f_path.dentry->d_inode->i_private;
char kbuf[25];
int enable;
if (!hdcp || !hdcp->rk628)
return -EINVAL;
if (count >= sizeof(kbuf))
return -ENOSPC;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
kbuf[count] = '\0';
if (kstrtoint(kbuf, 10, &enable))
return -EINVAL;
rk628_hdmirx_set_hdcp(hdcp->rk628, hdcp, enable);
return count;
}
static int rk628_hdmirx_hdcp_enable_open(struct inode *inode, struct file *file)
{
struct rk628_hdcp *hdcp = inode->i_private;
return single_open(file, rk628_hdmirx_hdcp_enable_show, hdcp);
}
static const struct file_operations rk628_hdmirx_hdcp_enable_fops = {
.owner = THIS_MODULE,
.open = rk628_hdmirx_hdcp_enable_open,
.read = seq_read,
.write = rk628_hdmirx_hdcp_enable_write,
.llseek = seq_lseek,
.release = single_release,
};
static void rk628_hdmirx_hdcp_enable_node(struct rk628_hdcp *hdcp, struct dentry *dir)
{
if (IS_ERR(dir))
return;
debugfs_create_file("enable", 0600, dir, hdcp, &rk628_hdmirx_hdcp_enable_fops);
}
static int rk628_hdmirx_hdcp_status_show(struct seq_file *s, void *v)
{
struct rk628_hdcp *hdcp = s->private;
struct rk628 *rk628 = hdcp->rk628;
u32 val;
if (!rk628 || !hdcp->enable) {
seq_puts(s, "HDCP Disable\n");
return 0;
}
rk628_i2c_read(rk628, HDMI_RX_HDCP_STS, &val);
if (val & (HDCP_ENC_STATE | HDCP_AUTH_START))
seq_puts(s, "HDCP Authenticated success\n");
else if (val & HDCP_ENC_STATE)
seq_puts(s, "HDCP Authenticated failed\n");
else
seq_puts(s, "HDCP Source No encrypted\n");
return 0;
}
static int rk628_hdmirx_hdcp_status_open(struct inode *inode, struct file *file)
{
struct rk628_hdcp *hdcp = inode->i_private;
return single_open(file, rk628_hdmirx_hdcp_status_show, hdcp);
}
static const struct file_operations rk628_hdmirx_hdcp_status_fops = {
.owner = THIS_MODULE,
.open = rk628_hdmirx_hdcp_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void rk628_hdmirx_hdcp_status_node(struct rk628_hdcp *hdcp, struct dentry *dir)
{
if (IS_ERR(dir))
return;
debugfs_create_file("status", 0600, dir, hdcp, &rk628_hdmirx_hdcp_status_fops);
}
static int rk628_hdmirx_status_show(struct seq_file *s, void *v)
{
struct rk628 *rk628 = s->private;
struct v4l2_dv_timings timings;
struct v4l2_bt_timings *bt = &timings.bt;
bool plugin;
u32 val, htot, vtot, fps, format;
u8 fmt, range, space;
plugin = rk628_hdmirx_tx_5v_power_detect(rk628->hdmirx_det_gpio);
seq_printf(s, "status: %s\n", plugin ? "plugin" : "plugout");
if (!plugin)
return 0;
rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val);
seq_puts(s, "Clk-Ch:");
if (val & 0x100)
seq_puts(s, "Lock\t");
else
seq_puts(s, "Unlock\t");
seq_puts(s, "Ch0:");
if (val & 0x200)
seq_puts(s, "Lock\t");
else
seq_puts(s, "Unlock\t");
seq_puts(s, "Ch1:");
if (val & 0x400)
seq_puts(s, "Lock\t");
else
seq_puts(s, "Unlock\t");
seq_puts(s, "Ch2:");
if (val & 0x800)
seq_puts(s, "Lock\n");
else
seq_puts(s, "Unlock\n");
fmt = rk628_hdmirx_get_format(rk628);
seq_printf(s, "Color Format: %s\n", bus_format_str[fmt]);
rk628_hdmirx_read_timing(rk628, &timings);
htot = bt->width + bt->hfrontporch + bt->hsync + bt->hbackporch;
vtot = bt->height + bt->vfrontporch + bt->vsync + bt->vbackporch;
fps = div_u64(bt->pixelclock, (htot * vtot));
seq_printf(s, "Timing: %ux%u%s%u (%ux%u)",
bt->width, bt->height, bt->interlaced ? "i" : "p",
fps, htot, vtot);
seq_printf(s, "\t\thfp:%d hs:%d hbp:%d vfp:%d vs:%d vbp:%d\n",
bt->hfrontporch, bt->hsync, bt->hbackporch,
bt->vfrontporch, bt->vsync, bt->vbackporch);
seq_printf(s, "Pixel Clk: %llu\n", bt->pixelclock);
rk628_i2c_read(rk628, HDMI_RX_PDEC_STS, &val);
seq_printf(s, "Mode: %s\n", (val & DVI_DET) ? "DVI" : "HDMI");
rk628_i2c_read(rk628, HDMI_RX_PDEC_GCP_AVMUTE, &format);
format = (format & PKTDEC_GCP_CD_MASK) >> 4;
seq_printf(s, "Color Depth: %u bit\n", format == 5 ? 10 : 8);
range = rk628_hdmirx_get_range(rk628);
seq_puts(s, "Color Range: ");
seq_printf(s, "%s\n", bus_color_range_str[range]);
space = rk628_hdmirx_get_color_space(rk628);
seq_puts(s, "Color Space: ");
if (space < 8)
seq_printf(s, "%s\n", bus_color_space_str[space]);
else
seq_puts(s, "Unknown\n");
return 0;
}
static int rk628_hdmirx_status_open(struct inode *inode, struct file *file)
{
struct rk628 *rk628 = inode->i_private;
return single_open(file, rk628_hdmirx_status_show, rk628);
}
static const struct file_operations rk628_hdmirx_status_fops = {
.owner = THIS_MODULE,
.open = rk628_hdmirx_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void rk628_hdmirx_status_node(struct rk628 *rk628, struct dentry *dir)
{
if (IS_ERR(dir))
return;
debugfs_create_file("status", 0600, dir, rk628, &rk628_hdmirx_status_fops);
}
void rk628_hdmirx_debugfs_create(struct rk628 *rk628, struct rk628_hdcp *hdcp)
{
struct dentry *hdmirx_dir, *dir;
if (IS_ERR(rk628->debug_dir))
return;
hdmirx_dir = debugfs_create_dir("hdmirx", rk628->debug_dir);
if (IS_ERR(hdmirx_dir))
return;
dir = debugfs_create_dir("hdcp", hdmirx_dir);
if (IS_ERR(dir))
return;
rk628_hdmirx_status_node(rk628, hdmirx_dir);
rk628_hdmirx_hdcp_enable_node(hdcp, dir);
rk628_hdmirx_hdcp_status_node(hdcp, dir);
}
EXPORT_SYMBOL(rk628_hdmirx_debugfs_create);