linux/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) STMicroelectronics SA 2014
   3 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
   4 * License terms:  GNU General Public License (GPL), version 2
   5 */
   6
   7#include "sti_hdmi_tx3g0c55phy.h"
   8
   9#define HDMI_SRZ_PLL_CFG                0x0504
  10#define HDMI_SRZ_TAP_1                  0x0508
  11#define HDMI_SRZ_TAP_2                  0x050C
  12#define HDMI_SRZ_TAP_3                  0x0510
  13#define HDMI_SRZ_CTRL                   0x0514
  14
  15#define HDMI_SRZ_PLL_CFG_POWER_DOWN     BIT(0)
  16#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT     1
  17#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ    0
  18#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ    1
  19#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ   2
  20#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ   3
  21#define HDMI_SRZ_PLL_CFG_VCOR_MASK      3
  22#define HDMI_SRZ_PLL_CFG_VCOR(x)        (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT)
  23#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT     8
  24#define HDMI_SRZ_PLL_CFG_NDIV_MASK      (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT)
  25#define HDMI_SRZ_PLL_CFG_MODE_SHIFT     16
  26#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ  0x1
  27#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ  0x4
  28#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ    0x5
  29#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6
  30#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ  0x7
  31#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ    0x8
  32#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ  0x9
  33#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA
  34#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ    0xB
  35#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ  0xC
  36#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ   0xD
  37#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE
  38#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ   0xF
  39#define HDMI_SRZ_PLL_CFG_MODE_MASK      0xF
  40#define HDMI_SRZ_PLL_CFG_MODE(x)        (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT)
  41
  42#define HDMI_SRZ_CTRL_POWER_DOWN        (1 << 0)
  43#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN  (1 << 1)
  44
  45/* sysconf registers */
  46#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */
  47#define HDMI_REJECTION_PLL_STATUS        0x0948 /* SYSTEM_CONFIG2594 */
  48
  49#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0
  50#define REJECTION_PLL_HDMI_ENABLE_MASK  (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT)
  51#define REJECTION_PLL_HDMI_PDIV_SHIFT   24
  52#define REJECTION_PLL_HDMI_PDIV_MASK    (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT)
  53#define REJECTION_PLL_HDMI_NDIV_SHIFT   16
  54#define REJECTION_PLL_HDMI_NDIV_MASK    (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT)
  55#define REJECTION_PLL_HDMI_MDIV_SHIFT   8
  56#define REJECTION_PLL_HDMI_MDIV_MASK    (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT)
  57
  58#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
  59
  60#define HDMI_TIMEOUT_PLL_LOCK  50   /*milliseconds */
  61
  62/**
  63 * pll mode structure
  64 *
  65 * A pointer to an array of these structures is passed to a TMDS (HDMI) output
  66 * via the control interface to provide board and SoC specific
  67 * configurations of the HDMI PHY. Each entry in the array specifies a hardware
  68 * specific configuration for a given TMDS clock frequency range. The array
  69 * should be terminated with an entry that has all fields set to zero.
  70 *
  71 * @min: Lower bound of TMDS clock frequency this entry applies to
  72 * @max: Upper bound of TMDS clock frequency this entry applies to
  73 * @mode: SoC specific register configuration
  74 */
  75struct pllmode {
  76        u32 min;
  77        u32 max;
  78        u32 mode;
  79};
  80
  81#define NB_PLL_MODE 7
  82static struct pllmode pllmodes[NB_PLL_MODE] = {
  83        {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ},
  84        {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ},
  85        {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ},
  86        {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ},
  87        {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ},
  88        {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ},
  89        {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ}
  90};
  91
  92#define NB_HDMI_PHY_CONFIG 5
  93static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
  94        {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} },
  95        {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} },
  96        {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} },
  97        {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} },
  98        {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} },
  99};
 100
 101#define PLL_CHANGE_DELAY        1 /* ms */
 102
 103/**
 104 * Disable the pll rejection
 105 *
 106 * @hdmi: pointer on the hdmi internal structure
 107 *
 108 * return true if the pll has been disabled
 109 */
 110static bool disable_pll_rejection(struct sti_hdmi *hdmi)
 111{
 112        u32 val;
 113
 114        DRM_DEBUG_DRIVER("\n");
 115
 116        val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
 117        val &= ~REJECTION_PLL_HDMI_ENABLE_MASK;
 118        writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
 119
 120        msleep(PLL_CHANGE_DELAY);
 121        val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
 122
 123        return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
 124}
 125
 126/**
 127 * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL
 128 * clock input to the new PHY PLL that generates the serializer clock
 129 * (TMDS*10) and the TMDS clock which is now fed back into the HDMI
 130 * formatter instead of the TMDS clock line from ClockGenB.
 131 *
 132 * @hdmi: pointer on the hdmi internal structure
 133 *
 134 * return true if pll has been correctly set
 135 */
 136static bool enable_pll_rejection(struct sti_hdmi *hdmi)
 137{
 138        unsigned int inputclock;
 139        u32 mdiv, ndiv, pdiv, val;
 140
 141        DRM_DEBUG_DRIVER("\n");
 142
 143        if (!disable_pll_rejection(hdmi))
 144                return false;
 145
 146        inputclock = hdmi->mode.clock * 1000;
 147
 148        DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock);
 149
 150
 151        /* Power up the HDMI rejection PLL
 152         * Note: On this SoC (stiH416) we are forced to have the input clock
 153         * be equal to the HDMI pixel clock.
 154         *
 155         * The values here have been suggested by validation however they are
 156         * still provisional and subject to change.
 157         *
 158         * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
 159         */
 160        if (inputclock < 50000000) {
 161                /*
 162                 * For slower clocks we need to multiply more to keep the
 163                 * internal VCO frequency within the physical specification
 164                 * of the PLL.
 165                 */
 166                pdiv = 4;
 167                ndiv = 240;
 168                mdiv = 30;
 169        } else {
 170                pdiv = 2;
 171                ndiv = 60;
 172                mdiv = 30;
 173        }
 174
 175        val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
 176
 177        val &= ~(REJECTION_PLL_HDMI_PDIV_MASK |
 178                REJECTION_PLL_HDMI_NDIV_MASK |
 179                REJECTION_PLL_HDMI_MDIV_MASK |
 180                REJECTION_PLL_HDMI_ENABLE_MASK);
 181
 182        val |=  (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) |
 183                (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) |
 184                (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) |
 185                (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT);
 186
 187        writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
 188
 189        msleep(PLL_CHANGE_DELAY);
 190        val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
 191
 192        return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
 193}
 194
 195/**
 196 * Start hdmi phy macro cell tx3g0c55
 197 *
 198 * @hdmi: pointer on the hdmi internal structure
 199 *
 200 * Return false if an error occur
 201 */
 202static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi)
 203{
 204        u32 ckpxpll = hdmi->mode.clock * 1000;
 205        u32 val, tmdsck, freqvco, pllctrl = 0;
 206        unsigned int i;
 207
 208        if (!enable_pll_rejection(hdmi))
 209                return false;
 210
 211        DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
 212
 213        /* Assuming no pixel repetition and 24bits color */
 214        tmdsck = ckpxpll;
 215        pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT;
 216
 217        /*
 218         * Setup the PLL mode parameter based on the ckpxpll. If we haven't got
 219         * a clock frequency supported by one of the specific PLL modes then we
 220         * will end up using the generic mode (0) which only supports a 10x
 221         * multiplier, hence only 24bit color.
 222         */
 223        for (i = 0; i < NB_PLL_MODE; i++) {
 224                if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max)
 225                        pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode);
 226        }
 227
 228        freqvco = tmdsck * 10;
 229        if (freqvco <= 425000000UL)
 230                pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ);
 231        else if (freqvco <= 850000000UL)
 232                pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ);
 233        else if (freqvco <= 1700000000UL)
 234                pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ);
 235        else if (freqvco <= 2970000000UL)
 236                pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ);
 237        else {
 238                DRM_ERROR("PHY serializer clock out of range\n");
 239                goto err;
 240        }
 241
 242        /*
 243         * Configure and power up the PHY PLL
 244         */
 245        hdmi->event_received = false;
 246        DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
 247        hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG);
 248
 249        /* wait PLL interrupt */
 250        wait_event_interruptible_timeout(hdmi->wait_event,
 251                                         hdmi->event_received == true,
 252                                         msecs_to_jiffies
 253                                         (HDMI_TIMEOUT_PLL_LOCK));
 254
 255        if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
 256                DRM_ERROR("hdmi phy pll not locked\n");
 257                goto err;
 258        }
 259
 260        DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
 261
 262        /*
 263         * To configure the source termination and pre-emphasis appropriately
 264         * for different high speed TMDS clock frequencies a phy configuration
 265         * table must be provided, tailored to the SoC and board combination.
 266         */
 267        for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
 268                if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
 269                    (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
 270                        val = hdmiphy_config[i].config[0];
 271                        hdmi_write(hdmi, val, HDMI_SRZ_TAP_1);
 272                        val = hdmiphy_config[i].config[1];
 273                        hdmi_write(hdmi, val, HDMI_SRZ_TAP_2);
 274                        val = hdmiphy_config[i].config[2];
 275                        hdmi_write(hdmi, val, HDMI_SRZ_TAP_3);
 276                        val = hdmiphy_config[i].config[3];
 277                        val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN;
 278                        val &= ~HDMI_SRZ_CTRL_POWER_DOWN;
 279                        hdmi_write(hdmi, val, HDMI_SRZ_CTRL);
 280
 281                        DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n",
 282                                         hdmiphy_config[i].config[0],
 283                                         hdmiphy_config[i].config[1],
 284                                         hdmiphy_config[i].config[2],
 285                                         hdmiphy_config[i].config[3]);
 286                        return true;
 287                }
 288        }
 289
 290        /*
 291         * Default, power up the serializer with no pre-emphasis or source
 292         * termination.
 293         */
 294        hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1);
 295        hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2);
 296        hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3);
 297        hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL);
 298
 299        return true;
 300
 301err:
 302        disable_pll_rejection(hdmi);
 303
 304        return false;
 305}
 306
 307/**
 308 * Stop hdmi phy macro cell tx3g0c55
 309 *
 310 * @hdmi: pointer on the hdmi internal structure
 311 */
 312static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi)
 313{
 314        DRM_DEBUG_DRIVER("\n");
 315
 316        hdmi->event_received = false;
 317
 318        hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL);
 319        hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG);
 320
 321        /* wait PLL interrupt */
 322        wait_event_interruptible_timeout(hdmi->wait_event,
 323                                         hdmi->event_received == true,
 324                                         msecs_to_jiffies
 325                                         (HDMI_TIMEOUT_PLL_LOCK));
 326
 327        if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
 328                DRM_ERROR("hdmi phy pll not well disabled\n");
 329
 330        disable_pll_rejection(hdmi);
 331}
 332
 333struct hdmi_phy_ops tx3g0c55phy_ops = {
 334        .start = sti_hdmi_tx3g0c55phy_start,
 335        .stop = sti_hdmi_tx3g0c55phy_stop,
 336};
 337