xref: /openbmc/linux/drivers/gpu/drm/msm/hdmi/hdmi.c (revision ba3d7bf3)
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 {
70c6a57a50Sjilai wang 	/*
71c6a57a50Sjilai wang 	 * at this point, hpd has been disabled,
72c6a57a50Sjilai wang 	 * after flush workq, it's safe to deinit hdcp
73c6a57a50Sjilai wang 	 */
74c6a57a50Sjilai wang 	if (hdmi->workq) {
75c6a57a50Sjilai wang 		flush_workqueue(hdmi->workq);
76c6a57a50Sjilai wang 		destroy_workqueue(hdmi->workq);
77c6a57a50Sjilai wang 	}
78c6a57a50Sjilai wang 	hdmi_hdcp_destroy(hdmi);
79c8afe684SRob Clark 
80e00012b2SArchit Taneja 	if (hdmi->phy_dev) {
81e00012b2SArchit Taneja 		put_device(hdmi->phy_dev);
82e00012b2SArchit Taneja 		hdmi->phy = NULL;
83e00012b2SArchit Taneja 		hdmi->phy_dev = NULL;
84e00012b2SArchit Taneja 	}
85e00012b2SArchit Taneja 
86c8afe684SRob Clark 	if (hdmi->i2c)
87c8afe684SRob Clark 		hdmi_i2c_destroy(hdmi->i2c);
88c8afe684SRob Clark 
89c0c0d9eeSRob Clark 	platform_set_drvdata(hdmi->pdev, NULL);
90c8afe684SRob Clark }
91c8afe684SRob Clark 
92e00012b2SArchit Taneja static int hdmi_get_phy(struct hdmi *hdmi)
93e00012b2SArchit Taneja {
94e00012b2SArchit Taneja 	struct platform_device *pdev = hdmi->pdev;
95e00012b2SArchit Taneja 	struct platform_device *phy_pdev;
96e00012b2SArchit Taneja 	struct device_node *phy_node;
97e00012b2SArchit Taneja 
98e00012b2SArchit Taneja 	phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0);
99e00012b2SArchit Taneja 	if (!phy_node) {
100e00012b2SArchit Taneja 		dev_err(&pdev->dev, "cannot find phy device\n");
101e00012b2SArchit Taneja 		return -ENXIO;
102e00012b2SArchit Taneja 	}
103e00012b2SArchit Taneja 
104e00012b2SArchit Taneja 	phy_pdev = of_find_device_by_node(phy_node);
105e00012b2SArchit Taneja 	if (phy_pdev)
106e00012b2SArchit Taneja 		hdmi->phy = platform_get_drvdata(phy_pdev);
107e00012b2SArchit Taneja 
108e00012b2SArchit Taneja 	of_node_put(phy_node);
109e00012b2SArchit Taneja 
110e00012b2SArchit Taneja 	if (!phy_pdev || !hdmi->phy) {
111e00012b2SArchit Taneja 		dev_err(&pdev->dev, "phy driver is not ready\n");
112e00012b2SArchit Taneja 		return -EPROBE_DEFER;
113e00012b2SArchit Taneja 	}
114e00012b2SArchit Taneja 
115e00012b2SArchit Taneja 	hdmi->phy_dev = get_device(&phy_pdev->dev);
116e00012b2SArchit Taneja 
117e00012b2SArchit Taneja 	return 0;
118e00012b2SArchit Taneja }
119e00012b2SArchit Taneja 
120067fef37SRob Clark /* construct hdmi at bind/probe time, grab all the resources.  If
121067fef37SRob Clark  * we are to EPROBE_DEFER we want to do it here, rather than later
122067fef37SRob Clark  * at modeset_init() time
123067fef37SRob Clark  */
124067fef37SRob Clark static struct hdmi *hdmi_init(struct platform_device *pdev)
125c8afe684SRob Clark {
126067fef37SRob Clark 	struct hdmi_platform_config *config = pdev->dev.platform_data;
127a3376e3eSRob Clark 	struct hdmi *hdmi = NULL;
128c6a57a50Sjilai wang 	struct resource *res;
129dada25bdSRob Clark 	int i, ret;
130c8afe684SRob Clark 
131067fef37SRob Clark 	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
132a3376e3eSRob Clark 	if (!hdmi) {
133a3376e3eSRob Clark 		ret = -ENOMEM;
134a3376e3eSRob Clark 		goto fail;
135a3376e3eSRob Clark 	}
136a3376e3eSRob Clark 
137c8afe684SRob Clark 	hdmi->pdev = pdev;
138dada25bdSRob Clark 	hdmi->config = config;
139c6a57a50Sjilai wang 	spin_lock_init(&hdmi->reg_lock);
140c0c0d9eeSRob Clark 
141dada25bdSRob Clark 	hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI");
142c8afe684SRob Clark 	if (IS_ERR(hdmi->mmio)) {
143c8afe684SRob Clark 		ret = PTR_ERR(hdmi->mmio);
144c8afe684SRob Clark 		goto fail;
145c8afe684SRob Clark 	}
146c8afe684SRob Clark 
147c6a57a50Sjilai wang 	/* HDCP needs physical address of hdmi register */
148c6a57a50Sjilai wang 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
149c6a57a50Sjilai wang 		config->mmio_name);
150c6a57a50Sjilai wang 	hdmi->mmio_phy_addr = res->start;
151c6a57a50Sjilai wang 
152c6a57a50Sjilai wang 	hdmi->qfprom_mmio = msm_ioremap(pdev,
153c6a57a50Sjilai wang 		config->qfprom_mmio_name, "HDMI_QFPROM");
154c6a57a50Sjilai wang 	if (IS_ERR(hdmi->qfprom_mmio)) {
155c6a57a50Sjilai wang 		dev_info(&pdev->dev, "can't find qfprom resource\n");
156c6a57a50Sjilai wang 		hdmi->qfprom_mmio = NULL;
157c6a57a50Sjilai wang 	}
158c6a57a50Sjilai wang 
159447fa529SStephane Viau 	hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) *
160447fa529SStephane Viau 			config->hpd_reg_cnt, GFP_KERNEL);
161447fa529SStephane Viau 	if (!hdmi->hpd_regs) {
162447fa529SStephane Viau 		ret = -ENOMEM;
163447fa529SStephane Viau 		goto fail;
164447fa529SStephane Viau 	}
165dada25bdSRob Clark 	for (i = 0; i < config->hpd_reg_cnt; i++) {
166dada25bdSRob Clark 		struct regulator *reg;
167dada25bdSRob Clark 
1683e87599bSRob Clark 		reg = devm_regulator_get(&pdev->dev,
16941e69778SRob Clark 				config->hpd_reg_names[i]);
170dada25bdSRob Clark 		if (IS_ERR(reg)) {
171dada25bdSRob Clark 			ret = PTR_ERR(reg);
172067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get hpd regulator: %s (%d)\n",
173dada25bdSRob Clark 					config->hpd_reg_names[i], ret);
174c8afe684SRob Clark 			goto fail;
175c8afe684SRob Clark 		}
176c8afe684SRob Clark 
177dada25bdSRob Clark 		hdmi->hpd_regs[i] = reg;
178dada25bdSRob Clark 	}
179c8afe684SRob Clark 
180447fa529SStephane Viau 	hdmi->pwr_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_regs[0]) *
181447fa529SStephane Viau 			config->pwr_reg_cnt, GFP_KERNEL);
182447fa529SStephane Viau 	if (!hdmi->pwr_regs) {
183447fa529SStephane Viau 		ret = -ENOMEM;
184447fa529SStephane Viau 		goto fail;
185447fa529SStephane Viau 	}
186dada25bdSRob Clark 	for (i = 0; i < config->pwr_reg_cnt; i++) {
187dada25bdSRob Clark 		struct regulator *reg;
188dada25bdSRob Clark 
1893e87599bSRob Clark 		reg = devm_regulator_get(&pdev->dev,
19041e69778SRob Clark 				config->pwr_reg_names[i]);
191dada25bdSRob Clark 		if (IS_ERR(reg)) {
192dada25bdSRob Clark 			ret = PTR_ERR(reg);
193067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get pwr regulator: %s (%d)\n",
194dada25bdSRob Clark 					config->pwr_reg_names[i], ret);
195c8afe684SRob Clark 			goto fail;
196c8afe684SRob Clark 		}
197c8afe684SRob Clark 
198dada25bdSRob Clark 		hdmi->pwr_regs[i] = reg;
199dada25bdSRob Clark 	}
200dada25bdSRob Clark 
201447fa529SStephane Viau 	hdmi->hpd_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_clks[0]) *
202447fa529SStephane Viau 			config->hpd_clk_cnt, GFP_KERNEL);
203447fa529SStephane Viau 	if (!hdmi->hpd_clks) {
204447fa529SStephane Viau 		ret = -ENOMEM;
205447fa529SStephane Viau 		goto fail;
206447fa529SStephane Viau 	}
207dada25bdSRob Clark 	for (i = 0; i < config->hpd_clk_cnt; i++) {
208dada25bdSRob Clark 		struct clk *clk;
209dada25bdSRob Clark 
210dada25bdSRob Clark 		clk = devm_clk_get(&pdev->dev, config->hpd_clk_names[i]);
211dada25bdSRob Clark 		if (IS_ERR(clk)) {
212dada25bdSRob Clark 			ret = PTR_ERR(clk);
213067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get hpd clk: %s (%d)\n",
214dada25bdSRob Clark 					config->hpd_clk_names[i], ret);
215c8afe684SRob Clark 			goto fail;
216c8afe684SRob Clark 		}
217c8afe684SRob Clark 
218dada25bdSRob Clark 		hdmi->hpd_clks[i] = clk;
219dada25bdSRob Clark 	}
220dada25bdSRob Clark 
221447fa529SStephane Viau 	hdmi->pwr_clks = devm_kzalloc(&pdev->dev, sizeof(hdmi->pwr_clks[0]) *
222447fa529SStephane Viau 			config->pwr_clk_cnt, GFP_KERNEL);
223447fa529SStephane Viau 	if (!hdmi->pwr_clks) {
224447fa529SStephane Viau 		ret = -ENOMEM;
225447fa529SStephane Viau 		goto fail;
226447fa529SStephane Viau 	}
227dada25bdSRob Clark 	for (i = 0; i < config->pwr_clk_cnt; i++) {
228dada25bdSRob Clark 		struct clk *clk;
229dada25bdSRob Clark 
230dada25bdSRob Clark 		clk = devm_clk_get(&pdev->dev, config->pwr_clk_names[i]);
231dada25bdSRob Clark 		if (IS_ERR(clk)) {
232dada25bdSRob Clark 			ret = PTR_ERR(clk);
233067fef37SRob Clark 			dev_err(&pdev->dev, "failed to get pwr clk: %s (%d)\n",
234dada25bdSRob Clark 					config->pwr_clk_names[i], ret);
235c8afe684SRob Clark 			goto fail;
236c8afe684SRob Clark 		}
237c8afe684SRob Clark 
238dada25bdSRob Clark 		hdmi->pwr_clks[i] = clk;
239dada25bdSRob Clark 	}
240dada25bdSRob Clark 
241c6a57a50Sjilai wang 	hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0);
242c6a57a50Sjilai wang 
243c8afe684SRob Clark 	hdmi->i2c = hdmi_i2c_init(hdmi);
244c8afe684SRob Clark 	if (IS_ERR(hdmi->i2c)) {
245c8afe684SRob Clark 		ret = PTR_ERR(hdmi->i2c);
246067fef37SRob Clark 		dev_err(&pdev->dev, "failed to get i2c: %d\n", ret);
247c8afe684SRob Clark 		hdmi->i2c = NULL;
248c8afe684SRob Clark 		goto fail;
249c8afe684SRob Clark 	}
250c8afe684SRob Clark 
251e00012b2SArchit Taneja 	ret = hdmi_get_phy(hdmi);
252e00012b2SArchit Taneja 	if (ret) {
253e00012b2SArchit Taneja 		dev_err(&pdev->dev, "failed to get phy\n");
254e00012b2SArchit Taneja 		goto fail;
255e00012b2SArchit Taneja 	}
256e00012b2SArchit Taneja 
257c6a57a50Sjilai wang 	hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi);
258c6a57a50Sjilai wang 	if (IS_ERR(hdmi->hdcp_ctrl)) {
259c6a57a50Sjilai wang 		dev_warn(&pdev->dev, "failed to init hdcp: disabled\n");
260c6a57a50Sjilai wang 		hdmi->hdcp_ctrl = NULL;
261c6a57a50Sjilai wang 	}
262c6a57a50Sjilai wang 
263067fef37SRob Clark 	return hdmi;
264067fef37SRob Clark 
265067fef37SRob Clark fail:
266067fef37SRob Clark 	if (hdmi)
267d1a717bdSRob Clark 		hdmi_destroy(hdmi);
268067fef37SRob Clark 
269067fef37SRob Clark 	return ERR_PTR(ret);
270067fef37SRob Clark }
271067fef37SRob Clark 
272067fef37SRob Clark /* Second part of initialization, the drm/kms level modeset_init,
273067fef37SRob Clark  * constructs/initializes mode objects, etc, is called from master
274067fef37SRob Clark  * driver (not hdmi sub-device's probe/bind!)
275067fef37SRob Clark  *
276067fef37SRob Clark  * Any resource (regulator/clk/etc) which could be missing at boot
277067fef37SRob Clark  * should be handled in hdmi_init() so that failure happens from
278067fef37SRob Clark  * hdmi sub-device's probe.
279067fef37SRob Clark  */
280067fef37SRob Clark int hdmi_modeset_init(struct hdmi *hdmi,
281067fef37SRob Clark 		struct drm_device *dev, struct drm_encoder *encoder)
282067fef37SRob Clark {
283067fef37SRob Clark 	struct msm_drm_private *priv = dev->dev_private;
284067fef37SRob Clark 	struct platform_device *pdev = hdmi->pdev;
285067fef37SRob Clark 	int ret;
286067fef37SRob Clark 
287067fef37SRob Clark 	hdmi->dev = dev;
288067fef37SRob Clark 	hdmi->encoder = encoder;
289067fef37SRob Clark 
290067fef37SRob Clark 	hdmi_audio_infoframe_init(&hdmi->audio.infoframe);
291067fef37SRob Clark 
292a3376e3eSRob Clark 	hdmi->bridge = hdmi_bridge_init(hdmi);
293a3376e3eSRob Clark 	if (IS_ERR(hdmi->bridge)) {
294a3376e3eSRob Clark 		ret = PTR_ERR(hdmi->bridge);
295a3376e3eSRob Clark 		dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret);
296a3376e3eSRob Clark 		hdmi->bridge = NULL;
297a3376e3eSRob Clark 		goto fail;
298a3376e3eSRob Clark 	}
299a3376e3eSRob Clark 
300a3376e3eSRob Clark 	hdmi->connector = hdmi_connector_init(hdmi);
301a3376e3eSRob Clark 	if (IS_ERR(hdmi->connector)) {
302a3376e3eSRob Clark 		ret = PTR_ERR(hdmi->connector);
303a3376e3eSRob Clark 		dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret);
304a3376e3eSRob Clark 		hdmi->connector = NULL;
305a3376e3eSRob Clark 		goto fail;
306a3376e3eSRob Clark 	}
307a3376e3eSRob Clark 
308f6a8eacaSRob Clark 	hdmi->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
309c8afe684SRob Clark 	if (hdmi->irq < 0) {
310c8afe684SRob Clark 		ret = hdmi->irq;
311c8afe684SRob Clark 		dev_err(dev->dev, "failed to get irq: %d\n", ret);
312c8afe684SRob Clark 		goto fail;
313c8afe684SRob Clark 	}
314c8afe684SRob Clark 
315f6a8eacaSRob Clark 	ret = devm_request_irq(&pdev->dev, hdmi->irq,
316f6a8eacaSRob Clark 			hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
317c8afe684SRob Clark 			"hdmi_isr", hdmi);
318c8afe684SRob Clark 	if (ret < 0) {
319c8afe684SRob Clark 		dev_err(dev->dev, "failed to request IRQ%u: %d\n",
320c8afe684SRob Clark 				hdmi->irq, ret);
321c8afe684SRob Clark 		goto fail;
322c8afe684SRob Clark 	}
323c8afe684SRob Clark 
324a3376e3eSRob Clark 	encoder->bridge = hdmi->bridge;
325a3376e3eSRob Clark 
326a3376e3eSRob Clark 	priv->bridges[priv->num_bridges++]       = hdmi->bridge;
327a3376e3eSRob Clark 	priv->connectors[priv->num_connectors++] = hdmi->connector;
328a3376e3eSRob Clark 
329c0c0d9eeSRob Clark 	platform_set_drvdata(pdev, hdmi);
330c0c0d9eeSRob Clark 
331067fef37SRob Clark 	return 0;
332c8afe684SRob Clark 
333c8afe684SRob Clark fail:
3343d3f8b1fSAjay Kumar 	/* bridge is normally destroyed by drm: */
335067fef37SRob Clark 	if (hdmi->bridge) {
3363d3f8b1fSAjay Kumar 		hdmi_bridge_destroy(hdmi->bridge);
337067fef37SRob Clark 		hdmi->bridge = NULL;
338067fef37SRob Clark 	}
339067fef37SRob Clark 	if (hdmi->connector) {
340a3376e3eSRob Clark 		hdmi->connector->funcs->destroy(hdmi->connector);
341067fef37SRob Clark 		hdmi->connector = NULL;
342a3376e3eSRob Clark 	}
343c8afe684SRob Clark 
344067fef37SRob Clark 	return ret;
345c8afe684SRob Clark }
346c8afe684SRob Clark 
347c8afe684SRob Clark /*
348c8afe684SRob Clark  * The hdmi device:
349c8afe684SRob Clark  */
350c8afe684SRob Clark 
3515eba5d87SStephane Viau #define HDMI_CFG(item, entry) \
3525eba5d87SStephane Viau 	.item ## _names = item ##_names_ ## entry, \
3535eba5d87SStephane Viau 	.item ## _cnt   = ARRAY_SIZE(item ## _names_ ## entry)
3545eba5d87SStephane Viau 
3550afbe59eSStephane Viau static const char *pwr_reg_names_none[] = {};
3560afbe59eSStephane Viau static const char *hpd_reg_names_none[] = {};
3570afbe59eSStephane Viau 
358ba3d7bf3SArchit Taneja static struct hdmi_platform_config hdmi_tx_8660_config;
3595eba5d87SStephane Viau 
3605eba5d87SStephane Viau static const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"};
3615eba5d87SStephane Viau static const char *hpd_clk_names_8960[] = {"core_clk", "master_iface_clk", "slave_iface_clk"};
3625eba5d87SStephane Viau 
3635eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8960_config = {
3645eba5d87SStephane Viau 		HDMI_CFG(hpd_reg, 8960),
3655eba5d87SStephane Viau 		HDMI_CFG(hpd_clk, 8960),
3665eba5d87SStephane Viau };
3675eba5d87SStephane Viau 
3685eba5d87SStephane Viau static const char *pwr_reg_names_8x74[] = {"core-vdda", "core-vcc"};
3695eba5d87SStephane Viau static const char *hpd_reg_names_8x74[] = {"hpd-gdsc", "hpd-5v"};
3705eba5d87SStephane Viau static const char *pwr_clk_names_8x74[] = {"extp_clk", "alt_iface_clk"};
3715eba5d87SStephane Viau static const char *hpd_clk_names_8x74[] = {"iface_clk", "core_clk", "mdp_core_clk"};
3725eba5d87SStephane Viau static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0};
3735eba5d87SStephane Viau 
3745cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8974_config = {
3755eba5d87SStephane Viau 		HDMI_CFG(pwr_reg, 8x74),
3765eba5d87SStephane Viau 		HDMI_CFG(hpd_reg, 8x74),
3775eba5d87SStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3785eba5d87SStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3795eba5d87SStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3805eba5d87SStephane Viau };
3815eba5d87SStephane Viau 
3825eba5d87SStephane Viau static const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"};
3835eba5d87SStephane Viau 
3845eba5d87SStephane Viau static struct hdmi_platform_config hdmi_tx_8084_config = {
3855eba5d87SStephane Viau 		HDMI_CFG(pwr_reg, 8x74),
3865eba5d87SStephane Viau 		HDMI_CFG(hpd_reg, 8084),
3875eba5d87SStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3885eba5d87SStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3895eba5d87SStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3905eba5d87SStephane Viau };
3915eba5d87SStephane Viau 
3925cf3a455SRob Clark static struct hdmi_platform_config hdmi_tx_8994_config = {
3933a84f846SStephane Viau 		HDMI_CFG(pwr_reg, 8x74),
3940afbe59eSStephane Viau 		HDMI_CFG(hpd_reg, none),
3950afbe59eSStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
3960afbe59eSStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
3970afbe59eSStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
3980afbe59eSStephane Viau };
3990afbe59eSStephane Viau 
4000afbe59eSStephane Viau static struct hdmi_platform_config hdmi_tx_8996_config = {
4010afbe59eSStephane Viau 		HDMI_CFG(pwr_reg, none),
4020afbe59eSStephane Viau 		HDMI_CFG(hpd_reg, none),
4033a84f846SStephane Viau 		HDMI_CFG(pwr_clk, 8x74),
4043a84f846SStephane Viau 		HDMI_CFG(hpd_clk, 8x74),
4053a84f846SStephane Viau 		.hpd_freq      = hpd_clk_freq_8x74,
4063a84f846SStephane Viau };
4073a84f846SStephane Viau 
408dc50f782SArchit Taneja static const struct {
409dc50f782SArchit Taneja 	const char *name;
410dc50f782SArchit Taneja 	const bool output;
411dc50f782SArchit Taneja 	const int value;
412dc50f782SArchit Taneja 	const char *label;
413dc50f782SArchit Taneja } hdmi_gpio_pdata[] = {
414dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-ddc-clk", true, 1, "HDMI_DDC_CLK" },
415dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-ddc-data", true, 1, "HDMI_DDC_DATA" },
416dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-hpd", false, 1, "HDMI_HPD" },
417dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-mux-en", true, 1, "HDMI_MUX_EN" },
418dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-mux-sel", true, 0, "HDMI_MUX_SEL" },
419dc50f782SArchit Taneja 	{ "qcom,hdmi-tx-mux-lpm", true, 1, "HDMI_MUX_LPM" },
420dc50f782SArchit Taneja };
421dc50f782SArchit Taneja 
422dc50f782SArchit Taneja static int get_gpio(struct device_node *of_node, const char *name)
423dada25bdSRob Clark {
424dada25bdSRob Clark 	int gpio = of_get_named_gpio(of_node, name, 0);
425dada25bdSRob Clark 	if (gpio < 0) {
42641e69778SRob Clark 		char name2[32];
42741e69778SRob Clark 		snprintf(name2, sizeof(name2), "%s-gpio", name);
42841e69778SRob Clark 		gpio = of_get_named_gpio(of_node, name2, 0);
42941e69778SRob Clark 		if (gpio < 0) {
4303a84f846SStephane Viau 			DBG("failed to get gpio: %s (%d)", name, gpio);
431dada25bdSRob Clark 			gpio = -1;
432dada25bdSRob Clark 		}
43341e69778SRob Clark 	}
434dada25bdSRob Clark 	return gpio;
435dada25bdSRob Clark }
436fc886107SMark Charlebois 
437fc886107SMark Charlebois static int hdmi_bind(struct device *dev, struct device *master, void *data)
438fc886107SMark Charlebois {
439d1a717bdSRob Clark 	struct drm_device *drm = dev_get_drvdata(master);
440d1a717bdSRob Clark 	struct msm_drm_private *priv = drm->dev_private;
4415eba5d87SStephane Viau 	static struct hdmi_platform_config *hdmi_cfg;
442067fef37SRob Clark 	struct hdmi *hdmi;
443fc886107SMark Charlebois 	struct device_node *of_node = dev->of_node;
444dc50f782SArchit Taneja 	int i;
445dada25bdSRob Clark 
4461fd6a441SArchit Taneja 	hdmi_cfg = (struct hdmi_platform_config *)
4471fd6a441SArchit Taneja 			of_device_get_match_data(dev);
4481fd6a441SArchit Taneja 	if (!hdmi_cfg) {
4491fd6a441SArchit Taneja 		dev_err(dev, "unknown hdmi_cfg: %s\n", of_node->name);
4505eba5d87SStephane Viau 		return -ENXIO;
45141e69778SRob Clark 	}
45241e69778SRob Clark 
4535eba5d87SStephane Viau 	hdmi_cfg->mmio_name     = "core_physical";
454c6a57a50Sjilai wang 	hdmi_cfg->qfprom_mmio_name = "qfprom_physical";
455dc50f782SArchit Taneja 
456dc50f782SArchit Taneja 	for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
457dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].num = get_gpio(of_node,
458dc50f782SArchit Taneja 						hdmi_gpio_pdata[i].name);
459dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].output = hdmi_gpio_pdata[i].output;
460dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].value = hdmi_gpio_pdata[i].value;
461dc50f782SArchit Taneja 		hdmi_cfg->gpios[i].label = hdmi_gpio_pdata[i].label;
462dc50f782SArchit Taneja 	}
463dada25bdSRob Clark 
4645eba5d87SStephane Viau 	dev->platform_data = hdmi_cfg;
4655eba5d87SStephane Viau 
466067fef37SRob Clark 	hdmi = hdmi_init(to_platform_device(dev));
467067fef37SRob Clark 	if (IS_ERR(hdmi))
468067fef37SRob Clark 		return PTR_ERR(hdmi);
469d1a717bdSRob Clark 	priv->hdmi = hdmi;
4705eba5d87SStephane Viau 
471c8afe684SRob Clark 	return 0;
472c8afe684SRob Clark }
473c8afe684SRob Clark 
474060530f1SRob Clark static void hdmi_unbind(struct device *dev, struct device *master,
475060530f1SRob Clark 		void *data)
476060530f1SRob Clark {
477d1a717bdSRob Clark 	struct drm_device *drm = dev_get_drvdata(master);
478d1a717bdSRob Clark 	struct msm_drm_private *priv = drm->dev_private;
479d1a717bdSRob Clark 	if (priv->hdmi) {
480d1a717bdSRob Clark 		hdmi_destroy(priv->hdmi);
481d1a717bdSRob Clark 		priv->hdmi = NULL;
482d1a717bdSRob Clark 	}
483060530f1SRob Clark }
484060530f1SRob Clark 
485060530f1SRob Clark static const struct component_ops hdmi_ops = {
486060530f1SRob Clark 		.bind   = hdmi_bind,
487060530f1SRob Clark 		.unbind = hdmi_unbind,
488060530f1SRob Clark };
489060530f1SRob Clark 
490060530f1SRob Clark static int hdmi_dev_probe(struct platform_device *pdev)
491060530f1SRob Clark {
492060530f1SRob Clark 	return component_add(&pdev->dev, &hdmi_ops);
493060530f1SRob Clark }
494060530f1SRob Clark 
495c8afe684SRob Clark static int hdmi_dev_remove(struct platform_device *pdev)
496c8afe684SRob Clark {
497060530f1SRob Clark 	component_del(&pdev->dev, &hdmi_ops);
498c8afe684SRob Clark 	return 0;
499c8afe684SRob Clark }
500c8afe684SRob Clark 
5011fd6a441SArchit Taneja static const struct of_device_id dt_match[] = {
5021fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config },
5031fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config },
5041fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config },
5051fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8974", .data = &hdmi_tx_8974_config },
5061fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8960", .data = &hdmi_tx_8960_config },
5071fd6a441SArchit Taneja 	{ .compatible = "qcom,hdmi-tx-8660", .data = &hdmi_tx_8660_config },
5081fd6a441SArchit Taneja 	{}
5091fd6a441SArchit Taneja };
5101fd6a441SArchit Taneja 
511c8afe684SRob Clark static struct platform_driver hdmi_driver = {
512c8afe684SRob Clark 	.probe = hdmi_dev_probe,
513c8afe684SRob Clark 	.remove = hdmi_dev_remove,
514dada25bdSRob Clark 	.driver = {
515dada25bdSRob Clark 		.name = "hdmi_msm",
516dada25bdSRob Clark 		.of_match_table = dt_match,
517dada25bdSRob Clark 	},
518c8afe684SRob Clark };
519c8afe684SRob Clark 
520c8afe684SRob Clark void __init hdmi_register(void)
521c8afe684SRob Clark {
52215b4a452SArchit Taneja 	hdmi_phy_driver_register();
523c8afe684SRob Clark 	platform_driver_register(&hdmi_driver);
524c8afe684SRob Clark }
525c8afe684SRob Clark 
526c8afe684SRob Clark void __exit hdmi_unregister(void)
527c8afe684SRob Clark {
528c8afe684SRob Clark 	platform_driver_unregister(&hdmi_driver);
52915b4a452SArchit Taneja 	hdmi_phy_driver_unregister();
530c8afe684SRob Clark }
531