Linux Audio

Check our new training course

Linux kernel drivers training

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