treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500
[sfrench/cifs-2.6.git] / drivers / gpu / drm / omapdrm / dss / hdmi_phy.c
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 }