1b7c7436aSJernej Skrabec // SPDX-License-Identifier: GPL-2.0+
2b7c7436aSJernej Skrabec /*
3b7c7436aSJernej Skrabec * Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net>
4b7c7436aSJernej Skrabec */
5b7c7436aSJernej Skrabec
64f86e817SJernej Skrabec #include <linux/delay.h>
7*722d4f06SRob Herring #include <linux/of.h>
89bf37977SSaravana Kannan #include <linux/of_platform.h>
9*722d4f06SRob Herring #include <linux/platform_device.h>
10b7c7436aSJernej Skrabec
11b7c7436aSJernej Skrabec #include "sun8i_dw_hdmi.h"
12b7c7436aSJernej Skrabec
13b7c7436aSJernej Skrabec /*
14b7c7436aSJernej Skrabec * Address can be actually any value. Here is set to same value as
15b7c7436aSJernej Skrabec * it is set in BSP driver.
16b7c7436aSJernej Skrabec */
17b7c7436aSJernej Skrabec #define I2C_ADDR 0x69
18b7c7436aSJernej Skrabec
190fb4b858SJernej Skrabec static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
200fb4b858SJernej Skrabec {
210fb4b858SJernej Skrabec 30666000, {
220fb4b858SJernej Skrabec { 0x00b3, 0x0000 },
230fb4b858SJernej Skrabec { 0x2153, 0x0000 },
240fb4b858SJernej Skrabec { 0x40f3, 0x0000 },
250fb4b858SJernej Skrabec },
260fb4b858SJernej Skrabec }, {
270fb4b858SJernej Skrabec 36800000, {
280fb4b858SJernej Skrabec { 0x00b3, 0x0000 },
290fb4b858SJernej Skrabec { 0x2153, 0x0000 },
300fb4b858SJernej Skrabec { 0x40a2, 0x0001 },
310fb4b858SJernej Skrabec },
320fb4b858SJernej Skrabec }, {
330fb4b858SJernej Skrabec 46000000, {
340fb4b858SJernej Skrabec { 0x00b3, 0x0000 },
350fb4b858SJernej Skrabec { 0x2142, 0x0001 },
360fb4b858SJernej Skrabec { 0x40a2, 0x0001 },
370fb4b858SJernej Skrabec },
380fb4b858SJernej Skrabec }, {
390fb4b858SJernej Skrabec 61333000, {
400fb4b858SJernej Skrabec { 0x0072, 0x0001 },
410fb4b858SJernej Skrabec { 0x2142, 0x0001 },
420fb4b858SJernej Skrabec { 0x40a2, 0x0001 },
430fb4b858SJernej Skrabec },
440fb4b858SJernej Skrabec }, {
450fb4b858SJernej Skrabec 73600000, {
460fb4b858SJernej Skrabec { 0x0072, 0x0001 },
470fb4b858SJernej Skrabec { 0x2142, 0x0001 },
480fb4b858SJernej Skrabec { 0x4061, 0x0002 },
490fb4b858SJernej Skrabec },
500fb4b858SJernej Skrabec }, {
510fb4b858SJernej Skrabec 92000000, {
520fb4b858SJernej Skrabec { 0x0072, 0x0001 },
530fb4b858SJernej Skrabec { 0x2145, 0x0002 },
540fb4b858SJernej Skrabec { 0x4061, 0x0002 },
550fb4b858SJernej Skrabec },
560fb4b858SJernej Skrabec }, {
570fb4b858SJernej Skrabec 122666000, {
580fb4b858SJernej Skrabec { 0x0051, 0x0002 },
590fb4b858SJernej Skrabec { 0x2145, 0x0002 },
600fb4b858SJernej Skrabec { 0x4061, 0x0002 },
610fb4b858SJernej Skrabec },
620fb4b858SJernej Skrabec }, {
630fb4b858SJernej Skrabec 147200000, {
640fb4b858SJernej Skrabec { 0x0051, 0x0002 },
650fb4b858SJernej Skrabec { 0x2145, 0x0002 },
660fb4b858SJernej Skrabec { 0x4064, 0x0003 },
670fb4b858SJernej Skrabec },
680fb4b858SJernej Skrabec }, {
690fb4b858SJernej Skrabec 184000000, {
700fb4b858SJernej Skrabec { 0x0051, 0x0002 },
710fb4b858SJernej Skrabec { 0x214c, 0x0003 },
720fb4b858SJernej Skrabec { 0x4064, 0x0003 },
730fb4b858SJernej Skrabec },
740fb4b858SJernej Skrabec }, {
750fb4b858SJernej Skrabec 226666000, {
760fb4b858SJernej Skrabec { 0x0040, 0x0003 },
770fb4b858SJernej Skrabec { 0x214c, 0x0003 },
780fb4b858SJernej Skrabec { 0x4064, 0x0003 },
790fb4b858SJernej Skrabec },
800fb4b858SJernej Skrabec }, {
810fb4b858SJernej Skrabec 272000000, {
820fb4b858SJernej Skrabec { 0x0040, 0x0003 },
830fb4b858SJernej Skrabec { 0x214c, 0x0003 },
840fb4b858SJernej Skrabec { 0x5a64, 0x0003 },
850fb4b858SJernej Skrabec },
860fb4b858SJernej Skrabec }, {
870fb4b858SJernej Skrabec 340000000, {
880fb4b858SJernej Skrabec { 0x0040, 0x0003 },
890fb4b858SJernej Skrabec { 0x3b4c, 0x0003 },
900fb4b858SJernej Skrabec { 0x5a64, 0x0003 },
910fb4b858SJernej Skrabec },
920fb4b858SJernej Skrabec }, {
930fb4b858SJernej Skrabec 594000000, {
940fb4b858SJernej Skrabec { 0x1a40, 0x0003 },
950fb4b858SJernej Skrabec { 0x3b4c, 0x0003 },
960fb4b858SJernej Skrabec { 0x5a64, 0x0003 },
970fb4b858SJernej Skrabec },
980fb4b858SJernej Skrabec }, {
990fb4b858SJernej Skrabec ~0UL, {
1000fb4b858SJernej Skrabec { 0x0000, 0x0000 },
1010fb4b858SJernej Skrabec { 0x0000, 0x0000 },
1020fb4b858SJernej Skrabec { 0x0000, 0x0000 },
1030fb4b858SJernej Skrabec },
1040fb4b858SJernej Skrabec }
1050fb4b858SJernej Skrabec };
1060fb4b858SJernej Skrabec
1070fb4b858SJernej Skrabec static const struct dw_hdmi_curr_ctrl sun50i_h6_cur_ctr[] = {
1080fb4b858SJernej Skrabec /* pixelclk bpp8 bpp10 bpp12 */
1090fb4b858SJernej Skrabec { 27000000, { 0x0012, 0x0000, 0x0000 }, },
1106a155216SJernej Skrabec { 74250000, { 0x0013, 0x001a, 0x001b }, },
1116a155216SJernej Skrabec { 148500000, { 0x0019, 0x0033, 0x0034 }, },
1126a155216SJernej Skrabec { 297000000, { 0x0019, 0x001b, 0x001b }, },
1136a155216SJernej Skrabec { 594000000, { 0x0010, 0x001b, 0x001b }, },
1140fb4b858SJernej Skrabec { ~0UL, { 0x0000, 0x0000, 0x0000 }, }
1150fb4b858SJernej Skrabec };
1160fb4b858SJernej Skrabec
1170fb4b858SJernej Skrabec static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = {
1180fb4b858SJernej Skrabec /*pixelclk symbol term vlev*/
1196a155216SJernej Skrabec { 27000000, 0x8009, 0x0007, 0x02b0 },
1206a155216SJernej Skrabec { 74250000, 0x8009, 0x0006, 0x022d },
1216a155216SJernej Skrabec { 148500000, 0x8029, 0x0006, 0x0270 },
1226a155216SJernej Skrabec { 297000000, 0x8039, 0x0005, 0x01ab },
1236a155216SJernej Skrabec { 594000000, 0x8029, 0x0000, 0x008a },
1240fb4b858SJernej Skrabec { ~0UL, 0x0000, 0x0000, 0x0000}
1250fb4b858SJernej Skrabec };
1260fb4b858SJernej Skrabec
sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy * phy,const struct drm_display_mode * mode)127ef2731e4SSamuel Holland static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
1286da62368SSamuel Holland const struct drm_display_mode *mode)
1296da62368SSamuel Holland {
1306da62368SSamuel Holland u32 val = 0;
1316da62368SSamuel Holland
1326da62368SSamuel Holland if (mode->flags & DRM_MODE_FLAG_NHSYNC)
1336da62368SSamuel Holland val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
1346da62368SSamuel Holland
1356da62368SSamuel Holland if (mode->flags & DRM_MODE_FLAG_NVSYNC)
1366da62368SSamuel Holland val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
1376da62368SSamuel Holland
1386da62368SSamuel Holland regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
1396da62368SSamuel Holland SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
1406da62368SSamuel Holland };
141ef2731e4SSamuel Holland
sun8i_a83t_hdmi_phy_config(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)142ef2731e4SSamuel Holland static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
143ef2731e4SSamuel Holland const struct drm_display_info *display,
144ef2731e4SSamuel Holland const struct drm_display_mode *mode)
145b7c7436aSJernej Skrabec {
146ef2731e4SSamuel Holland unsigned int clk_rate = mode->crtc_clock * 1000;
147ef2731e4SSamuel Holland struct sun8i_hdmi_phy *phy = data;
148ef2731e4SSamuel Holland
149ef2731e4SSamuel Holland sun8i_hdmi_phy_set_polarity(phy, mode);
150ef2731e4SSamuel Holland
151b7c7436aSJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
152b7c7436aSJernej Skrabec SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
153b7c7436aSJernej Skrabec SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
154b7c7436aSJernej Skrabec
155b7c7436aSJernej Skrabec /* power down */
156b7c7436aSJernej Skrabec dw_hdmi_phy_gen2_txpwron(hdmi, 0);
157b7c7436aSJernej Skrabec dw_hdmi_phy_gen2_pddq(hdmi, 1);
158b7c7436aSJernej Skrabec
1598fb241e2SSandor Yu dw_hdmi_phy_gen2_reset(hdmi);
160b7c7436aSJernej Skrabec
161b7c7436aSJernej Skrabec dw_hdmi_phy_gen2_pddq(hdmi, 0);
162b7c7436aSJernej Skrabec
163b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR);
164b7c7436aSJernej Skrabec
165b7c7436aSJernej Skrabec /*
166b7c7436aSJernej Skrabec * Values are taken from BSP HDMI driver. Although AW didn't
167b7c7436aSJernej Skrabec * release any documentation, explanation of this values can
168b7c7436aSJernej Skrabec * be found in i.MX 6Dual/6Quad Reference Manual.
169b7c7436aSJernej Skrabec */
1706fd90310SJernej Skrabec if (clk_rate <= 27000000) {
171b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
172b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
173b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
174b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
175b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
176b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
1776fd90310SJernej Skrabec } else if (clk_rate <= 74250000) {
178b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
179b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
180b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
181b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
182b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
183b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
1846fd90310SJernej Skrabec } else if (clk_rate <= 148500000) {
185b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
186b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
187b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
188b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
189b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e);
190b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09);
191b7c7436aSJernej Skrabec } else {
192b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06);
193b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
194b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
195b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
196b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e);
197b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09);
198b7c7436aSJernej Skrabec }
199b7c7436aSJernej Skrabec
200b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e);
201b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);
202b7c7436aSJernej Skrabec dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17);
203b7c7436aSJernej Skrabec
204b7c7436aSJernej Skrabec dw_hdmi_phy_gen2_txpwron(hdmi, 1);
205b7c7436aSJernej Skrabec
206b7c7436aSJernej Skrabec return 0;
2074f86e817SJernej Skrabec }
2084f86e817SJernej Skrabec
sun8i_a83t_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)2096da62368SSamuel Holland static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
2106da62368SSamuel Holland {
2116da62368SSamuel Holland struct sun8i_hdmi_phy *phy = data;
2126da62368SSamuel Holland
2136da62368SSamuel Holland dw_hdmi_phy_gen2_txpwron(hdmi, 0);
2146da62368SSamuel Holland dw_hdmi_phy_gen2_pddq(hdmi, 1);
2156da62368SSamuel Holland
2166da62368SSamuel Holland regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
2176da62368SSamuel Holland SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
2186da62368SSamuel Holland }
2196da62368SSamuel Holland
2206da62368SSamuel Holland static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
2216da62368SSamuel Holland .init = sun8i_a83t_hdmi_phy_config,
2226da62368SSamuel Holland .disable = sun8i_a83t_hdmi_phy_disable,
2236da62368SSamuel Holland .read_hpd = dw_hdmi_phy_read_hpd,
2246da62368SSamuel Holland .update_hpd = dw_hdmi_phy_update_hpd,
2256da62368SSamuel Holland .setup_hpd = dw_hdmi_phy_setup_hpd,
2266da62368SSamuel Holland };
2276da62368SSamuel Holland
sun8i_h3_hdmi_phy_config(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)228ef2731e4SSamuel Holland static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
229ef2731e4SSamuel Holland const struct drm_display_info *display,
230ef2731e4SSamuel Holland const struct drm_display_mode *mode)
2314f86e817SJernej Skrabec {
232ef2731e4SSamuel Holland unsigned int clk_rate = mode->crtc_clock * 1000;
233ef2731e4SSamuel Holland struct sun8i_hdmi_phy *phy = data;
2344f86e817SJernej Skrabec u32 pll_cfg1_init;
2354f86e817SJernej Skrabec u32 pll_cfg2_init;
2364f86e817SJernej Skrabec u32 ana_cfg1_end;
2374f86e817SJernej Skrabec u32 ana_cfg2_init;
2384f86e817SJernej Skrabec u32 ana_cfg3_init;
2394f86e817SJernej Skrabec u32 b_offset = 0;
2404f86e817SJernej Skrabec u32 val;
2414f86e817SJernej Skrabec
242ef2731e4SSamuel Holland if (phy->variant->has_phy_clk)
243ef2731e4SSamuel Holland clk_set_rate(phy->clk_phy, clk_rate);
244ef2731e4SSamuel Holland
245ef2731e4SSamuel Holland sun8i_hdmi_phy_set_polarity(phy, mode);
246ef2731e4SSamuel Holland
2474f86e817SJernej Skrabec /* bandwidth / frequency independent settings */
2484f86e817SJernej Skrabec
2494f86e817SJernej Skrabec pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
2504f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
2514f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
2524f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
2534f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
2544f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CS |
2554f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
2564f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
2574f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_BWS;
2584f86e817SJernej Skrabec
2594f86e817SJernej Skrabec pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
2604f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
2614f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
2624f86e817SJernej Skrabec
2634f86e817SJernej Skrabec ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
2644f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
2654f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
2664f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
2674f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
2684f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
2694f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
2704f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
2714f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
2724f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK |
2734f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
2744f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
2754f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
2764f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
2774f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
2784f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
2794f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
2804f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
2814f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
2824f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
2834f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
2844f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
2854f86e817SJernej Skrabec
2864f86e817SJernej Skrabec ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
2874f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
2884f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
2894f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
2904f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
2914f86e817SJernej Skrabec
2924f86e817SJernej Skrabec ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
2934f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
2944f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
2954f86e817SJernej Skrabec
2964f86e817SJernej Skrabec /* bandwidth / frequency dependent settings */
2974f86e817SJernej Skrabec if (clk_rate <= 27000000) {
2984f86e817SJernej Skrabec pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
2994f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
3004f86e817SJernej Skrabec pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
3014f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG2_S(4);
3024f86e817SJernej Skrabec ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
3034f86e817SJernej Skrabec ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
3044f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
3054f86e817SJernej Skrabec ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
3064f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
3074f86e817SJernej Skrabec } else if (clk_rate <= 74250000) {
3084f86e817SJernej Skrabec pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
3094f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
3104f86e817SJernej Skrabec pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
3114f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG2_S(5);
3124f86e817SJernej Skrabec ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
3134f86e817SJernej Skrabec ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
3144f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
3154f86e817SJernej Skrabec ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
3164f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
3174f86e817SJernej Skrabec } else if (clk_rate <= 148500000) {
3184f86e817SJernej Skrabec pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
3194f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
3204f86e817SJernej Skrabec pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
3214f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG2_S(6);
3224f86e817SJernej Skrabec ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
3234f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
3244f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
3254f86e817SJernej Skrabec ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
3264f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
3274f86e817SJernej Skrabec } else {
3284f86e817SJernej Skrabec b_offset = 2;
3294f86e817SJernej Skrabec pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
3304f86e817SJernej Skrabec pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
3314f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG2_S(7);
3324f86e817SJernej Skrabec ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
3334f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
3344f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
3354f86e817SJernej Skrabec ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
336831adffbSJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
337831adffbSJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
3384f86e817SJernej Skrabec }
3394f86e817SJernej Skrabec
3404f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
3414f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
3424f86e817SJernej Skrabec
34309f380e3SJernej Skrabec /*
34409f380e3SJernej Skrabec * NOTE: We have to be careful not to overwrite PHY parent
34509f380e3SJernej Skrabec * clock selection bit and clock divider.
34609f380e3SJernej Skrabec */
34709f380e3SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
34809f380e3SJernej Skrabec (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
34909f380e3SJernej Skrabec pll_cfg1_init);
3504f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
3514f86e817SJernej Skrabec (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
3524f86e817SJernej Skrabec pll_cfg2_init);
3534f86e817SJernej Skrabec usleep_range(10000, 15000);
3544f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG,
3554f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2);
3564f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
3574f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_PLLEN,
3584f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
3594f86e817SJernej Skrabec msleep(100);
3604f86e817SJernej Skrabec
3614f86e817SJernej Skrabec /* get B value */
3624f86e817SJernej Skrabec regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
3634f86e817SJernej Skrabec val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >>
3644f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
3654f86e817SJernej Skrabec val = min(val + b_offset, (u32)0x3f);
3664f86e817SJernej Skrabec
3674f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
3684f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
3694f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_REG_OD,
3704f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
3714f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
3724f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
3734f86e817SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK,
3744f86e817SJernej Skrabec val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT);
3754f86e817SJernej Skrabec msleep(100);
3764f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end);
3774f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init);
3784f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init);
3794f86e817SJernej Skrabec
3804f86e817SJernej Skrabec return 0;
3814f86e817SJernej Skrabec }
382b7c7436aSJernej Skrabec
sun8i_h3_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)383ef2731e4SSamuel Holland static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
3844f86e817SJernej Skrabec {
385ef2731e4SSamuel Holland struct sun8i_hdmi_phy *phy = data;
386ef2731e4SSamuel Holland
3874f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
3884f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
3894f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
3904f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
3914f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
3924f86e817SJernej Skrabec }
3934f86e817SJernej Skrabec
394ef2731e4SSamuel Holland static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
395ef2731e4SSamuel Holland .init = sun8i_h3_hdmi_phy_config,
396ef2731e4SSamuel Holland .disable = sun8i_h3_hdmi_phy_disable,
397ef2731e4SSamuel Holland .read_hpd = dw_hdmi_phy_read_hpd,
398ef2731e4SSamuel Holland .update_hpd = dw_hdmi_phy_update_hpd,
399ef2731e4SSamuel Holland .setup_hpd = dw_hdmi_phy_setup_hpd,
400b7c7436aSJernej Skrabec };
401b7c7436aSJernej Skrabec
sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy * phy)402cd54074eSJernej Skrabec static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
403cd54074eSJernej Skrabec {
404cd54074eSJernej Skrabec /* enable read access to HDMI controller */
405cd54074eSJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
406cd54074eSJernej Skrabec SUN8I_HDMI_PHY_READ_EN_MAGIC);
407cd54074eSJernej Skrabec
408cd54074eSJernej Skrabec /* unscramble register offsets */
409cd54074eSJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
410cd54074eSJernej Skrabec SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
411cd54074eSJernej Skrabec }
412cd54074eSJernej Skrabec
sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy * phy)4130fb4b858SJernej Skrabec static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy)
4140fb4b858SJernej Skrabec {
4150fb4b858SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
4160fb4b858SJernej Skrabec SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
4170fb4b858SJernej Skrabec SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
4180fb4b858SJernej Skrabec
4190fb4b858SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
4200fb4b858SJernej Skrabec 0xffff0000, 0x80c00000);
4210fb4b858SJernej Skrabec }
4220fb4b858SJernej Skrabec
sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy * phy)4236fd90310SJernej Skrabec static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
424b7c7436aSJernej Skrabec {
425cd54074eSJernej Skrabec sun8i_hdmi_phy_unlock(phy);
426cd54074eSJernej Skrabec
427b7c7436aSJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
428b7c7436aSJernej Skrabec SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
429b7c7436aSJernej Skrabec SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
430b7c7436aSJernej Skrabec
431b7c7436aSJernej Skrabec /*
432b7c7436aSJernej Skrabec * Set PHY I2C address. It must match to the address set by
433b7c7436aSJernej Skrabec * dw_hdmi_phy_set_slave_addr().
434b7c7436aSJernej Skrabec */
435b7c7436aSJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
436b7c7436aSJernej Skrabec SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK,
437b7c7436aSJernej Skrabec SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
438b7c7436aSJernej Skrabec }
439b7c7436aSJernej Skrabec
sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy * phy)4404f86e817SJernej Skrabec static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
4414f86e817SJernej Skrabec {
4424f86e817SJernej Skrabec unsigned int val;
4434f86e817SJernej Skrabec
444cd54074eSJernej Skrabec sun8i_hdmi_phy_unlock(phy);
445cd54074eSJernej Skrabec
4464f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0);
4474f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4484f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENBI,
4494f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
4504f86e817SJernej Skrabec udelay(5);
4514f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4524f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN,
4534f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
4544f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4554f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENVBS,
4564f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
4574f86e817SJernej Skrabec usleep_range(10, 20);
4584f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4594f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_LDOEN,
4604f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
4614f86e817SJernej Skrabec udelay(5);
4624f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4634f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_CKEN,
4644f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
4654f86e817SJernej Skrabec usleep_range(40, 100);
4664f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4674f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL,
4684f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
4694f86e817SJernej Skrabec usleep_range(100, 200);
4704f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4714f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG,
4724f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
4734f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4744f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
4754f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
4764f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2,
4774f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
4784f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
4794f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
4804f86e817SJernej Skrabec
4814f86e817SJernej Skrabec /* wait for calibration to finish */
4824f86e817SJernej Skrabec regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val,
4834f86e817SJernej Skrabec (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D),
4844f86e817SJernej Skrabec 100, 2000);
4854f86e817SJernej Skrabec
4864f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4874f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK,
4884f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
4894f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
4904f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
4914f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
4924f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
4934f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK,
4944f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
4954f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
4964f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
4974f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
4984f86e817SJernej Skrabec
4994f86e817SJernej Skrabec /* enable DDC communication */
5004f86e817SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG,
5014f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
5024f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_SDAEN,
5034f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
5044f86e817SJernej Skrabec SUN8I_HDMI_PHY_ANA_CFG3_SDAEN);
5054f86e817SJernej Skrabec
50609f380e3SJernej Skrabec /* reset PHY PLL clock parent */
50709f380e3SJernej Skrabec regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
50809f380e3SJernej Skrabec SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
50909f380e3SJernej Skrabec
5104f86e817SJernej Skrabec /* set HW control of CEC pins */
5114f86e817SJernej Skrabec regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
5124f86e817SJernej Skrabec
5134f86e817SJernej Skrabec /* read calibration data */
5144f86e817SJernej Skrabec regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
5154f86e817SJernej Skrabec phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
5164f86e817SJernej Skrabec }
5174f86e817SJernej Skrabec
sun8i_hdmi_phy_init(struct sun8i_hdmi_phy * phy)518c64c8e04SJernej Skrabec int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
5196fd90310SJernej Skrabec {
520c64c8e04SJernej Skrabec int ret;
521c64c8e04SJernej Skrabec
522c64c8e04SJernej Skrabec ret = reset_control_deassert(phy->rst_phy);
523c64c8e04SJernej Skrabec if (ret) {
524c64c8e04SJernej Skrabec dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
525c64c8e04SJernej Skrabec return ret;
526c64c8e04SJernej Skrabec }
527c64c8e04SJernej Skrabec
528c64c8e04SJernej Skrabec ret = clk_prepare_enable(phy->clk_bus);
529c64c8e04SJernej Skrabec if (ret) {
530c64c8e04SJernej Skrabec dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
531c64c8e04SJernej Skrabec goto err_assert_rst_phy;
532c64c8e04SJernej Skrabec }
533c64c8e04SJernej Skrabec
534c64c8e04SJernej Skrabec ret = clk_prepare_enable(phy->clk_mod);
535c64c8e04SJernej Skrabec if (ret) {
536c64c8e04SJernej Skrabec dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
537c64c8e04SJernej Skrabec goto err_disable_clk_bus;
538c64c8e04SJernej Skrabec }
539c64c8e04SJernej Skrabec
540c64c8e04SJernej Skrabec if (phy->variant->has_phy_clk) {
541c64c8e04SJernej Skrabec ret = sun8i_phy_clk_create(phy, phy->dev,
542c64c8e04SJernej Skrabec phy->variant->has_second_pll);
543c64c8e04SJernej Skrabec if (ret) {
544c64c8e04SJernej Skrabec dev_err(phy->dev, "Couldn't create the PHY clock\n");
545c64c8e04SJernej Skrabec goto err_disable_clk_mod;
546c64c8e04SJernej Skrabec }
547c64c8e04SJernej Skrabec
548c64c8e04SJernej Skrabec clk_prepare_enable(phy->clk_phy);
549c64c8e04SJernej Skrabec }
550c64c8e04SJernej Skrabec
5516fd90310SJernej Skrabec phy->variant->phy_init(phy);
552c64c8e04SJernej Skrabec
553c64c8e04SJernej Skrabec return 0;
554c64c8e04SJernej Skrabec
555c64c8e04SJernej Skrabec err_disable_clk_mod:
556c64c8e04SJernej Skrabec clk_disable_unprepare(phy->clk_mod);
557c64c8e04SJernej Skrabec err_disable_clk_bus:
558c64c8e04SJernej Skrabec clk_disable_unprepare(phy->clk_bus);
559c64c8e04SJernej Skrabec err_assert_rst_phy:
560c64c8e04SJernej Skrabec reset_control_assert(phy->rst_phy);
561c64c8e04SJernej Skrabec
562c64c8e04SJernej Skrabec return ret;
563c64c8e04SJernej Skrabec }
564c64c8e04SJernej Skrabec
sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy * phy)565c64c8e04SJernej Skrabec void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
566c64c8e04SJernej Skrabec {
567c64c8e04SJernej Skrabec clk_disable_unprepare(phy->clk_mod);
568c64c8e04SJernej Skrabec clk_disable_unprepare(phy->clk_bus);
569c64c8e04SJernej Skrabec clk_disable_unprepare(phy->clk_phy);
570c64c8e04SJernej Skrabec
571c64c8e04SJernej Skrabec reset_control_assert(phy->rst_phy);
5726fd90310SJernej Skrabec }
5736fd90310SJernej Skrabec
sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy * phy,struct dw_hdmi_plat_data * plat_data)574c71c9b2fSJernej Skrabec void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
575c71c9b2fSJernej Skrabec struct dw_hdmi_plat_data *plat_data)
576b7c7436aSJernej Skrabec {
5771a395a56SSamuel Holland const struct sun8i_hdmi_phy_variant *variant = phy->variant;
578c71c9b2fSJernej Skrabec
579cdf3e5e1SSamuel Holland if (variant->phy_ops) {
580cdf3e5e1SSamuel Holland plat_data->phy_ops = variant->phy_ops;
581c71c9b2fSJernej Skrabec plat_data->phy_name = "sun8i_dw_hdmi_phy";
582c71c9b2fSJernej Skrabec plat_data->phy_data = phy;
583c71c9b2fSJernej Skrabec } else {
584c71c9b2fSJernej Skrabec plat_data->mpll_cfg = variant->mpll_cfg;
585c71c9b2fSJernej Skrabec plat_data->cur_ctr = variant->cur_ctr;
586c71c9b2fSJernej Skrabec plat_data->phy_config = variant->phy_cfg;
587c71c9b2fSJernej Skrabec }
588b7c7436aSJernej Skrabec }
589b7c7436aSJernej Skrabec
590f13478c9SRikard Falkeborn static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
591b7c7436aSJernej Skrabec .reg_bits = 32,
592b7c7436aSJernej Skrabec .val_bits = 32,
593b7c7436aSJernej Skrabec .reg_stride = 4,
5944f86e817SJernej Skrabec .max_register = SUN8I_HDMI_PHY_CEC_REG,
595b7c7436aSJernej Skrabec .name = "phy"
596b7c7436aSJernej Skrabec };
597b7c7436aSJernej Skrabec
5986fd90310SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
599ef2731e4SSamuel Holland .phy_ops = &sun8i_a83t_hdmi_phy_ops,
6006fd90310SJernej Skrabec .phy_init = &sun8i_hdmi_phy_init_a83t,
6016fd90310SJernej Skrabec };
6026fd90310SJernej Skrabec
6034f86e817SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
6044f86e817SJernej Skrabec .has_phy_clk = true,
605ef2731e4SSamuel Holland .phy_ops = &sun8i_h3_hdmi_phy_ops,
6064f86e817SJernej Skrabec .phy_init = &sun8i_hdmi_phy_init_h3,
6074f86e817SJernej Skrabec };
6084f86e817SJernej Skrabec
6093536faefSIcenowy Zheng static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
6103536faefSIcenowy Zheng .has_phy_clk = true,
6113536faefSIcenowy Zheng .has_second_pll = true,
612ef2731e4SSamuel Holland .phy_ops = &sun8i_h3_hdmi_phy_ops,
6133536faefSIcenowy Zheng .phy_init = &sun8i_hdmi_phy_init_h3,
6143536faefSIcenowy Zheng };
6153536faefSIcenowy Zheng
616058262b1SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
617058262b1SJernej Skrabec .has_phy_clk = true,
618ef2731e4SSamuel Holland .phy_ops = &sun8i_h3_hdmi_phy_ops,
619058262b1SJernej Skrabec .phy_init = &sun8i_hdmi_phy_init_h3,
620058262b1SJernej Skrabec };
621058262b1SJernej Skrabec
6220fb4b858SJernej Skrabec static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = {
6230fb4b858SJernej Skrabec .cur_ctr = sun50i_h6_cur_ctr,
6240fb4b858SJernej Skrabec .mpll_cfg = sun50i_h6_mpll_cfg,
6250fb4b858SJernej Skrabec .phy_cfg = sun50i_h6_phy_config,
6260fb4b858SJernej Skrabec .phy_init = &sun50i_hdmi_phy_init_h6,
6270fb4b858SJernej Skrabec };
6280fb4b858SJernej Skrabec
629b7c7436aSJernej Skrabec static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
6306fd90310SJernej Skrabec {
6316fd90310SJernej Skrabec .compatible = "allwinner,sun8i-a83t-hdmi-phy",
6326fd90310SJernej Skrabec .data = &sun8i_a83t_hdmi_phy,
6336fd90310SJernej Skrabec },
6344f86e817SJernej Skrabec {
6354f86e817SJernej Skrabec .compatible = "allwinner,sun8i-h3-hdmi-phy",
6364f86e817SJernej Skrabec .data = &sun8i_h3_hdmi_phy,
6374f86e817SJernej Skrabec },
6383536faefSIcenowy Zheng {
6393536faefSIcenowy Zheng .compatible = "allwinner,sun8i-r40-hdmi-phy",
6403536faefSIcenowy Zheng .data = &sun8i_r40_hdmi_phy,
6413536faefSIcenowy Zheng },
642058262b1SJernej Skrabec {
643058262b1SJernej Skrabec .compatible = "allwinner,sun50i-a64-hdmi-phy",
644058262b1SJernej Skrabec .data = &sun50i_a64_hdmi_phy,
645058262b1SJernej Skrabec },
6460fb4b858SJernej Skrabec {
6470fb4b858SJernej Skrabec .compatible = "allwinner,sun50i-h6-hdmi-phy",
6480fb4b858SJernej Skrabec .data = &sun50i_h6_hdmi_phy,
6490fb4b858SJernej Skrabec },
650b7c7436aSJernej Skrabec { /* sentinel */ }
651b7c7436aSJernej Skrabec };
652b7c7436aSJernej Skrabec
sun8i_hdmi_phy_get(struct sun8i_dw_hdmi * hdmi,struct device_node * node)6539bf37977SSaravana Kannan int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
6549bf37977SSaravana Kannan {
6559bf37977SSaravana Kannan struct platform_device *pdev = of_find_device_by_node(node);
6569bf37977SSaravana Kannan struct sun8i_hdmi_phy *phy;
6579bf37977SSaravana Kannan
6589bf37977SSaravana Kannan if (!pdev)
6599bf37977SSaravana Kannan return -EPROBE_DEFER;
6609bf37977SSaravana Kannan
6619bf37977SSaravana Kannan phy = platform_get_drvdata(pdev);
662c71af3daSMiaoqian Lin if (!phy) {
663c71af3daSMiaoqian Lin put_device(&pdev->dev);
6649bf37977SSaravana Kannan return -EPROBE_DEFER;
665c71af3daSMiaoqian Lin }
6669bf37977SSaravana Kannan
6679bf37977SSaravana Kannan hdmi->phy = phy;
6689bf37977SSaravana Kannan
6699bf37977SSaravana Kannan put_device(&pdev->dev);
6709bf37977SSaravana Kannan
6719bf37977SSaravana Kannan return 0;
6729bf37977SSaravana Kannan }
6739bf37977SSaravana Kannan
sun8i_hdmi_phy_probe(struct platform_device * pdev)6749bf37977SSaravana Kannan static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
675b7c7436aSJernej Skrabec {
6769bf37977SSaravana Kannan struct device *dev = &pdev->dev;
677b7c7436aSJernej Skrabec struct sun8i_hdmi_phy *phy;
678b7c7436aSJernej Skrabec void __iomem *regs;
679b7c7436aSJernej Skrabec
680b7c7436aSJernej Skrabec phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
681b7c7436aSJernej Skrabec if (!phy)
682b7c7436aSJernej Skrabec return -ENOMEM;
683b7c7436aSJernej Skrabec
6841a395a56SSamuel Holland phy->variant = of_device_get_match_data(dev);
685c64c8e04SJernej Skrabec phy->dev = dev;
6866fd90310SJernej Skrabec
687719216ebSSamuel Holland regs = devm_platform_ioremap_resource(pdev, 0);
68894c25fb1SSamuel Holland if (IS_ERR(regs))
68994c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(regs),
69094c25fb1SSamuel Holland "Couldn't map the HDMI PHY registers\n");
691b7c7436aSJernej Skrabec
692b7c7436aSJernej Skrabec phy->regs = devm_regmap_init_mmio(dev, regs,
693b7c7436aSJernej Skrabec &sun8i_hdmi_phy_regmap_config);
69494c25fb1SSamuel Holland if (IS_ERR(phy->regs))
69594c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(phy->regs),
69694c25fb1SSamuel Holland "Couldn't create the HDMI PHY regmap\n");
697b7c7436aSJernej Skrabec
69894c25fb1SSamuel Holland phy->clk_bus = devm_clk_get(dev, "bus");
69994c25fb1SSamuel Holland if (IS_ERR(phy->clk_bus))
70094c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
70194c25fb1SSamuel Holland "Could not get bus clock\n");
702b7c7436aSJernej Skrabec
70394c25fb1SSamuel Holland phy->clk_mod = devm_clk_get(dev, "mod");
70494c25fb1SSamuel Holland if (IS_ERR(phy->clk_mod))
70594c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
70694c25fb1SSamuel Holland "Could not get mod clock\n");
707b7c7436aSJernej Skrabec
7084f86e817SJernej Skrabec if (phy->variant->has_phy_clk) {
70994c25fb1SSamuel Holland phy->clk_pll0 = devm_clk_get(dev, "pll-0");
71094c25fb1SSamuel Holland if (IS_ERR(phy->clk_pll0))
71194c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
71294c25fb1SSamuel Holland "Could not get pll-0 clock\n");
7134f86e817SJernej Skrabec
714aef13fd8SJernej Skrabec if (phy->variant->has_second_pll) {
71594c25fb1SSamuel Holland phy->clk_pll1 = devm_clk_get(dev, "pll-1");
71694c25fb1SSamuel Holland if (IS_ERR(phy->clk_pll1))
71794c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(phy->clk_pll1),
71894c25fb1SSamuel Holland "Could not get pll-1 clock\n");
719aef13fd8SJernej Skrabec }
7204f86e817SJernej Skrabec }
7214f86e817SJernej Skrabec
72294c25fb1SSamuel Holland phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
72394c25fb1SSamuel Holland if (IS_ERR(phy->rst_phy))
72494c25fb1SSamuel Holland return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
72594c25fb1SSamuel Holland "Could not get phy reset control\n");
726b7c7436aSJernej Skrabec
7279bf37977SSaravana Kannan platform_set_drvdata(pdev, phy);
728b7c7436aSJernej Skrabec
729b7c7436aSJernej Skrabec return 0;
730b7c7436aSJernej Skrabec }
7319bf37977SSaravana Kannan
7329bf37977SSaravana Kannan struct platform_driver sun8i_hdmi_phy_driver = {
7339bf37977SSaravana Kannan .probe = sun8i_hdmi_phy_probe,
7349bf37977SSaravana Kannan .driver = {
7359bf37977SSaravana Kannan .name = "sun8i-hdmi-phy",
7369bf37977SSaravana Kannan .of_match_table = sun8i_hdmi_phy_of_table,
7379bf37977SSaravana Kannan },
7389bf37977SSaravana Kannan };
739