1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014 The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2013 Red Hat |
5 | * Author: Rob Clark <robdclark@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/of_irq.h> |
9 | #include <linux/of_gpio.h> |
10 | #include <linux/of_platform.h> |
11 | #include <linux/platform_device.h> |
12 | |
13 | #include <drm/drm_bridge_connector.h> |
14 | #include <drm/drm_of.h> |
15 | |
16 | #include <sound/hdmi-codec.h> |
17 | #include "hdmi.h" |
18 | |
19 | void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) |
20 | { |
21 | uint32_t ctrl = 0; |
22 | unsigned long flags; |
23 | |
24 | spin_lock_irqsave(&hdmi->reg_lock, flags); |
25 | if (power_on) { |
26 | ctrl |= HDMI_CTRL_ENABLE; |
27 | if (!hdmi->hdmi_mode) { |
28 | ctrl |= HDMI_CTRL_HDMI; |
29 | hdmi_write(hdmi, REG_HDMI_CTRL, data: ctrl); |
30 | ctrl &= ~HDMI_CTRL_HDMI; |
31 | } else { |
32 | ctrl |= HDMI_CTRL_HDMI; |
33 | } |
34 | } else { |
35 | ctrl = HDMI_CTRL_HDMI; |
36 | } |
37 | |
38 | hdmi_write(hdmi, REG_HDMI_CTRL, data: ctrl); |
39 | spin_unlock_irqrestore(lock: &hdmi->reg_lock, flags); |
40 | DBG("HDMI Core: %s, HDMI_CTRL=0x%08x" , |
41 | power_on ? "Enable" : "Disable" , ctrl); |
42 | } |
43 | |
44 | static irqreturn_t msm_hdmi_irq(int irq, void *dev_id) |
45 | { |
46 | struct hdmi *hdmi = dev_id; |
47 | |
48 | /* Process HPD: */ |
49 | msm_hdmi_hpd_irq(bridge: hdmi->bridge); |
50 | |
51 | /* Process DDC: */ |
52 | msm_hdmi_i2c_irq(i2c: hdmi->i2c); |
53 | |
54 | /* Process HDCP: */ |
55 | if (hdmi->hdcp_ctrl) |
56 | msm_hdmi_hdcp_irq(hdcp_ctrl: hdmi->hdcp_ctrl); |
57 | |
58 | /* TODO audio.. */ |
59 | |
60 | return IRQ_HANDLED; |
61 | } |
62 | |
63 | static void msm_hdmi_destroy(struct hdmi *hdmi) |
64 | { |
65 | /* |
66 | * at this point, hpd has been disabled, |
67 | * after flush workq, it's safe to deinit hdcp |
68 | */ |
69 | if (hdmi->workq) |
70 | destroy_workqueue(wq: hdmi->workq); |
71 | msm_hdmi_hdcp_destroy(hdmi); |
72 | |
73 | if (hdmi->i2c) |
74 | msm_hdmi_i2c_destroy(i2c: hdmi->i2c); |
75 | } |
76 | |
77 | static void msm_hdmi_put_phy(struct hdmi *hdmi) |
78 | { |
79 | if (hdmi->phy_dev) { |
80 | put_device(dev: hdmi->phy_dev); |
81 | hdmi->phy = NULL; |
82 | hdmi->phy_dev = NULL; |
83 | } |
84 | } |
85 | |
86 | static int msm_hdmi_get_phy(struct hdmi *hdmi) |
87 | { |
88 | struct platform_device *pdev = hdmi->pdev; |
89 | struct platform_device *phy_pdev; |
90 | struct device_node *phy_node; |
91 | |
92 | phy_node = of_parse_phandle(np: pdev->dev.of_node, phandle_name: "phys" , index: 0); |
93 | if (!phy_node) { |
94 | DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n" ); |
95 | return -ENXIO; |
96 | } |
97 | |
98 | phy_pdev = of_find_device_by_node(np: phy_node); |
99 | of_node_put(node: phy_node); |
100 | |
101 | if (!phy_pdev) |
102 | return dev_err_probe(dev: &pdev->dev, err: -EPROBE_DEFER, fmt: "phy driver is not ready\n" ); |
103 | |
104 | hdmi->phy = platform_get_drvdata(pdev: phy_pdev); |
105 | if (!hdmi->phy) { |
106 | put_device(dev: &phy_pdev->dev); |
107 | return dev_err_probe(dev: &pdev->dev, err: -EPROBE_DEFER, fmt: "phy driver is not ready\n" ); |
108 | } |
109 | |
110 | hdmi->phy_dev = &phy_pdev->dev; |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | /* construct hdmi at bind/probe time, grab all the resources. If |
116 | * we are to EPROBE_DEFER we want to do it here, rather than later |
117 | * at modeset_init() time |
118 | */ |
119 | static int msm_hdmi_init(struct hdmi *hdmi) |
120 | { |
121 | struct platform_device *pdev = hdmi->pdev; |
122 | int ret; |
123 | |
124 | hdmi->workq = alloc_ordered_workqueue("msm_hdmi" , 0); |
125 | if (!hdmi->workq) { |
126 | ret = -ENOMEM; |
127 | goto fail; |
128 | } |
129 | |
130 | hdmi->i2c = msm_hdmi_i2c_init(hdmi); |
131 | if (IS_ERR(ptr: hdmi->i2c)) { |
132 | ret = PTR_ERR(ptr: hdmi->i2c); |
133 | DRM_DEV_ERROR(&pdev->dev, "failed to get i2c: %d\n" , ret); |
134 | hdmi->i2c = NULL; |
135 | goto fail; |
136 | } |
137 | |
138 | hdmi->hdcp_ctrl = msm_hdmi_hdcp_init(hdmi); |
139 | if (IS_ERR(ptr: hdmi->hdcp_ctrl)) { |
140 | dev_warn(&pdev->dev, "failed to init hdcp: disabled\n" ); |
141 | hdmi->hdcp_ctrl = NULL; |
142 | } |
143 | |
144 | return 0; |
145 | |
146 | fail: |
147 | msm_hdmi_destroy(hdmi); |
148 | |
149 | return ret; |
150 | } |
151 | |
152 | /* Second part of initialization, the drm/kms level modeset_init, |
153 | * constructs/initializes mode objects, etc, is called from master |
154 | * driver (not hdmi sub-device's probe/bind!) |
155 | * |
156 | * Any resource (regulator/clk/etc) which could be missing at boot |
157 | * should be handled in msm_hdmi_init() so that failure happens from |
158 | * hdmi sub-device's probe. |
159 | */ |
160 | int msm_hdmi_modeset_init(struct hdmi *hdmi, |
161 | struct drm_device *dev, struct drm_encoder *encoder) |
162 | { |
163 | int ret; |
164 | |
165 | hdmi->dev = dev; |
166 | hdmi->encoder = encoder; |
167 | |
168 | hdmi_audio_infoframe_init(frame: &hdmi->audio.infoframe); |
169 | |
170 | ret = msm_hdmi_bridge_init(hdmi); |
171 | if (ret) { |
172 | DRM_DEV_ERROR(dev->dev, "failed to create HDMI bridge: %d\n" , ret); |
173 | goto fail; |
174 | } |
175 | |
176 | if (hdmi->next_bridge) { |
177 | ret = drm_bridge_attach(encoder: hdmi->encoder, bridge: hdmi->next_bridge, previous: hdmi->bridge, |
178 | flags: DRM_BRIDGE_ATTACH_NO_CONNECTOR); |
179 | if (ret) { |
180 | DRM_DEV_ERROR(dev->dev, "failed to attach next HDMI bridge: %d\n" , ret); |
181 | goto fail; |
182 | } |
183 | } |
184 | |
185 | hdmi->connector = drm_bridge_connector_init(drm: hdmi->dev, encoder); |
186 | if (IS_ERR(ptr: hdmi->connector)) { |
187 | ret = PTR_ERR(ptr: hdmi->connector); |
188 | DRM_DEV_ERROR(dev->dev, "failed to create HDMI connector: %d\n" , ret); |
189 | hdmi->connector = NULL; |
190 | goto fail; |
191 | } |
192 | |
193 | drm_connector_attach_encoder(connector: hdmi->connector, encoder: hdmi->encoder); |
194 | |
195 | ret = devm_request_irq(dev: dev->dev, irq: hdmi->irq, |
196 | handler: msm_hdmi_irq, IRQF_TRIGGER_HIGH, |
197 | devname: "hdmi_isr" , dev_id: hdmi); |
198 | if (ret < 0) { |
199 | DRM_DEV_ERROR(dev->dev, "failed to request IRQ%u: %d\n" , |
200 | hdmi->irq, ret); |
201 | goto fail; |
202 | } |
203 | |
204 | ret = msm_hdmi_hpd_enable(bridge: hdmi->bridge); |
205 | if (ret < 0) { |
206 | DRM_DEV_ERROR(&hdmi->pdev->dev, "failed to enable HPD: %d\n" , ret); |
207 | goto fail; |
208 | } |
209 | |
210 | return 0; |
211 | |
212 | fail: |
213 | if (hdmi->connector) { |
214 | hdmi->connector->funcs->destroy(hdmi->connector); |
215 | hdmi->connector = NULL; |
216 | } |
217 | |
218 | return ret; |
219 | } |
220 | |
221 | /* |
222 | * The hdmi device: |
223 | */ |
224 | |
225 | #define HDMI_CFG(item, entry) \ |
226 | .item ## _names = item ##_names_ ## entry, \ |
227 | .item ## _cnt = ARRAY_SIZE(item ## _names_ ## entry) |
228 | |
229 | static const char *hpd_reg_names_8960[] = {"core-vdda" }; |
230 | static const char *hpd_clk_names_8960[] = {"core" , "master_iface" , "slave_iface" }; |
231 | |
232 | static const struct hdmi_platform_config hdmi_tx_8960_config = { |
233 | HDMI_CFG(hpd_reg, 8960), |
234 | HDMI_CFG(hpd_clk, 8960), |
235 | }; |
236 | |
237 | static const char *pwr_reg_names_8x74[] = {"core-vdda" , "core-vcc" }; |
238 | static const char *pwr_clk_names_8x74[] = {"extp" , "alt_iface" }; |
239 | static const char *hpd_clk_names_8x74[] = {"iface" , "core" , "mdp_core" }; |
240 | static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; |
241 | |
242 | static const struct hdmi_platform_config hdmi_tx_8974_config = { |
243 | HDMI_CFG(pwr_reg, 8x74), |
244 | HDMI_CFG(pwr_clk, 8x74), |
245 | HDMI_CFG(hpd_clk, 8x74), |
246 | .hpd_freq = hpd_clk_freq_8x74, |
247 | }; |
248 | |
249 | /* |
250 | * HDMI audio codec callbacks |
251 | */ |
252 | static int msm_hdmi_audio_hw_params(struct device *dev, void *data, |
253 | struct hdmi_codec_daifmt *daifmt, |
254 | struct hdmi_codec_params *params) |
255 | { |
256 | struct hdmi *hdmi = dev_get_drvdata(dev); |
257 | unsigned int chan; |
258 | unsigned int channel_allocation = 0; |
259 | unsigned int rate; |
260 | unsigned int level_shift = 0; /* 0dB */ |
261 | bool down_mix = false; |
262 | |
263 | DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n" , params->sample_rate, |
264 | params->sample_width, params->cea.channels); |
265 | |
266 | switch (params->cea.channels) { |
267 | case 2: |
268 | /* FR and FL speakers */ |
269 | channel_allocation = 0; |
270 | chan = MSM_HDMI_AUDIO_CHANNEL_2; |
271 | break; |
272 | case 4: |
273 | /* FC, LFE, FR and FL speakers */ |
274 | channel_allocation = 0x3; |
275 | chan = MSM_HDMI_AUDIO_CHANNEL_4; |
276 | break; |
277 | case 6: |
278 | /* RR, RL, FC, LFE, FR and FL speakers */ |
279 | channel_allocation = 0x0B; |
280 | chan = MSM_HDMI_AUDIO_CHANNEL_6; |
281 | break; |
282 | case 8: |
283 | /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */ |
284 | channel_allocation = 0x1F; |
285 | chan = MSM_HDMI_AUDIO_CHANNEL_8; |
286 | break; |
287 | default: |
288 | return -EINVAL; |
289 | } |
290 | |
291 | switch (params->sample_rate) { |
292 | case 32000: |
293 | rate = HDMI_SAMPLE_RATE_32KHZ; |
294 | break; |
295 | case 44100: |
296 | rate = HDMI_SAMPLE_RATE_44_1KHZ; |
297 | break; |
298 | case 48000: |
299 | rate = HDMI_SAMPLE_RATE_48KHZ; |
300 | break; |
301 | case 88200: |
302 | rate = HDMI_SAMPLE_RATE_88_2KHZ; |
303 | break; |
304 | case 96000: |
305 | rate = HDMI_SAMPLE_RATE_96KHZ; |
306 | break; |
307 | case 176400: |
308 | rate = HDMI_SAMPLE_RATE_176_4KHZ; |
309 | break; |
310 | case 192000: |
311 | rate = HDMI_SAMPLE_RATE_192KHZ; |
312 | break; |
313 | default: |
314 | DRM_DEV_ERROR(dev, "rate[%d] not supported!\n" , |
315 | params->sample_rate); |
316 | return -EINVAL; |
317 | } |
318 | |
319 | msm_hdmi_audio_set_sample_rate(hdmi, rate); |
320 | msm_hdmi_audio_info_setup(hdmi, enabled: 1, num_of_channels: chan, channel_allocation, |
321 | level_shift, down_mix); |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static void msm_hdmi_audio_shutdown(struct device *dev, void *data) |
327 | { |
328 | struct hdmi *hdmi = dev_get_drvdata(dev); |
329 | |
330 | msm_hdmi_audio_info_setup(hdmi, enabled: 0, num_of_channels: 0, channel_allocation: 0, level_shift: 0, down_mix: 0); |
331 | } |
332 | |
333 | static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { |
334 | .hw_params = msm_hdmi_audio_hw_params, |
335 | .audio_shutdown = msm_hdmi_audio_shutdown, |
336 | }; |
337 | |
338 | static struct hdmi_codec_pdata codec_data = { |
339 | .ops = &msm_hdmi_audio_codec_ops, |
340 | .max_i2s_channels = 8, |
341 | .i2s = 1, |
342 | }; |
343 | |
344 | static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) |
345 | { |
346 | hdmi->audio_pdev = platform_device_register_data(parent: dev, |
347 | HDMI_CODEC_DRV_NAME, |
348 | PLATFORM_DEVID_AUTO, |
349 | data: &codec_data, |
350 | size: sizeof(codec_data)); |
351 | return PTR_ERR_OR_ZERO(ptr: hdmi->audio_pdev); |
352 | } |
353 | |
354 | static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) |
355 | { |
356 | struct msm_drm_private *priv = dev_get_drvdata(dev: master); |
357 | struct hdmi *hdmi = dev_get_drvdata(dev); |
358 | int err; |
359 | |
360 | err = msm_hdmi_init(hdmi); |
361 | if (err) |
362 | return err; |
363 | priv->hdmi = hdmi; |
364 | |
365 | err = msm_hdmi_register_audio_driver(hdmi, dev); |
366 | if (err) { |
367 | DRM_ERROR("Failed to attach an audio codec %d\n" , err); |
368 | hdmi->audio_pdev = NULL; |
369 | } |
370 | |
371 | return 0; |
372 | } |
373 | |
374 | static void msm_hdmi_unbind(struct device *dev, struct device *master, |
375 | void *data) |
376 | { |
377 | struct msm_drm_private *priv = dev_get_drvdata(dev: master); |
378 | |
379 | if (priv->hdmi) { |
380 | if (priv->hdmi->audio_pdev) |
381 | platform_device_unregister(priv->hdmi->audio_pdev); |
382 | |
383 | if (priv->hdmi->bridge) |
384 | msm_hdmi_hpd_disable(hdmi: priv->hdmi); |
385 | |
386 | msm_hdmi_destroy(hdmi: priv->hdmi); |
387 | priv->hdmi = NULL; |
388 | } |
389 | } |
390 | |
391 | static const struct component_ops msm_hdmi_ops = { |
392 | .bind = msm_hdmi_bind, |
393 | .unbind = msm_hdmi_unbind, |
394 | }; |
395 | |
396 | static int msm_hdmi_dev_probe(struct platform_device *pdev) |
397 | { |
398 | const struct hdmi_platform_config *config; |
399 | struct device *dev = &pdev->dev; |
400 | struct hdmi *hdmi; |
401 | struct resource *res; |
402 | int i, ret; |
403 | |
404 | config = of_device_get_match_data(dev); |
405 | if (!config) |
406 | return -EINVAL; |
407 | |
408 | hdmi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*hdmi), GFP_KERNEL); |
409 | if (!hdmi) |
410 | return -ENOMEM; |
411 | |
412 | hdmi->pdev = pdev; |
413 | hdmi->config = config; |
414 | spin_lock_init(&hdmi->reg_lock); |
415 | |
416 | ret = drm_of_find_panel_or_bridge(np: pdev->dev.of_node, port: 1, endpoint: 0, NULL, bridge: &hdmi->next_bridge); |
417 | if (ret && ret != -ENODEV) |
418 | return ret; |
419 | |
420 | hdmi->mmio = msm_ioremap(pdev, "core_physical" ); |
421 | if (IS_ERR(ptr: hdmi->mmio)) |
422 | return PTR_ERR(ptr: hdmi->mmio); |
423 | |
424 | /* HDCP needs physical address of hdmi register */ |
425 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
426 | "core_physical" ); |
427 | if (!res) |
428 | return -EINVAL; |
429 | hdmi->mmio_phy_addr = res->start; |
430 | |
431 | hdmi->qfprom_mmio = msm_ioremap(pdev, "qfprom_physical" ); |
432 | if (IS_ERR(ptr: hdmi->qfprom_mmio)) { |
433 | DRM_DEV_INFO(&pdev->dev, "can't find qfprom resource\n" ); |
434 | hdmi->qfprom_mmio = NULL; |
435 | } |
436 | |
437 | hdmi->irq = platform_get_irq(pdev, 0); |
438 | if (hdmi->irq < 0) |
439 | return hdmi->irq; |
440 | |
441 | hdmi->hpd_regs = devm_kcalloc(dev: &pdev->dev, |
442 | n: config->hpd_reg_cnt, |
443 | size: sizeof(hdmi->hpd_regs[0]), |
444 | GFP_KERNEL); |
445 | if (!hdmi->hpd_regs) |
446 | return -ENOMEM; |
447 | |
448 | for (i = 0; i < config->hpd_reg_cnt; i++) |
449 | hdmi->hpd_regs[i].supply = config->hpd_reg_names[i]; |
450 | |
451 | ret = devm_regulator_bulk_get(dev: &pdev->dev, num_consumers: config->hpd_reg_cnt, consumers: hdmi->hpd_regs); |
452 | if (ret) |
453 | return dev_err_probe(dev, err: ret, fmt: "failed to get hpd regulators\n" ); |
454 | |
455 | hdmi->pwr_regs = devm_kcalloc(dev: &pdev->dev, |
456 | n: config->pwr_reg_cnt, |
457 | size: sizeof(hdmi->pwr_regs[0]), |
458 | GFP_KERNEL); |
459 | if (!hdmi->pwr_regs) |
460 | return -ENOMEM; |
461 | |
462 | for (i = 0; i < config->pwr_reg_cnt; i++) |
463 | hdmi->pwr_regs[i].supply = config->pwr_reg_names[i]; |
464 | |
465 | ret = devm_regulator_bulk_get(dev: &pdev->dev, num_consumers: config->pwr_reg_cnt, consumers: hdmi->pwr_regs); |
466 | if (ret) |
467 | return dev_err_probe(dev, err: ret, fmt: "failed to get pwr regulators\n" ); |
468 | |
469 | hdmi->hpd_clks = devm_kcalloc(dev: &pdev->dev, |
470 | n: config->hpd_clk_cnt, |
471 | size: sizeof(hdmi->hpd_clks[0]), |
472 | GFP_KERNEL); |
473 | if (!hdmi->hpd_clks) |
474 | return -ENOMEM; |
475 | |
476 | for (i = 0; i < config->hpd_clk_cnt; i++) { |
477 | struct clk *clk; |
478 | |
479 | clk = msm_clk_get(pdev, config->hpd_clk_names[i]); |
480 | if (IS_ERR(ptr: clk)) |
481 | return dev_err_probe(dev, err: PTR_ERR(ptr: clk), |
482 | fmt: "failed to get hpd clk: %s\n" , |
483 | config->hpd_clk_names[i]); |
484 | |
485 | hdmi->hpd_clks[i] = clk; |
486 | } |
487 | |
488 | hdmi->pwr_clks = devm_kcalloc(dev: &pdev->dev, |
489 | n: config->pwr_clk_cnt, |
490 | size: sizeof(hdmi->pwr_clks[0]), |
491 | GFP_KERNEL); |
492 | if (!hdmi->pwr_clks) |
493 | return -ENOMEM; |
494 | |
495 | for (i = 0; i < config->pwr_clk_cnt; i++) { |
496 | struct clk *clk; |
497 | |
498 | clk = msm_clk_get(pdev, config->pwr_clk_names[i]); |
499 | if (IS_ERR(ptr: clk)) |
500 | return dev_err_probe(dev, err: PTR_ERR(ptr: clk), |
501 | fmt: "failed to get pwr clk: %s\n" , |
502 | config->pwr_clk_names[i]); |
503 | |
504 | hdmi->pwr_clks[i] = clk; |
505 | } |
506 | |
507 | hdmi->hpd_gpiod = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "hpd" , flags: GPIOD_IN); |
508 | /* This will catch e.g. -EPROBE_DEFER */ |
509 | if (IS_ERR(ptr: hdmi->hpd_gpiod)) |
510 | return dev_err_probe(dev, err: PTR_ERR(ptr: hdmi->hpd_gpiod), |
511 | fmt: "failed to get hpd gpio\n" ); |
512 | |
513 | if (!hdmi->hpd_gpiod) |
514 | DBG("failed to get HPD gpio" ); |
515 | |
516 | if (hdmi->hpd_gpiod) |
517 | gpiod_set_consumer_name(desc: hdmi->hpd_gpiod, name: "HDMI_HPD" ); |
518 | |
519 | ret = msm_hdmi_get_phy(hdmi); |
520 | if (ret) { |
521 | DRM_DEV_ERROR(&pdev->dev, "failed to get phy\n" ); |
522 | return ret; |
523 | } |
524 | |
525 | ret = devm_pm_runtime_enable(&pdev->dev); |
526 | if (ret) |
527 | goto err_put_phy; |
528 | |
529 | platform_set_drvdata(pdev, data: hdmi); |
530 | |
531 | ret = component_add(&pdev->dev, &msm_hdmi_ops); |
532 | if (ret) |
533 | goto err_put_phy; |
534 | |
535 | return 0; |
536 | |
537 | err_put_phy: |
538 | msm_hdmi_put_phy(hdmi); |
539 | return ret; |
540 | } |
541 | |
542 | static void msm_hdmi_dev_remove(struct platform_device *pdev) |
543 | { |
544 | struct hdmi *hdmi = dev_get_drvdata(dev: &pdev->dev); |
545 | |
546 | component_del(&pdev->dev, &msm_hdmi_ops); |
547 | |
548 | msm_hdmi_put_phy(hdmi); |
549 | } |
550 | |
551 | static const struct of_device_id msm_hdmi_dt_match[] = { |
552 | { .compatible = "qcom,hdmi-tx-8996" , .data = &hdmi_tx_8974_config }, |
553 | { .compatible = "qcom,hdmi-tx-8994" , .data = &hdmi_tx_8974_config }, |
554 | { .compatible = "qcom,hdmi-tx-8084" , .data = &hdmi_tx_8974_config }, |
555 | { .compatible = "qcom,hdmi-tx-8974" , .data = &hdmi_tx_8974_config }, |
556 | { .compatible = "qcom,hdmi-tx-8960" , .data = &hdmi_tx_8960_config }, |
557 | { .compatible = "qcom,hdmi-tx-8660" , .data = &hdmi_tx_8960_config }, |
558 | {} |
559 | }; |
560 | |
561 | static struct platform_driver msm_hdmi_driver = { |
562 | .probe = msm_hdmi_dev_probe, |
563 | .remove_new = msm_hdmi_dev_remove, |
564 | .driver = { |
565 | .name = "hdmi_msm" , |
566 | .of_match_table = msm_hdmi_dt_match, |
567 | }, |
568 | }; |
569 | |
570 | void __init msm_hdmi_register(void) |
571 | { |
572 | msm_hdmi_phy_driver_register(); |
573 | platform_driver_register(&msm_hdmi_driver); |
574 | } |
575 | |
576 | void __exit msm_hdmi_unregister(void) |
577 | { |
578 | platform_driver_unregister(&msm_hdmi_driver); |
579 | msm_hdmi_phy_driver_unregister(); |
580 | } |
581 | |