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
20struct hdmi_phy_8x74 {
21	struct hdmi_phy base;
22	struct hdmi *hdmi;
23	void __iomem *mmio;
24};
25#define to_hdmi_phy_8x74(x) container_of(x, struct hdmi_phy_8x74, base)
26
27
28static void phy_write(struct hdmi_phy_8x74 *phy, u32 reg, u32 data)
29{
30	msm_writel(data, phy->mmio + reg);
31}
32
33//static u32 phy_read(struct hdmi_phy_8x74 *phy, u32 reg)
34//{
35//	return msm_readl(phy->mmio + reg);
36//}
37
38static void hdmi_phy_8x74_destroy(struct hdmi_phy *phy)
39{
40	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
41	kfree(phy_8x74);
42}
43
44static void hdmi_phy_8x74_reset(struct hdmi_phy *phy)
45{
46	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
47	struct hdmi *hdmi = phy_8x74->hdmi;
48	unsigned int val;
49
50	/* NOTE that HDMI_PHY_CTL is in core mmio, not phy mmio: */
51
52	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
53
54	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
55		/* pull low */
56		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
57				val & ~HDMI_PHY_CTRL_SW_RESET);
58	} else {
59		/* pull high */
60		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
61				val | HDMI_PHY_CTRL_SW_RESET);
62	}
63
64	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
65		/* pull low */
66		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
67				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
68	} else {
69		/* pull high */
70		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
71				val | HDMI_PHY_CTRL_SW_RESET_PLL);
72	}
73
74	msleep(100);
75
76	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
77		/* pull high */
78		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
79				val | HDMI_PHY_CTRL_SW_RESET);
80	} else {
81		/* pull low */
82		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
83				val & ~HDMI_PHY_CTRL_SW_RESET);
84	}
85
86	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
87		/* pull high */
88		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
89				val | HDMI_PHY_CTRL_SW_RESET_PLL);
90	} else {
91		/* pull low */
92		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
93				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
94	}
95}
96
97static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy,
98		unsigned long int pixclock)
99{
100	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
101
102	phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG0,   0x1b);
103	phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG1,   0xf2);
104	phy_write(phy_8x74, REG_HDMI_8x74_BIST_CFG0,  0x0);
105	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN0, 0x0);
106	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN1, 0x0);
107	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN2, 0x0);
108	phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN3, 0x0);
109	phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL1,   0x20);
110}
111
112static void hdmi_phy_8x74_powerdown(struct hdmi_phy *phy)
113{
114	struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy);
115	phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL0, 0x7f);
116}
117
118static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = {
119		.destroy = hdmi_phy_8x74_destroy,
120		.reset = hdmi_phy_8x74_reset,
121		.powerup = hdmi_phy_8x74_powerup,
122		.powerdown = hdmi_phy_8x74_powerdown,
123};
124
125struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi)
126{
127	struct hdmi_phy_8x74 *phy_8x74;
128	struct hdmi_phy *phy = NULL;
129	int ret;
130
131	phy_8x74 = kzalloc(sizeof(*phy_8x74), GFP_KERNEL);
132	if (!phy_8x74) {
133		ret = -ENOMEM;
134		goto fail;
135	}
136
137	phy = &phy_8x74->base;
138
139	phy->funcs = &hdmi_phy_8x74_funcs;
140
141	phy_8x74->hdmi = hdmi;
142
143	/* for 8x74, the phy mmio is mapped separately: */
144	phy_8x74->mmio = msm_ioremap(hdmi->pdev,
145			"phy_physical", "HDMI_8x74");
146	if (IS_ERR(phy_8x74->mmio)) {
147		ret = PTR_ERR(phy_8x74->mmio);
148		goto fail;
149	}
150
151	return phy;
152
153fail:
154	if (phy)
155		hdmi_phy_8x74_destroy(phy);
156	return ERR_PTR(ret);
157}
158