Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) STMicroelectronics SA 2014
0004  * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
0005  */
0006 
0007 #include <drm/drm_print.h>
0008 
0009 #include "sti_hdmi_tx3g4c28phy.h"
0010 
0011 #define HDMI_SRZ_CFG                             0x504
0012 #define HDMI_SRZ_PLL_CFG                         0x510
0013 #define HDMI_SRZ_ICNTL                           0x518
0014 #define HDMI_SRZ_CALCODE_EXT                     0x520
0015 
0016 #define HDMI_SRZ_CFG_EN                          BIT(0)
0017 #define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
0018 #define HDMI_SRZ_CFG_EXTERNAL_DATA               BIT(16)
0019 #define HDMI_SRZ_CFG_RBIAS_EXT                   BIT(17)
0020 #define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      BIT(18)
0021 #define HDMI_SRZ_CFG_EN_BIASRES_DETECTION        BIT(19)
0022 #define HDMI_SRZ_CFG_EN_SRC_TERMINATION          BIT(24)
0023 
0024 #define HDMI_SRZ_CFG_INTERNAL_MASK  (HDMI_SRZ_CFG_EN     | \
0025         HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
0026         HDMI_SRZ_CFG_EXTERNAL_DATA               | \
0027         HDMI_SRZ_CFG_RBIAS_EXT                   | \
0028         HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      | \
0029         HDMI_SRZ_CFG_EN_BIASRES_DETECTION        | \
0030         HDMI_SRZ_CFG_EN_SRC_TERMINATION)
0031 
0032 #define PLL_CFG_EN                               BIT(0)
0033 #define PLL_CFG_NDIV_SHIFT                       (8)
0034 #define PLL_CFG_IDF_SHIFT                        (16)
0035 #define PLL_CFG_ODF_SHIFT                        (24)
0036 
0037 #define ODF_DIV_1                                (0)
0038 #define ODF_DIV_2                                (1)
0039 #define ODF_DIV_4                                (2)
0040 #define ODF_DIV_8                                (3)
0041 
0042 #define HDMI_TIMEOUT_PLL_LOCK  50  /*milliseconds */
0043 
0044 struct plldividers_s {
0045     uint32_t min;
0046     uint32_t max;
0047     uint32_t idf;
0048     uint32_t odf;
0049 };
0050 
0051 /*
0052  * Functional specification recommended values
0053  */
0054 #define NB_PLL_MODE 5
0055 static struct plldividers_s plldividers[NB_PLL_MODE] = {
0056     {0, 20000000, 1, ODF_DIV_8},
0057     {20000000, 42500000, 2, ODF_DIV_8},
0058     {42500000, 85000000, 4, ODF_DIV_4},
0059     {85000000, 170000000, 8, ODF_DIV_2},
0060     {170000000, 340000000, 16, ODF_DIV_1}
0061 };
0062 
0063 #define NB_HDMI_PHY_CONFIG 2
0064 static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
0065     {0, 250000000, {0x0, 0x0, 0x0, 0x0} },
0066     {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
0067 };
0068 
0069 /**
0070  * sti_hdmi_tx3g4c28phy_start - Start hdmi phy macro cell tx3g4c28
0071  *
0072  * @hdmi: pointer on the hdmi internal structure
0073  *
0074  * Return false if an error occur
0075  */
0076 static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
0077 {
0078     u32 ckpxpll = hdmi->mode.clock * 1000;
0079     u32 val, tmdsck, idf, odf, pllctrl = 0;
0080     bool foundplldivides = false;
0081     int i;
0082 
0083     DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
0084 
0085     for (i = 0; i < NB_PLL_MODE; i++) {
0086         if (ckpxpll >= plldividers[i].min &&
0087             ckpxpll < plldividers[i].max) {
0088             idf = plldividers[i].idf;
0089             odf = plldividers[i].odf;
0090             foundplldivides = true;
0091             break;
0092         }
0093     }
0094 
0095     if (!foundplldivides) {
0096         DRM_ERROR("input TMDS clock speed (%d) not supported\n",
0097               ckpxpll);
0098         goto err;
0099     }
0100 
0101     /* Assuming no pixel repetition and 24bits color */
0102     tmdsck = ckpxpll;
0103     pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
0104 
0105     if (tmdsck > 340000000) {
0106         DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
0107         goto err;
0108     }
0109 
0110     pllctrl |= idf << PLL_CFG_IDF_SHIFT;
0111     pllctrl |= odf << PLL_CFG_ODF_SHIFT;
0112 
0113     /*
0114      * Configure and power up the PHY PLL
0115      */
0116     hdmi->event_received = false;
0117     DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
0118     hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
0119 
0120     /* wait PLL interrupt */
0121     wait_event_interruptible_timeout(hdmi->wait_event,
0122                      hdmi->event_received == true,
0123                      msecs_to_jiffies
0124                      (HDMI_TIMEOUT_PLL_LOCK));
0125 
0126     if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
0127         DRM_ERROR("hdmi phy pll not locked\n");
0128         goto err;
0129     }
0130 
0131     DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
0132 
0133     val = (HDMI_SRZ_CFG_EN |
0134            HDMI_SRZ_CFG_EXTERNAL_DATA |
0135            HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
0136            HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
0137 
0138     if (tmdsck > 165000000)
0139         val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
0140 
0141     /*
0142      * To configure the source termination and pre-emphasis appropriately
0143      * for different high speed TMDS clock frequencies a phy configuration
0144      * table must be provided, tailored to the SoC and board combination.
0145      */
0146     for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
0147         if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
0148             (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
0149             val |= (hdmiphy_config[i].config[0]
0150                 & ~HDMI_SRZ_CFG_INTERNAL_MASK);
0151             hdmi_write(hdmi, val, HDMI_SRZ_CFG);
0152 
0153             val = hdmiphy_config[i].config[1];
0154             hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
0155 
0156             val = hdmiphy_config[i].config[2];
0157             hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
0158 
0159             DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
0160                      hdmiphy_config[i].config[0],
0161                      hdmiphy_config[i].config[1],
0162                      hdmiphy_config[i].config[2]);
0163             return true;
0164         }
0165     }
0166 
0167     /*
0168      * Default, power up the serializer with no pre-emphasis or
0169      * output swing correction
0170      */
0171     hdmi_write(hdmi, val,  HDMI_SRZ_CFG);
0172     hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL);
0173     hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT);
0174 
0175     return true;
0176 
0177 err:
0178     return false;
0179 }
0180 
0181 /**
0182  * sti_hdmi_tx3g4c28phy_stop - Stop hdmi phy macro cell tx3g4c28
0183  *
0184  * @hdmi: pointer on the hdmi internal structure
0185  */
0186 static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
0187 {
0188     int val = 0;
0189 
0190     DRM_DEBUG_DRIVER("\n");
0191 
0192     hdmi->event_received = false;
0193 
0194     val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
0195     val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
0196 
0197     hdmi_write(hdmi, val, HDMI_SRZ_CFG);
0198     hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
0199 
0200     /* wait PLL interrupt */
0201     wait_event_interruptible_timeout(hdmi->wait_event,
0202                      hdmi->event_received == true,
0203                      msecs_to_jiffies
0204                      (HDMI_TIMEOUT_PLL_LOCK));
0205 
0206     if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
0207         DRM_ERROR("hdmi phy pll not well disabled\n");
0208 }
0209 
0210 struct hdmi_phy_ops tx3g4c28phy_ops = {
0211     .start = sti_hdmi_tx3g4c28phy_start,
0212     .stop = sti_hdmi_tx3g4c28phy_stop,
0213 };