1 /*
2  * Copyright (C) 2013 Red Hat
3  * Author: Rob Clark <robdclark@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "hdmi.h"
19 
20 struct hdmi_phy_8960 {
21 	struct hdmi_phy base;
22 	struct hdmi *hdmi;
23 };
24 #define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base)
25 
26 static void hdmi_phy_8960_destroy(struct hdmi_phy *phy)
27 {
28 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
29 	kfree(phy_8960);
30 }
31 
32 static void hdmi_phy_8960_reset(struct hdmi_phy *phy)
33 {
34 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
35 	struct hdmi *hdmi = phy_8960->hdmi;
36 	unsigned int val;
37 
38 	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
39 
40 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
41 		/* pull low */
42 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
43 				val & ~HDMI_PHY_CTRL_SW_RESET);
44 	} else {
45 		/* pull high */
46 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
47 				val | HDMI_PHY_CTRL_SW_RESET);
48 	}
49 
50 	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
51 		/* pull low */
52 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
53 				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
54 	} else {
55 		/* pull high */
56 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
57 				val | HDMI_PHY_CTRL_SW_RESET_PLL);
58 	}
59 
60 	msleep(100);
61 
62 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
63 		/* pull high */
64 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
65 				val | HDMI_PHY_CTRL_SW_RESET);
66 	} else {
67 		/* pull low */
68 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
69 				val & ~HDMI_PHY_CTRL_SW_RESET);
70 	}
71 
72 	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
73 		/* pull high */
74 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
75 				val | HDMI_PHY_CTRL_SW_RESET_PLL);
76 	} else {
77 		/* pull low */
78 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
79 				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
80 	}
81 }
82 
83 static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
84 		unsigned long int pixclock)
85 {
86 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
87 	struct hdmi *hdmi = phy_8960->hdmi;
88 
89 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
90 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
91 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
92 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00);
93 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00);
94 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00);
95 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00);
96 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00);
97 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00);
98 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00);
99 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20);
100 }
101 
102 static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
103 {
104 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
105 	struct hdmi *hdmi = phy_8960->hdmi;
106 
107 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
108 }
109 
110 static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
111 		.destroy = hdmi_phy_8960_destroy,
112 		.reset = hdmi_phy_8960_reset,
113 		.powerup = hdmi_phy_8960_powerup,
114 		.powerdown = hdmi_phy_8960_powerdown,
115 };
116 
117 struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
118 {
119 	struct hdmi_phy_8960 *phy_8960;
120 	struct hdmi_phy *phy = NULL;
121 	int ret;
122 
123 	phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
124 	if (!phy_8960) {
125 		ret = -ENOMEM;
126 		goto fail;
127 	}
128 
129 	phy = &phy_8960->base;
130 
131 	phy->funcs = &hdmi_phy_8960_funcs;
132 
133 	phy_8960->hdmi = hdmi;
134 
135 	return phy;
136 
137 fail:
138 	if (phy)
139 		hdmi_phy_8960_destroy(phy);
140 	return ERR_PTR(ret);
141 }
142