xref: /openbmc/linux/drivers/gpu/drm/msm/hdmi/hdmi.c (revision 15b4a452)
1c8afe684SRob Clark /*
25eba5d87SStephane Viau  * Copyright (c) 2014 The Linux Foundation. All rights reserved.
3c8afe684SRob Clark  * Copyright (C) 2013 Red Hat
4c8afe684SRob Clark  * Author: Rob Clark <robdclark@gmail.com>
5c8afe684SRob Clark  *
6c8afe684SRob Clark  * This program is free software; you can redistribute it and/or modify it
7c8afe684SRob Clark  * under the terms of the GNU General Public License version 2 as published by
8c8afe684SRob Clark  * the Free Software Foundation.
9c8afe684SRob Clark  *
10c8afe684SRob Clark  * This program is distributed in the hope that it will be useful, but WITHOUT
11c8afe684SRob Clark  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12c8afe684SRob Clark  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13c8afe684SRob Clark  * more details.
14c8afe684SRob Clark  *
15c8afe684SRob Clark  * You should have received a copy of the GNU General Public License along with
16c8afe684SRob Clark  * this program.  If not, see <http://www.gnu.org/licenses/>.
17c8afe684SRob Clark  */
18c8afe684SRob Clark 
19f6a8eacaSRob Clark #include <linux/of_irq.h>
201fd6a441SArchit Taneja #include <linux/of_gpio.h>
211fd6a441SArchit Taneja 
22c8afe684SRob Clark #include "hdmi.h"
23c8afe684SRob Clark 
24c8afe684SRob Clark void hdmi_set_mode(struct hdmi *hdmi, bool power_on)
25c8afe684SRob Clark {
26c8afe684SRob Clark 	uint32_t ctrl = 0;
27c6a57a50Sjilai wang 	unsigned long flags;
28c8afe684SRob Clark 
29c6a57a50Sjilai wang 	spin_lock_irqsave(&hdmi->reg_lock, flags);
30c8afe684SRob Clark 	if (power_on) {
31c8afe684SRob Clark 		ctrl |= HDMI_CTRL_ENABLE;
32c8afe684SRob Clark 		if (!hdmi->hdmi_mode) {
33c8afe684SRob Clark 			ctrl |= HDMI_CTRL_HDMI;
34c8afe684SRob Clark 			hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
35c8afe684SRob Clark 			ctrl &= ~HDMI_CTRL_HDMI;
36c8afe684SRob Clark 		} else {
37c8afe684SRob Clark 			ctrl |= HDMI_CTRL_HDMI;
38c8afe684SRob Clark 		}
39c8afe684SRob Clark 	} else {
40c8afe684SRob Clark 		ctrl = HDMI_CTRL_HDMI;
41c8afe684SRob Clark 	}
42c8afe684SRob Clark 
43c8afe684SRob Clark 	hdmi_write(hdmi, REG_HDMI_CTRL, ctrl);
44c6a57a50Sjilai wang 	spin_unlock_irqrestore(&hdmi->reg_lock, flags);
45c8afe684SRob Clark 	DBG("HDMI Core: %s, HDMI_CTRL=0x%08x",
46c8afe684SRob Clark 			power_on ? "Enable" : "Disable", ctrl);
47c8afe684SRob Clark }
48c8afe684SRob Clark 
49f6a8eacaSRob Clark static irqreturn_t hdmi_irq(int irq, void *dev_id)
50c8afe684SRob Clark {
51c8afe684SRob Clark 	struct hdmi *hdmi = dev_id;
52c8afe684SRob Clark 
53c8afe684SRob Clark 	/* Process HPD: */
54c8afe684SRob Clark 	hdmi_connector_irq(hdmi->connector);
55c8afe684SRob Clark 
56c8afe684SRob Clark 	/* Process DDC: */
57c8afe684SRob Clark 	hdmi_i2c_irq(hdmi->i2c);
58c8afe684SRob Clark 
59c6a57a50Sjilai wang 	/* Process HDCP: */
60c6a57a50Sjilai wang 	if (hdmi->hdcp_ctrl)
61c6a57a50Sjilai wang 		hdmi_hdcp_irq(hdmi->hdcp_ctrl);
62c6a57a50Sjilai wang 
63c8afe684SRob Clark 	/* TODO audio.. */
64c8afe684SRob Clark 
65c8afe684SRob Clark 	return IRQ_HANDLED;
66c8afe684SRob Clark }
67c8afe684SRob Clark 
68d1a717bdSRob Clark static void hdmi_destroy(struct hdmi *hdmi)
69c8afe684SRob Clark {
70c8afe684SRob Clark 	struct hdmi_phy *phy = hdmi->phy;
71c8afe684SRob Clark 
72c6a57a50Sjilai wang 	/*
73c6a57a50Sjilai wang 	 * at this point, hpd has been disabled,
74c6a57a50Sjilai wang 	 * after flush workq, it's safe to deinit hdcp
75c6a57a50Sjilai wang 	 */
76c6a57a50Sjilai wang 	if (hdmi->workq) {
77c6a57a50Sjilai wang 		flush_workqueue(hdmi->workq);
78c6a57a50Sjilai wang 		destroy_workqueue(hdmi->workq);
79c6a57a50Sjilai wang 	}
80c6a57a50Sjilai wang 	hdmi_hdcp_destroy(hdmi);
81c8afe684SRob Clark 	if (phy)
82c8afe684SRob Clark 		phy->funcs->destroy(phy);
83c8afe684SRob Clark 
84c8afe684SRob Clark 	if (hdmi->i2c)
85c8afe684SRob Clark 		hdmi_i2c_destroy(hdmi->i2c);
86c8afe684SRob Clark 
87c0c0d9eeSRob Clark 	platform_set_drvdata(hdmi->pdev, NULL);
88c8afe684SRob Clark }
89c8afe684SRob Clark 
90067fef37SRob Clark /* construct hdmi at bind/probe time, grab all the resources.  If
91067fef37SRob Clark  * we are to EPROBE_DEFER we want to do it here, rather than later
92067fef37SRob Clark  * at modeset_init() time
93067fef37SRob Clark  */
94067fef37SRob Clark static struct hdmi *hdmi_init(struct platform_device *pdev)
95c8afe684SRob Clark {
96067fef37SRob Clark 	struct hdmi_platform_config *config = pdev->dev.platform_data;
97a3376e3eSRob Clark 	struct hdmi *hdmi = NULL;
98c6a57a50Sjilai wang 	struct resource *res;
99dada25bdSRob Clark 	int i, ret;
100c8afe684SRob Clark 
101067fef37SRob Clark 	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
102a3376e3eSRob Clark 	if (!hdmi) {
103a3376e3eSRob Clark 		ret = -ENOMEM;
104a3376e3eSRob Clark 		goto fail;
105a3376e3eSRob Clark 	}
106a3376e3eSRob Clark 
107c8afe684SRob Clark 	hdmi->pdev = pdev;
108dada25bdSRob Clark 	hdmi->config = config;
109c6a57a50Sjilai wang 	spin_lock_init(&hdmi->reg_lock);
110c0c0d9eeSRob Clark 
111c8afe684SRob Clark 	/* not sure about which phy maps to which msm.. probably I miss some */
1123a84f846SStephane Viau 	if (config->phy_init) {
113c8afe684SRob Clark 		hdmi->phy = config->phy_init(hdmi);
114c8afe684SRob Clark 
115c8afe684SRob Clark 		if (IS_ERR(hdmi->phy)) {
116c8afe684SRob Clark 			ret = PTR_ERR(hdmi->phy);
117067fef37SRob Clark 			dev_err(&pdev->dev, "failed to load phy: %d\n", ret);
118c8afe684SRob Clark 			hdmi->phy = NULL;
119c8afe684SRob Clark 			goto fail;
120c8afe684SRob Clark 		}
1213a84f846SStephane Viau 	}
122c8afe684SRob Clark 
123dada25bdSRob Clark 	hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
124c8afe684SRob Clark 	if (IS_ERR(hdmi->mmio)) {
125c8afe684SRob Clark 		ret = PTR_ERR(hdmi->mmio);
126c8afe684SRob Clark 		goto fail;
127c8afe684SRob Clark 	}
128c8afe684SRob Clark 
129c6a57a50Sjilai wang 	/* HDCP needs physical address of hdmi register */
130c6a57a50Sjilai wang 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
131c6a57a50Sjilai wang 		config->mmio_name);
132c6a57a50Sjilai wang 	hdmi->mmio_phy_addr = res->start;
133c6a57a50Sjilai wang 
134c6a57a50Sjilai wang 	hdmi->qfprom_mmio = msm_ioremap(pdev,
135c6a57a50Sjilai wang 		config->qfprom_mmio_name, "HDMI_QFPROM");
136c6a57a50Sjilai wang 	if (IS_ERR(hdmi->qfprom_mmio)) {
137c6a57a50Sjilai wang 		dev_info(&pdev->dev, "can't find qfprom resource\n");
138c6a57a50Sjilai wang 		hdmi->qfprom_mmio = NULL;
139c6a57a50Sjilai wang 	}
140c6a57a50Sjilai wang 
141447fa529SStephane Viau 	hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) *
142447fa529SStephane Viau 			config->hpd_reg_cnt, GFP_KERNEL);
143447fa529SStephane Viau 	if (!hdmi->hpd_regs) {
144447fa529SStephane Viau 		ret = -ENOMEM;
145447fa529SStephane Viau 		goto fail;
146447fa529SStephane Viau 	}
147dada25bdSRob Clark 	for (i = 0; i < config->hpd_reg_cnt; i++) {
148dada25bdSRob Clark 		struct regulator *reg;
149dada25bdSRob Clark 
1503e87599bSRob Clark 		reg = devm_regulator_get(&pdev->dev,
15141e69778SRob Clark 				config->hpd_reg_names[i]);
152dada25bdSRob Clark 		if (IS_ERR(reg)) {
153dada25bdSRob Clark 			ret = PTR_ERR(reg);
154067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get hpd regulator: %s (%d)\n",
155dada25bdSRob Clark 					config->hpd_reg_names[i], ret);
156c8afe684SRob Clark 			goto fail;
157c8afe684SRob Clark 		}
158c8afe684SRob Clark 
159dada25bdSRob Clark 		hdmi->hpd_regs[i] = reg;
160dada25bdSRob Clark 	}
161c8afe684SRob Clark 
162447fa529SStephane Viau 	hdmi->pwr_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_regs[0]) *
163447fa529SStephane Viau 			config->pwr_reg_cnt, GFP_KERNEL);
164447fa529SStephane Viau 	if (!hdmi->pwr_regs) {
165447fa529SStephane Viau 		ret = -ENOMEM;
166447fa529SStephane Viau 		goto fail;
167447fa529SStephane Viau 	}
168dada25bdSRob Clark 	for (i = 0; i < config->pwr_reg_cnt; i++) {
169dada25bdSRob Clark 		struct regulator *reg;
170dada25bdSRob Clark 
1713e87599bSRob Clark 		reg = devm_regulator_get(&pdev->dev,
17241e69778SRob Clark 				config->pwr_reg_names[i]);
173dada25bdSRob Clark 		if (IS_ERR(reg)) {
174dada25bdSRob Clark 			ret = PTR_ERR(reg);
175067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get pwr regulator: %s (%d)\n",
176dada25bdSRob Clark 					config->pwr_reg_names[i], ret);
177c8afe684SRob Clark 			goto fail;
178c8afe684SRob Clark 		}
179c8afe684SRob Clark 
180dada25bdSRob Clark 		hdmi->pwr_regs[i] = reg;
181dada25bdSRob Clark 	}
182dada25bdSRob Clark 
183447fa529SStephane Viau 	hdmi->hpd_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_clks[0]) *
184447fa529SStephane Viau 			config->hpd_clk_cnt, GFP_KERNEL);
185447fa529SStephane Viau 	if (!hdmi->hpd_clks) {
186447fa529SStephane Viau 		ret = -ENOMEM;
187447fa529SStephane Viau 		goto fail;
188447fa529SStephane Viau 	}
189dada25bdSRob Clark 	for (i = 0; i < config->hpd_clk_cnt; i++) {
190dada25bdSRob Clark 		struct clk *clk;
191dada25bdSRob Clark 
192dada25bdSRob Clark 		clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
193dada25bdSRob Clark 		if (IS_ERR(clk)) {
194dada25bdSRob Clark 			ret = PTR_ERR(clk);
195067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get hpd clk: %s (%d)\n",
196dada25bdSRob Clark 					config->hpd_clk_names[i], ret);
197c8afe684SRob Clark 			goto fail;
198c8afe684SRob Clark 		}
199c8afe684SRob Clark 
200dada25bdSRob Clark 		hdmi->hpd_clks[i] = clk;
201dada25bdSRob Clark 	}
202dada25bdSRob Clark 
203447fa529SStephane Viau 	hdmi->pwr_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_clks[0]) *
204447fa529SStephane Viau 			config->pwr_clk_cnt, GFP_KERNEL);
205447fa529SStephane Viau 	if (!hdmi->pwr_clks) {
206447fa529SStephane Viau 		ret = -ENOMEM;
207447fa529SStephane Viau 		goto fail;
208447fa529SStephane Viau 	}
209dada25bdSRob Clark 	for (i = 0; i < config->pwr_clk_cnt; i++) {
210dada25bdSRob Clark 		struct clk *clk;
211dada25bdSRob Clark 
212dada25bdSRob Clark 		clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
213dada25bdSRob Clark 		if (IS_ERR(clk)) {
214dada25bdSRob Clark 			ret = PTR_ERR(clk);
215067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get pwr clk: %s (%d)\n",
216dada25bdSRob Clark 					config->pwr_clk_names[i], ret);
217c8afe684SRob Clark 			goto fail;
218c8afe684SRob Clark 		}
219c8afe684SRob Clark 
220dada25bdSRob Clark 		hdmi->pwr_clks[i] = clk;
221dada25bdSRob Clark 	}
222dada25bdSRob Clark 
223c6a57a50Sjilai wang 	hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0);
224c6a57a50Sjilai wang 
225c8afe684SRob Clark 	hdmi->i2c = hdmi_i2c_init(hdmi);
226c8afe684SRob Clark 	if (IS_ERR(hdmi->i2c)) {
227c8afe684SRob Clark 		ret = PTR_ERR(hdmi->i2c);
228067fef37SRob Clark 		dev_err(&pdev->dev, "failed to get i2c: %d\n", ret);
229c8afe684SRob Clark 		hdmi->i2c = NULL;
230c8afe684SRob Clark 		goto fail;
231c8afe684SRob Clark 	}
232c8afe684SRob Clark 
233c6a57a50Sjilai wang 	hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi);
234c6a57a50Sjilai wang 	if (IS_ERR(hdmi->hdcp_ctrl)) {
235c6a57a50Sjilai wang 		dev_warn(&pdev->dev, "failed to init hdcp: disabled\n");
236c6a57a50Sjilai wang 		hdmi->hdcp_ctrl = NULL;
237c6a57a50Sjilai wang 	}
238c6a57a50Sjilai wang 
239067fef37SRob Clark 	return hdmi;
240067fef37SRob Clark 
241067fef37SRob Clark fail:
242067fef37SRob Clark 	if (hdmi)
243d1a717bdSRob Clark 		hdmi_destroy(hdmi);
244067fef37SRob Clark 
245067fef37SRob Clark 	return ERR_PTR(ret);
246067fef37SRob Clark }
247067fef37SRob Clark 
248067fef37SRob Clark /* Second part of initialization, the drm/kms level modeset_init,
249067fef37SRob Clark  * constructs/initializes mode objects, etc, is called from master
250067fef37SRob Clark  * driver (not hdmi sub-device's probe/bind!)
251067fef37SRob Clark  *
252067fef37SRob Clark  * Any resource (regulator/clk/etc) which could be missing at boot
253067fef37SRob Clark  * should be handled in hdmi_init() so that failure happens from
254067fef37SRob Clark  * hdmi sub-device's probe.
255067fef37SRob Clark  */
256067fef37SRob Clark int hdmi_modeset_init(struct hdmi *hdmi,
257067fef37SRob Clark 		struct drm_device *dev, struct drm_encoder *encoder)
258067fef37SRob Clark {
259067fef37SRob Clark 	struct msm_drm_private *priv = dev->dev_private;
260067fef37SRob Clark 	struct platform_device *pdev = hdmi->pdev;
261067fef37SRob Clark 	int ret;
262067fef37SRob Clark 
263067fef37SRob Clark 	hdmi->dev = dev;
264067fef37SRob Clark 	hdmi->encoder = encoder;
265067fef37SRob Clark 
266067fef37SRob Clark 	hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
267067fef37SRob Clark 
268a3376e3eSRob Clark 	hdmi->bridge = hdmi_bridge_init(hdmi);
269a3376e3eSRob Clark 	if (IS_ERR(hdmi->bridge)) {
270a3376e3eSRob Clark 		ret = PTR_ERR(hdmi->bridge);
271a3376e3eSRob Clark 		dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret);
272a3376e3eSRob Clark 		hdmi->bridge = NULL;
273a3376e3eSRob Clark 		goto fail;
274a3376e3eSRob Clark 	}
275a3376e3eSRob Clark 
276a3376e3eSRob Clark 	hdmi->connector = hdmi_connector_init(hdmi);
277a3376e3eSRob Clark 	if (IS_ERR(hdmi->connector)) {
278a3376e3eSRob Clark 		ret = PTR_ERR(hdmi->connector);
279a3376e3eSRob Clark 		dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret);
280a3376e3eSRob Clark 		hdmi->connector = NULL;
281a3376e3eSRob Clark 		goto fail;
282a3376e3eSRob Clark 	}
283a3376e3eSRob Clark 
284f6a8eacaSRob Clark 	hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
285c8afe684SRob Clark 	if (hdmi->irq < 0) {
286c8afe684SRob Clark 		ret = hdmi->irq;
287c8afe684SRob Clark 		dev_err(dev->dev, "failed to get irq: %d\n", ret);
288c8afe684SRob Clark 		goto fail;
289c8afe684SRob Clark 	}
290c8afe684SRob Clark 
291f6a8eacaSRob Clark 	ret = devm_request_irq(&pdev->dev, hdmi->irq,
292f6a8eacaSRob Clark 			hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
293c8afe684SRob Clark 			"hdmi_isr", hdmi);
294c8afe684SRob Clark 	if (ret < 0) {
295c8afe684SRob Clark 		dev_err(dev->dev, "failed to request IRQ%u: %d\n",
296c8afe684SRob Clark 				hdmi->irq, ret);
297c8afe684SRob Clark 		goto fail;
298c8afe684SRob Clark 	}
299c8afe684SRob Clark 
300a3376e3eSRob Clark 	encoder->bridge = hdmi->bridge;
301a3376e3eSRob Clark 
302a3376e3eSRob Clark 	priv->bridges[priv->num_bridges++]       = hdmi->bridge;
303a3376e3eSRob Clark 	priv->connectors[priv->num_connectors++] = hdmi->connector;
304a3376e3eSRob Clark 
305c0c0d9eeSRob Clark 	platform_set_drvdata(pdev, hdmi);
306c0c0d9eeSRob Clark 
307067fef37SRob Clark 	return 0;
308c8afe684SRob Clark 
309c8afe684SRob Clark fail:
3103d3f8b1fSAjay Kumar 	/* bridge is normally destroyed by drm: */
311067fef37SRob Clark 	if (hdmi->bridge) {
3123d3f8b1fSAjay Kumar 		hdmi_bridge_destroy(hdmi->bridge);
313067fef37SRob Clark 		hdmi->bridge = NULL;
314067fef37SRob Clark 	}
315067fef37SRob Clark 	if (hdmi->connector) {
316a3376e3eSRob Clark 		hdmi->connector->funcs->destroy(hdmi->connector);
317067fef37SRob Clark 		hdmi->connector = NULL;
318a3376e3eSRob Clark 	}
319c8afe684SRob Clark 
320067fef37SRob Clark 	return ret;
321c8afe684SRob Clark }
322c8afe684SRob Clark 
323c8afe684SRob Clark /*
324c8afe684SRob Clark  * The hdmi device:
325c8afe684SRob Clark  */
326c8afe684SRob Clark 
3275eba5d87SStephane Viau #define HDMI_CFG(item, entry) \
3285eba5d87SStephane Viau 	.item ## _names = item ##_names_ ## entry, \
3295eba5d87SStephane Viau 	.item ## _cnt   = ARRAY_SIZE(item ## _names_ ## entry)
3305eba5d87SStephane Viau 
3310afbe59eSStephane Viau static const char *pwr_reg_names_none[] = {};
3320afbe59eSStephane Viau static const char *hpd_reg_names_none[] = {};
3330afbe59eSStephane Viau 
3345eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8660_config = {
3355eba5d87SStephane Viau 		.phy_init = hdmi_phy_8x60_init,
3365eba5d87SStephane Viau };
3375eba5d87SStephane Viau 
3385eba5d87SStephane Viau static const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"};
3395eba5d87SStephane Viau static const char *hpd_clk_names_8960[] = {"core_clk", "master_iface_clk", "slave_iface_clk"};
3405eba5d87SStephane Viau 
3415eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8960_config = {
3425eba5d87SStephane Viau 		.phy_init = hdmi_phy_8960_init,
3435eba5d87SStephane Viau 		HDMI_CFG(hpd_reg, 8960),
3445eba5d87SStephane Viau 		HDMI_CFG(hpd_clk, 8960),
3455eba5d87SStephane Viau };
3465eba5d87SStephane Viau 
3475eba5d87SStephane Viau static const char *pwr_reg_names_8x74[] = {"core-vdda", "core-vcc"};
3485eba5d87SStephane Viau static const char *hpd_reg_names_8x74[] = {"hpd-gdsc", "hpd-5v"};
3495eba5d87SStephane Viau static const char *pwr_clk_names_8x74[] = {"extp_clk", "alt_iface_clk"};
3505eba5d87SStephane Viau static const char *hpd_clk_names_8x74[] = {"iface_clk", "core_clk", "mdp_core_clk"};
3515eba5d87SStephane Viau static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0};
3525eba5d87SStephane Viau 
3535cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8974_config = {
3545eba5d87SStephane Viau 		.phy_init = hdmi_phy_8x74_init,
3555eba5d87SStephane Viau 		HDMI_CFG(pwr_reg, 8x74),
3565eba5d87SStephane Viau 		HDMI_CFG(hpd_reg, 8x74),
3575eba5d87SStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3585eba5d87SStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3595eba5d87SStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3605eba5d87SStephane Viau };
3615eba5d87SStephane Viau 
3625eba5d87SStephane Viau static const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"};
3635eba5d87SStephane Viau 
3645eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8084_config = {
3655eba5d87SStephane Viau 		.phy_init = hdmi_phy_8x74_init,
3665eba5d87SStephane Viau 		HDMI_CFG(pwr_reg, 8x74),
3675eba5d87SStephane Viau 		HDMI_CFG(hpd_reg, 8084),
3685eba5d87SStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3695eba5d87SStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3705eba5d87SStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3715eba5d87SStephane Viau };
3725eba5d87SStephane Viau 
3735cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8994_config = {
3743a84f846SStephane Viau 		.phy_init = NULL, /* nothing to do for this HDMI PHY 20nm */
3753a84f846SStephane Viau 		HDMI_CFG(pwr_reg, 8x74),
3760afbe59eSStephane Viau 		HDMI_CFG(hpd_reg, none),
3770afbe59eSStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3780afbe59eSStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3790afbe59eSStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3800afbe59eSStephane Viau };
3810afbe59eSStephane Viau 
3820afbe59eSStephane Viau static struct hdmi_platform_config hdmi_tx_8996_config = {
3830afbe59eSStephane Viau 		.phy_init = NULL,
3840afbe59eSStephane Viau 		HDMI_CFG(pwr_reg, none),
3850afbe59eSStephane Viau 		HDMI_CFG(hpd_reg, none),
3863a84f846SStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3873a84f846SStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3883a84f846SStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3893a84f846SStephane Viau };
3903a84f846SStephane Viau 
391dc50f782SArchit Taneja static const struct {
392dc50f782SArchit Taneja 	const char *name;
393dc50f782SArchit Taneja 	const bool output;
394dc50f782SArchit Taneja 	const int value;
395dc50f782SArchit Taneja 	const char *label;
396dc50f782SArchit Taneja } hdmi_gpio_pdata[] = {
397dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-ddc-clk", true, 1, "HDMI_DDC_CLK" },
398dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-ddc-data", true, 1, "HDMI_DDC_DATA" },
399dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-hpd", false, 1, "HDMI_HPD" },
400dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-mux-en", true, 1, "HDMI_MUX_EN" },
401dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-mux-sel", true, 0, "HDMI_MUX_SEL" },
402dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-mux-lpm", true, 1, "HDMI_MUX_LPM" },
403dc50f782SArchit Taneja };
404dc50f782SArchit Taneja 
405dc50f782SArchit Taneja static int get_gpio(struct device_node *of_node, const char *name)
406dada25bdSRob Clark {
407dada25bdSRob Clark 	int gpio = of_get_named_gpio(of_node, name, 0);
408dada25bdSRob Clark 	if (gpio < 0) {
40941e69778SRob Clark 		char name2[32];
41041e69778SRob Clark 		snprintf(name2, sizeof(name2), "%s-gpio", name);
41141e69778SRob Clark 		gpio = of_get_named_gpio(of_node, name2, 0);
41241e69778SRob Clark 		if (gpio < 0) {
4133a84f846SStephane Viau 			DBG("failed to get gpio: %s (%d)", name, gpio);
414dada25bdSRob Clark 			gpio = -1;
415dada25bdSRob Clark 		}
41641e69778SRob Clark 	}
417dada25bdSRob Clark 	return gpio;
418dada25bdSRob Clark }
419fc886107SMark Charlebois 
420fc886107SMark Charlebois static int hdmi_bind(struct device *dev, struct device *master, void *data)
421fc886107SMark Charlebois {
422d1a717bdSRob Clark 	struct drm_device *drm = dev_get_drvdata(master);
423d1a717bdSRob Clark 	struct msm_drm_private *priv = drm->dev_private;
4245eba5d87SStephane Viau 	static struct hdmi_platform_config *hdmi_cfg;
425067fef37SRob Clark 	struct hdmi *hdmi;
426fc886107SMark Charlebois 	struct device_node *of_node = dev->of_node;
427dc50f782SArchit Taneja 	int i;
428dada25bdSRob Clark 
4291fd6a441SArchit Taneja 	hdmi_cfg = (struct hdmi_platform_config *)
4301fd6a441SArchit Taneja 			of_device_get_match_data(dev);
4311fd6a441SArchit Taneja 	if (!hdmi_cfg) {
4321fd6a441SArchit Taneja 		dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name);
4335eba5d87SStephane Viau 		return -ENXIO;
43441e69778SRob Clark 	}
43541e69778SRob Clark 
4365eba5d87SStephane Viau 	hdmi_cfg->mmio_name     = "core_physical";
437c6a57a50Sjilai wang 	hdmi_cfg->qfprom_mmio_name = "qfprom_physical";
438dc50f782SArchit Taneja 
439dc50f782SArchit Taneja 	for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
440dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].num = get_gpio(of_node,
441dc50f782SArchit Taneja 						hdmi_gpio_pdata[i].name);
442dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].output = hdmi_gpio_pdata[i].output;
443dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].value = hdmi_gpio_pdata[i].value;
444dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].label = hdmi_gpio_pdata[i].label;
445dc50f782SArchit Taneja 	}
446dada25bdSRob Clark 
4475eba5d87SStephane Viau 	dev->platform_data = hdmi_cfg;
4485eba5d87SStephane Viau 
449067fef37SRob Clark 	hdmi = hdmi_init(to_platform_device(dev));
450067fef37SRob Clark 	if (IS_ERR(hdmi))
451067fef37SRob Clark 		return PTR_ERR(hdmi);
452d1a717bdSRob Clark 	priv->hdmi = hdmi;
4535eba5d87SStephane Viau 
454c8afe684SRob Clark 	return 0;
455c8afe684SRob Clark }
456c8afe684SRob Clark 
457060530f1SRob Clark static void hdmi_unbind(struct device *dev, struct device *master,
458060530f1SRob Clark 		void *data)
459060530f1SRob Clark {
460d1a717bdSRob Clark 	struct drm_device *drm = dev_get_drvdata(master);
461d1a717bdSRob Clark 	struct msm_drm_private *priv = drm->dev_private;
462d1a717bdSRob Clark 	if (priv->hdmi) {
463d1a717bdSRob Clark 		hdmi_destroy(priv->hdmi);
464d1a717bdSRob Clark 		priv->hdmi = NULL;
465d1a717bdSRob Clark 	}
466060530f1SRob Clark }
467060530f1SRob Clark 
468060530f1SRob Clark static const struct component_ops hdmi_ops = {
469060530f1SRob Clark 		.bind   = hdmi_bind,
470060530f1SRob Clark 		.unbind = hdmi_unbind,
471060530f1SRob Clark };
472060530f1SRob Clark 
473060530f1SRob Clark static int hdmi_dev_probe(struct platform_device *pdev)
474060530f1SRob Clark {
475060530f1SRob Clark 	return component_add(&pdev->dev, &hdmi_ops);
476060530f1SRob Clark }
477060530f1SRob Clark 
478c8afe684SRob Clark static int hdmi_dev_remove(struct platform_device *pdev)
479c8afe684SRob Clark {
480060530f1SRob Clark 	component_del(&pdev->dev, &hdmi_ops);
481c8afe684SRob Clark 	return 0;
482c8afe684SRob Clark }
483c8afe684SRob Clark 
4841fd6a441SArchit Taneja static const struct of_device_id dt_match[] = {
4851fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config },
4861fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config },
4871fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config },
4881fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config },
4891fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config },
4901fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config },
4911fd6a441SArchit Taneja 	{}
4921fd6a441SArchit Taneja };
4931fd6a441SArchit Taneja 
494c8afe684SRob Clark static struct platform_driver hdmi_driver = {
495c8afe684SRob Clark 	.probe = hdmi_dev_probe,
496c8afe684SRob Clark 	.remove = hdmi_dev_remove,
497dada25bdSRob Clark 	.driver = {
498dada25bdSRob Clark 		.name = "hdmi_msm",
499dada25bdSRob Clark 		.of_match_table = dt_match,
500dada25bdSRob Clark 	},
501c8afe684SRob Clark };
502c8afe684SRob Clark 
503c8afe684SRob Clark void __init hdmi_register(void)
504c8afe684SRob Clark {
50515b4a452SArchit Taneja 	hdmi_phy_driver_register();
506c8afe684SRob Clark 	platform_driver_register(&hdmi_driver);
507c8afe684SRob Clark }
508c8afe684SRob Clark 
509c8afe684SRob Clark void __exit hdmi_unregister(void)
510c8afe684SRob Clark {
511c8afe684SRob Clark 	platform_driver_unregister(&hdmi_driver);
51215b4a452SArchit Taneja 	hdmi_phy_driver_unregister();
513c8afe684SRob Clark }
514