xref: /linux/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c (revision 4e95bc268b915c3a19ec8b9110f61e4ea41a1ed0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * HDMI PHY
4  *
5  * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/err.h>
10 #include <linux/io.h>
11 #include <linux/platform_device.h>
12 #include <linux/slab.h>
13 #include <linux/seq_file.h>
14 
15 #include "omapdss.h"
16 #include "dss.h"
17 #include "hdmi.h"
18 
19 void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
20 {
21 #define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
22 		hdmi_read_reg(phy->base, r))
23 
24 	DUMPPHY(HDMI_TXPHY_TX_CTRL);
25 	DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
26 	DUMPPHY(HDMI_TXPHY_POWER_CTRL);
27 	DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
28 	if (phy->features->bist_ctrl)
29 		DUMPPHY(HDMI_TXPHY_BIST_CONTROL);
30 }
31 
32 int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes)
33 {
34 	int i;
35 
36 	for (i = 0; i < 8; i += 2) {
37 		u8 lane, pol;
38 		int dx, dy;
39 
40 		dx = lanes[i];
41 		dy = lanes[i + 1];
42 
43 		if (dx < 0 || dx >= 8)
44 			return -EINVAL;
45 
46 		if (dy < 0 || dy >= 8)
47 			return -EINVAL;
48 
49 		if (dx & 1) {
50 			if (dy != dx - 1)
51 				return -EINVAL;
52 			pol = 1;
53 		} else {
54 			if (dy != dx + 1)
55 				return -EINVAL;
56 			pol = 0;
57 		}
58 
59 		lane = dx / 2;
60 
61 		phy->lane_function[lane] = i / 2;
62 		phy->lane_polarity[lane] = pol;
63 	}
64 
65 	return 0;
66 }
67 
68 static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
69 {
70 	static const u16 pad_cfg_list[] = {
71 		0x0123,
72 		0x0132,
73 		0x0312,
74 		0x0321,
75 		0x0231,
76 		0x0213,
77 		0x1023,
78 		0x1032,
79 		0x3012,
80 		0x3021,
81 		0x2031,
82 		0x2013,
83 		0x1203,
84 		0x1302,
85 		0x3102,
86 		0x3201,
87 		0x2301,
88 		0x2103,
89 		0x1230,
90 		0x1320,
91 		0x3120,
92 		0x3210,
93 		0x2310,
94 		0x2130,
95 	};
96 
97 	u16 lane_cfg = 0;
98 	int i;
99 	unsigned int lane_cfg_val;
100 	u16 pol_val = 0;
101 
102 	for (i = 0; i < 4; ++i)
103 		lane_cfg |= phy->lane_function[i] << ((3 - i) * 4);
104 
105 	pol_val |= phy->lane_polarity[0] << 0;
106 	pol_val |= phy->lane_polarity[1] << 3;
107 	pol_val |= phy->lane_polarity[2] << 2;
108 	pol_val |= phy->lane_polarity[3] << 1;
109 
110 	for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i)
111 		if (pad_cfg_list[i] == lane_cfg)
112 			break;
113 
114 	if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list)))
115 		i = 0;
116 
117 	lane_cfg_val = i;
118 
119 	REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22);
120 	REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
121 }
122 
123 int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk,
124 	unsigned long lfbitclk)
125 {
126 	u8 freqout;
127 
128 	/*
129 	 * Read address 0 in order to get the SCP reset done completed
130 	 * Dummy access performed to make sure reset is done
131 	 */
132 	hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL);
133 
134 	/*
135 	 * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the
136 	 * HDMI_PHYPWRCMD_LDOON command.
137 	*/
138 	if (phy->features->bist_ctrl)
139 		REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11);
140 
141 	/*
142 	 * If the hfbitclk != lfbitclk, it means the lfbitclk was configured
143 	 * to be used for TMDS.
144 	 */
145 	if (hfbitclk != lfbitclk)
146 		freqout = 0;
147 	else if (hfbitclk / 10 < phy->features->max_phy)
148 		freqout = 1;
149 	else
150 		freqout = 2;
151 
152 	/*
153 	 * Write to phy address 0 to configure the clock
154 	 * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
155 	 */
156 	REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30);
157 
158 	/* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
159 	hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
160 
161 	/* Setup max LDO voltage */
162 	if (phy->features->ldo_voltage)
163 		REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
164 
165 	hdmi_phy_configure_lanes(phy);
166 
167 	return 0;
168 }
169 
170 static const struct hdmi_phy_features omap44xx_phy_feats = {
171 	.bist_ctrl	=	false,
172 	.ldo_voltage	=	true,
173 	.max_phy	=	185675000,
174 };
175 
176 static const struct hdmi_phy_features omap54xx_phy_feats = {
177 	.bist_ctrl	=	true,
178 	.ldo_voltage	=	false,
179 	.max_phy	=	186000000,
180 };
181 
182 int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy,
183 		  unsigned int version)
184 {
185 	struct resource *res;
186 
187 	if (version == 4)
188 		phy->features = &omap44xx_phy_feats;
189 	else
190 		phy->features = &omap54xx_phy_feats;
191 
192 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
193 	phy->base = devm_ioremap_resource(&pdev->dev, res);
194 	if (IS_ERR(phy->base))
195 		return PTR_ERR(phy->base);
196 
197 	return 0;
198 }
199