summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorSandor Yu <Sandor.yu@nxp.com>2018-05-10 11:25:40 +0800
committerJason Liu <jason.hui.liu@nxp.com>2018-10-29 11:10:38 +0800
commitf46db91ee73f882abd82a32aeaf7ab661158c0af (patch)
tree9e2596c6d38cf348f6323a7ef9a3611cbb5b7f9a /drivers/media/platform
parent8ec22b9030fe5bbe09695b64912c2bab1f4bd586 (diff)
MLK-18267-4: hdmi rx: hdmi rx driver for imx8qm
Enable hdmi rx driver for imx8qm. Driver implement with V4L2 architecture. RGB32 and YUV444 are verified. Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/imx8/Kconfig2
-rw-r--r--drivers/media/platform/imx8/Makefile1
-rw-r--r--drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.c1223
-rw-r--r--drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.h140
-rw-r--r--drivers/media/platform/imx8/hdmi/Kconfig4
-rw-r--r--drivers/media/platform/imx8/hdmi/Makefile1
-rw-r--r--drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c414
-rw-r--r--drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c874
-rw-r--r--drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h131
-rw-r--r--drivers/media/platform/imx8/mxc-isi-cap.c10
-rw-r--r--drivers/media/platform/imx8/mxc-isi-core.c3
-rw-r--r--drivers/media/platform/imx8/mxc-isi-core.h1
-rw-r--r--drivers/media/platform/imx8/mxc-isi-hw.c92
-rw-r--r--drivers/media/platform/imx8/mxc-isi-hw.h2
-rw-r--r--drivers/media/platform/imx8/mxc-media-dev.c53
-rw-r--r--drivers/media/platform/imx8/mxc-media-dev.h13
16 files changed, 2900 insertions, 64 deletions
diff --git a/drivers/media/platform/imx8/Kconfig b/drivers/media/platform/imx8/Kconfig
index dcef1e20faa4..b8c4a985812c 100644
--- a/drivers/media/platform/imx8/Kconfig
+++ b/drivers/media/platform/imx8/Kconfig
@@ -49,6 +49,8 @@ config IMX8_JPEG
select VIDEOBUF2_DMA_CONTIG
default y
+source "drivers/media/platform/imx8/hdmi/Kconfig"
+
endmenu
endif #VIDEO_MX8_CAPTURE
diff --git a/drivers/media/platform/imx8/Makefile b/drivers/media/platform/imx8/Makefile
index 1cd16ee765fd..892a53c91f18 100644
--- a/drivers/media/platform/imx8/Makefile
+++ b/drivers/media/platform/imx8/Makefile
@@ -11,3 +11,4 @@ max9286_gmsl-objs := max9286.o
obj-$(CONFIG_GMSL_MAX9286) += max9286_gmsl.o
obj-$(CONFIG_IMX8_MEDIA_DEVICE) += mxc-media-dev.o
obj-$(CONFIG_IMX8_JPEG) += mxc-jpeg-hw.o mxc-jpeg.o
+obj-$(CONFIG_IMX8_HDMI_RX) += hdmi/
diff --git a/drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.c b/drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.c
new file mode 100644
index 000000000000..77434120b003
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.c
@@ -0,0 +1,1223 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2016-2017 Cadence Design Systems, Inc.
+ * All rights reserved worldwide.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright 2018 NXP
+ *
+ ******************************************************************************
+ *
+ * API_AFE_ss28fdsoi_hdmirx.c
+ *
+ ******************************************************************************
+ */
+
+#include "API_AFE_ss28fdsoi_hdmirx.h"
+
+static inline void write16(state_struct *state, u32 addr, u16 val)
+{
+ Afe_write(state, addr, val);
+}
+
+static inline void multi_write16(state_struct *state, u32 addr,
+ u16 val)
+{
+ u16 addr_tmp = addr;
+
+ if ((addr & 0x1E00) == LINK_ID << 9) {
+ addr_tmp |= LINK_WRITE;
+ Afe_write(state, addr_tmp, val);
+ } else {
+ Afe_write(state, addr_tmp, val);
+ }
+}
+
+static inline u16 read16(state_struct *state, u32 addr)
+{
+ u16 reg_val;
+
+ reg_val = Afe_read(state, addr);
+ return reg_val;
+}
+
+u16 inside_i(u16 value, u16 left_sharp_corner,
+ u16 right_sharp_corner)
+{
+ if (value < left_sharp_corner)
+ return 0;
+ if (value > right_sharp_corner)
+ return 0;
+ return 1;
+}
+
+u16 inside_f(u32 value, u32 left_sharp_corner, u32 right_sharp_corner)
+{
+ if (value < left_sharp_corner)
+ return 0;
+ if (value > right_sharp_corner)
+ return 0;
+ return 1;
+}
+
+void arc_config(state_struct *state)
+{
+ u16 reg_val;
+
+ write16(state, TX_DIG_CTRL_REG_2_ADDR, 0x0024);
+
+ reg_val = read16(state, TX_ANA_CTRL_REG_1_ADDR);
+ reg_val |= 0x2000;
+ write16(state, TX_ANA_CTRL_REG_1_ADDR, reg_val);
+
+ write16(state, TX_ANA_CTRL_REG_2_ADDR, 0x0100);
+ write16(state, TX_ANA_CTRL_REG_2_ADDR, 0x0300);
+ write16(state, TX_ANA_CTRL_REG_3_ADDR, 0x0000);
+ write16(state, TX_ANA_CTRL_REG_1_ADDR, 0x2008);
+ write16(state, TX_ANA_CTRL_REG_1_ADDR, 0x2018);
+ write16(state, TX_ANA_CTRL_REG_1_ADDR, 0x2098);
+ write16(state, TX_ANA_CTRL_REG_2_ADDR, 0x030C);
+ write16(state, TX_ANA_CTRL_REG_5_ADDR, 0x0000);
+ write16(state, TX_ANA_CTRL_REG_4_ADDR, 0x4001);
+ write16(state, TX_ANA_CTRL_REG_1_ADDR, 0x2198);
+ write16(state, TX_ANA_CTRL_REG_2_ADDR, 0x030D);
+ write16(state, TX_ANA_CTRL_REG_2_ADDR, 0x030F);
+}
+
+void pma_config(state_struct *state)
+{
+ int i;
+ u16 reg_val = 0;
+ pr_info("pma_config() Configuring PMA\n");
+
+ write16(state, CMN_CMSMT_REF_CLK_TMR_VALUE_ADDR, 0x0801);
+ write16(state, RX_CLK_SLICER_CAL_INIT_TMR_ADDR, 0x00FF);
+ write16(state, CMN_RXCAL_INIT_TMR_ADDR, 0x003F);
+ write16(state, CMN_DIAG_PLL0_TEST_MODE_ADDR, 0x0022);
+ multi_write16(state, XCVR_PSM_CAL_TMR_ADDR, 0x0160);
+
+ /* Drives the rx_differential_invert PMA input for the associated lane */
+ for (i = 0; i < 3; i++) {
+ reg_val = 0x0c61;
+ write16(state, (PHY_PMA_XCVR_CTRL_ADDR | (i << 6)), reg_val);
+ }
+}
+
+void pre_data_rate_change(state_struct *state)
+{
+ u16 reg_val;
+
+ pr_info("pre_data_rate_change() Set the A3 power mode\n");
+ reg_val = read16(state, PHY_MODE_CTL_ADDR);
+ reg_val &= 0xFFF0;
+ reg_val |= 0x0008;
+ write16(state, PHY_MODE_CTL_ADDR, reg_val);
+
+ msleep(20);
+
+ /* Disable PLL */
+ pr_info("pre_data_rate_change() Disable PLL0\n");
+ reg_val = read16(state, PHY_MODE_CTL_ADDR);
+ reg_val &= 0xEFFF;
+ write16(state, PHY_MODE_CTL_ADDR, reg_val);
+
+ msleep(20);
+}
+
+u8 pma_cmn_ready(state_struct *state)
+{
+ u32 i;
+
+ for (i = 0; i < 20; i++) {
+ if (read16(state, PHY_PMA_CMN_CTRL1_ADDR) & (1 << 0))
+ break;
+ msleep(10);
+ }
+ if (i == 20)
+ return -1;
+ return 0;
+}
+
+u8 pma_rx_clk_signal_detect(state_struct *state)
+{
+ u32 i;
+
+ for (i = 0; i < 20; i++) {
+ if (read16(state, PHY_MODE_CTL_ADDR) & (1 << 8))
+ break;
+ msleep(10);
+ }
+ if (i == 20)
+ return -1;
+ return 0;
+}
+
+u32 pma_rx_clk_freq_detect(state_struct *state)
+{
+ u16 reg_val;
+ u32 rx_clk_freq;
+ u32 i;
+
+ pr_info("pma_rx_clk_freq_detect() Starting Rx clock frequency detection...\n");
+
+ /* Start frequency detection: */
+ write16(state, CMN_CMSMT_CLK_FREQ_MSMT_CTRL_ADDR, 0x8000);
+
+ /* Wait for pma_rx_clk_freq_detect_done */
+ for (i = 0; i < 20; i++) {
+ if (read16(state, CMN_CMSMT_CLK_FREQ_MSMT_CTRL_ADDR) & (1 << 14))
+ break;
+ msleep(10);
+ }
+ if (i == 20)
+ return -1;
+
+ /* Read the measured value */
+ reg_val = read16(state, CMN_CMSMT_TEST_CLK_CNT_VALUE_ADDR);
+
+ /* Calculate TMDS clock frequency */
+ rx_clk_freq = reg_val * REFCLK_FREQ_KHZ / 2048;
+
+ /* Turn off frequency measurement: */
+ write16(state, CMN_CMSMT_CLK_FREQ_MSMT_CTRL_ADDR, 0x0000);
+ pr_info("pma_rx_clk_freq_detect() Starting Rx clock frequency detection... DONE (TMDS clock freq: %d kHz)\n",
+ rx_clk_freq);
+ return rx_clk_freq;
+}
+
+void pma_pll_config(state_struct *state,
+ u32 rx_clk_freq,
+ clk_ratio_t clk_ratio,
+ tmds_bit_clock_ratio_t tmds_bit_clk_ratio,
+ unsigned char data_rate_change)
+{
+ int i, loop;
+ u16 reg_val;
+ u64 vco_freq_khz;
+
+ reg_field_t cmnda_pll0_ip_div;
+ reg_field_t cmnda_pll0_hs_sym_div_sel;
+ reg_field_t cmn_pll0_fb_div_high_ovrd_en;
+ reg_field_t cmnda_pll0_fb_div_high_out;
+ reg_field_t cmn_pll0_fb_div_low_ovrd_en;
+ reg_field_t cmnda_pll0_fb_div_low_out;
+ reg_field_t cmn_pll_clk_osr;
+ reg_field_t cmn_pll_clk_div2_ratio;
+ reg_field_t cmn_pll_clk_div2_sel;
+ reg_field_t rx_diag_smplr_osr;
+ reg_field_t rx_psc_a0;
+ reg_field_t rx_ree_pergcsm_eqenm_ph1;
+ reg_field_t rx_ree_pergcsm_eqenm_ph2;
+ reg_field_t vga_gain_accum_override_en;
+ reg_field_t vga_gain_accum_override;
+ reg_field_t vga_gain_tgt_adj_override_en;
+ reg_field_t vga_gain_tgt_adj_override;
+ reg_field_t ree_gen_sm_en_usb;
+ reg_field_t ree_gen_sm_en_periodic;
+ reg_field_t ana_en_epath_gen_ctrl_sm_usb;
+ reg_field_t ana_en_epath_gen_ctrl_sm_periodic;
+ reg_field_t rxda_eq_range_sel;
+ reg_field_t rxda_vga_sa_range_sel;
+ reg_field_t vco_ring_select;
+ reg_field_t cmnda_pll0_v2i_prog;
+ reg_field_t cmnda_pll0_coarse_prog;
+ reg_field_t cmnda_pll0_cp_gain;
+ reg_field_t cmnda_pll0_const_ndac_cntrl;
+ reg_field_t cmnda_pll0_const_pmos_cntrl;
+ reg_field_t cmnda_pll0_ptat_ndac_cntrl;
+ reg_field_t rxda_pi_iq_bias_trim;
+ reg_field_t rxda_pi_iq_pload_bias_trim;
+ reg_field_t rxda_pi_iq_pload_trim;
+ reg_field_t rxda_pi_e_bias_trim;
+ reg_field_t rxda_pi_e_pload_bias_trim;
+ reg_field_t rxda_pi_e_pload_trim;
+ reg_field_t rxda_pi_range_sel;
+ reg_field_t rxda_pi_cal_cm_trim;
+ reg_field_t xcvr_pll_en;
+ reg_field_t xcvr_link_reset_n;
+ reg_field_t xcvr_power_state_req;
+ reg_field_t xcvr_power_state_ack;
+ reg_field_t iso_pma_cmn_pll0_clk_datart1_div;
+ reg_field_t iso_pma_cmn_pll0_clk_datart0_div;
+ reg_field_t iso_pma_cmn_pll0_clk_en;
+
+ /* Set fields' labels */
+ cmnda_pll0_ip_div.label = "cmnda_pll0_ip_div";
+ cmnda_pll0_hs_sym_div_sel.label = "cmnda_pll0_hs_sym_div_sel";
+ cmn_pll0_fb_div_high_ovrd_en.label = "cmn_pll0_fb_div_high_ovrd_en";
+ cmnda_pll0_fb_div_high_out.label = "cmnda_pll0_fb_div_high_out";
+ cmn_pll0_fb_div_low_ovrd_en.label = "cmn_pll0_fb_div_low_ovrd_en";
+ cmnda_pll0_fb_div_low_out.label = "cmnda_pll0_fb_div_low_out";
+ cmn_pll_clk_osr.label = "cmn_pll_clk_osr";
+ cmn_pll_clk_div2_ratio.label = "cmn_pll_clk_div2_ratio";
+ cmn_pll_clk_div2_sel.label = "cmn_pll_clk_div2_sel";
+ rx_diag_smplr_osr.label = "rx_diag_smplr_osr";
+ rx_psc_a0.label = "rx_psc_a0";
+ rx_ree_pergcsm_eqenm_ph1.label = "rx_ree_pergcsm_eqenm_ph1";
+ rx_ree_pergcsm_eqenm_ph2.label = "rx_ree_pergcsm_eqenm_ph2";
+ vga_gain_accum_override_en.label = "vga_gain_accum_override_en";
+ vga_gain_accum_override.label = "vga_gain_accum_override";
+ vga_gain_tgt_adj_override_en.label = "vga_gain_tgt_adj_override_en";
+ vga_gain_tgt_adj_override.label = "vga_gain_tgt_adj_override";
+ ree_gen_sm_en_usb.label = "ree_gen_sm_en_usb";
+ ree_gen_sm_en_periodic.label = "ree_gen_sm_en_periodic";
+ ana_en_epath_gen_ctrl_sm_usb.label = "ana_en_epath_gen_ctrl_sm_usb";
+ ana_en_epath_gen_ctrl_sm_periodic.label =
+ "ana_en_epath_gen_ctrl_sm_periodic";
+ rxda_eq_range_sel.label = "rxda_eq_range_sel";
+ rxda_vga_sa_range_sel.label = "rxda_vga_sa_range_sel";
+ vco_ring_select.label = "vco_ring_select";
+ cmnda_pll0_v2i_prog.label = "cmnda_pll0_v2i_prog";
+ cmnda_pll0_coarse_prog.label = "cmnda_pll0_coarse_prog";
+ cmnda_pll0_cp_gain.label = "cmnda_pll0_cp_gain";
+ cmnda_pll0_const_ndac_cntrl.label = "cmnda_pll0_const_ndac_cntrl";
+ cmnda_pll0_const_pmos_cntrl.label = "cmnda_pll0_const_pmos_cntrl";
+ cmnda_pll0_ptat_ndac_cntrl.label = "cmnda_pll0_ptat_ndac_cntrl";
+ rxda_pi_iq_bias_trim.label = "rxda_pi_iq_bias_trim";
+ rxda_pi_iq_pload_bias_trim.label = "rxda_pi_iq_pload_bias_trim";
+ rxda_pi_iq_pload_trim.label = "rxda_pi_iq_pload_trim";
+ rxda_pi_e_bias_trim.label = "rxda_pi_e_bias_trim";
+ rxda_pi_e_pload_bias_trim.label = "rxda_pi_e_pload_bias_trim";
+ rxda_pi_e_pload_trim.label = "rxda_pi_e_pload_trim";
+ rxda_pi_range_sel.label = "rxda_pi_range_sel";
+ rxda_pi_cal_cm_trim.label = "rxda_pi_cal_cm_trim";
+ xcvr_pll_en.label = "xcvr_pll_en";
+ xcvr_link_reset_n.label = "xcvr_link_reset_n";
+ xcvr_power_state_req.label = "xcvr_power_state_req";
+ xcvr_power_state_ack.label = "xcvr_power_state_ack";
+ iso_pma_cmn_pll0_clk_datart1_div.label = "iso_pma_cmn_pll0_clk_datart1_div";
+ iso_pma_cmn_pll0_clk_datart0_div.label = "iso_pma_cmn_pll0_clk_datart0_div";
+ iso_pma_cmn_pll0_clk_en.label = "iso_pma_cmn_pll0_clk_en";
+
+ /* Set field position in a target register */
+ cmnda_pll0_ip_div.msb = 7;
+ cmnda_pll0_ip_div.lsb = 0;
+ cmnda_pll0_hs_sym_div_sel.msb = 9;
+ cmnda_pll0_hs_sym_div_sel.lsb = 8;
+ cmn_pll0_fb_div_high_ovrd_en.msb = 15;
+ cmn_pll0_fb_div_high_ovrd_en.lsb = 15;
+ cmnda_pll0_fb_div_high_out.msb = 9;
+ cmnda_pll0_fb_div_high_out.lsb = 0;
+ cmn_pll0_fb_div_low_ovrd_en.msb = 15;
+ cmn_pll0_fb_div_low_ovrd_en.lsb = 15;
+ cmnda_pll0_fb_div_low_out.msb = 9;
+ cmnda_pll0_fb_div_low_out.lsb = 0;
+ cmn_pll_clk_osr.msb = 2;
+ cmn_pll_clk_osr.lsb = 0;
+ cmn_pll_clk_div2_ratio.msb = 6;
+ cmn_pll_clk_div2_ratio.lsb = 4;
+ cmn_pll_clk_div2_sel.msb = 9;
+ cmn_pll_clk_div2_sel.lsb = 8;
+ rx_diag_smplr_osr.msb = 2;
+ rx_diag_smplr_osr.lsb = 0;
+ rx_psc_a0.msb = 15;
+ rx_psc_a0.lsb = 0;
+ rx_ree_pergcsm_eqenm_ph1.msb = 15;
+ rx_ree_pergcsm_eqenm_ph1.lsb = 0;
+ rx_ree_pergcsm_eqenm_ph2.msb = 15;
+ rx_ree_pergcsm_eqenm_ph2.lsb = 0;
+ vga_gain_accum_override_en.msb = 7;
+ vga_gain_accum_override_en.lsb = 7;
+ vga_gain_accum_override.msb = 4;
+ vga_gain_accum_override.lsb = 0;
+ vga_gain_tgt_adj_override_en.msb = 15;
+ vga_gain_tgt_adj_override_en.lsb = 15;
+ vga_gain_tgt_adj_override.msb = 12;
+ vga_gain_tgt_adj_override.lsb = 8;
+ ree_gen_sm_en_usb.msb = 3;
+ ree_gen_sm_en_usb.lsb = 0;
+ ree_gen_sm_en_periodic.msb = 11;
+ ree_gen_sm_en_periodic.lsb = 8;
+ ana_en_epath_gen_ctrl_sm_usb.msb = 7;
+ ana_en_epath_gen_ctrl_sm_usb.lsb = 4;
+ ana_en_epath_gen_ctrl_sm_periodic.msb = 15;
+ ana_en_epath_gen_ctrl_sm_periodic.lsb = 12;
+ rxda_eq_range_sel.msb = 2;
+ rxda_eq_range_sel.lsb = 0;
+ rxda_vga_sa_range_sel.msb = 6;
+ rxda_vga_sa_range_sel.lsb = 4;
+ vco_ring_select.msb = 12;
+ vco_ring_select.lsb = 12;
+ cmnda_pll0_v2i_prog.msb = 5;
+ cmnda_pll0_v2i_prog.lsb = 4;
+ cmnda_pll0_coarse_prog.msb = 2;
+ cmnda_pll0_coarse_prog.lsb = 0;
+ cmnda_pll0_cp_gain.msb = 8;
+ cmnda_pll0_cp_gain.lsb = 0;
+ cmnda_pll0_const_ndac_cntrl.msb = 11;
+ cmnda_pll0_const_ndac_cntrl.lsb = 8;
+ cmnda_pll0_const_pmos_cntrl.msb = 7;
+ cmnda_pll0_const_pmos_cntrl.lsb = 0;
+ cmnda_pll0_ptat_ndac_cntrl.msb = 5;
+ cmnda_pll0_ptat_ndac_cntrl.lsb = 0;
+ rxda_pi_iq_bias_trim.msb = 14;
+ rxda_pi_iq_bias_trim.lsb = 12;
+ rxda_pi_iq_pload_bias_trim.msb = 10;
+ rxda_pi_iq_pload_bias_trim.lsb = 8;
+ rxda_pi_iq_pload_trim.msb = 7;
+ rxda_pi_iq_pload_trim.lsb = 0;
+ rxda_pi_e_bias_trim.msb = 14;
+ rxda_pi_e_bias_trim.lsb = 12;
+ rxda_pi_e_pload_bias_trim.msb = 10;
+ rxda_pi_e_pload_bias_trim.lsb = 8;
+ rxda_pi_e_pload_trim.msb = 7;
+ rxda_pi_e_pload_trim.lsb = 0;
+ rxda_pi_range_sel.msb = 11;
+ rxda_pi_range_sel.lsb = 8;
+ rxda_pi_cal_cm_trim.msb = 7;
+ rxda_pi_cal_cm_trim.lsb = 0;
+ xcvr_pll_en.msb = 12;
+ xcvr_pll_en.lsb = 12;
+ xcvr_link_reset_n.msb = 13;
+ xcvr_link_reset_n.lsb = 13;
+ xcvr_power_state_req.msb = 3;
+ xcvr_power_state_req.lsb = 0;
+ xcvr_power_state_ack.msb = 7;
+ xcvr_power_state_ack.lsb = 4;
+ iso_pma_cmn_pll0_clk_datart1_div.msb = 14;
+ iso_pma_cmn_pll0_clk_datart1_div.lsb = 11;
+ iso_pma_cmn_pll0_clk_datart0_div.msb = 10;
+ iso_pma_cmn_pll0_clk_datart0_div.lsb = 7;
+ iso_pma_cmn_pll0_clk_en.msb = 5;
+ iso_pma_cmn_pll0_clk_en.lsb = 5;
+
+ pr_info("pma_pll_config() Configuring PLL0 ...\n");
+
+ if (tmds_bit_clk_ratio == TMDS_BIT_CLOCK_RATIO_1_10) {
+ if (inside_f(rx_clk_freq, 18750, 37500)) {
+ set_field_value(&cmnda_pll0_ip_div, 0x1);
+ set_field_value(&cmnda_pll0_hs_sym_div_sel, 0x0);
+ set_field_value(&cmn_pll0_fb_div_high_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_high_out, 0x1E);
+ set_field_value(&cmn_pll0_fb_div_low_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_low_out, 0x7E);
+ set_field_value(&rx_diag_smplr_osr, 0x4);
+ set_field_value(&rx_psc_a0, 0x8BF5);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph1, 0x0080);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph2, 0x0080);
+ set_field_value(&vga_gain_accum_override_en, 0x1);
+ set_field_value(&vga_gain_accum_override, 0x1A);
+ set_field_value(&vga_gain_tgt_adj_override_en, 0x0);
+ set_field_value(&vga_gain_tgt_adj_override, 0x00);
+ set_field_value(&ree_gen_sm_en_usb, 0x1);
+ set_field_value(&ree_gen_sm_en_periodic, 0x1);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_usb, 0x0);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_periodic,
+ 0x0);
+ set_field_value(&rxda_eq_range_sel, 0x1);
+ set_field_value(&rxda_vga_sa_range_sel, 0x2);
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x0);
+ break;
+ case CLK_RATIO_5_4:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x4);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_2:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x5);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_2_1:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x3);
+ break;
+ case CLK_RATIO_1_2:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_5_8:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x1);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_4:
+ set_field_value(&cmn_pll_clk_osr, 0x4);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x2);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ }
+ } else if (inside_f(rx_clk_freq, 37500, 75000)) {
+ set_field_value(&cmnda_pll0_ip_div, 0x1);
+ set_field_value(&cmnda_pll0_hs_sym_div_sel, 0x0);
+ set_field_value(&cmn_pll0_fb_div_high_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_high_out, 0x0E);
+ set_field_value(&cmn_pll0_fb_div_low_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_low_out, 0x3E);
+ set_field_value(&rx_diag_smplr_osr, 0x3);
+ set_field_value(&rx_psc_a0, 0x8BF5);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph1, 0x0080);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph2, 0x0080);
+ set_field_value(&vga_gain_accum_override_en, 0x1);
+ set_field_value(&vga_gain_accum_override, 0x1A);
+ set_field_value(&vga_gain_tgt_adj_override_en, 0x0);
+ set_field_value(&vga_gain_tgt_adj_override, 0x00);
+ set_field_value(&ree_gen_sm_en_usb, 0x1);
+ set_field_value(&ree_gen_sm_en_periodic, 0x1);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_usb, 0x0);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_periodic,
+ 0x0);
+ set_field_value(&rxda_eq_range_sel, 0x1);
+ set_field_value(&rxda_vga_sa_range_sel, 0x2);
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x0);
+ break;
+ case CLK_RATIO_5_4:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x4);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_2:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x5);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_2_1:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x3);
+ break;
+ case CLK_RATIO_1_2:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_5_8:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x1);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_4:
+ set_field_value(&cmn_pll_clk_osr, 0x3);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x2);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ }
+ } else if (inside_f(rx_clk_freq, 75000, 150000)) {
+ set_field_value(&cmnda_pll0_ip_div, 0x1);
+ set_field_value(&cmnda_pll0_hs_sym_div_sel, 0x0);
+ set_field_value(&cmn_pll0_fb_div_high_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_high_out, 0x0A);
+ set_field_value(&cmn_pll0_fb_div_low_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_low_out, 0x1A);
+ set_field_value(&rx_diag_smplr_osr, 0x2);
+ set_field_value(&rx_psc_a0, 0x8BF5);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph1, 0x0080);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph2, 0x0080);
+ set_field_value(&vga_gain_accum_override_en, 0x1);
+ set_field_value(&vga_gain_accum_override, 0x1A);
+ set_field_value(&vga_gain_tgt_adj_override_en, 0x0);
+ set_field_value(&vga_gain_tgt_adj_override, 0x00);
+ set_field_value(&ree_gen_sm_en_usb, 0x1);
+ set_field_value(&ree_gen_sm_en_periodic, 0x1);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_usb, 0x0);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_periodic,
+ 0x0);
+ set_field_value(&rxda_eq_range_sel, 0x1);
+ set_field_value(&rxda_vga_sa_range_sel, 0x2);
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x0);
+ break;
+ case CLK_RATIO_5_4:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x4);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_2:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x5);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_2_1:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x3);
+ break;
+ case CLK_RATIO_1_2:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_5_8:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x1);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_4:
+ set_field_value(&cmn_pll_clk_osr, 0x2);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x2);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ }
+ } else if (inside_f(rx_clk_freq, 150000, 300000)) {
+ set_field_value(&cmnda_pll0_ip_div, 0x2);
+ set_field_value(&cmnda_pll0_hs_sym_div_sel, 0x0);
+ set_field_value(&cmn_pll0_fb_div_high_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_high_out, 0x0A);
+ set_field_value(&cmn_pll0_fb_div_low_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_low_out, 0x1A);
+ set_field_value(&rx_diag_smplr_osr, 0x1);
+ set_field_value(&rx_psc_a0, 0x8BF5);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph1, 0x0080);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph2, 0x0080);
+ set_field_value(&vga_gain_accum_override_en, 0x1);
+ set_field_value(&vga_gain_accum_override, 0x1A);
+ set_field_value(&vga_gain_tgt_adj_override_en, 0x0);
+ set_field_value(&vga_gain_tgt_adj_override, 0x00);
+ set_field_value(&ree_gen_sm_en_usb, 0x1);
+ set_field_value(&ree_gen_sm_en_periodic, 0x1);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_usb, 0x0);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_periodic,
+ 0x0);
+ set_field_value(&rxda_eq_range_sel, 0x2);
+ set_field_value(&rxda_vga_sa_range_sel, 0x3);
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x0);
+ break;
+ case CLK_RATIO_5_4:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x4);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_2:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x5);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_2_1:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x3);
+ break;
+ case CLK_RATIO_1_2:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_5_8:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x1);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_4:
+ set_field_value(&cmn_pll_clk_osr, 0x1);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x2);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ }
+ } else if (inside_f(rx_clk_freq, 300000, 340000)) {
+ set_field_value(&cmnda_pll0_ip_div, 0x3);
+ set_field_value(&cmnda_pll0_hs_sym_div_sel, 0x0);
+ set_field_value(&cmn_pll0_fb_div_high_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_high_out, 0x0A);
+ set_field_value(&cmn_pll0_fb_div_low_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_low_out, 0x10);
+ set_field_value(&rx_diag_smplr_osr, 0x0);
+ set_field_value(&rx_psc_a0, 0x8BF5);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph1, 0x0080);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph2, 0x0080);
+ set_field_value(&vga_gain_accum_override_en, 0x1);
+ set_field_value(&vga_gain_accum_override, 0x1A);
+ set_field_value(&vga_gain_tgt_adj_override_en, 0x0);
+ set_field_value(&vga_gain_tgt_adj_override, 0x00);
+ set_field_value(&ree_gen_sm_en_usb, 0x1);
+ set_field_value(&ree_gen_sm_en_periodic, 0x1);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_usb, 0x0);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_periodic,
+ 0x0);
+ set_field_value(&rxda_eq_range_sel, 0x3);
+ set_field_value(&rxda_vga_sa_range_sel, 0x3);
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x0);
+ break;
+ case CLK_RATIO_5_4:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x4);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_2:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x5);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_2_1:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x3);
+ break;
+ case CLK_RATIO_1_2:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_5_8:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x1);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_4:
+ set_field_value(&cmn_pll_clk_osr, 0x0);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x2);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ }
+ } else
+ pr_err("TMDS clock frequency (%d KHz) is out of range\n", rx_clk_freq);
+
+ } else { /* TMDS_BIT_CLOCK_RATIO_1_40 */
+ if (inside_f(rx_clk_freq, 85000, 150000)) {
+ set_field_value(&cmnda_pll0_ip_div, 0x1);
+ set_field_value(&cmnda_pll0_hs_sym_div_sel, 0x0);
+ set_field_value(&cmn_pll0_fb_div_high_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_high_out, 0x0A);
+ set_field_value(&cmn_pll0_fb_div_low_ovrd_en, 0x1);
+ set_field_value(&cmnda_pll0_fb_div_low_out, 0x1A);
+ set_field_value(&rx_diag_smplr_osr, 0x0);
+ set_field_value(&rx_psc_a0, 0x8BFD);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph1, 0x019F);
+ set_field_value(&rx_ree_pergcsm_eqenm_ph2, 0x019F);
+ set_field_value(&vga_gain_accum_override_en, 0x0);
+ set_field_value(&vga_gain_accum_override, 0x01);
+ set_field_value(&vga_gain_tgt_adj_override_en, 0x0);
+ set_field_value(&vga_gain_tgt_adj_override, 0x1F);
+ set_field_value(&ree_gen_sm_en_usb, 0x1);
+ set_field_value(&ree_gen_sm_en_periodic, 0x1);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_usb, 0x0);
+ set_field_value(&ana_en_epath_gen_ctrl_sm_periodic, 0x1);
+ set_field_value(&rxda_eq_range_sel, 0x3);
+ set_field_value(&rxda_vga_sa_range_sel, 0x3);
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x0);
+ break;
+ case CLK_RATIO_5_4:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x4);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_2:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x5);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_2_1:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x3);
+ break;
+ case CLK_RATIO_1_2:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x0);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_5_8:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x1);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ case CLK_RATIO_3_4:
+ set_field_value(&cmn_pll_clk_osr, 0x00);
+ set_field_value(&cmn_pll_clk_div2_ratio, 0x2);
+ set_field_value(&cmn_pll_clk_div2_sel, 0x1);
+ break;
+ }
+ } else
+ pr_err("pma_pll_config() *E: TMDS clock frequency (%d kHz) is out of range\n",
+ rx_clk_freq);
+ }
+
+ vco_freq_khz =
+ (2048 * (u64) rx_clk_freq * (1 << cmn_pll_clk_osr.value) * tmds_bit_clk_ratio) / 2047;
+
+ pr_info("VCO frequency (refclk: %d kHz, TMDS clk: %d kHz, OSR: %0d, tmds_bit_clk_ratio: %d) equals %llu kHz\n",
+ REFCLK_FREQ_KHZ, rx_clk_freq, 1 << cmn_pll_clk_osr.value,
+ tmds_bit_clk_ratio, vco_freq_khz);
+
+ if (inside_f(vco_freq_khz, 3000000, 3400000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x0);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x0);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x3);
+ switch (cmnda_pll0_fb_div_low_out.value) {
+ case 0x7E:
+ set_field_value(&cmnda_pll0_cp_gain, 0x78);
+ break;
+ case 0x3E:
+ set_field_value(&cmnda_pll0_cp_gain, 0x78);
+ break;
+ case 0x10:
+ set_field_value(&cmnda_pll0_cp_gain, 0x58);
+ break;
+ default:
+ set_field_value(&cmnda_pll0_cp_gain, 0x78);
+ }
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x04);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x0D);
+ } else if (inside_f(vco_freq_khz, 3400000, 3687000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ switch (cmnda_pll0_fb_div_low_out.value) {
+ case 0x7E:
+ set_field_value(&cmnda_pll0_cp_gain, 0x68);
+ break;
+ case 0x3E:
+ set_field_value(&cmnda_pll0_cp_gain, 0x68);
+ break;
+ case 0x10:
+ set_field_value(&cmnda_pll0_cp_gain, 0x59);
+ break;
+ default:
+ set_field_value(&cmnda_pll0_cp_gain, 0x68);
+ }
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x8E);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x2F);
+ } else if (inside_f(vco_freq_khz, 3687000, 3999000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ set_field_value(&cmnda_pll0_cp_gain, 0x64);
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x8E);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x2F);
+ } else if (inside_f(vco_freq_khz, 3999000, 4337000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ set_field_value(&cmnda_pll0_cp_gain, 0x56);
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x8E);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x2F);
+ } else if (inside_f(vco_freq_khz, 4337000, 4703000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ set_field_value(&cmnda_pll0_cp_gain, 0x58);
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x8E);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x2F);
+ } else if (inside_f(vco_freq_khz, 4703000, 5101000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ set_field_value(&cmnda_pll0_cp_gain, 0x54);
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x04);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x0D);
+ } else if (inside_f(vco_freq_khz, 5101000, 5532000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ set_field_value(&cmnda_pll0_cp_gain, 0x49);
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x04);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x0D);
+ } else if (inside_f(vco_freq_khz, 5532000, 6000000 - 1000)) {
+ set_field_value(&vco_ring_select, 0x1);
+ set_field_value(&cmnda_pll0_v2i_prog, 0x1);
+ set_field_value(&cmnda_pll0_coarse_prog, 0x7);
+ set_field_value(&cmnda_pll0_cp_gain, 0x3E);
+ set_field_value(&cmnda_pll0_const_ndac_cntrl, 0x0);
+ set_field_value(&cmnda_pll0_const_pmos_cntrl, 0x04);
+ set_field_value(&cmnda_pll0_ptat_ndac_cntrl, 0x0D);
+ } else
+ pr_err("%s VCO frequency (%llu KHz) is out of range\n", __func__, vco_freq_khz);
+
+ if (inside_f(vco_freq_khz, 3000000, 4000000)) {
+ set_field_value(&rxda_pi_iq_bias_trim, 0x5);
+ set_field_value(&rxda_pi_iq_pload_bias_trim, 0x2);
+ set_field_value(&rxda_pi_iq_pload_trim, 0x3F);
+ set_field_value(&rxda_pi_e_bias_trim, 0x5);
+ set_field_value(&rxda_pi_e_pload_bias_trim, 0x2);
+ set_field_value(&rxda_pi_e_pload_trim, 0x3F);
+ set_field_value(&rxda_pi_range_sel, 0x2);
+ set_field_value(&rxda_pi_cal_cm_trim, 0x00);
+ } else if (inside_f(vco_freq_khz, 4000000, 6000000)) {
+ set_field_value(&rxda_pi_iq_bias_trim, 0x5);
+ set_field_value(&rxda_pi_iq_pload_bias_trim, 0x4);
+ set_field_value(&rxda_pi_iq_pload_trim, 0x3F);
+ set_field_value(&rxda_pi_e_bias_trim, 0x5);
+ set_field_value(&rxda_pi_e_pload_bias_trim, 0x4);
+ set_field_value(&rxda_pi_e_pload_trim, 0x3F);
+ set_field_value(&rxda_pi_range_sel, 0x3);
+ set_field_value(&rxda_pi_cal_cm_trim, 0x00);
+ }
+ set_field_value(&xcvr_pll_en, 0x1);
+ set_field_value(&xcvr_link_reset_n, 0x0);
+ set_field_value(&xcvr_power_state_req, 0x0);
+ set_field_value(&iso_pma_cmn_pll0_clk_datart1_div, 0x1);
+ set_field_value(&iso_pma_cmn_pll0_clk_datart0_div, 0x2);
+ set_field_value(&iso_pma_cmn_pll0_clk_en, 0x1);
+
+ /*******************************************************
+ * Register setting
+ ********************************************************/
+
+ /* CMN_DIAG_PLL0_INCLK_CTRL */
+ reg_val = set_reg_value(cmnda_pll0_ip_div);
+ reg_val |= set_reg_value(cmnda_pll0_hs_sym_div_sel);
+ write16(state, CMN_DIAG_PLL0_INCLK_CTRL_ADDR, reg_val);
+
+ /* CMN_DIAG_PLL0_FBH_OVRD */
+ reg_val = set_reg_value(cmn_pll0_fb_div_high_ovrd_en);
+ reg_val |= set_reg_value(cmnda_pll0_fb_div_high_out);
+ write16(state, CMN_DIAG_PLL0_FBH_OVRD_ADDR, reg_val);
+
+ /* CMN_DIAG_PLL0_FBL_OVRD */
+ reg_val = set_reg_value(cmn_pll0_fb_div_low_ovrd_en);
+ reg_val |= set_reg_value(cmnda_pll0_fb_div_low_out);
+ write16(state, CMN_DIAG_PLL0_FBL_OVRD_ADDR, reg_val);
+
+ /* CMN_PLL0_DIV2SEL_OSR_CTRL */
+ reg_val = set_reg_value(cmn_pll_clk_osr);
+ reg_val |= set_reg_value(cmn_pll_clk_div2_ratio);
+ reg_val |= set_reg_value(cmn_pll_clk_div2_sel);
+ write16(state, CMN_PLL0_DIV2SEL_OSR_CTRL_ADDR, reg_val);
+
+ /* RX_DIAG_SMPLR_OSR */
+ reg_val = set_reg_value(rx_diag_smplr_osr);
+ multi_write16(state, RX_DIAG_SMPLR_OSR_ADDR, reg_val);
+
+ /* RX_PSC_A0 */
+ reg_val = set_reg_value(rx_psc_a0);
+ multi_write16(state, RX_PSC_A0_ADDR, reg_val);
+
+ /* RX_REE_PERGCSM_EQENM_PH1 */
+ reg_val = set_reg_value(rx_ree_pergcsm_eqenm_ph1);
+ multi_write16(state, RX_REE_PERGCSM_EQENM_PH1_ADDR, reg_val);
+
+ /* RX_REE_PERGCSM_EQENM_PH1 */
+ reg_val = set_reg_value(rx_ree_pergcsm_eqenm_ph2);
+ multi_write16(state, RX_REE_PERGCSM_EQENM_PH2_ADDR, reg_val);
+
+ /* RX_REE_VGA_GAIN_OVRD */
+ reg_val = set_reg_value(vga_gain_accum_override_en);
+ reg_val |= set_reg_value(vga_gain_accum_override);
+ reg_val |= set_reg_value(vga_gain_tgt_adj_override_en);
+ reg_val |= set_reg_value(vga_gain_tgt_adj_override);
+ multi_write16(state, RX_REE_VGA_GAIN_OVRD_ADDR, reg_val);
+
+ /* RX_REE_SMGM_CTRL1 */
+ reg_val = set_reg_value(ree_gen_sm_en_usb);
+ reg_val |= set_reg_value(ree_gen_sm_en_periodic);
+ reg_val |= set_reg_value(ana_en_epath_gen_ctrl_sm_usb);
+ reg_val |= set_reg_value(ana_en_epath_gen_ctrl_sm_periodic);
+ multi_write16(state, RX_REE_SMGM_CTRL1_ADDR, reg_val);
+
+ /* RX_DIAG_DFE_CTRL2 */
+ reg_val = set_reg_value(rxda_eq_range_sel);
+ reg_val |= set_reg_value(rxda_vga_sa_range_sel);
+ multi_write16(state, RX_DIAG_DFE_CTRL2_ADDR, reg_val);
+
+ /* CMN_PLLSM0_USER_DEF_CTRL */
+ reg_val = set_reg_value(vco_ring_select);
+ write16(state, CMN_PLLSM0_USER_DEF_CTRL_ADDR, reg_val);
+
+ /* CMN_DIAG_PLL0_V2I_TUNE */
+ reg_val = set_reg_value(cmnda_pll0_v2i_prog);
+ reg_val |= set_reg_value(cmnda_pll0_coarse_prog);
+ write16(state, CMN_DIAG_PLL0_V2I_TUNE_ADDR, reg_val);
+
+ /* CMN_DIAG_PLL0_CP_TUNE */
+ reg_val = set_reg_value(cmnda_pll0_cp_gain);
+ write16(state, CMN_DIAG_PLL0_CP_TUNE_ADDR, reg_val);
+
+ /* CMN_DIAG_PLL0_PTATIS_TUNE1 */
+ reg_val = set_reg_value(cmnda_pll0_const_ndac_cntrl);
+ reg_val |= set_reg_value(cmnda_pll0_const_pmos_cntrl);
+ write16(state, CMN_DIAG_PLL0_PTATIS_TUNE1_ADDR, reg_val);
+
+ /* CMN_DIAG_PLL0_PTATIS_TUNE2 */
+ reg_val = set_reg_value(cmnda_pll0_ptat_ndac_cntrl);
+ write16(state, CMN_DIAG_PLL0_PTATIS_TUNE2_ADDR, reg_val);
+
+ /* RX_DIAG_ILL_IQ_TRIM0 */
+ reg_val = set_reg_value(rxda_pi_iq_bias_trim);
+ reg_val |= set_reg_value(rxda_pi_iq_pload_bias_trim);
+ reg_val |= set_reg_value(rxda_pi_iq_pload_trim);
+ write16(state, RX_DIAG_ILL_IQ_TRIM0_ADDR, reg_val);
+
+ /* RX_DIAG_ILL_E_TRIM0 */
+ reg_val = set_reg_value(rxda_pi_e_bias_trim);
+ reg_val |= set_reg_value(rxda_pi_e_pload_bias_trim);
+ reg_val |= set_reg_value(rxda_pi_e_pload_trim);
+ write16(state, RX_DIAG_ILL_E_TRIM0_ADDR, reg_val);
+
+ /* RX_DIAG_ILL_IQE_TRIM2 */
+ reg_val = set_reg_value(rxda_pi_range_sel);
+ reg_val |= set_reg_value(rxda_pi_cal_cm_trim);
+ write16(state, RX_DIAG_ILL_IQE_TRIM2_ADDR, reg_val);
+
+ /* Enable PLL */
+ /* PHY_MODE_CTL */
+ reg_val = set_reg_value(xcvr_pll_en);
+ reg_val |= set_reg_value(xcvr_link_reset_n);
+ reg_val |= set_reg_value(xcvr_power_state_req);
+ write16(state, PHY_MODE_CTL_ADDR, reg_val);
+
+ /* Wait for PLL0 ready: */
+ /* PHY_PMA_CMN_CTRL2 */
+ for (i = 0; i < 20; i++) {
+ if (read16(state, PHY_PMA_CMN_CTRL2_ADDR) & (1 << 0))
+ break;
+ msleep(10);
+ }
+ if (i == 20)
+ pr_info("pma_pll_ready failed\n");
+
+ /* Turn on output clocks: */
+ /* PHY_PMA_CMN_CTRL2 */
+ reg_val = set_reg_value(iso_pma_cmn_pll0_clk_datart1_div);
+ reg_val |= set_reg_value(iso_pma_cmn_pll0_clk_datart0_div);
+ reg_val |= set_reg_value(iso_pma_cmn_pll0_clk_en);
+ write16(state, PHY_PMA_CMN_CTRL2_ADDR, reg_val);
+
+ if (data_rate_change) {
+ pr_info("pma_pll_config() Disable Rx Eq Training\n");
+ for (i = 0; i < 3; i++) {
+ reg_val =
+ read16(state, PHY_PMA_XCVR_CTRL_ADDR | (i << 6));
+ reg_val &= 0xFFEF;
+ write16(state, PHY_PMA_XCVR_CTRL_ADDR | (i << 6), reg_val);
+ }
+ }
+ /* Get current power state: */
+ /* PHY_MODE_CTL */
+ reg_val = read16(state, PHY_MODE_CTL_ADDR);
+ reg_val &= 0x00F0;
+ pr_info("pma_pll_config() Current power state: 0x%02X\n", (reg_val >> 4));
+
+ /* Deassert link reset: */
+ /* PHY_MODE_CTL */
+ pr_info("pma_pll_config() Deassert link reset\n");
+ set_field_value(&xcvr_link_reset_n, 0x1);
+ reg_val |= set_reg_value(xcvr_pll_en);
+ reg_val |= set_reg_value(xcvr_link_reset_n);
+ write16(state, PHY_MODE_CTL_ADDR, reg_val);
+
+ /* Wait for xcvr_psm_ready for all the lanes */
+ loop = 0;
+ do {
+ reg_val = (1 << 13);
+ for (i = 0; i < 3; i++) {
+ reg_val &= read16(state, PHY_PMA_XCVR_CTRL_ADDR | (i << 6)) & (1 << 13);
+ pr_info("pma_pll_config() xcvr_psm_ready(%0d): 0x%0X\n", i, reg_val >> 13);
+ }
+ msleep(10);
+ loop++;
+ } while (!reg_val && loop < 20);
+ /* Timeout */
+ if (loop == 20)
+ pr_err("pma_pll_config() Waiting for xcvr_psm_ready... failed\n");
+
+ /* Set A0 power state: */
+ /* PHY_MODE_CTL */
+ set_field_value(&xcvr_power_state_req, 0x1);
+ reg_val = set_reg_value(xcvr_pll_en);
+ reg_val |= set_reg_value(xcvr_link_reset_n);
+ reg_val |= set_reg_value(xcvr_power_state_req);
+ write16(state, PHY_MODE_CTL_ADDR, reg_val);
+ pr_info("pma_pll_config() Requested A0 power mode\n");
+
+ /* Wait for A0 power mode acknowledged: */
+ /* PHY_MODE_CTL */
+ set_field_value(&xcvr_power_state_ack, 0x1);
+
+ for (i = 0; i < 20; i++) {
+ if (((read16(state, PHY_MODE_CTL_ADDR) & 0x00F0) == set_reg_value(xcvr_power_state_ack)))
+ break;
+ msleep(10);
+ }
+ if (i == 20)
+ pr_err("Waiting for A0 power mode acknowledged failed\n");
+
+ if (data_rate_change) {
+ pr_info("pma_pll_config() Enable Rx Eq Training\n");
+ for (i = 0; i < 3; i++) {
+ reg_val =
+ read16(state, PHY_PMA_XCVR_CTRL_ADDR | (i << 6));
+ reg_val |= 0x0010;
+ write16(state, PHY_PMA_XCVR_CTRL_ADDR | (i << 6),
+ reg_val);
+ }
+ }
+}
+
+clk_ratio_t clk_ratio_detect(state_struct *state,
+ u32 rx_clk_freq, /* khz */
+ u32 pxl_clk_freq, /* khz */
+ u8 vic,
+ pixel_encoding_t pixel_encoding,
+ tmds_bit_clock_ratio_t tmds_bit_clk_ratio)
+{
+ clk_ratio_t clk_ratio_detected = CLK_RATIO_1_1;
+
+ u64 tmds_freq_nominal_1_1, tmds_freq_nominal_1_1_min,
+ tmds_freq_nominal_1_1_max;
+ u64 tmds_freq_nominal_5_4, tmds_freq_nominal_5_4_min,
+ tmds_freq_nominal_5_4_max;
+ u64 tmds_freq_nominal_3_2, tmds_freq_nominal_3_2_min,
+ tmds_freq_nominal_3_2_max;
+ u64 tmds_freq_nominal_2_1, tmds_freq_nominal_2_1_min,
+ tmds_freq_nominal_2_1_max;
+ u64 tmds_freq_nominal_1_2, tmds_freq_nominal_1_2_min,
+ tmds_freq_nominal_1_2_max;
+ u64 tmds_freq_nominal_5_8, tmds_freq_nominal_5_8_min,
+ tmds_freq_nominal_5_8_max;
+ u64 tmds_freq_nominal_3_4, tmds_freq_nominal_3_4_min,
+ tmds_freq_nominal_3_4_max;
+ u64 min, max;
+
+ /* Check the TMDS/pixel clock ratio. */
+ pr_info("VIC %0d, pixel encoding: %0d, TMDS bit clock ratio: %0d and TMDS clk %d KHz\n",
+ vic, pixel_encoding, tmds_bit_clk_ratio, rx_clk_freq);
+
+ tmds_freq_nominal_1_1 = pxl_clk_freq;
+
+ min = 990;
+ max = 1010;
+
+ tmds_freq_nominal_5_4 = tmds_freq_nominal_1_1;
+ tmds_freq_nominal_3_2 = tmds_freq_nominal_1_1;
+ tmds_freq_nominal_2_1 = tmds_freq_nominal_1_1;
+ tmds_freq_nominal_1_2 = tmds_freq_nominal_1_1;
+ tmds_freq_nominal_5_8 = tmds_freq_nominal_1_1;
+ tmds_freq_nominal_3_4 = tmds_freq_nominal_1_1;
+
+ /* Exclude some of the clock ratios based on pixel excoding */
+ switch (pixel_encoding) {
+ case PIXEL_ENCODING_YUV422:
+ tmds_freq_nominal_5_4 = 0;
+ tmds_freq_nominal_3_2 = 0;
+ tmds_freq_nominal_2_1 = 0;
+ tmds_freq_nominal_1_2 = 0;
+ tmds_freq_nominal_5_8 = 0;
+ tmds_freq_nominal_3_4 = 0;
+ break;
+ case PIXEL_ENCODING_YUV420:
+ tmds_freq_nominal_5_4 = 0;
+ tmds_freq_nominal_3_2 = 0;
+ tmds_freq_nominal_2_1 = 0;
+ break;
+ default: /* RGB/YUV444 */
+ tmds_freq_nominal_1_2 = 0;
+ tmds_freq_nominal_5_8 = 0;
+ tmds_freq_nominal_3_4 = 0;
+ }
+
+ tmds_freq_nominal_1_1_min =
+ min * tmds_freq_nominal_1_1 * 10 * 1 / (tmds_bit_clk_ratio * 1000 * 1);
+ tmds_freq_nominal_1_1_max =
+ max * tmds_freq_nominal_1_1 * 10 * 1 / (tmds_bit_clk_ratio * 1000 * 1);
+ tmds_freq_nominal_5_4_min =
+ min * tmds_freq_nominal_5_4 * 10 * 5 / (tmds_bit_clk_ratio * 1000 * 4);
+ tmds_freq_nominal_5_4_max =
+ max * tmds_freq_nominal_5_4 * 10 * 5 / (tmds_bit_clk_ratio * 1000 * 4);
+ tmds_freq_nominal_3_2_min =
+ min * tmds_freq_nominal_3_2 * 10 * 3 / (tmds_bit_clk_ratio * 1000 * 2);
+ tmds_freq_nominal_3_2_max =
+ max * tmds_freq_nominal_3_2 * 10 * 3 / (tmds_bit_clk_ratio * 1000 * 2);
+ tmds_freq_nominal_2_1_min =
+ min * tmds_freq_nominal_2_1 * 10 * 2 / (tmds_bit_clk_ratio * 1000 * 1);
+ tmds_freq_nominal_2_1_max =
+ max * tmds_freq_nominal_2_1 * 10 * 2 / (tmds_bit_clk_ratio * 1000 * 1);
+ tmds_freq_nominal_1_2_min =
+ min * tmds_freq_nominal_1_2 * 10 * 1 / (tmds_bit_clk_ratio * 1000 * 2);
+ tmds_freq_nominal_1_2_max =
+ max * tmds_freq_nominal_1_2 * 10 * 1 / (tmds_bit_clk_ratio * 1000 * 2);
+ tmds_freq_nominal_5_8_min =
+ min * tmds_freq_nominal_5_8 * 10 * 5 / (tmds_bit_clk_ratio * 1000 * 8);
+ tmds_freq_nominal_5_8_max =
+ max * tmds_freq_nominal_5_8 * 10 * 5 / (tmds_bit_clk_ratio * 1000 * 8);
+ tmds_freq_nominal_3_4_min =
+ min * tmds_freq_nominal_3_4 * 10 * 3 / (tmds_bit_clk_ratio * 1000 * 4);
+ tmds_freq_nominal_3_4_max =
+ max * tmds_freq_nominal_3_4 * 10 * 3 / (tmds_bit_clk_ratio * 1000 * 4);
+
+ if (rx_clk_freq > tmds_freq_nominal_1_1_min
+ && rx_clk_freq < tmds_freq_nominal_1_1_max) {
+ clk_ratio_detected = CLK_RATIO_1_1;
+ pr_info("Detected TMDS/pixel clock ratio of 1:1\n");
+ } else if (rx_clk_freq > tmds_freq_nominal_5_4_min
+ && rx_clk_freq < tmds_freq_nominal_5_4_max) {
+ clk_ratio_detected = CLK_RATIO_5_4;
+ pr_info("Detected TMDS/pixel clock ratio of 5:4\n");
+ } else if (rx_clk_freq > tmds_freq_nominal_3_2_min
+ && rx_clk_freq < tmds_freq_nominal_3_2_max) {
+ clk_ratio_detected = CLK_RATIO_3_2;
+ pr_info("Detected TMDS/pixel clock ratio of 3:2\n");
+ } else if (rx_clk_freq > tmds_freq_nominal_2_1_min
+ && rx_clk_freq < tmds_freq_nominal_2_1_max) {
+ clk_ratio_detected = CLK_RATIO_2_1;
+ pr_info("Detected TMDS/pixel clock ratio of 2:1\n");
+ } else if (rx_clk_freq > tmds_freq_nominal_1_2_min
+ && rx_clk_freq < tmds_freq_nominal_1_2_max) {
+ clk_ratio_detected = CLK_RATIO_1_2;
+ pr_info("Detected TMDS/pixel clock ratio of 1:2\n");
+ } else if (rx_clk_freq > tmds_freq_nominal_5_8_min
+ && rx_clk_freq < tmds_freq_nominal_5_8_max) {
+ clk_ratio_detected = CLK_RATIO_5_8;
+ pr_info("Detected TMDS/pixel clock ratio of 5:8\n");
+ } else if (rx_clk_freq > tmds_freq_nominal_3_4_min
+ && rx_clk_freq < tmds_freq_nominal_3_4_max) {
+ clk_ratio_detected = CLK_RATIO_3_4;
+ pr_info("Detected TMDS/pixel clock ratio of 3:4\n");
+ } else {
+ pr_err("Failed to detected TMDS/pixel clock ratio\n");
+ pr_err("VIC: %02d and TMDS clock of %d KHz\n", vic, rx_clk_freq);
+ }
+
+ return clk_ratio_detected;
+}
diff --git a/drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.h b/drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.h
new file mode 100644
index 000000000000..f8b9c867b75a
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.h
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2016-2017 Cadence Design Systems, Inc.
+ * All rights reserved worldwide.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright 2018 NXP
+ *
+ ******************************************************************************
+ *
+ * API_AFE_ss28fdsoi_hdmirx.h
+ *
+ ******************************************************************************
+ */
+
+#ifndef API_AFE_SS28FDSOI_HDMIRX_H_
+#define API_AFE_SS28FDSOI_HDMIRX_H_
+
+#include <linux/io.h>
+#include "../../../../mxc/hdp/all.h"
+
+#define REFCLK_FREQ_KHZ 24000
+#define LINK_WRITE 0x2000
+#define LINK_ID 0x0
+
+#define CMN_PLLSM0_PLLEN_TMR_ADDR 0x0029
+#define CMN_PLLSM0_PLLPRE_TMR_ADDR 0x002A
+#define CMN_PLLSM0_PLLVREF_TMR_ADDR 0x002B
+#define CMN_PLLSM0_PLLLOCK_TMR_ADDR 0x002C
+#define CMN_PLLSM0_USER_DEF_CTRL_ADDR 0x002F
+#define CMN_PLL0_VCOCAL_CTRL_ADDR 0x0080
+#define CMN_PLL0_VCOCAL_OVRD_ADDR 0x0083
+#define CMN_PLL0_LOCK_REFCNT_START_ADDR 0x0090
+#define CMN_PLL0_LOCK_PLLCNT_START_ADDR 0x0092
+#define CMN_PLL0_DIV2SEL_OSR_CTRL_ADDR 0x009B
+#define CMN_ICAL_CTRL_ADDR 0x00C0
+#define CMN_ICAL_OVRD_ADDR 0x00C1
+#define CMN_RXCAL_CTRL_ADDR 0x00D0
+#define CMN_RXCAL_OVRD_ADDR 0x00D1
+#define CMN_RXCAL_INIT_TMR_ADDR 0x00D4
+#define CMN_TXPUCAL_OVRD_ADDR 0x00E1
+#define CMN_TXPDCAL_OVRD_ADDR 0x00F1
+#define CMN_CMSMT_CLK_FREQ_MSMT_CTRL_ADDR 0x01A0
+#define CMN_CMSMT_REF_CLK_TMR_VALUE_ADDR 0x01A2
+#define CMN_CMSMT_TEST_CLK_CNT_VALUE_ADDR 0x01A3
+#define CMN_DIAG_PLL0_FBH_OVRD_ADDR 0x01C0
+#define CMN_DIAG_PLL0_FBL_OVRD_ADDR 0x01C1
+#define CMN_DIAG_PLL0_TEST_MODE_ADDR 0x01C4
+#define CMN_DIAG_PLL0_INCLK_CTRL_ADDR 0x01CA
+#define CMN_DIAG_PLL0_V2I_TUNE_ADDR 0x01C5
+#define CMN_DIAG_PLL0_CP_TUNE_ADDR 0x01C6
+#define CMN_DIAG_PLL0_PTATIS_TUNE1_ADDR 0x01C8
+#define CMN_DIAG_PLL0_PTATIS_TUNE2_ADDR 0x01C9
+#define XCVR_PSM_CAL_TMR_ADDR 0x4002
+#define XCVR_PSM_A0IN_TMR_ADDR 0x4003
+#define XCVR_DIAG_RX_LANE_CAL_RST_TMR_ADDR 0x40EA
+#define TX_ANA_CTRL_REG_1_ADDR 0x5020
+#define TX_ANA_CTRL_REG_2_ADDR 0x5021
+#define TX_DIG_CTRL_REG_2_ADDR 0x5024
+#define TX_ANA_CTRL_REG_3_ADDR 0x5026
+#define TX_ANA_CTRL_REG_4_ADDR 0x5027
+#define TX_ANA_CTRL_REG_5_ADDR 0x5029
+#define RX_PSC_A0_ADDR 0x8000
+#define RX_IQPI_ILL_CAL_OVRD_ADDR 0x8023
+#define RX_EPI_ILL_CAL_OVRD_ADDR 0x8033
+#define RX_SLC_CTRL_ADDR 0x80E0
+#define RX_REE_PERGCSM_EQENM_PH1_ADDR 0x8179
+#define RX_REE_PERGCSM_EQENM_PH2_ADDR 0x817A
+#define RX_REE_VGA_GAIN_OVRD_ADDR 0x81AD
+#define RX_REE_SMGM_CTRL1_ADDR 0x81BD
+#define RX_DIAG_ILL_IQ_TRIM0_ADDR 0x81C1
+#define RX_DIAG_ILL_E_TRIM0_ADDR 0x81C2
+#define RX_DIAG_ILL_IQE_TRIM2_ADDR 0x81C5
+#define RX_DIAG_DFE_CTRL2_ADDR 0x81D4
+#define RX_DIAG_SC2C_DELAY_ADDR 0x81E1
+#define RX_DIAG_SMPLR_OSR_ADDR 0x81E2
+#define RX_CLK_SLICER_CAL_OVRD_ADDR 0x8621
+#define RX_CLK_SLICER_CAL_INIT_TMR_ADDR 0x8624
+#define PHY_MODE_CTL_ADDR 0xC008
+#define PHY_PMA_CMN_CTRL1_ADDR 0xC800
+#define PHY_PMA_CMN_CTRL2_ADDR 0xC801
+#define PHY_PMA_SSM_STATE_ADDR 0xC802
+#define PHY_PMA_PLL_SM_STATE_ADDR 0xC803
+#define PHY_PMA_XCVR_CTRL_ADDR 0xCC00
+
+typedef enum {
+ TMDS_BIT_CLOCK_RATIO_1_10 = 10,
+ TMDS_BIT_CLOCK_RATIO_1_40 = 40
+} tmds_bit_clock_ratio_t;
+
+typedef enum {
+ PIXEL_ENCODING_RGB = 0,
+ PIXEL_ENCODING_YUV422 = 1,
+ PIXEL_ENCODING_YUV444 = 2,
+ PIXEL_ENCODING_YUV420 = 3,
+} pixel_encoding_t;
+
+void speedup_config(state_struct *state);
+void arc_config(state_struct *state);
+void pma_config(state_struct *state);
+u8 pma_cmn_ready(state_struct *state);
+u8 pma_rx_clk_signal_detect(state_struct *state);
+u32 pma_rx_clk_freq_detect(state_struct *state);
+void pre_data_rate_change(state_struct *state);
+void pma_pll_config(state_struct *state, u32, clk_ratio_t, tmds_bit_clock_ratio_t, unsigned char);
+clk_ratio_t clk_ratio_detect(state_struct *state, u32, u32, u8, pixel_encoding_t, tmds_bit_clock_ratio_t);
+void phy_status(state_struct *state);
+
+#endif
diff --git a/drivers/media/platform/imx8/hdmi/Kconfig b/drivers/media/platform/imx8/hdmi/Kconfig
new file mode 100644
index 000000000000..ed2736f82de3
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/Kconfig
@@ -0,0 +1,4 @@
+config IMX8_HDMI_RX
+ tristate "IMX8 HDMI RX Controller"
+ select MX8_HDP_RX
+ default y
diff --git a/drivers/media/platform/imx8/hdmi/Makefile b/drivers/media/platform/imx8/hdmi/Makefile
new file mode 100644
index 000000000000..9a466a52bc80
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_IMX8_HDMI_RX) += mxc-hdmi-rx.o mxc-hdmi-hw.o API_AFE_ss28fdsoi_hdmirx.o
diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c
new file mode 100644
index 000000000000..f524f7bcda35
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "API_AFE_ss28fdsoi_hdmirx.h"
+#include "mxc-hdmi-rx.h"
+
+u8 block0[128] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x1e, 0x6d, 0xfd, 0x5a, 0x9b, 0x5f, 0x02, 0x00,
+ 0x07, 0x19, 0x01, 0x04, 0xb5, 0x3c, 0x22, 0x78,
+ 0x9f, 0x30, 0x35, 0xa7, 0x55, 0x4e, 0xa3, 0x26,
+ 0x0f, 0x50, 0x54, 0x21, 0x08, 0x00, 0x71, 0x40,
+ 0x81, 0x80, 0x81, 0xc0, 0xa9, 0xc0, 0xd1, 0xc0,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x50, 0xd0,
+ 0x00, 0xa0, 0xf0, 0x70, 0x3e, 0x80, 0x08, 0x90,
+ 0x65, 0x0c, 0x58, 0x54, 0x21, 0x00, 0x00, 0x1a,
+ 0x28, 0x68, 0x00, 0xa0, 0xf0, 0x70, 0x3e, 0x80,
+ 0x08, 0x90, 0x65, 0x0c, 0x58, 0x54, 0x21, 0x00,
+ 0x00, 0x1a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x28,
+ 0x3d, 0x87, 0x87, 0x38, 0x01, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x32, 0x37, 0x4d, 0x55, 0x36, 0x37, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xd4
+};
+
+u8 block1[128] = {
+ 0x02, 0x03, 0x11, 0x71, 0x44, 0x90, 0x04, 0x03,
+ 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00,
+ 0x00, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d,
+ 0x40, 0x58, 0x2c, 0x45, 0x00, 0x58, 0x54, 0x21,
+ 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41
+};
+
+S_HDMI_SCDC_SET_MSG scdcExampleData = {
+ .sink_ver = 6,
+ .manufacturer_oui_1 = 0x1,
+ .manufacturer_oui_2 = 0x2,
+ .manufacturer_oui_3 = 0x3,
+ .devId = {1, 2, 3, 4, 5, 6, 7, 8},
+ .hardware_major_rev = 12,
+ .hardware_minor_rev = 13,
+ .software_major_rev = 0xAB,
+ .software_minor_rev = 0XBC,
+ .manufacturerSpecific = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}
+};
+
+u8 hdmi_infoframe_poll(state_struct *state)
+{
+ GENERAL_Read_Register_response regresp;
+ u32 regread;
+ struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+
+ /* Unmask "AVI InfoFrame received" interrupt */
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_MASK << 2),
+ F_TMDS_MHL_HD_MASK(0xFD));
+ dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt unmasked.\n");
+
+ /* Unmask "tmds_mhl_hd_int" interrupt */
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (MHL_HD_INT_MASK << 2),
+ F_MHL_HD_INT_MASK(0xE));
+ dev_dbg(&hdmi_rx->pdev->dev, "tmds_mhl_hd_int interrupt unmasked.\n");
+
+ /* MHL_HD_INT_STAT */
+ dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...\n");
+ do {
+ CDN_API_General_Read_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2),
+ &regresp);
+ } while (!(regresp.val & (1 << 0)));
+ dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...DONE\n");
+
+ /* Mask "AVI InfoFrame received" interrupt */
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_MASK << 2),
+ F_TMDS_MHL_HD_MASK(0xFF));
+ dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt masked.\n");
+
+ /* Mask "tmds_mhl_hd_int" interrupt */
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (MHL_HD_INT_MASK << 2),
+ F_MHL_HD_INT_MASK(0xF));
+ dev_dbg(&hdmi_rx->pdev->dev, "tmds_mhl_hd_int interrupt masked.\n");
+
+ /* Read back TMDS_MHL_HD_INT_STAT to clear the interrupt */
+ CDN_API_General_Read_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_STAT << 2),
+ &regresp);
+ dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt cleared.\n");
+
+ /* Packet 0 read request */
+ cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2),
+ F_PACKET_RDN_WR(0x0) | F_PACKET_NUM(0x0));
+ dev_dbg(&hdmi_rx->pdev->dev, "PIF Packet 0 read request.\n");
+
+ /* Wait for pkt_host_rdy_status */
+ dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...\n");
+ do {
+ cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
+ } while (!(regread & (1 << 16)));
+ dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...DONE\n");
+
+ return 0;
+}
+
+/* Exemplary code to configure the controller */
+/* for the AVI_InfoFrame reception */
+static void get_avi_infoframe(state_struct *state)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+ GENERAL_Read_Register_response regresp;
+
+ dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
+ /* Get VIC code and pixel encoding */
+ hdmi_infoframe_poll(state);
+
+ CDN_API_General_Read_Register_blocking(state, ADDR_SINK_MHL_HD + (PKT_AVI_DATA_LOW << 2), &regresp);
+ hdmi_rx->vic_code = (regresp.val & 0xFF000000) >> 24;
+ hdmi_rx->pixel_encoding = (regresp.val & 0x00000060) >> 5;
+}
+
+static void get_vendor_infoframe(state_struct *state)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+ u32 regread;
+
+ dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
+ /* Set info_type1 to vendor Info Frame */
+ cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2),
+ F_INFO_TYPE1(0x81));
+
+ hdmi_infoframe_poll(state);
+
+ /* Get IEEE OUI */
+ cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), &regread);
+ if (regread >> 8 == 0x000c03)
+ dev_info(&hdmi_rx->pdev->dev, "HDMI 1.4b Vendor Specific Infoframe\n");
+ else if (regread >> 8 == 0xC45DD8)
+ dev_info(&hdmi_rx->pdev->dev, "HDMI 2.0 Vendor Specific Infoframe\n");
+ else
+ dev_err(&hdmi_rx->pdev->dev,
+ "Error Vendro Infoframe IEEE OUI=0x%6X\n", regread >> 8);
+
+ /* Get HDMI Video format */
+ cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INFO_DATA2 << 2), &regread);
+
+ /* Extened resoluction format */
+ if (((regread >> 5) & 0x0000007) == 1) {
+ hdmi_rx->hdmi_vic = (regread >> 8) & 0xff;
+ dev_info(&hdmi_rx->pdev->dev, "hdmi_vic=%d\n", hdmi_rx->hdmi_vic);
+ }
+
+ /* Clear info_type1 */
+ cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2),
+ F_INFO_TYPE1(0x00));
+}
+
+static void get_color_depth(struct mxc_hdmi_rx_dev *hdmi_rx,
+ clk_ratio_t clk_ratio)
+{
+ u8 pixel_encoding = hdmi_rx->pixel_encoding;
+
+ hdmi_rx->color_depth = 8;
+
+ switch (pixel_encoding) {
+ case PIXEL_ENCODING_YUV422:
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ hdmi_rx->color_depth = 8;
+ break;
+ case CLK_RATIO_5_4:
+ case CLK_RATIO_3_2:
+ case CLK_RATIO_2_1:
+ case CLK_RATIO_1_2:
+ case CLK_RATIO_5_8:
+ case CLK_RATIO_3_4:
+ default:
+ pr_err("YUV422 Not supported clk ration\n");
+ }
+ break;
+ case PIXEL_ENCODING_YUV420:
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ hdmi_rx->color_depth = 16;
+ break;
+ case CLK_RATIO_1_2:
+ hdmi_rx->color_depth = 8;
+ break;
+ case CLK_RATIO_5_8:
+ hdmi_rx->color_depth = 10;
+ break;
+ case CLK_RATIO_3_4:
+ hdmi_rx->color_depth = 12;
+ break;
+ case CLK_RATIO_5_4:
+ case CLK_RATIO_3_2:
+ case CLK_RATIO_2_1:
+ default:
+ pr_err("YUV420 Not supported clk ration\n");
+ }
+ break;
+ default: /* RGB/YUV444 */
+ switch (clk_ratio) {
+ case CLK_RATIO_1_1:
+ hdmi_rx->color_depth = 8;
+ break;
+ case CLK_RATIO_5_4:
+ hdmi_rx->color_depth = 10;
+ break;
+ case CLK_RATIO_3_2:
+ hdmi_rx->color_depth = 12;
+ break;
+ case CLK_RATIO_2_1:
+ hdmi_rx->color_depth = 16;
+ break;
+ case CLK_RATIO_1_2:
+ case CLK_RATIO_5_8:
+ case CLK_RATIO_3_4:
+ default:
+ pr_err("RGB/YUV444 Not supported clk ration\n");
+ }
+ }
+}
+
+int hdmi_rx_init(state_struct *state)
+{
+ u32 ret = 0;
+ /* Set uCPU Clock frequency for FW's use [MHz]; */
+ CDN_API_SetClock(state, 200);
+
+ /* Relase uCPU */
+ cdn_apb_write(state, ADDR_APB_CFG, 0);
+
+ /* Check if the firmware is running */
+ ret = CDN_API_CheckAlive_blocking(state);
+ return ret;
+}
+
+/* Bring-up sequence for the HDMI-RX */
+int hdmirx_startup(state_struct *state)
+{
+ u8 sts;
+ u32 rx_clk_freq;
+ u8 event5V = 0;
+ u8 data_rate_change = 0;
+ u8 scrambling_en;
+ clk_ratio_t clk_ratio, clk_ratio_detected;
+ tmds_bit_clock_ratio_t tmds_bit_clock_ratio;
+ struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+ S_HDMI_SCDC_GET_MSG *scdcData = &hdmi_rx->scdcData;
+ u8 ret = 0;
+
+ /* Start from TMDS/pixel clock ratio of 1:1.
+ * It affects only pixel clock frequency as the character/data clocks are generated based on
+ * a measured TMDS clock.
+ * This guarantees that the TMDS characters are correctly decoded in the controller regardless
+ * of the pixel clock ratio being programmed. */
+ clk_ratio = CLK_RATIO_1_1;
+
+ /* Set driver and firmware active */
+ CDN_API_MainControl_blocking(state, 1, &sts);
+
+ /* Set EDID - block 0 */
+ CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 0, &block0[0]);
+
+ /* Set EDID - block 1 */
+ CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 1, &block1[0]);
+ dev_dbg(&hdmi_rx->pdev->dev, "EDID block 0/1 set complete.\n");
+
+ /* Set SCDC data sample */
+ CDN_API_HDMIRX_SET_SCDC_SLAVE_blocking(state, &scdcExampleData);
+ dev_dbg(&hdmi_rx->pdev->dev, "SCDC set complete.\n");
+
+ /* Get TMDS_Bit_Clock_Ratio and Scrambling setting */
+ CDN_API_HDMIRX_GET_SCDC_SLAVE_blocking(state, scdcData);
+ tmds_bit_clock_ratio =
+ ((scdcData->TMDS_Config & (1 << 1)) >> 1) ?
+ TMDS_BIT_CLOCK_RATIO_1_40 : TMDS_BIT_CLOCK_RATIO_1_10;
+ scrambling_en = scdcData->TMDS_Config & (1 << 0);
+ dev_dbg(&hdmi_rx->pdev->dev,
+ "TMDS ratio: 1/%0d, Scrambling %0d).\n", tmds_bit_clock_ratio, scrambling_en);
+
+ /* Clear HPD */
+ CDN_API_HDMIRX_SetHpd_blocking(state, 0);
+ dev_dbg(&hdmi_rx->pdev->dev, "Clear HDP\n");
+
+ /* Wait for 5v */
+ while ((event5V & (1 << HDMI_RX_EVENT_5V_VAL)) == 0) {
+ CDN_API_HDMIRX_ReadEvent(state, &event5V);
+ dev_dbg(&hdmi_rx->pdev->dev, "event5V = 0x%02X\n", event5V);
+ }
+ /* Got 5v, set hpd */
+ msleep(100); /* provide minimum low pulse length (100ms) */
+ CDN_API_HDMIRX_SetHpd_blocking(state, 1);
+ dev_dbg(&hdmi_rx->pdev->dev, "Set HDP\n");
+
+ /* Configure the PHY */
+ pma_config(state);
+
+ /* Reset HDMI RX PHY */
+ imx8qm_hdmi_phy_reset(state, 1);
+
+ ret = pma_cmn_ready(state);
+ if (ret < 0)
+ pr_err("pma_cmn_ready failed\n");
+
+ msleep(500);
+
+ arc_config(state);
+
+ ret = pma_rx_clk_signal_detect(state);
+ if (ret < 0)
+ pr_err("pma_rx_clk_signal_detect failed\n");
+ /* Get TMDS clock frequency */
+ rx_clk_freq = pma_rx_clk_freq_detect(state);
+
+ pma_pll_config(state, rx_clk_freq, clk_ratio, tmds_bit_clock_ratio,
+ data_rate_change);
+ msleep(500);
+
+ /* Setup the scrambling mode */
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (TMDS_SCR_CTRL << 2),
+ F_SCRAMBLER_MODE(scrambling_en));
+ dev_info(&hdmi_rx->pdev->dev,
+ "Scrambling %s.\n", (scrambling_en) ? "enabled" : "disabled");
+ /*Just to initiate the counters: */
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (TMDS_SCR_CNT_INT_CTRL << 2),
+ F_SCRAMBLER_SSCP_LINE_DET_THR(0) |
+ F_SCRAMBLER_CTRL_LINE_DET_THR(0));
+ CDN_API_General_Write_Register_blocking(state,
+ ADDR_SINK_MHL_HD + (TMDS_SCR_VALID_CTRL << 2),
+ F_SCRAMBLER_SSCP_LINE_VALID_THR(1) |
+ F_SCRAMBLER_CTRL_LINE_VALID_THR(0));
+
+ /* The PHY got programmed with the assumed TMDS/pixel clock ratio of 1:1.
+ * Implement the link training procedure to find out the real clock ratio:
+ * 1. Wait for AVI InfoFrame packet
+ * 2. Get the VIC code and pixel encoding from the packet
+ * 3. Evaluate the TMDS/pixel clock ratio based on the vic_table.c
+ * 4. Compare the programmed clock ratio with evaluated one
+ * 5. If mismatch found - reprogram the PHY
+ * 6. Enable the video data path in the controller */
+
+ get_avi_infoframe(state);
+ get_vendor_infoframe(state);
+
+ dev_dbg(&hdmi_rx->pdev->dev,
+ "get_avi_infoframe() vic_code: %0d, pixel_encoding: %0d.\n",
+ hdmi_rx->vic_code, hdmi_rx->pixel_encoding);
+ mxc_hdmi_frame_timing(hdmi_rx);
+
+ clk_ratio_detected = clk_ratio_detect(state, rx_clk_freq,
+ hdmi_rx->timings->timings.bt.
+ pixelclock / 1000,
+ hdmi_rx->vic_code,
+ hdmi_rx->pixel_encoding,
+ tmds_bit_clock_ratio);
+
+ data_rate_change = (clk_ratio != clk_ratio_detected);
+ if (data_rate_change) {
+ dev_dbg(&hdmi_rx->pdev->dev,
+ "TMDS/pixel clock ratio mismatch detected (programmed: %0d, detected: %0d)\n",
+ clk_ratio, clk_ratio_detected);
+
+ /* Reconfigure the PHY */
+ pre_data_rate_change(state);
+ pma_rx_clk_signal_detect(state);
+ rx_clk_freq = pma_rx_clk_freq_detect(state);
+ pma_pll_config(state, rx_clk_freq, clk_ratio_detected,
+ tmds_bit_clock_ratio, data_rate_change);
+ } else
+ dev_info(&hdmi_rx->pdev->dev, "TMDS/pixel clock ratio correct\n");
+
+ get_color_depth(hdmi_rx, clk_ratio_detected);
+ dev_dbg(&hdmi_rx->pdev->dev, "Get colordepth is %d bpc\n", hdmi_rx->color_depth);
+
+ /* Do post PHY programming settings */
+ CDN_API_MainControl_blocking(state, 0x80, &sts);
+ dev_dbg(&hdmi_rx->pdev->dev,
+ "CDN_API_MainControl_blocking() Stage 2 complete.\n");
+
+ /* Initialize HDMI RX */
+ CDN_API_HDMIRX_Init_blocking(state);
+ dev_dbg(&hdmi_rx->pdev->dev,
+ "CDN_API_HDMIRX_Init_blocking() complete.\n");
+
+ return 0;
+}
diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c
new file mode 100644
index 000000000000..6cd53c929d88
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "mxc-hdmi-rx.h"
+#include "API_AFE_ss28fdsoi_hdmirx.h"
+
+#define MXC_HDMI_DRIVER_NAME "iMX HDMI RX"
+#define MXC_HDMI_MIN_WIDTH 640
+#define MXC_HDMI_MAX_WIDTH 3840
+#define MXC_HDMI_MIN_HEIGHT 480
+#define MXC_HDMI_MAX_HEIGHT 2160
+
+/* V4L2_DV_BT_CEA_640X480P59_94 */
+#define MXC_HDMI_MIN_PIXELCLOCK 24000000
+/* V4L2_DV_BT_CEA_3840X2160P30 */
+#define MXC_HDMI_MAX_PIXELCLOCK 297000000
+
+#define imx_sd_to_hdmi(sd) container_of(sd, struct mxc_hdmi_rx_dev, sd)
+
+static const struct v4l2_dv_timings_cap mxc_hdmi_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+
+ V4L2_INIT_BT_TIMINGS(MXC_HDMI_MIN_WIDTH, MXC_HDMI_MAX_WIDTH,
+ MXC_HDMI_MIN_HEIGHT, MXC_HDMI_MAX_HEIGHT,
+ MXC_HDMI_MIN_PIXELCLOCK,
+ MXC_HDMI_MAX_PIXELCLOCK,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
+ V4L2_DV_BT_CAP_PROGRESSIVE)
+};
+
+struct mxc_hdmi_rx_dev_video_standards
+mxc_hdmi_video_standards[] = {
+ {V4L2_DV_BT_CEA_640X480P59_94, 1, 0, 60},
+ {V4L2_DV_BT_CEA_720X480P59_94, 2, 0, 60},
+ {V4L2_DV_BT_CEA_720X480P59_94, 3, 0, 60},
+ {V4L2_DV_BT_CEA_720X576P50, 18, 0, 50},
+ {V4L2_DV_BT_CEA_720X576P50, 17, 0, 50},
+ {V4L2_DV_BT_CEA_1280X720P60, 4, 0, 60},
+ {V4L2_DV_BT_CEA_1280X720P50, 19, 0, 50},
+ {V4L2_DV_BT_CEA_1280X720P30, 62, 0, 30},
+ {V4L2_DV_BT_CEA_1280X720P25, 61, 0, 25},
+ {V4L2_DV_BT_CEA_1280X720P24, 60, 0, 24},
+ {V4L2_DV_BT_CEA_1920X1080P60, 16, 0, 60},
+ {V4L2_DV_BT_CEA_1920X1080P50, 31, 0, 50},
+ {V4L2_DV_BT_CEA_1920X1080P30, 34, 0, 30},
+ {V4L2_DV_BT_CEA_1920X1080P25, 33, 0, 25},
+ {V4L2_DV_BT_CEA_1920X1080P24, 32, 0, 24},
+ {V4L2_DV_BT_CEA_3840X2160P24, 93, 3, 24},
+ {V4L2_DV_BT_CEA_3840X2160P25, 94, 2, 25},
+ {V4L2_DV_BT_CEA_3840X2160P30, 95, 1, 30},
+ {V4L2_DV_BT_CEA_4096X2160P24, 98, 4, 24},
+ {V4L2_DV_BT_CEA_4096X2160P25, 99, 0, 25},
+ {V4L2_DV_BT_CEA_4096X2160P30, 100, 0, 30},
+ /* SVGA */
+ {V4L2_DV_BT_DMT_800X600P56, 0x0, 0, 56},
+ {V4L2_DV_BT_DMT_800X600P60, 0x0, 0, 60},
+ {V4L2_DV_BT_DMT_800X600P72, 0x0, 0, 72},
+ {V4L2_DV_BT_DMT_800X600P75, 0x0, 0, 75},
+ {V4L2_DV_BT_DMT_800X600P85, 0x0, 0, 85},
+ /* SXGA */
+ {V4L2_DV_BT_DMT_1280X1024P60, 0x0, 0, 60},
+ {V4L2_DV_BT_DMT_1280X1024P75, 0x0, 0, 75},
+ /* VGA */
+ { V4L2_DV_BT_DMT_640X480P72, 0x0, 0, 72},
+ { V4L2_DV_BT_DMT_640X480P75, 0x0, 0, 75},
+ { V4L2_DV_BT_DMT_640X480P85, 0x0, 0, 85},
+ /* XGA */
+ { V4L2_DV_BT_DMT_1024X768P60, 0x0, 0, 60},
+ { V4L2_DV_BT_DMT_1024X768P70, 0x0, 0, 70},
+ { V4L2_DV_BT_DMT_1024X768P75, 0x0, 0, 75},
+ { V4L2_DV_BT_DMT_1024X768P85, 0x0, 0, 85},
+ /* UXGA */
+ { V4L2_DV_BT_DMT_1600X1200P60, 0x0, 0, 60},
+};
+
+int mxc_hdmi_frame_timing(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ struct mxc_hdmi_rx_dev_video_standards *stds;
+ u32 i, vic, hdmi_vic;
+
+ vic = hdmi_rx->vic_code;
+ hdmi_vic = hdmi_rx->hdmi_vic;
+ stds = mxc_hdmi_video_standards;
+
+ if (vic == 0 && hdmi_vic != 0) {
+ for (i = 0; i < ARRAY_SIZE(mxc_hdmi_video_standards); i++) {
+ if (stds[i].hdmi_vic == hdmi_vic) {
+ hdmi_rx->timings = &stds[i];
+ return true;
+ }
+ }
+ } else if (vic > 109) {
+ dev_err(&hdmi_rx->pdev->dev,
+ "Unsupported mode vic=%d, hdmi_vic=%d\n", vic, hdmi_vic);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mxc_hdmi_video_standards); i++) {
+ if (stds[i].vic == vic) {
+ hdmi_rx->timings = &stds[i];
+ return true;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(mxc_hdmi_video_standards))
+ return -EINVAL;
+
+ return true;
+}
+
+static int mxc_hdmi_clock_init(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ struct device *dev = &hdmi_rx->pdev->dev;
+
+ hdmi_rx->ref_clk = devm_clk_get(dev, "ref_clk");
+ if (IS_ERR(hdmi_rx->ref_clk)) {
+ dev_err(dev, "failed to get hdmi rx ref clk\n");
+ return PTR_ERR(hdmi_rx->ref_clk);
+ }
+
+ hdmi_rx->pxl_clk = devm_clk_get(dev, "pxl_clk");
+ if (IS_ERR(hdmi_rx->pxl_clk)) {
+ dev_err(dev, "failed to get hdmi rx pxl clk\n");
+ return PTR_ERR(hdmi_rx->pxl_clk);
+ }
+ hdmi_rx->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(hdmi_rx->pclk)) {
+ dev_err(dev, "failed to get hdmi rx pclk\n");
+ return PTR_ERR(hdmi_rx->pclk);
+ }
+
+ hdmi_rx->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(hdmi_rx->sclk)) {
+ dev_err(dev, "failed to get hdmi rx sclk\n");
+ return PTR_ERR(hdmi_rx->sclk);
+ }
+
+ hdmi_rx->enc_clk = devm_clk_get(dev, "enc_clk");
+ if (IS_ERR(hdmi_rx->enc_clk)) {
+ dev_err(dev, "failed to get hdmi rx enc clk\n");
+ return PTR_ERR(hdmi_rx->enc_clk);
+ }
+
+ hdmi_rx->spdif_clk = devm_clk_get(dev, "spdif_clk");
+ if (IS_ERR(hdmi_rx->spdif_clk)) {
+ dev_err(dev, "failed to get hdmi rx spdif clk\n");
+ return PTR_ERR(hdmi_rx->spdif_clk);
+ }
+
+ hdmi_rx->pxl_link_clk = devm_clk_get(dev, "pxl_link_clk");
+ if (IS_ERR(hdmi_rx->pxl_link_clk)) {
+ dev_err(dev, "failed to get hdmi rx pixel link clk\n");
+ return PTR_ERR(hdmi_rx->pxl_link_clk);
+ }
+
+ return 0;
+}
+
+static int mxc_hdmi_clock_enable(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ struct device *dev = &hdmi_rx->pdev->dev;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+ ret = clk_prepare_enable(hdmi_rx->ref_clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre ref_clk error %d\n", __func__, ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(hdmi_rx->pxl_clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre pxl_clk error %d\n", __func__, ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(hdmi_rx->enc_clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre enc_clk error %d\n", __func__, ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(hdmi_rx->sclk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre sclk error %d\n", __func__, ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(hdmi_rx->pclk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre pclk error %d\n", __func__, ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(hdmi_rx->spdif_clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre spdif_clk error %d\n", __func__, ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(hdmi_rx->pxl_link_clk);
+ if (ret < 0) {
+ dev_err(dev, "%s, pre pxl_link_clk error %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mxc_hdmi_clock_disable(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
+
+ clk_disable_unprepare(hdmi_rx->ref_clk);
+ clk_disable_unprepare(hdmi_rx->pxl_clk);
+ clk_disable_unprepare(hdmi_rx->enc_clk);
+ clk_disable_unprepare(hdmi_rx->sclk);
+ clk_disable_unprepare(hdmi_rx->pclk);
+ clk_disable_unprepare(hdmi_rx->spdif_clk);
+ clk_disable_unprepare(hdmi_rx->pxl_link_clk);
+}
+
+static void imx8qm_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ u32 val;
+
+ switch (hdmi_rx->pixel_encoding) {
+ case PIXEL_ENCODING_YUV422:
+ val = 3;
+ break;
+ case PIXEL_ENCODING_YUV420:
+ val = 4;
+ break;
+ case PIXEL_ENCODING_YUV444:
+ val = 2;
+ break;
+ case PIXEL_ENCODING_RGB:
+ val = 0;
+ break;
+ default:
+ val = 0x6;
+ }
+
+ /* HDMI RX H/Vsync Polarity */
+ if (hdmi_rx->timings->timings.bt.polarities & V4L2_DV_VSYNC_POS_POL)
+ val |= 1 << PL_ENC_CTL_PXL_VCP;
+ if (hdmi_rx->timings->timings.bt.polarities & V4L2_DV_HSYNC_POS_POL)
+ val |= 1 << PL_ENC_CTL_PXL_HCP;
+
+ writel(val, hdmi_rx->mem.ss_base);
+}
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_video_ops
+ */
+
+static int mxc_hdmi_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+ int ret = 0;
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->timeperframe.denominator = hdmi_rx->timings->fps;
+ cparm->timeperframe.numerator = 1;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ ret = -EINVAL;
+ break;
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mxc_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+ u32 val;
+
+ dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
+ imx8qm_pixel_link_encoder(hdmi_rx);
+
+ if (enable) {
+ val = readl(hdmi_rx->mem.ss_base);
+ val |= 1 << PL_ENC_CTL_PXL_EN;
+ writel(val, hdmi_rx->mem.ss_base);
+ mdelay(17);
+
+ val = readl(hdmi_rx->mem.ss_base);
+ val |= 1 << PL_ENC_CTL_PXL_VAL;
+ writel(val, hdmi_rx->mem.ss_base);
+ } else {
+ val = readl(hdmi_rx->mem.ss_base);
+ val |= ~(1 << PL_ENC_CTL_PXL_VAL);
+ writel(val, hdmi_rx->mem.ss_base);
+ mdelay(17);
+
+ val = readl(hdmi_rx->mem.ss_base);
+ val |= ~(1 << PL_ENC_CTL_PXL_EN);
+ writel(val, hdmi_rx->mem.ss_base);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx_video_ops_hdmi = {
+ .s_stream = mxc_hdmi_s_stream,
+ .g_parm = mxc_hdmi_g_parm,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations hdmi_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_pad_ops
+ */
+static int mxc_hdmi_enum_framesizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+
+ if (fse->index > 1)
+ return -EINVAL;
+
+ fse->min_width = hdmi_rx->timings->timings.bt.width;
+ fse->max_width = fse->min_width;
+
+ fse->min_height = hdmi_rx->timings->timings.bt.height;
+ fse->max_height = fse->min_height;
+ return 0;
+}
+static int mxc_hdmi_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+
+ if (fie->index < 0 || fie->index > 8)
+ return -EINVAL;
+
+ if (fie->width == 0 || fie->height == 0 ||
+ fie->code == 0) {
+ pr_warning("Please assign pixel format, width and height.\n");
+ return -EINVAL;
+ }
+
+ fie->interval.numerator = 1;
+
+ /* TODO Reserved to extension */
+ fie->interval.denominator = hdmi_rx->timings->fps;
+ return 0;
+}
+
+static int mxc_hdmi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_RGB888_1X24;
+
+ return 0;
+}
+
+static int mxc_hdmi_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+ struct v4l2_mbus_framefmt *mbusformat = &sdformat->format;
+
+ if (sdformat->pad != MXC_HDMI_RX_PAD_SOURCE)
+ return -EINVAL;
+
+ switch (hdmi_rx->pixel_encoding) {
+ case PIXEL_ENCODING_YUV422:
+ mbusformat->code = MEDIA_BUS_FMT_YUYV8_1X16;
+ break;
+ case PIXEL_ENCODING_YUV420:
+ mbusformat->code = MEDIA_BUS_FMT_UV8_1X8;
+ break;
+ case PIXEL_ENCODING_YUV444:
+ mbusformat->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ break;
+ default:
+ mbusformat->code = MEDIA_BUS_FMT_RGB888_1X24;
+ }
+
+ mbusformat->width = hdmi_rx->timings->timings.bt.width;
+ mbusformat->height = hdmi_rx->timings->timings.bt.height;
+ mbusformat->colorspace = V4L2_COLORSPACE_JPEG;
+
+ return 0;
+}
+
+static int mxc_hdmi_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (!hdmi_rx->edid.present)
+ return -ENODATA;
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = hdmi_rx->edid.blocks;
+ return 0;
+ }
+
+ if (edid->start_block >= hdmi_rx->edid.blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > hdmi_rx->edid.blocks)
+ edid->blocks = hdmi_rx->edid.blocks - edid->start_block;
+
+ memcpy(edid->edid, hdmi_rx->edid.edid + edid->start_block * 128,
+ edid->blocks * 128);
+
+ return 0;
+}
+
+static int mxc_hdmi_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+ state_struct *state = &hdmi_rx->state;
+ int err, i;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->start_block != 0)
+ return -EINVAL;
+
+ if (edid->blocks == 0) {
+ /* Default EDID */
+ hdmi_rx->edid.blocks = 2;
+ hdmi_rx->edid.present = true;
+ } else if (edid->blocks > 4) {
+ edid->blocks = 4;
+ return -E2BIG;
+ } else {
+ memcpy(hdmi_rx->edid.edid, edid->edid, 128 * edid->blocks);
+ hdmi_rx->edid.blocks = edid->blocks;
+ hdmi_rx->edid.present = true;
+ }
+
+ for (i = 0; i < hdmi_rx->edid.blocks; i++) {
+ /* EDID block */
+ err = CDN_API_HDMIRX_SET_EDID_blocking(state, 0, i, &hdmi_rx->edid.edid[i * 128]);
+ if (err != CDN_OK) {
+ v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad);
+ return -err;
+ }
+ }
+
+ return 0;
+}
+
+static bool mxc_hdmi_check_dv_timings(const struct v4l2_dv_timings *timings,
+ void *hdl)
+{
+ const struct mxc_hdmi_rx_dev_video_standards *stds =
+ mxc_hdmi_video_standards;
+ u32 i;
+
+ for (i = 0; stds[i].timings.bt.width; i++)
+ if (v4l2_match_dv_timings(timings, &stds[i].timings, 0, false))
+ return true;
+
+ return false;
+}
+
+static int mxc_hdmi_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ return v4l2_enum_dv_timings_cap(timings, &mxc_hdmi_timings_cap,
+ mxc_hdmi_check_dv_timings, NULL);
+}
+
+static int mxc_hdmi_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ *cap = mxc_hdmi_timings_cap;
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops imx_pad_ops_hdmi = {
+ .enum_mbus_code = mxc_hdmi_enum_mbus_code,
+ .enum_frame_size = mxc_hdmi_enum_framesizes,
+ .enum_frame_interval = mxc_hdmi_enum_frame_interval,
+ .get_fmt = mxc_hdmi_get_format,
+ .get_edid = mxc_hdmi_get_edid,
+ .set_edid = mxc_hdmi_set_edid,
+ .dv_timings_cap = mxc_hdmi_dv_timings_cap,
+ .enum_dv_timings = mxc_hdmi_enum_dv_timings,
+};
+
+static int mxc_hdmi_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+ state_struct *state = &hdmi_rx->state;
+ struct device *dev = &hdmi_rx->pdev->dev;
+ u8 sts;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (on) {
+ pm_runtime_get_sync(dev);
+ hdmirx_startup(&hdmi_rx->state);
+ } else {
+ /* Reset HDMI RX PHY */
+ CDN_API_HDMIRX_Stop_blocking(state);
+ CDN_API_MainControl_blocking(state, 0, &sts);
+ pm_runtime_put_sync(dev);
+ }
+
+ return 0;
+}
+static struct v4l2_subdev_core_ops imx_core_ops_hdmi = {
+ .s_power = mxc_hdmi_s_power,
+};
+
+/* -----------------------------------------------------------------------------
+ * v4l2_subdev_ops
+ */
+static const struct v4l2_subdev_ops imx_ops_hdmi = {
+ .core = &imx_core_ops_hdmi,
+ .video = &imx_video_ops_hdmi,
+ .pad = &imx_pad_ops_hdmi,
+};
+
+void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+ sc_err_t sciErr;
+
+ dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
+ /* set the pixel link mode and pixel type */
+ sciErr = sc_misc_set_control(hdmi_rx->ipcHndl, SC_R_HDMI_RX, SC_C_PHY_RESET, reset);
+ if (sciErr != SC_ERR_NONE)
+ DRM_ERROR("SC_R_HDMI PHY reset failed %d!\n", sciErr);
+}
+
+static int imx8qm_hdp_read(struct hdp_mem *mem, u32 addr, u32 *value)
+{
+ u32 temp;
+ void *tmp_addr = (addr & 0xfff) + mem->regs_base;
+ void *off_addr = 0x4 + mem->ss_base;;
+
+ __raw_writel(addr >> 12, off_addr);
+ temp = __raw_readl((volatile u32 *)tmp_addr);
+
+ *value = temp;
+ return 0;
+}
+
+static int imx8qm_hdp_write(struct hdp_mem *mem, u32 addr, u32 value)
+{
+ void *tmp_addr = (addr & 0xfff) + mem->regs_base;
+ void *off_addr = 0x4 + mem->ss_base;;
+
+ __raw_writel(addr >> 12, off_addr);
+
+ __raw_writel(value, (volatile u32 *) tmp_addr);
+
+ return 0;
+}
+
+static int imx8qm_hdp_sread(struct hdp_mem *mem, u32 addr, u32 *value)
+{
+ u32 temp;
+ void *tmp_addr = (addr & 0xfff) + mem->regs_base;
+ void *off_addr = 0xc + mem->ss_base;;
+
+ __raw_writel(addr >> 12, off_addr);
+
+ temp = __raw_readl((volatile u32 *)tmp_addr);
+ *value = temp;
+ return 0;
+}
+
+static int imx8qm_hdp_swrite(struct hdp_mem *mem, u32 addr, u32 value)
+{
+ void *tmp_addr = (addr & 0xfff) + mem->regs_base;
+ void *off_addr = 0xc + mem->ss_base;
+
+ __raw_writel(addr >> 12, off_addr);
+ __raw_writel(value, (volatile u32 *)tmp_addr);
+
+ return 0;
+}
+static struct hdp_rw_func imx8qm_rw = {
+ .read_reg = imx8qm_hdp_read,
+ .write_reg = imx8qm_hdp_write,
+ .sread_reg = imx8qm_hdp_sread,
+ .swrite_reg = imx8qm_hdp_swrite,
+};
+
+static void mxc_hdmi_state_init(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ state_struct *state = &hdmi_rx->state;
+
+ memset(state, 0, sizeof(state_struct));
+ mutex_init(&state->mutex);
+
+ state->mem = &hdmi_rx->mem;
+ state->rw = &imx8qm_rw;
+}
+
+int mxc_hdmi_init(struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ sc_err_t sciErr;
+ u32 ret = 0;
+
+ dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
+ mxc_hdmi_state_init(hdmi_rx);
+
+ sciErr = sc_ipc_getMuID(&hdmi_rx->mu_id);
+ if (sciErr != SC_ERR_NONE) {
+ DRM_ERROR("Cannot obtain MU ID\n");
+ return -EINVAL;
+ }
+
+ sciErr = sc_ipc_open(&hdmi_rx->ipcHndl, hdmi_rx->mu_id);
+ if (sciErr != SC_ERR_NONE) {
+ DRM_ERROR("sc_ipc_open failed! (sciError = %d)\n", sciErr);
+ return -EINVAL;
+ }
+
+ ret = hdmi_rx_init(&hdmi_rx->state);
+
+ return ret;
+}
+
+static int mxc_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxc_hdmi_rx_dev *hdmi_rx;
+ struct resource *res;
+ int ret = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+ hdmi_rx = devm_kzalloc(dev, sizeof(*hdmi_rx), GFP_KERNEL);
+ if (!hdmi_rx)
+ return -ENOMEM;
+
+ hdmi_rx->pdev = pdev;
+
+ spin_lock_init(&hdmi_rx->slock);
+
+ /* register map */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hdmi_rx->mem.regs_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hdmi_rx->mem.regs_base)) {
+ dev_err(dev, "Failed to get HDMI RX CTRL base register\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ hdmi_rx->mem.ss_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hdmi_rx->mem.ss_base)) {
+ dev_err(dev, "Failed to get HDMI RX CRS base register\n");
+ return -EINVAL;
+ }
+
+#if 0
+ /* TODO B0 */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_warn(dev, "Failed to get IRQ resource\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(dev, res->start, mxc_hdmi_irq_handler,
+ 0, dev_name(dev), mxc_hdmi);
+ if (ret < 0) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ return -EINVAL;
+ }
+#endif
+
+ v4l2_subdev_init(&hdmi_rx->sd, &imx_ops_hdmi);
+ /* sd.dev may use by match_of */
+ hdmi_rx->sd.dev = dev;
+
+ /* the owner is the same as the i2c_client's driver owner */
+ hdmi_rx->sd.owner = THIS_MODULE;
+ hdmi_rx->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ hdmi_rx->sd.entity.function = MEDIA_ENT_F_IO_DTV;
+
+ /* This allows to retrieve the platform device id by the host driver */
+ v4l2_set_subdevdata(&hdmi_rx->sd, pdev);
+
+ /* initialize name */
+ snprintf(hdmi_rx->sd.name, sizeof(hdmi_rx->sd.name), "%s",
+ MXC_HDMI_RX_SUBDEV_NAME);
+
+ hdmi_rx->pads[MXC_HDMI_RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ hdmi_rx->pads[MXC_HDMI_RX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ hdmi_rx->sd.entity.ops = &hdmi_media_ops;
+ ret = media_entity_pads_init(&hdmi_rx->sd.entity,
+ MXC_HDMI_RX_PADS_NUM, hdmi_rx->pads);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, hdmi_rx);
+ ret = v4l2_async_register_subdev(&hdmi_rx->sd);
+ if (ret < 0) {
+ dev_err(dev,
+ "%s--Async register failed, ret=%d\n", __func__, ret);
+ media_entity_cleanup(&hdmi_rx->sd.entity);
+ }
+
+ mxc_hdmi_clock_init(hdmi_rx);
+
+ hdmi_rx->flags = MXC_HDMI_RX_PM_POWERED;
+
+ mxc_hdmi_clock_enable(hdmi_rx);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ ret = mxc_hdmi_init(hdmi_rx);
+ pm_runtime_put_sync(dev);
+ if (ret) {
+ dev_info(dev, "mxc hdmi rx init failed\n");
+ goto failed;
+ }
+
+ dev_info(dev, "mxc hdmi rx probe successfully\n");
+
+ return ret;
+failed:
+ v4l2_async_unregister_subdev(&hdmi_rx->sd);
+ media_entity_cleanup(&hdmi_rx->sd.entity);
+
+ mxc_hdmi_clock_disable(hdmi_rx);
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static int mxc_hdmi_remove(struct platform_device *pdev)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ dev_dbg(dev, "%s\n", __func__);
+ v4l2_async_unregister_subdev(&hdmi_rx->sd);
+ media_entity_cleanup(&hdmi_rx->sd.entity);
+
+ mxc_hdmi_clock_disable(hdmi_rx);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mxc_hdmi_pm_suspend(struct device *dev)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ if ((hdmi_rx->flags & MXC_HDMI_RX_PM_SUSPENDED) ||
+ (hdmi_rx->flags & MXC_HDMI_RX_RUNTIME_SUSPEND))
+ return 0;
+
+ mxc_hdmi_clock_disable(hdmi_rx);
+ hdmi_rx->flags |= MXC_HDMI_RX_PM_SUSPENDED;
+ hdmi_rx->flags &= ~MXC_HDMI_RX_PM_POWERED;
+
+ return 0;
+}
+
+static int mxc_hdmi_pm_resume(struct device *dev)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (hdmi_rx->flags & MXC_HDMI_RX_PM_POWERED)
+ return 0;
+
+ hdmi_rx->flags |= MXC_HDMI_RX_PM_POWERED;
+ hdmi_rx->flags &= ~MXC_HDMI_RX_PM_SUSPENDED;
+
+ ret = mxc_hdmi_clock_enable(hdmi_rx);
+ return (ret) ? -EAGAIN : 0;
+}
+#endif
+
+static int mxc_hdmi_runtime_suspend(struct device *dev)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (hdmi_rx->flags & MXC_HDMI_RX_RUNTIME_SUSPEND)
+ return 0;
+
+ if (hdmi_rx->flags & MXC_HDMI_RX_PM_POWERED) {
+ mxc_hdmi_clock_disable(hdmi_rx);
+ hdmi_rx->flags |= MXC_HDMI_RX_RUNTIME_SUSPEND;
+ hdmi_rx->flags &= ~MXC_HDMI_RX_PM_POWERED;
+ }
+ return 0;
+}
+
+static int mxc_hdmi_runtime_resume(struct device *dev)
+{
+ struct mxc_hdmi_rx_dev *hdmi_rx = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (hdmi_rx->flags & MXC_HDMI_RX_PM_POWERED)
+ return 0;
+
+ if (hdmi_rx->flags & MXC_HDMI_RX_RUNTIME_SUSPEND) {
+ mxc_hdmi_clock_enable(hdmi_rx);
+ hdmi_rx->flags |= MXC_HDMI_RX_PM_POWERED;
+ hdmi_rx->flags &= ~MXC_HDMI_RX_RUNTIME_SUSPEND;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops mxc_hdmi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mxc_hdmi_pm_suspend, mxc_hdmi_pm_resume)
+ SET_RUNTIME_PM_OPS(mxc_hdmi_runtime_suspend, mxc_hdmi_runtime_resume, NULL)
+};
+
+static const struct of_device_id mxc_hdmi_of_match[] = {
+ {.compatible = "fsl,imx-hdmi-rx",},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mxc_hdmi_of_match);
+
+static struct platform_driver mxc_hdmi_driver = {
+ .probe = mxc_hdmi_probe,
+ .remove = mxc_hdmi_remove,
+ .driver = {
+ .of_match_table = mxc_hdmi_of_match,
+ .name = MXC_HDMI_RX_DRIVER_NAME,
+ .pm = &mxc_hdmi_pm_ops,
+ }
+};
+
+module_platform_driver(mxc_hdmi_driver);
diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h
new file mode 100644
index 000000000000..6f15f7bd89c3
--- /dev/null
+++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MXC_HDMI_RX_
+#define _MXC_HDMI_RX_
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <soc/imx8/sc/sci.h>
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+
+#include <uapi/linux/v4l2-dv-timings.h>
+
+#include "../../../../mxc/hdp/all.h"
+
+#define state_to_mxc_hdmirx(env) \
+ container_of(env, struct mxc_hdmi_rx_dev, state)
+
+#define MXC_HDMI_RX_DRIVER_NAME "mxc-hdmi-rx"
+#define MXC_HDMI_RX_SUBDEV_NAME MXC_HDMI_RX_DRIVER_NAME
+
+#define MXC_HDMI_RX_NODE_NAME "hdmi_rx"
+
+#define MXC_HDMI_RX_MAX_DEVS 2
+#define MXC_HDMI_RX_MAX_LANES 4
+
+#define MXC_HDMI_RX_PAD_SINK 1
+#define MXC_HDMI_RX_PAD_SOURCE 2
+#define MXC_HDMI_RX_PADS_NUM 3
+
+#define CSR_PIXEL_LINK_ENC_CTL 0x00
+#define PL_ENC_CTL_PXL_VAL 15
+#define PL_ENC_CTL_PXL_VPP 14
+#define PL_ENC_CTL_PXL_HPP 13
+#define PL_ENC_CTL_PXL_VCP 12
+#define PL_ENC_CTL_PXL_HCP 11
+#define PL_ENC_CTL_PXL_ADD 9
+#define PL_ENC_CTL_PXL_EXT 7
+#define PL_ENC_CTL_PXL_EN 6
+#define PL_ENC_CTL_PXL_ITC 4
+#define PL_ENC_CTL_PXL_ODD_EVEN 3
+#define PL_ENC_CTL_PXL_TYP 1
+#define PL_ENC_CTL_PXL_YUV 0
+
+#define CSR_HDP_RX_CTRL_CTRL0 0x04
+#define CSR_HDP_RX_CTRL_CTRL1 0x08
+
+struct mxc_hdmi_rx_dev_video_standards {
+ struct v4l2_dv_timings timings;
+ u8 vic;
+ u8 hdmi_vic;
+ u8 fps;
+};
+
+struct mxc_hdmi_rx_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ wait_queue_head_t irq_queue;
+ struct media_pad pads[MXC_HDMI_RX_PADS_NUM];
+
+ struct platform_device *pdev;
+ struct v4l2_device *v4l2_dev;
+ struct v4l2_subdev sd;
+ struct v4l2_async_subdev asd;
+ struct v4l2_ctrl_handler ctrl_hdl;
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_fract aspect_ratio;
+ struct {
+ u8 edid[512];
+ u32 present;
+ u32 blocks;
+ } edid;
+
+ state_struct state;
+ struct clk *sclk;
+ struct clk *pclk;
+ struct clk *ref_clk;
+ struct clk *pxl_clk;
+ struct clk *enc_clk;
+ struct clk *spdif_clk;
+ struct clk *pxl_link_clk;
+ struct hdp_rw_func *rw;
+ struct hdp_mem mem;
+
+ u32 flags;
+ sc_ipc_t ipcHndl;
+ u32 mu_id;
+ S_HDMI_SCDC_GET_MSG scdcData;
+
+ struct mxc_hdmi_rx_dev_video_standards *timings;
+ u8 vic_code;
+ u8 hdmi_vic;
+ u8 pixel_encoding;
+ u8 color_depth;
+};
+
+enum mxc_hdmi_rx_power_state {
+ MXC_HDMI_RX_PM_SUSPENDED = 0x01,
+ MXC_HDMI_RX_PM_POWERED = 0x02,
+ MXC_HDMI_RX_RUNTIME_SUSPEND = 0x04,
+};
+
+int hdmirx_startup(state_struct *state);
+void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset);
+int hdmi_rx_init(state_struct *state);
+int mxc_hdmi_frame_timing(struct mxc_hdmi_rx_dev *hdmi_rx);
+
+#endif
diff --git a/drivers/media/platform/imx8/mxc-isi-cap.c b/drivers/media/platform/imx8/mxc-isi-cap.c
index e8a2b2b001af..ae43f874cd79 100644
--- a/drivers/media/platform/imx8/mxc-isi-cap.c
+++ b/drivers/media/platform/imx8/mxc-isi-cap.c
@@ -167,6 +167,7 @@ struct mxc_isi_fmt *mxc_isi_get_src_fmt(struct v4l2_subdev_format *sd_fmt)
/* two fmt RGB32 and YUV444 from pixellink */
if (sd_fmt->format.code == MEDIA_BUS_FMT_YUYV8_1X16 ||
sd_fmt->format.code == MEDIA_BUS_FMT_YVYU8_2X8 ||
+ sd_fmt->format.code == MEDIA_BUS_FMT_AYUV8_1X32 ||
sd_fmt->format.code == MEDIA_BUS_FMT_UYVY8_2X8)
index = 1;
else
@@ -859,11 +860,11 @@ static int mxc_isi_cap_streamon(struct file *file, void *priv,
int ret;
dev_dbg(&mxc_isi->pdev->dev, "%s\n", __func__);
- mxc_isi_pipeline_enable(mxc_isi, 1);
+ mxc_isi_channel_enable(mxc_isi);
ret = vb2_ioctl_streamon(file, priv, type);
- mxc_isi_channel_enable(mxc_isi);
+ mxc_isi_pipeline_enable(mxc_isi, 1);
return ret;
}
@@ -875,10 +876,11 @@ static int mxc_isi_cap_streamoff(struct file *file, void *priv,
int ret;
dev_dbg(&mxc_isi->pdev->dev, "%s\n", __func__);
- mxc_isi_pipeline_enable(mxc_isi, 0);
+ mxc_isi_channel_disable(mxc_isi);
ret = vb2_ioctl_streamoff(file, priv, type);
- mxc_isi_channel_disable(mxc_isi);
+
+ mxc_isi_pipeline_enable(mxc_isi, 0);
return ret;
}
diff --git a/drivers/media/platform/imx8/mxc-isi-core.c b/drivers/media/platform/imx8/mxc-isi-core.c
index 66ed18ebc42a..b57c6cf630c4 100644
--- a/drivers/media/platform/imx8/mxc-isi-core.c
+++ b/drivers/media/platform/imx8/mxc-isi-core.c
@@ -102,6 +102,8 @@ static int mxc_isi_parse_dt(struct mxc_isi_dev *mxc_isi)
dev_dbg(dev, "%s, isi_%d,interface(%d, %d, %d)\n", __func__, mxc_isi->id,
mxc_isi->interface[0], mxc_isi->interface[1], mxc_isi->interface[2]);
+
+ mxc_isi->chain_buf = of_property_read_bool(node, "fsl,chain_buf");
return 0;
}
@@ -177,6 +179,7 @@ static int mxc_isi_probe(struct platform_device *pdev)
goto err_sclk;
}
+ mxc_isi_channel_set_chain_buf(mxc_isi);
mxc_isi->flags = MXC_ISI_PM_POWERED;
pm_runtime_set_active(dev);
diff --git a/drivers/media/platform/imx8/mxc-isi-core.h b/drivers/media/platform/imx8/mxc-isi-core.h
index f77227fc8350..44af7aa736e8 100644
--- a/drivers/media/platform/imx8/mxc-isi-core.h
+++ b/drivers/media/platform/imx8/mxc-isi-core.h
@@ -277,6 +277,7 @@ struct mxc_isi_dev {
u32 interface[MAX_PORTS];
u32 flags;
+ u8 chain_buf;
/* scale factor */
u32 xfactor;
diff --git a/drivers/media/platform/imx8/mxc-isi-hw.c b/drivers/media/platform/imx8/mxc-isi-hw.c
index 54e168bc33eb..526f1e6aa0e0 100644
--- a/drivers/media/platform/imx8/mxc-isi-hw.c
+++ b/drivers/media/platform/imx8/mxc-isi-hw.c
@@ -21,44 +21,44 @@ void dump_isi_regs(struct mxc_isi_dev *mxc_isi)
struct device *dev = &mxc_isi->pdev->dev;
dev_dbg(dev, "ISI CHNLC register dump, isi%d\n", mxc_isi->id);
- dev_dbg(dev, "CHNL_CTRL 0x0h = 0x%x\n", readl(mxc_isi->regs + 0x0));
- dev_dbg(dev, "CHNL_IMG_CTRL 0x4h = 0x%x\n", readl(mxc_isi->regs + 0x4));
- dev_dbg(dev, "CHNL_OUT_BUF_CTRL 0x8h = 0x%x\n", readl(mxc_isi->regs + 0x8));
- dev_dbg(dev, "CHNL_IMG_CFG 0xCh = 0x%x\n", readl(mxc_isi->regs + 0xC));
- dev_dbg(dev, "CHNL_IER 0x10h = 0x%x\n", readl(mxc_isi->regs + 0x10));
- dev_dbg(dev, "CHNL_STS 0x14h = 0x%x\n", readl(mxc_isi->regs + 0x14));
- dev_dbg(dev, "CHNL_SCALE_FACTOR 0x18h = 0x%x\n", readl(mxc_isi->regs + 0x18));
- dev_dbg(dev, "CHNL_SCALE_OFFSET 0x1Ch = 0x%x\n", readl(mxc_isi->regs + 0x1C));
- dev_dbg(dev, "CHNL_CROP_ULC 0x20h = 0x%x\n", readl(mxc_isi->regs + 0x20));
- dev_dbg(dev, "CHNL_CROP_LRC 0x24h = 0x%x\n", readl(mxc_isi->regs + 0x24));
- dev_dbg(dev, "CHNL_CSC_COEFF0 0x28h = 0x%x\n", readl(mxc_isi->regs + 0x28));
- dev_dbg(dev, "CHNL_CSC_COEFF1 0x2Ch = 0x%x\n", readl(mxc_isi->regs + 0x2C));
- dev_dbg(dev, "CHNL_CSC_COEFF2 0x30h = 0x%x\n", readl(mxc_isi->regs + 0x30));
- dev_dbg(dev, "CHNL_CSC_COEFF3 0x34h = 0x%x\n", readl(mxc_isi->regs + 0x34));
- dev_dbg(dev, "CHNL_CSC_COEFF4 0x38h = 0x%x\n", readl(mxc_isi->regs + 0x38));
- dev_dbg(dev, "CHNL_CSC_COEFF5 0x3Ch = 0x%x\n", readl(mxc_isi->regs + 0x3C));
- dev_dbg(dev, "CHNL_ROI_0_ALPHA 0x40h = 0x%x\n", readl(mxc_isi->regs + 0x40));
- dev_dbg(dev, "CHNL_ROI_0_ULC 0x44h = 0x%x\n", readl(mxc_isi->regs + 0x44));
- dev_dbg(dev, "CHNL_ROI_0_LRC 0x48h = 0x%x\n", readl(mxc_isi->regs + 0x48));
- dev_dbg(dev, "CHNL_ROI_1_ALPHA 0x4Ch = 0x%x\n", readl(mxc_isi->regs + 0x4C));
- dev_dbg(dev, "CHNL_ROI_1_ULC 0x50h = 0x%x\n", readl(mxc_isi->regs + 0x50));
- dev_dbg(dev, "CHNL_ROI_1_LRC 0x54h = 0x%x\n", readl(mxc_isi->regs + 0x54));
- dev_dbg(dev, "CHNL_ROI_2_ALPHA 0x58h = 0x%x\n", readl(mxc_isi->regs + 0x58));
- dev_dbg(dev, "CHNL_ROI_2_ULC 0x5Ch = 0x%x\n", readl(mxc_isi->regs + 0x5C));
- dev_dbg(dev, "CHNL_ROI_2_LRC 0x60h = 0x%x\n", readl(mxc_isi->regs + 0x60));
- dev_dbg(dev, "CHNL_ROI_3_ALPHA 0x64h = 0x%x\n", readl(mxc_isi->regs + 0x64));
- dev_dbg(dev, "CHNL_ROI_3_ULC 0x68h = 0x%x\n", readl(mxc_isi->regs + 0x68));
- dev_dbg(dev, "CHNL_ROI_3_LRC 0x6Ch = 0x%x\n", readl(mxc_isi->regs + 0x6C));
- dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_Y 0x70h = 0x%x\n", readl(mxc_isi->regs + 0x70));
- dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_U 0x74h = 0x%x\n", readl(mxc_isi->regs + 0x74));
- dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_V 0x78h = 0x%x\n", readl(mxc_isi->regs + 0x78));
- dev_dbg(dev, "CHNL_OUT_BUF_PITCH 0x7Ch = 0x%x\n", readl(mxc_isi->regs + 0x7C));
- dev_dbg(dev, "CHNL_IN_BUF_ADDR 0x80h = 0x%x\n", readl(mxc_isi->regs + 0x80));
- dev_dbg(dev, "CHNL_IN_BUF_PITCH 0x84h = 0x%x\n", readl(mxc_isi->regs + 0x84));
- dev_dbg(dev, "CHNL_MEM_RD_CTRL 0x88h = 0x%x\n", readl(mxc_isi->regs + 0x88));
- dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_Y 0x8Ch = 0x%x\n", readl(mxc_isi->regs + 0x8C));
- dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_U 0x90h = 0x%x\n", readl(mxc_isi->regs + 0x90));
- dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_V 0x94h = 0x%x\n", readl(mxc_isi->regs + 0x94));
+ dev_dbg(dev, "CHNL_CTRL 0x0h = 0x%8x\n", readl(mxc_isi->regs + 0x0));
+ dev_dbg(dev, "CHNL_IMG_CTRL 0x4h = 0x%8x\n", readl(mxc_isi->regs + 0x4));
+ dev_dbg(dev, "CHNL_OUT_BUF_CTRL 0x8h = 0x%8x\n", readl(mxc_isi->regs + 0x8));
+ dev_dbg(dev, "CHNL_IMG_CFG 0xCh = 0x%8x\n", readl(mxc_isi->regs + 0xC));
+ dev_dbg(dev, "CHNL_IER 0x10h = 0x%8x\n", readl(mxc_isi->regs + 0x10));
+ dev_dbg(dev, "CHNL_STS 0x14h = 0x%8x\n", readl(mxc_isi->regs + 0x14));
+ dev_dbg(dev, "CHNL_SCALE_FACTOR 0x18h = 0x%8x\n", readl(mxc_isi->regs + 0x18));
+ dev_dbg(dev, "CHNL_SCALE_OFFSET 0x1Ch = 0x%8x\n", readl(mxc_isi->regs + 0x1C));
+ dev_dbg(dev, "CHNL_CROP_ULC 0x20h = 0x%8x\n", readl(mxc_isi->regs + 0x20));
+ dev_dbg(dev, "CHNL_CROP_LRC 0x24h = 0x%8x\n", readl(mxc_isi->regs + 0x24));
+ dev_dbg(dev, "CHNL_CSC_COEFF0 0x28h = 0x%8x\n", readl(mxc_isi->regs + 0x28));
+ dev_dbg(dev, "CHNL_CSC_COEFF1 0x2Ch = 0x%8x\n", readl(mxc_isi->regs + 0x2C));
+ dev_dbg(dev, "CHNL_CSC_COEFF2 0x30h = 0x%8x\n", readl(mxc_isi->regs + 0x30));
+ dev_dbg(dev, "CHNL_CSC_COEFF3 0x34h = 0x%8x\n", readl(mxc_isi->regs + 0x34));
+ dev_dbg(dev, "CHNL_CSC_COEFF4 0x38h = 0x%8x\n", readl(mxc_isi->regs + 0x38));
+ dev_dbg(dev, "CHNL_CSC_COEFF5 0x3Ch = 0x%8x\n", readl(mxc_isi->regs + 0x3C));
+ dev_dbg(dev, "CHNL_ROI_0_ALPHA 0x40h = 0x%8x\n", readl(mxc_isi->regs + 0x40));
+ dev_dbg(dev, "CHNL_ROI_0_ULC 0x44h = 0x%8x\n", readl(mxc_isi->regs + 0x44));
+ dev_dbg(dev, "CHNL_ROI_0_LRC 0x48h = 0x%8x\n", readl(mxc_isi->regs + 0x48));
+ dev_dbg(dev, "CHNL_ROI_1_ALPHA 0x4Ch = 0x%8x\n", readl(mxc_isi->regs + 0x4C));
+ dev_dbg(dev, "CHNL_ROI_1_ULC 0x50h = 0x%8x\n", readl(mxc_isi->regs + 0x50));
+ dev_dbg(dev, "CHNL_ROI_1_LRC 0x54h = 0x%8x\n", readl(mxc_isi->regs + 0x54));
+ dev_dbg(dev, "CHNL_ROI_2_ALPHA 0x58h = 0x%8x\n", readl(mxc_isi->regs + 0x58));
+ dev_dbg(dev, "CHNL_ROI_2_ULC 0x5Ch = 0x%8x\n", readl(mxc_isi->regs + 0x5C));
+ dev_dbg(dev, "CHNL_ROI_2_LRC 0x60h = 0x%8x\n", readl(mxc_isi->regs + 0x60));
+ dev_dbg(dev, "CHNL_ROI_3_ALPHA 0x64h = 0x%8x\n", readl(mxc_isi->regs + 0x64));
+ dev_dbg(dev, "CHNL_ROI_3_ULC 0x68h = 0x%8x\n", readl(mxc_isi->regs + 0x68));
+ dev_dbg(dev, "CHNL_ROI_3_LRC 0x6Ch = 0x%8x\n", readl(mxc_isi->regs + 0x6C));
+ dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_Y 0x70h = 0x%8x\n", readl(mxc_isi->regs + 0x70));
+ dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_U 0x74h = 0x%8x\n", readl(mxc_isi->regs + 0x74));
+ dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_V 0x78h = 0x%8x\n", readl(mxc_isi->regs + 0x78));
+ dev_dbg(dev, "CHNL_OUT_BUF_PITCH 0x7Ch = 0x%8x\n", readl(mxc_isi->regs + 0x7C));
+ dev_dbg(dev, "CHNL_IN_BUF_ADDR 0x80h = 0x%8x\n", readl(mxc_isi->regs + 0x80));
+ dev_dbg(dev, "CHNL_IN_BUF_PITCH 0x84h = 0x%8x\n", readl(mxc_isi->regs + 0x84));
+ dev_dbg(dev, "CHNL_MEM_RD_CTRL 0x88h = 0x%8x\n", readl(mxc_isi->regs + 0x88));
+ dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_Y 0x8Ch = 0x%8x\n", readl(mxc_isi->regs + 0x8C));
+ dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_U 0x90h = 0x%8x\n", readl(mxc_isi->regs + 0x90));
+ dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_V 0x94h = 0x%8x\n", readl(mxc_isi->regs + 0x94));
}
#else
void dump_isi_regs(struct mxc_isi_dev *mxc_isi)
@@ -156,6 +156,8 @@ void mxc_isi_channel_hw_reset(struct mxc_isi_dev *mxc_isi)
if (sciErr != SC_ERR_NONE)
pr_err("sc_misc_MIPI reset failed! (sciError = %d)\n", sciErr);
+ msleep(10);
+
sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_ISI_CH0, SC_PM_PW_MODE_ON);
if (sciErr != SC_ERR_NONE)
pr_err("sc_misc_MIPI reset failed! (sciError = %d)\n", sciErr);
@@ -307,17 +309,18 @@ void mxc_isi_channel_set_alpha(struct mxc_isi_dev *mxc_isi)
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
}
-void mxc_isi_channel_set_4k(struct mxc_isi_dev *mxc_isi)
+void mxc_isi_channel_set_chain_buf(struct mxc_isi_dev *mxc_isi)
{
- struct mxc_isi_frame *src_f = &mxc_isi->isi_cap.src_f;
u32 val;
- val = readl(mxc_isi->regs + CHNL_CTRL);
- val &= ~CHNL_CTRL_CHAIN_BUF_MASK;
- if (src_f->width > 2046)
+ if (mxc_isi->chain_buf) {
+ printk("%s\n", __func__);
+ val = readl(mxc_isi->regs + CHNL_CTRL);
+ val &= ~CHNL_CTRL_CHAIN_BUF_MASK;
val |= (CHNL_CTRL_CHAIN_BUF_2_CHAIN << CHNL_CTRL_CHAIN_BUF_OFFSET);
- writel(val, mxc_isi->regs + CHNL_CTRL);
+ writel(val, mxc_isi->regs + CHNL_CTRL);
+ }
}
void mxc_isi_channel_deinterlace_init(struct mxc_isi_dev *mxc_isi)
@@ -520,7 +523,6 @@ void mxc_isi_channel_config(struct mxc_isi_dev *mxc_isi)
if (mxc_isi->alphaen)
mxc_isi_channel_set_alpha(mxc_isi);
- mxc_isi_channel_set_4k(mxc_isi);
#endif
val = readl(mxc_isi->regs + CHNL_CTRL);
diff --git a/drivers/media/platform/imx8/mxc-isi-hw.h b/drivers/media/platform/imx8/mxc-isi-hw.h
index 77e7469c9b4a..737008b41fd0 100644
--- a/drivers/media/platform/imx8/mxc-isi-hw.h
+++ b/drivers/media/platform/imx8/mxc-isi-hw.h
@@ -469,7 +469,7 @@ void mxc_isi_channel_set_flip(struct mxc_isi_dev *mxc_isi);
void mxc_isi_channel_set_csc(struct mxc_isi_dev *mxc_isi);
void mxc_isi_channel_set_alpha_roi0(struct mxc_isi_dev *mxc_isi, struct v4l2_rect *rect);
void mxc_isi_channel_set_alpha(struct mxc_isi_dev *mxc_isi);
-void mxc_isi_channel_set_4k(struct mxc_isi_dev *mxc_isi);
+void mxc_isi_channel_set_chain_buf(struct mxc_isi_dev *mxc_isi);
void mxc_isi_channel_set_deinterlace(struct mxc_isi_dev *mxc_isi);
void mxc_isi_channel_set_crop(struct mxc_isi_dev *mxc_isi);
void mxc_isi_channel_set_memory_image(struct mxc_isi_dev *mxc_isi);
diff --git a/drivers/media/platform/imx8/mxc-media-dev.c b/drivers/media/platform/imx8/mxc-media-dev.c
index 9f7b920b9222..6acefd892d13 100644
--- a/drivers/media/platform/imx8/mxc-media-dev.c
+++ b/drivers/media/platform/imx8/mxc-media-dev.c
@@ -161,6 +161,13 @@ static int mxc_md_create_links(struct mxc_md *mxc_md)
break;
case ISI_INPUT_INTERFACE_HDMI:
+ if (mxc_md->hdmi_rx == NULL)
+ continue;
+ source = &mxc_md->hdmi_rx->sd.entity;
+ source_pad = MXC_HDMI_RX_PAD_SOURCE;
+ sink_pad = MXC_ISI_SD_PAD_SINK_HDMI;
+ break;
+
case ISI_INPUT_INTERFACE_DC0:
case ISI_INPUT_INTERFACE_DC1:
case ISI_INPUT_INTERFACE_MEM:
@@ -227,15 +234,15 @@ static int mxc_md_create_links(struct mxc_md *mxc_md)
return ret;
v4l2_info(&mxc_md->v4l2_dev, "created link [%s] => [%s]\n",
sensor->sd->entity.name, pcsidev->sd.entity.name);
- } else {
+ } else if (mxc_md->mipi_csi2) {
mipi_csi2 = mxc_md->mipi_csi2[sensor->id];
if (mipi_csi2 == NULL)
continue;
source = &sensor->sd->entity;
sink = &mipi_csi2->sd.entity;
- source_pad = 0; //sensor source pad: MIPI_CSI2_SENS_VC0_PAD_SOURCE
- sink_pad = source_pad; //mipi sink pad: MXC_MIPI_CSI2_VC0_PAD_SINK;
+ source_pad = 0; /* sensor source pad: MIPI_CSI2_SENS_VC0_PAD_SOURCE */
+ sink_pad = source_pad; /* mipi sink pad: MXC_MIPI_CSI2_VC0_PAD_SINK; */
if (mipi_csi2->vchannel == true)
mipi_vc = 4;
@@ -276,6 +283,7 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
struct mxc_sensor_info *sensor = NULL;
int i;
+ dev_dbg(&mxc_md->pdev->dev, "%s\n", __func__);
/* Find platform data for this sensor subdev */
for (i = 0; i < ARRAY_SIZE(mxc_md->sensor); i++) {
if (mxc_md->sensor[i].asd.match.fwnode.fwnode ==
@@ -303,6 +311,7 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
struct mxc_md *mxc_md = notifier_to_mxc_md(notifier);
int ret;
+ dev_dbg(&mxc_md->pdev->dev, "%s\n", __func__);
mutex_lock(&mxc_md->media_dev.graph_mutex);
ret = mxc_md_create_links(mxc_md);
@@ -330,7 +339,7 @@ void mxc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
return;
}
-/* Register mipi sensor sub-devices */
+/* Register mipi sensor / Parallel CSI / HDMI Rx sub-devices */
static int register_sensor_entities(struct mxc_md *mxc_md)
{
struct device_node *parent = mxc_md->pdev->dev.of_node;
@@ -340,10 +349,20 @@ static int register_sensor_entities(struct mxc_md *mxc_md)
mxc_md->num_sensors = 0;
- /* Attach sensors linked to MIPI CSI2 */
+ /* Attach sensors linked to MIPI CSI2 / paralle csi / HDMI Rx */
for_each_available_child_of_node(parent, node) {
struct device_node *port;
+ if (!of_node_cmp(node->name, "hdmi_rx")) {
+ mxc_md->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ mxc_md->sensor[index].asd.match.fwnode.fwnode = of_fwnode_handle(node);
+ mxc_md->async_subdevs[index] = &mxc_md->sensor[index].asd;
+
+ mxc_md->num_sensors++;
+ index++;
+ continue;
+ }
+
if (of_node_cmp(node->name, "csi") &&
of_node_cmp(node->name, "pcsi"))
continue;
@@ -362,7 +381,7 @@ static int register_sensor_entities(struct mxc_md *mxc_md)
return -EINVAL;
v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint);
- if (WARN_ON(endpoint.base.port >= MXC_MAX_MIPI_SENSORS)) {
+ if (WARN_ON(endpoint.base.port >= MXC_MAX_SENSORS)) {
v4l2_err(&mxc_md->v4l2_dev, "Failed to get sensor endpoint\n");
return -EINVAL;
}
@@ -398,6 +417,7 @@ static int register_isi_entity(struct mxc_md *mxc_md, struct mxc_isi_dev *mxc_is
struct v4l2_subdev *sd = &mxc_isi->isi_cap.sd;
int ret;
+ dev_dbg(&mxc_md->pdev->dev, "%s\n", __func__);
if (WARN_ON(mxc_isi->id >= MXC_ISI_MAX_DEVS || mxc_md->mxc_isi[mxc_isi->id]))
return -EBUSY;
@@ -447,6 +467,18 @@ static int register_parallel_csi_entity(struct mxc_md *mxc_md,
return ret;
}
+static int register_hdmi_rx_entity(struct mxc_md *mxc_md,
+ struct mxc_hdmi_rx_dev *hdmi_rx)
+{
+ struct v4l2_subdev *sd = &hdmi_rx->sd;;
+
+ dev_dbg(&mxc_md->pdev->dev, "%s\n", __func__);
+ sd->grp_id = GRP_ID_MXC_HDMI_RX;
+ mxc_md->hdmi_rx = hdmi_rx;
+
+ return 0;
+}
+
static int mxc_md_register_platform_entity(struct mxc_md *mxc_md,
struct platform_device *pdev,
int plat_entity)
@@ -474,6 +506,9 @@ static int mxc_md_register_platform_entity(struct mxc_md *mxc_md,
case IDX_PARALLEL_CSI:
ret = register_parallel_csi_entity(mxc_md, drvdata);
break;
+ case IDX_HDMI_RX:
+ ret = register_hdmi_rx_entity(mxc_md, drvdata);
+ break;
default:
ret = -ENODEV;
}
@@ -492,7 +527,7 @@ dev_unlock:
return ret;
}
-/* Register ISI, MIPI CSI2 and HDMI In Media entities */
+/* Register ISI, MIPI CSI2 and HDMI Rx Media entities */
static int mxc_md_register_platform_entities(struct mxc_md *mxc_md,
struct device_node *parent)
{
@@ -514,6 +549,8 @@ static int mxc_md_register_platform_entities(struct mxc_md *mxc_md,
plat_entity = IDX_MIPI_CSI2;
else if (!strcmp(node->name, PARALLEL_CSI_OF_NODE_NAME))
plat_entity = IDX_PARALLEL_CSI;
+ else if (!strcmp(node->name, MXC_HDMI_RX_NODE_NAME))
+ plat_entity = IDX_HDMI_RX;
if (plat_entity >= 0)
ret = mxc_md_register_platform_entity(mxc_md, pdev,
@@ -617,7 +654,7 @@ static int mxc_md_probe(struct platform_device *pdev)
ret = v4l2_async_notifier_register(&mxc_md->v4l2_dev,
&mxc_md->subdev_notifier);
if (ret < 0) {
- printk("Sensor register failed\n");
+ dev_warn(&mxc_md->pdev->dev, "Sensor register failed\n");
goto err_m_ent;
}
}
diff --git a/drivers/media/platform/imx8/mxc-media-dev.h b/drivers/media/platform/imx8/mxc-media-dev.h
index 00ebd9f195da..abcd6548a984 100644
--- a/drivers/media/platform/imx8/mxc-media-dev.h
+++ b/drivers/media/platform/imx8/mxc-media-dev.h
@@ -39,9 +39,10 @@
#include "mxc-mipi-csi2.h"
#include "mxc-isi-core.h"
#include "mxc-isi-hw.h"
+#include "hdmi/mxc-hdmi-rx.h"
#define MXC_MD_DRIVER_NAME "mxc-md"
-#define MXC_MAX_MIPI_SENSORS 2
+#define MXC_MAX_SENSORS 3
#define MJPEG_DEC_OF_NODE_NAME "jpegdec"
#define MJPEG_ENC_OF_NODE_NAME "jpegenc"
@@ -52,7 +53,7 @@
#define GRP_ID_MXC_SENSOR (1 << 8)
#define GRP_ID_MXC_ISI (1 << 9)
#define GRP_ID_MXC_MIPI_CSI2 (1 << 11)
-#define GRP_ID_MXC_HDMI_IN (1 << 12)
+#define GRP_ID_MXC_HDMI_RX (1 << 12)
#define GRP_ID_MXC_MJPEG_DEC (1 << 13)
#define GRP_ID_MXC_MJPEG_ENC (1 << 14)
#define GRP_ID_MXC_PARALLEL_CSI (1 << 15)
@@ -61,7 +62,7 @@ enum mxc_subdev_index {
IDX_SENSOR,
IDX_ISI,
IDX_MIPI_CSI2,
- IDX_HDMI_IN,
+ IDX_HDMI_RX,
IDX_MJPEG_ENC,
IDX_MJPEG_DEC,
IDX_PARALLEL_CSI,
@@ -88,10 +89,10 @@ struct mxc_mjpeg_enc{
struct mxc_md {
struct mxc_isi_dev *mxc_isi[MXC_ISI_MAX_DEVS];
- struct mxc_hdmi_in_dev *hdmi_in;
+ struct mxc_hdmi_rx_dev *hdmi_rx;
struct mxc_parallel_csi_dev *pcsidev;
struct mxc_mipi_csi2_dev *mipi_csi2[MXC_MIPI_CSI2_MAX_DEVS];
- struct mxc_sensor_info sensor[MXC_MAX_MIPI_SENSORS];
+ struct mxc_sensor_info sensor[MXC_MAX_SENSORS];
struct mxc_mjpeg_dec *mjpeg_dec;
struct mxc_mjpeg_enc *mjpeg_enc;
@@ -103,7 +104,7 @@ struct mxc_md {
struct platform_device *pdev;
struct v4l2_async_notifier subdev_notifier;
- struct v4l2_async_subdev *async_subdevs[MXC_MAX_MIPI_SENSORS];
+ struct v4l2_async_subdev *async_subdevs[MXC_MAX_SENSORS];
};
static inline struct mxc_md *notifier_to_mxc_md(struct v4l2_async_notifier *n)