123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /*
- * Copyright (C) STMicroelectronics SA 2014
- * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
- * License terms: GNU General Public License (GPL), version 2
- */
- #include "sti_hdmi_tx3g4c28phy.h"
- #define HDMI_SRZ_CFG 0x504
- #define HDMI_SRZ_PLL_CFG 0x510
- #define HDMI_SRZ_ICNTL 0x518
- #define HDMI_SRZ_CALCODE_EXT 0x520
- #define HDMI_SRZ_CFG_EN BIT(0)
- #define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
- #define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16)
- #define HDMI_SRZ_CFG_RBIAS_EXT BIT(17)
- #define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18)
- #define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19)
- #define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24)
- #define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \
- HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
- HDMI_SRZ_CFG_EXTERNAL_DATA | \
- HDMI_SRZ_CFG_RBIAS_EXT | \
- HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \
- HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \
- HDMI_SRZ_CFG_EN_SRC_TERMINATION)
- #define PLL_CFG_EN BIT(0)
- #define PLL_CFG_NDIV_SHIFT (8)
- #define PLL_CFG_IDF_SHIFT (16)
- #define PLL_CFG_ODF_SHIFT (24)
- #define ODF_DIV_1 (0)
- #define ODF_DIV_2 (1)
- #define ODF_DIV_4 (2)
- #define ODF_DIV_8 (3)
- #define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
- struct plldividers_s {
- uint32_t min;
- uint32_t max;
- uint32_t idf;
- uint32_t odf;
- };
- /*
- * Functional specification recommended values
- */
- #define NB_PLL_MODE 5
- static struct plldividers_s plldividers[NB_PLL_MODE] = {
- {0, 20000000, 1, ODF_DIV_8},
- {20000000, 42500000, 2, ODF_DIV_8},
- {42500000, 85000000, 4, ODF_DIV_4},
- {85000000, 170000000, 8, ODF_DIV_2},
- {170000000, 340000000, 16, ODF_DIV_1}
- };
- #define NB_HDMI_PHY_CONFIG 2
- static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
- {0, 250000000, {0x0, 0x0, 0x0, 0x0} },
- {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
- };
- /**
- * Start hdmi phy macro cell tx3g4c28
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * Return false if an error occur
- */
- static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
- {
- u32 ckpxpll = hdmi->mode.clock * 1000;
- u32 val, tmdsck, idf, odf, pllctrl = 0;
- bool foundplldivides = false;
- int i;
- DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
- for (i = 0; i < NB_PLL_MODE; i++) {
- if (ckpxpll >= plldividers[i].min &&
- ckpxpll < plldividers[i].max) {
- idf = plldividers[i].idf;
- odf = plldividers[i].odf;
- foundplldivides = true;
- break;
- }
- }
- if (!foundplldivides) {
- DRM_ERROR("input TMDS clock speed (%d) not supported\n",
- ckpxpll);
- goto err;
- }
- /* Assuming no pixel repetition and 24bits color */
- tmdsck = ckpxpll;
- pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
- if (tmdsck > 340000000) {
- DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
- goto err;
- }
- pllctrl |= idf << PLL_CFG_IDF_SHIFT;
- pllctrl |= odf << PLL_CFG_ODF_SHIFT;
- /*
- * Configure and power up the PHY PLL
- */
- hdmi->event_received = false;
- DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
- hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
- /* wait PLL interrupt */
- wait_event_interruptible_timeout(hdmi->wait_event,
- hdmi->event_received == true,
- msecs_to_jiffies
- (HDMI_TIMEOUT_PLL_LOCK));
- if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
- DRM_ERROR("hdmi phy pll not locked\n");
- goto err;
- }
- DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
- val = (HDMI_SRZ_CFG_EN |
- HDMI_SRZ_CFG_EXTERNAL_DATA |
- HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
- HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
- if (tmdsck > 165000000)
- val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
- /*
- * To configure the source termination and pre-emphasis appropriately
- * for different high speed TMDS clock frequencies a phy configuration
- * table must be provided, tailored to the SoC and board combination.
- */
- for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
- if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
- (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
- val |= (hdmiphy_config[i].config[0]
- & ~HDMI_SRZ_CFG_INTERNAL_MASK);
- hdmi_write(hdmi, val, HDMI_SRZ_CFG);
- val = hdmiphy_config[i].config[1];
- hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
- val = hdmiphy_config[i].config[2];
- hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
- DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
- hdmiphy_config[i].config[0],
- hdmiphy_config[i].config[1],
- hdmiphy_config[i].config[2]);
- return true;
- }
- }
- /*
- * Default, power up the serializer with no pre-emphasis or
- * output swing correction
- */
- hdmi_write(hdmi, val, HDMI_SRZ_CFG);
- hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL);
- hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT);
- return true;
- err:
- return false;
- }
- /**
- * Stop hdmi phy macro cell tx3g4c28
- *
- * @hdmi: pointer on the hdmi internal structure
- */
- static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
- {
- int val = 0;
- DRM_DEBUG_DRIVER("\n");
- hdmi->event_received = false;
- val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
- val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
- hdmi_write(hdmi, val, HDMI_SRZ_CFG);
- hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
- /* wait PLL interrupt */
- wait_event_interruptible_timeout(hdmi->wait_event,
- hdmi->event_received == true,
- msecs_to_jiffies
- (HDMI_TIMEOUT_PLL_LOCK));
- if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
- DRM_ERROR("hdmi phy pll not well disabled\n");
- }
- struct hdmi_phy_ops tx3g4c28phy_ops = {
- .start = sti_hdmi_tx3g4c28phy_start,
- .stop = sti_hdmi_tx3g4c28phy_stop,
- };
|