// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 Rockchip Electronics Co., Ltd. * * Author: Shunqing Chen */ #include #include #include #include #include #include #include #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);