diff options
author | Sandor Yu <Sandor.yu@nxp.com> | 2018-05-10 11:25:40 +0800 |
---|---|---|
committer | Jason Liu <jason.hui.liu@nxp.com> | 2018-10-29 11:10:38 +0800 |
commit | f46db91ee73f882abd82a32aeaf7ab661158c0af (patch) | |
tree | 9e2596c6d38cf348f6323a7ef9a3611cbb5b7f9a /drivers/media/platform | |
parent | 8ec22b9030fe5bbe09695b64912c2bab1f4bd586 (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/Kconfig | 2 | ||||
-rw-r--r-- | drivers/media/platform/imx8/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.c | 1223 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/API_AFE_ss28fdsoi_hdmirx.h | 140 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/Kconfig | 4 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c | 414 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c | 874 | ||||
-rw-r--r-- | drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h | 131 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-isi-cap.c | 10 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-isi-core.c | 3 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-isi-core.h | 1 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-isi-hw.c | 92 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-isi-hw.h | 2 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-media-dev.c | 53 | ||||
-rw-r--r-- | drivers/media/platform/imx8/mxc-media-dev.h | 13 |
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), + ®resp); + } 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), + ®resp); + 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), ®read); + } 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), ®resp); + 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), ®read); + 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), ®read); + + /* 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) |