linux/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.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_tx3g4c28phy.h"
   8
   9#define HDMI_SRZ_CFG                             0x504
  10#define HDMI_SRZ_PLL_CFG                         0x510
  11#define HDMI_SRZ_ICNTL                           0x518
  12#define HDMI_SRZ_CALCODE_EXT                     0x520
  13
  14#define HDMI_SRZ_CFG_EN                          BIT(0)
  15#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
  16#define HDMI_SRZ_CFG_EXTERNAL_DATA               BIT(16)
  17#define HDMI_SRZ_CFG_RBIAS_EXT                   BIT(17)
  18#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      BIT(18)
  19#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION        BIT(19)
  20#define HDMI_SRZ_CFG_EN_SRC_TERMINATION          BIT(24)
  21
  22#define HDMI_SRZ_CFG_INTERNAL_MASK  (HDMI_SRZ_CFG_EN     | \
  23                HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
  24                HDMI_SRZ_CFG_EXTERNAL_DATA               | \
  25                HDMI_SRZ_CFG_RBIAS_EXT                   | \
  26                HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      | \
  27                HDMI_SRZ_CFG_EN_BIASRES_DETECTION        | \
  28                HDMI_SRZ_CFG_EN_SRC_TERMINATION)
  29
  30#define PLL_CFG_EN                               BIT(0)
  31#define PLL_CFG_NDIV_SHIFT                       (8)
  32#define PLL_CFG_IDF_SHIFT                        (16)
  33#define PLL_CFG_ODF_SHIFT                        (24)
  34
  35#define ODF_DIV_1                                (0)
  36#define ODF_DIV_2                                (1)
  37#define ODF_DIV_4                                (2)
  38#define ODF_DIV_8                                (3)
  39
  40#define HDMI_TIMEOUT_PLL_LOCK  50  /*milliseconds */
  41
  42struct plldividers_s {
  43        uint32_t min;
  44        uint32_t max;
  45        uint32_t idf;
  46        uint32_t odf;
  47};
  48
  49/*
  50 * Functional specification recommended values
  51 */
  52#define NB_PLL_MODE 5
  53static struct plldividers_s plldividers[NB_PLL_MODE] = {
  54        {0, 20000000, 1, ODF_DIV_8},
  55        {20000000, 42500000, 2, ODF_DIV_8},
  56        {42500000, 85000000, 4, ODF_DIV_4},
  57        {85000000, 170000000, 8, ODF_DIV_2},
  58        {170000000, 340000000, 16, ODF_DIV_1}
  59};
  60
  61#define NB_HDMI_PHY_CONFIG 2
  62static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
  63        {0, 250000000, {0x0, 0x0, 0x0, 0x0} },
  64        {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
  65};
  66
  67/**
  68 * Start hdmi phy macro cell tx3g4c28
  69 *
  70 * @hdmi: pointer on the hdmi internal structure
  71 *
  72 * Return false if an error occur
  73 */
  74static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
  75{
  76        u32 ckpxpll = hdmi->mode.clock * 1000;
  77        u32 val, tmdsck, idf, odf, pllctrl = 0;
  78        bool foundplldivides = false;
  79        int i;
  80
  81        DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
  82
  83        for (i = 0; i < NB_PLL_MODE; i++) {
  84                if (ckpxpll >= plldividers[i].min &&
  85                    ckpxpll < plldividers[i].max) {
  86                        idf = plldividers[i].idf;
  87                        odf = plldividers[i].odf;
  88                        foundplldivides = true;
  89                        break;
  90                }
  91        }
  92
  93        if (!foundplldivides) {
  94                DRM_ERROR("input TMDS clock speed (%d) not supported\n",
  95                          ckpxpll);
  96                goto err;
  97        }
  98
  99        /* Assuming no pixel repetition and 24bits color */
 100        tmdsck = ckpxpll;
 101        pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
 102
 103        if (tmdsck > 340000000) {
 104                DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
 105                goto err;
 106        }
 107
 108        pllctrl |= idf << PLL_CFG_IDF_SHIFT;
 109        pllctrl |= odf << PLL_CFG_ODF_SHIFT;
 110
 111        /*
 112         * Configure and power up the PHY PLL
 113         */
 114        hdmi->event_received = false;
 115        DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
 116        hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
 117
 118        /* wait PLL interrupt */
 119        wait_event_interruptible_timeout(hdmi->wait_event,
 120                                         hdmi->event_received == true,
 121                                         msecs_to_jiffies
 122                                         (HDMI_TIMEOUT_PLL_LOCK));
 123
 124        if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
 125                DRM_ERROR("hdmi phy pll not locked\n");
 126                goto err;
 127        }
 128
 129        DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
 130
 131        val = (HDMI_SRZ_CFG_EN |
 132               HDMI_SRZ_CFG_EXTERNAL_DATA |
 133               HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
 134               HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
 135
 136        if (tmdsck > 165000000)
 137                val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
 138
 139        /*
 140         * To configure the source termination and pre-emphasis appropriately
 141         * for different high speed TMDS clock frequencies a phy configuration
 142         * table must be provided, tailored to the SoC and board combination.
 143         */
 144        for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
 145                if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
 146                    (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
 147                        val |= (hdmiphy_config[i].config[0]
 148                                & ~HDMI_SRZ_CFG_INTERNAL_MASK);
 149                        hdmi_write(hdmi, val, HDMI_SRZ_CFG);
 150
 151                        val = hdmiphy_config[i].config[1];
 152                        hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
 153
 154                        val = hdmiphy_config[i].config[2];
 155                        hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
 156
 157                        DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
 158                                         hdmiphy_config[i].config[0],
 159                                         hdmiphy_config[i].config[1],
 160                                         hdmiphy_config[i].config[2]);
 161                        return true;
 162                }
 163        }
 164
 165        /*
 166         * Default, power up the serializer with no pre-emphasis or
 167         * output swing correction
 168         */
 169        hdmi_write(hdmi, val,  HDMI_SRZ_CFG);
 170        hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL);
 171        hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT);
 172
 173        return true;
 174
 175err:
 176        return false;
 177}
 178
 179/**
 180 * Stop hdmi phy macro cell tx3g4c28
 181 *
 182 * @hdmi: pointer on the hdmi internal structure
 183 */
 184static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
 185{
 186        int val = 0;
 187
 188        DRM_DEBUG_DRIVER("\n");
 189
 190        hdmi->event_received = false;
 191
 192        val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
 193        val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
 194
 195        hdmi_write(hdmi, val, HDMI_SRZ_CFG);
 196        hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
 197
 198        /* wait PLL interrupt */
 199        wait_event_interruptible_timeout(hdmi->wait_event,
 200                                         hdmi->event_received == true,
 201                                         msecs_to_jiffies
 202                                         (HDMI_TIMEOUT_PLL_LOCK));
 203
 204        if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
 205                DRM_ERROR("hdmi phy pll not well disabled\n");
 206}
 207
 208struct hdmi_phy_ops tx3g4c28phy_ops = {
 209        .start = sti_hdmi_tx3g4c28phy_start,
 210        .stop = sti_hdmi_tx3g4c28phy_stop,
 211};
 212