2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
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.
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
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/>.
20 void hdmi_set_mode(struct hdmi
*hdmi
, bool power_on
)
25 ctrl
|= HDMI_CTRL_ENABLE
;
26 if (!hdmi
->hdmi_mode
) {
27 ctrl
|= HDMI_CTRL_HDMI
;
28 hdmi_write(hdmi
, REG_HDMI_CTRL
, ctrl
);
29 ctrl
&= ~HDMI_CTRL_HDMI
;
31 ctrl
|= HDMI_CTRL_HDMI
;
34 ctrl
= HDMI_CTRL_HDMI
;
37 hdmi_write(hdmi
, REG_HDMI_CTRL
, ctrl
);
38 DBG("HDMI Core: %s, HDMI_CTRL=0x%08x",
39 power_on
? "Enable" : "Disable", ctrl
);
42 irqreturn_t
hdmi_irq(int irq
, void *dev_id
)
44 struct hdmi
*hdmi
= dev_id
;
47 hdmi_connector_irq(hdmi
->connector
);
50 hdmi_i2c_irq(hdmi
->i2c
);
57 void hdmi_destroy(struct kref
*kref
)
59 struct hdmi
*hdmi
= container_of(kref
, struct hdmi
, refcount
);
60 struct hdmi_phy
*phy
= hdmi
->phy
;
63 phy
->funcs
->destroy(phy
);
66 hdmi_i2c_destroy(hdmi
->i2c
);
68 platform_set_drvdata(hdmi
->pdev
, NULL
);
71 /* initialize connector */
72 struct hdmi
*hdmi_init(struct drm_device
*dev
, struct drm_encoder
*encoder
)
74 struct hdmi
*hdmi
= NULL
;
75 struct msm_drm_private
*priv
= dev
->dev_private
;
76 struct platform_device
*pdev
= priv
->hdmi_pdev
;
77 struct hdmi_platform_config
*config
;
81 dev_err(dev
->dev
, "no hdmi device\n");
86 config
= pdev
->dev
.platform_data
;
88 hdmi
= kzalloc(sizeof(*hdmi
), GFP_KERNEL
);
94 kref_init(&hdmi
->refcount
);
98 hdmi
->config
= config
;
99 hdmi
->encoder
= encoder
;
101 hdmi_audio_infoframe_init(&hdmi
->audio
.infoframe
);
103 /* not sure about which phy maps to which msm.. probably I miss some */
104 if (config
->phy_init
)
105 hdmi
->phy
= config
->phy_init(hdmi
);
107 hdmi
->phy
= ERR_PTR(-ENXIO
);
109 if (IS_ERR(hdmi
->phy
)) {
110 ret
= PTR_ERR(hdmi
->phy
);
111 dev_err(dev
->dev
, "failed to load phy: %d\n", ret
);
116 hdmi
->mmio
= msm_ioremap(pdev
, config
->mmio_name
, "HDMI");
117 if (IS_ERR(hdmi
->mmio
)) {
118 ret
= PTR_ERR(hdmi
->mmio
);
122 BUG_ON(config
->hpd_reg_cnt
> ARRAY_SIZE(hdmi
->hpd_regs
));
123 for (i
= 0; i
< config
->hpd_reg_cnt
; i
++) {
124 struct regulator
*reg
;
126 reg
= devm_regulator_get(&pdev
->dev
,
127 config
->hpd_reg_names
[i
]);
130 dev_err(dev
->dev
, "failed to get hpd regulator: %s (%d)\n",
131 config
->hpd_reg_names
[i
], ret
);
135 hdmi
->hpd_regs
[i
] = reg
;
138 BUG_ON(config
->pwr_reg_cnt
> ARRAY_SIZE(hdmi
->pwr_regs
));
139 for (i
= 0; i
< config
->pwr_reg_cnt
; i
++) {
140 struct regulator
*reg
;
142 reg
= devm_regulator_get(&pdev
->dev
,
143 config
->pwr_reg_names
[i
]);
146 dev_err(dev
->dev
, "failed to get pwr regulator: %s (%d)\n",
147 config
->pwr_reg_names
[i
], ret
);
151 hdmi
->pwr_regs
[i
] = reg
;
154 BUG_ON(config
->hpd_clk_cnt
> ARRAY_SIZE(hdmi
->hpd_clks
));
155 for (i
= 0; i
< config
->hpd_clk_cnt
; i
++) {
158 clk
= devm_clk_get(&pdev
->dev
, config
->hpd_clk_names
[i
]);
161 dev_err(dev
->dev
, "failed to get hpd clk: %s (%d)\n",
162 config
->hpd_clk_names
[i
], ret
);
166 hdmi
->hpd_clks
[i
] = clk
;
169 BUG_ON(config
->pwr_clk_cnt
> ARRAY_SIZE(hdmi
->pwr_clks
));
170 for (i
= 0; i
< config
->pwr_clk_cnt
; i
++) {
173 clk
= devm_clk_get(&pdev
->dev
, config
->pwr_clk_names
[i
]);
176 dev_err(dev
->dev
, "failed to get pwr clk: %s (%d)\n",
177 config
->pwr_clk_names
[i
], ret
);
181 hdmi
->pwr_clks
[i
] = clk
;
184 hdmi
->i2c
= hdmi_i2c_init(hdmi
);
185 if (IS_ERR(hdmi
->i2c
)) {
186 ret
= PTR_ERR(hdmi
->i2c
);
187 dev_err(dev
->dev
, "failed to get i2c: %d\n", ret
);
192 hdmi
->bridge
= hdmi_bridge_init(hdmi
);
193 if (IS_ERR(hdmi
->bridge
)) {
194 ret
= PTR_ERR(hdmi
->bridge
);
195 dev_err(dev
->dev
, "failed to create HDMI bridge: %d\n", ret
);
200 hdmi
->connector
= hdmi_connector_init(hdmi
);
201 if (IS_ERR(hdmi
->connector
)) {
202 ret
= PTR_ERR(hdmi
->connector
);
203 dev_err(dev
->dev
, "failed to create HDMI connector: %d\n", ret
);
204 hdmi
->connector
= NULL
;
208 if (!config
->shared_irq
) {
209 hdmi
->irq
= platform_get_irq(pdev
, 0);
212 dev_err(dev
->dev
, "failed to get irq: %d\n", ret
);
216 ret
= devm_request_threaded_irq(&pdev
->dev
, hdmi
->irq
,
217 NULL
, hdmi_irq
, IRQF_TRIGGER_HIGH
| IRQF_ONESHOT
,
220 dev_err(dev
->dev
, "failed to request IRQ%u: %d\n",
226 encoder
->bridge
= hdmi
->bridge
;
228 priv
->bridges
[priv
->num_bridges
++] = hdmi
->bridge
;
229 priv
->connectors
[priv
->num_connectors
++] = hdmi
->connector
;
231 platform_set_drvdata(pdev
, hdmi
);
237 /* bridge/connector are normally destroyed by drm: */
239 hdmi
->bridge
->funcs
->destroy(hdmi
->bridge
);
241 hdmi
->connector
->funcs
->destroy(hdmi
->connector
);
242 hdmi_destroy(&hdmi
->refcount
);
252 #include <linux/of_gpio.h>
254 static void set_hdmi_pdev(struct drm_device
*dev
,
255 struct platform_device
*pdev
)
257 struct msm_drm_private
*priv
= dev
->dev_private
;
258 priv
->hdmi_pdev
= pdev
;
262 static int get_gpio(struct device
*dev
, struct device_node
*of_node
, const char *name
)
264 int gpio
= of_get_named_gpio(of_node
, name
, 0);
267 snprintf(name2
, sizeof(name2
), "%s-gpio", name
);
268 gpio
= of_get_named_gpio(of_node
, name2
, 0);
270 dev_err(dev
, "failed to get gpio: %s (%d)\n",
279 static int hdmi_bind(struct device
*dev
, struct device
*master
, void *data
)
281 static struct hdmi_platform_config config
= {};
283 struct device_node
*of_node
= dev
->of_node
;
285 if (of_device_is_compatible(of_node
, "qcom,hdmi-tx-8074")) {
286 static const char *hpd_reg_names
[] = {"hpd-gdsc", "hpd-5v"};
287 static const char *pwr_reg_names
[] = {"core-vdda", "core-vcc"};
288 static const char *hpd_clk_names
[] = {"iface_clk", "core_clk", "mdp_core_clk"};
289 static unsigned long hpd_clk_freq
[] = {0, 19200000, 0};
290 static const char *pwr_clk_names
[] = {"extp_clk", "alt_iface_clk"};
291 config
.phy_init
= hdmi_phy_8x74_init
;
292 config
.hpd_reg_names
= hpd_reg_names
;
293 config
.hpd_reg_cnt
= ARRAY_SIZE(hpd_reg_names
);
294 config
.pwr_reg_names
= pwr_reg_names
;
295 config
.pwr_reg_cnt
= ARRAY_SIZE(pwr_reg_names
);
296 config
.hpd_clk_names
= hpd_clk_names
;
297 config
.hpd_freq
= hpd_clk_freq
;
298 config
.hpd_clk_cnt
= ARRAY_SIZE(hpd_clk_names
);
299 config
.pwr_clk_names
= pwr_clk_names
;
300 config
.pwr_clk_cnt
= ARRAY_SIZE(pwr_clk_names
);
301 config
.shared_irq
= true;
302 } else if (of_device_is_compatible(of_node
, "qcom,hdmi-tx-8960")) {
303 static const char *hpd_clk_names
[] = {"core_clk", "master_iface_clk", "slave_iface_clk"};
304 static const char *hpd_reg_names
[] = {"core-vdda", "hdmi-mux"};
305 config
.phy_init
= hdmi_phy_8960_init
;
306 config
.hpd_reg_names
= hpd_reg_names
;
307 config
.hpd_reg_cnt
= ARRAY_SIZE(hpd_reg_names
);
308 config
.hpd_clk_names
= hpd_clk_names
;
309 config
.hpd_clk_cnt
= ARRAY_SIZE(hpd_clk_names
);
310 } else if (of_device_is_compatible(of_node
, "qcom,hdmi-tx-8660")) {
311 config
.phy_init
= hdmi_phy_8x60_init
;
313 dev_err(dev
, "unknown phy: %s\n", of_node
->name
);
316 config
.mmio_name
= "core_physical";
317 config
.ddc_clk_gpio
= get_gpio(dev
, of_node
, "qcom,hdmi-tx-ddc-clk");
318 config
.ddc_data_gpio
= get_gpio(dev
, of_node
, "qcom,hdmi-tx-ddc-data");
319 config
.hpd_gpio
= get_gpio(dev
, of_node
, "qcom,hdmi-tx-hpd");
320 config
.mux_en_gpio
= get_gpio(dev
, of_node
, "qcom,hdmi-tx-mux-en");
321 config
.mux_sel_gpio
= get_gpio(dev
, of_node
, "qcom,hdmi-tx-mux-sel");
322 config
.mux_lpm_gpio
= get_gpio(dev
, of_node
, "qcom,hdmi-tx-mux-lpm");
325 static const char *hpd_clk_names
[] = {
326 "core_clk", "master_iface_clk", "slave_iface_clk",
328 if (cpu_is_apq8064()) {
329 static const char *hpd_reg_names
[] = {"8921_hdmi_mvs"};
330 config
.phy_init
= hdmi_phy_8960_init
;
331 config
.mmio_name
= "hdmi_msm_hdmi_addr";
332 config
.hpd_reg_names
= hpd_reg_names
;
333 config
.hpd_reg_cnt
= ARRAY_SIZE(hpd_reg_names
);
334 config
.hpd_clk_names
= hpd_clk_names
;
335 config
.hpd_clk_cnt
= ARRAY_SIZE(hpd_clk_names
);
336 config
.ddc_clk_gpio
= 70;
337 config
.ddc_data_gpio
= 71;
338 config
.hpd_gpio
= 72;
339 config
.mux_en_gpio
= -1;
340 config
.mux_sel_gpio
= -1;
341 } else if (cpu_is_msm8960() || cpu_is_msm8960ab()) {
342 static const char *hpd_reg_names
[] = {"8921_hdmi_mvs"};
343 config
.phy_init
= hdmi_phy_8960_init
;
344 config
.mmio_name
= "hdmi_msm_hdmi_addr";
345 config
.hpd_reg_names
= hpd_reg_names
;
346 config
.hpd_reg_cnt
= ARRAY_SIZE(hpd_reg_names
);
347 config
.hpd_clk_names
= hpd_clk_names
;
348 config
.hpd_clk_cnt
= ARRAY_SIZE(hpd_clk_names
);
349 config
.ddc_clk_gpio
= 100;
350 config
.ddc_data_gpio
= 101;
351 config
.hpd_gpio
= 102;
352 config
.mux_en_gpio
= -1;
353 config
.mux_sel_gpio
= -1;
354 } else if (cpu_is_msm8x60()) {
355 static const char *hpd_reg_names
[] = {
356 "8901_hdmi_mvs", "8901_mpp0"
358 config
.phy_init
= hdmi_phy_8x60_init
;
359 config
.mmio_name
= "hdmi_msm_hdmi_addr";
360 config
.hpd_reg_names
= hpd_reg_names
;
361 config
.hpd_reg_cnt
= ARRAY_SIZE(hpd_reg_names
);
362 config
.hpd_clk_names
= hpd_clk_names
;
363 config
.hpd_clk_cnt
= ARRAY_SIZE(hpd_clk_names
);
364 config
.ddc_clk_gpio
= 170;
365 config
.ddc_data_gpio
= 171;
366 config
.hpd_gpio
= 172;
367 config
.mux_en_gpio
= -1;
368 config
.mux_sel_gpio
= -1;
371 dev
->platform_data
= &config
;
372 set_hdmi_pdev(dev_get_drvdata(master
), to_platform_device(dev
));
376 static void hdmi_unbind(struct device
*dev
, struct device
*master
,
379 set_hdmi_pdev(dev_get_drvdata(master
), NULL
);
382 static const struct component_ops hdmi_ops
= {
384 .unbind
= hdmi_unbind
,
387 static int hdmi_dev_probe(struct platform_device
*pdev
)
389 return component_add(&pdev
->dev
, &hdmi_ops
);
392 static int hdmi_dev_remove(struct platform_device
*pdev
)
394 component_del(&pdev
->dev
, &hdmi_ops
);
398 static const struct of_device_id dt_match
[] = {
399 { .compatible
= "qcom,hdmi-tx-8074" },
400 { .compatible
= "qcom,hdmi-tx-8960" },
401 { .compatible
= "qcom,hdmi-tx-8660" },
405 static struct platform_driver hdmi_driver
= {
406 .probe
= hdmi_dev_probe
,
407 .remove
= hdmi_dev_remove
,
410 .of_match_table
= dt_match
,
414 void __init
hdmi_register(void)
416 platform_driver_register(&hdmi_driver
);
419 void __exit
hdmi_unregister(void)
421 platform_driver_unregister(&hdmi_driver
);