Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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};