123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- /*
- * 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_tx3g0c55phy.h"
- #define HDMI_SRZ_PLL_CFG 0x0504
- #define HDMI_SRZ_TAP_1 0x0508
- #define HDMI_SRZ_TAP_2 0x050C
- #define HDMI_SRZ_TAP_3 0x0510
- #define HDMI_SRZ_CTRL 0x0514
- #define HDMI_SRZ_PLL_CFG_POWER_DOWN BIT(0)
- #define HDMI_SRZ_PLL_CFG_VCOR_SHIFT 1
- #define HDMI_SRZ_PLL_CFG_VCOR_425MHZ 0
- #define HDMI_SRZ_PLL_CFG_VCOR_850MHZ 1
- #define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ 2
- #define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ 3
- #define HDMI_SRZ_PLL_CFG_VCOR_MASK 3
- #define HDMI_SRZ_PLL_CFG_VCOR(x) (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT)
- #define HDMI_SRZ_PLL_CFG_NDIV_SHIFT 8
- #define HDMI_SRZ_PLL_CFG_NDIV_MASK (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT)
- #define HDMI_SRZ_PLL_CFG_MODE_SHIFT 16
- #define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ 0x1
- #define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ 0x4
- #define HDMI_SRZ_PLL_CFG_MODE_27_MHZ 0x5
- #define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6
- #define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ 0x7
- #define HDMI_SRZ_PLL_CFG_MODE_54_MHZ 0x8
- #define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ 0x9
- #define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA
- #define HDMI_SRZ_PLL_CFG_MODE_81_MHZ 0xB
- #define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ 0xC
- #define HDMI_SRZ_PLL_CFG_MODE_108_MHZ 0xD
- #define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE
- #define HDMI_SRZ_PLL_CFG_MODE_165_MHZ 0xF
- #define HDMI_SRZ_PLL_CFG_MODE_MASK 0xF
- #define HDMI_SRZ_PLL_CFG_MODE(x) (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT)
- #define HDMI_SRZ_CTRL_POWER_DOWN (1 << 0)
- #define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN (1 << 1)
- /* sysconf registers */
- #define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */
- #define HDMI_REJECTION_PLL_STATUS 0x0948 /* SYSTEM_CONFIG2594 */
- #define REJECTION_PLL_HDMI_ENABLE_SHIFT 0
- #define REJECTION_PLL_HDMI_ENABLE_MASK (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT)
- #define REJECTION_PLL_HDMI_PDIV_SHIFT 24
- #define REJECTION_PLL_HDMI_PDIV_MASK (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT)
- #define REJECTION_PLL_HDMI_NDIV_SHIFT 16
- #define REJECTION_PLL_HDMI_NDIV_MASK (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT)
- #define REJECTION_PLL_HDMI_MDIV_SHIFT 8
- #define REJECTION_PLL_HDMI_MDIV_MASK (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT)
- #define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
- #define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
- /**
- * pll mode structure
- *
- * A pointer to an array of these structures is passed to a TMDS (HDMI) output
- * via the control interface to provide board and SoC specific
- * configurations of the HDMI PHY. Each entry in the array specifies a hardware
- * specific configuration for a given TMDS clock frequency range. The array
- * should be terminated with an entry that has all fields set to zero.
- *
- * @min: Lower bound of TMDS clock frequency this entry applies to
- * @max: Upper bound of TMDS clock frequency this entry applies to
- * @mode: SoC specific register configuration
- */
- struct pllmode {
- u32 min;
- u32 max;
- u32 mode;
- };
- #define NB_PLL_MODE 7
- static struct pllmode pllmodes[NB_PLL_MODE] = {
- {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ},
- {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ},
- {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ},
- {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ},
- {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ},
- {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ},
- {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ}
- };
- #define NB_HDMI_PHY_CONFIG 5
- static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
- {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} },
- {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} },
- {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} },
- {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} },
- {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} },
- };
- #define PLL_CHANGE_DELAY 1 /* ms */
- /**
- * Disable the pll rejection
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * return true if the pll has been disabled
- */
- static bool disable_pll_rejection(struct sti_hdmi *hdmi)
- {
- u32 val;
- DRM_DEBUG_DRIVER("\n");
- val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
- val &= ~REJECTION_PLL_HDMI_ENABLE_MASK;
- writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
- msleep(PLL_CHANGE_DELAY);
- val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
- return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
- }
- /**
- * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL
- * clock input to the new PHY PLL that generates the serializer clock
- * (TMDS*10) and the TMDS clock which is now fed back into the HDMI
- * formatter instead of the TMDS clock line from ClockGenB.
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * return true if pll has been correctly set
- */
- static bool enable_pll_rejection(struct sti_hdmi *hdmi)
- {
- unsigned int inputclock;
- u32 mdiv, ndiv, pdiv, val;
- DRM_DEBUG_DRIVER("\n");
- if (!disable_pll_rejection(hdmi))
- return false;
- inputclock = hdmi->mode.clock * 1000;
- DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock);
- /* Power up the HDMI rejection PLL
- * Note: On this SoC (stiH416) we are forced to have the input clock
- * be equal to the HDMI pixel clock.
- *
- * The values here have been suggested by validation however they are
- * still provisional and subject to change.
- *
- * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
- */
- if (inputclock < 50000000) {
- /*
- * For slower clocks we need to multiply more to keep the
- * internal VCO frequency within the physical specification
- * of the PLL.
- */
- pdiv = 4;
- ndiv = 240;
- mdiv = 30;
- } else {
- pdiv = 2;
- ndiv = 60;
- mdiv = 30;
- }
- val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
- val &= ~(REJECTION_PLL_HDMI_PDIV_MASK |
- REJECTION_PLL_HDMI_NDIV_MASK |
- REJECTION_PLL_HDMI_MDIV_MASK |
- REJECTION_PLL_HDMI_ENABLE_MASK);
- val |= (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) |
- (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) |
- (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) |
- (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT);
- writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
- msleep(PLL_CHANGE_DELAY);
- val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
- return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
- }
- /**
- * Start hdmi phy macro cell tx3g0c55
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * Return false if an error occur
- */
- static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi)
- {
- u32 ckpxpll = hdmi->mode.clock * 1000;
- u32 val, tmdsck, freqvco, pllctrl = 0;
- unsigned int i;
- if (!enable_pll_rejection(hdmi))
- return false;
- DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
- /* Assuming no pixel repetition and 24bits color */
- tmdsck = ckpxpll;
- pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT;
- /*
- * Setup the PLL mode parameter based on the ckpxpll. If we haven't got
- * a clock frequency supported by one of the specific PLL modes then we
- * will end up using the generic mode (0) which only supports a 10x
- * multiplier, hence only 24bit color.
- */
- for (i = 0; i < NB_PLL_MODE; i++) {
- if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max)
- pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode);
- }
- freqvco = tmdsck * 10;
- if (freqvco <= 425000000UL)
- pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ);
- else if (freqvco <= 850000000UL)
- pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ);
- else if (freqvco <= 1700000000UL)
- pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ);
- else if (freqvco <= 2970000000UL)
- pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ);
- else {
- DRM_ERROR("PHY serializer clock out of range\n");
- goto err;
- }
- /*
- * Configure and power up the PHY PLL
- */
- hdmi->event_received = false;
- DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
- hdmi_write(hdmi, pllctrl, 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");
- /*
- * 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_write(hdmi, val, HDMI_SRZ_TAP_1);
- val = hdmiphy_config[i].config[1];
- hdmi_write(hdmi, val, HDMI_SRZ_TAP_2);
- val = hdmiphy_config[i].config[2];
- hdmi_write(hdmi, val, HDMI_SRZ_TAP_3);
- val = hdmiphy_config[i].config[3];
- val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN;
- val &= ~HDMI_SRZ_CTRL_POWER_DOWN;
- hdmi_write(hdmi, val, HDMI_SRZ_CTRL);
- DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n",
- hdmiphy_config[i].config[0],
- hdmiphy_config[i].config[1],
- hdmiphy_config[i].config[2],
- hdmiphy_config[i].config[3]);
- return true;
- }
- }
- /*
- * Default, power up the serializer with no pre-emphasis or source
- * termination.
- */
- hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1);
- hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2);
- hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3);
- hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL);
- return true;
- err:
- disable_pll_rejection(hdmi);
- return false;
- }
- /**
- * Stop hdmi phy macro cell tx3g0c55
- *
- * @hdmi: pointer on the hdmi internal structure
- */
- static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi)
- {
- DRM_DEBUG_DRIVER("\n");
- hdmi->event_received = false;
- hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL);
- hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, 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");
- disable_pll_rejection(hdmi);
- }
- struct hdmi_phy_ops tx3g0c55phy_ops = {
- .start = sti_hdmi_tx3g0c55phy_start,
- .stop = sti_hdmi_tx3g0c55phy_stop,
- };
|