1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27eb8f069SAndrzej Hajda /*
3*e7447128SJagan Teki  * Samsung MIPI DSIM glue for Exynos SoCs.
47eb8f069SAndrzej Hajda  *
57eb8f069SAndrzej Hajda  * Copyright (c) 2014 Samsung Electronics Co., Ltd
67eb8f069SAndrzej Hajda  *
77eb8f069SAndrzej Hajda  * Contacts: Tomasz Figa <t.figa@samsung.com>
87eb8f069SAndrzej Hajda  */
97eb8f069SAndrzej Hajda 
102bda34d7SSam Ravnborg #include <linux/component.h>
119a320415SYoungJun Cho #include <linux/of_device.h>
122bda34d7SSam Ravnborg 
13*e7447128SJagan Teki #include <drm/bridge/samsung-dsim.h>
142bda34d7SSam Ravnborg #include <drm/drm_probe_helper.h>
153e1fe32dSThomas Zimmermann #include <drm/drm_simple_kms_helper.h>
162bda34d7SSam Ravnborg 
17e17ddeccSYoungJun Cho #include "exynos_drm_crtc.h"
187eb8f069SAndrzej Hajda #include "exynos_drm_drv.h"
197eb8f069SAndrzej Hajda 
207eb8f069SAndrzej Hajda struct exynos_dsi {
2170e360f9SJagan Teki 	struct drm_encoder encoder;
227eb8f069SAndrzej Hajda };
237eb8f069SAndrzej Hajda 
24*e7447128SJagan Teki static irqreturn_t exynos_dsi_te_irq_handler(struct samsung_dsim *dsim)
255cd5db80SAndrzej Hajda {
26*e7447128SJagan Teki 	struct exynos_dsi *dsi = dsim->priv;
27*e7447128SJagan Teki 	struct drm_encoder *encoder = &dsi->encoder;
2848b64ba8SJagan Teki 
2948b64ba8SJagan Teki 	if (dsim->state & DSIM_STATE_VIDOUT_AVAILABLE)
3048b64ba8SJagan Teki 		exynos_drm_crtc_te_handler(encoder->crtc);
3148b64ba8SJagan Teki 
3248b64ba8SJagan Teki 	return IRQ_HANDLED;
3348b64ba8SJagan Teki }
3448b64ba8SJagan Teki 
35*e7447128SJagan Teki static int exynos_dsi_host_attach(struct samsung_dsim *dsim,
3670e360f9SJagan Teki 				  struct mipi_dsi_device *device)
3770e360f9SJagan Teki {
38*e7447128SJagan Teki 	struct exynos_dsi *dsi = dsim->priv;
39*e7447128SJagan Teki 	struct drm_encoder *encoder = &dsi->encoder;
4070e360f9SJagan Teki 	struct drm_device *drm = encoder->dev;
4170e360f9SJagan Teki 
4270e360f9SJagan Teki 	drm_bridge_attach(encoder, &dsim->bridge,
4370e360f9SJagan Teki 			  list_first_entry_or_null(&encoder->bridge_chain,
4470e360f9SJagan Teki 						   struct drm_bridge,
4570e360f9SJagan Teki 						   chain_node), 0);
4670e360f9SJagan Teki 
4770e360f9SJagan Teki 	mutex_lock(&drm->mode_config.mutex);
4870e360f9SJagan Teki 
4970e360f9SJagan Teki 	dsim->lanes = device->lanes;
5070e360f9SJagan Teki 	dsim->format = device->format;
5170e360f9SJagan Teki 	dsim->mode_flags = device->mode_flags;
5270e360f9SJagan Teki 	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
5370e360f9SJagan Teki 			!(dsim->mode_flags & MIPI_DSI_MODE_VIDEO);
5470e360f9SJagan Teki 
5570e360f9SJagan Teki 	mutex_unlock(&drm->mode_config.mutex);
5670e360f9SJagan Teki 
5770e360f9SJagan Teki 	if (drm->mode_config.poll_enabled)
5870e360f9SJagan Teki 		drm_kms_helper_hotplug_event(drm);
5970e360f9SJagan Teki 
6070e360f9SJagan Teki 	return 0;
6170e360f9SJagan Teki }
6270e360f9SJagan Teki 
63*e7447128SJagan Teki static void exynos_dsi_host_detach(struct samsung_dsim *dsim,
6470e360f9SJagan Teki 				   struct mipi_dsi_device *device)
6570e360f9SJagan Teki {
66*e7447128SJagan Teki 	struct exynos_dsi *dsi = dsim->priv;
67*e7447128SJagan Teki 	struct drm_device *drm = dsi->encoder.dev;
6870e360f9SJagan Teki 
6970e360f9SJagan Teki 	if (drm->mode_config.poll_enabled)
7070e360f9SJagan Teki 		drm_kms_helper_hotplug_event(drm);
7170e360f9SJagan Teki }
7270e360f9SJagan Teki 
73*e7447128SJagan Teki static int exynos_dsi_bind(struct device *dev, struct device *master, void *data)
74f37cd5e8SInki Dae {
75*e7447128SJagan Teki 	struct samsung_dsim *dsim = dev_get_drvdata(dev);
76*e7447128SJagan Teki 	struct exynos_dsi *dsi = dsim->priv;
77*e7447128SJagan Teki 	struct drm_encoder *encoder = &dsi->encoder;
78f37cd5e8SInki Dae 	struct drm_device *drm_dev = data;
79f37cd5e8SInki Dae 	int ret;
80f37cd5e8SInki Dae 
813e1fe32dSThomas Zimmermann 	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
822b8376c8SGustavo Padovan 
831ca582f1SAndrzej Hajda 	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
841ca582f1SAndrzej Hajda 	if (ret < 0)
851ca582f1SAndrzej Hajda 		return ret;
861ca582f1SAndrzej Hajda 
87*e7447128SJagan Teki 	return mipi_dsi_host_register(&dsim->dsi_host);
88f37cd5e8SInki Dae }
89f37cd5e8SInki Dae 
90*e7447128SJagan Teki static void exynos_dsi_unbind(struct device *dev, struct device *master, void *data)
91f37cd5e8SInki Dae {
92*e7447128SJagan Teki 	struct samsung_dsim *dsim = dev_get_drvdata(dev);
93f37cd5e8SInki Dae 
94*e7447128SJagan Teki 	dsim->bridge.funcs->atomic_disable(&dsim->bridge, NULL);
95f37cd5e8SInki Dae 
96*e7447128SJagan Teki 	mipi_dsi_host_unregister(&dsim->dsi_host);
97f37cd5e8SInki Dae }
98f37cd5e8SInki Dae 
99f37cd5e8SInki Dae static const struct component_ops exynos_dsi_component_ops = {
100f37cd5e8SInki Dae 	.bind	= exynos_dsi_bind,
101f37cd5e8SInki Dae 	.unbind	= exynos_dsi_unbind,
102f37cd5e8SInki Dae };
103f37cd5e8SInki Dae 
104*e7447128SJagan Teki static int exynos_dsi_register_host(struct samsung_dsim *dsim)
10570e360f9SJagan Teki {
106*e7447128SJagan Teki 	struct exynos_dsi *dsi;
10770e360f9SJagan Teki 
108*e7447128SJagan Teki 	dsi = devm_kzalloc(dsim->dev, sizeof(*dsi), GFP_KERNEL);
109*e7447128SJagan Teki 	if (!dsi)
11070e360f9SJagan Teki 		return -ENOMEM;
11170e360f9SJagan Teki 
112*e7447128SJagan Teki 	dsim->priv = dsi;
11370e360f9SJagan Teki 	dsim->bridge.pre_enable_prev_first = true;
11470e360f9SJagan Teki 
11570e360f9SJagan Teki 	return component_add(dsim->dev, &exynos_dsi_component_ops);
11670e360f9SJagan Teki }
11770e360f9SJagan Teki 
118*e7447128SJagan Teki static void exynos_dsi_unregister_host(struct samsung_dsim *dsim)
11970e360f9SJagan Teki {
12070e360f9SJagan Teki 	component_del(dsim->dev, &exynos_dsi_component_ops);
12170e360f9SJagan Teki }
12270e360f9SJagan Teki 
123*e7447128SJagan Teki static const struct samsung_dsim_host_ops exynos_dsi_exynos_host_ops = {
12470e360f9SJagan Teki 	.register_host = exynos_dsi_register_host,
12570e360f9SJagan Teki 	.unregister_host = exynos_dsi_unregister_host,
126*e7447128SJagan Teki 	.attach = exynos_dsi_host_attach,
127*e7447128SJagan Teki 	.detach = exynos_dsi_host_detach,
128*e7447128SJagan Teki 	.te_irq_handler = exynos_dsi_te_irq_handler,
12970e360f9SJagan Teki };
13070e360f9SJagan Teki 
131*e7447128SJagan Teki static const struct samsung_dsim_plat_data exynos3250_dsi_pdata = {
1327e9f0d32SJagan Teki 	.hw_type = DSIM_TYPE_EXYNOS3250,
133*e7447128SJagan Teki 	.host_ops = &exynos_dsi_exynos_host_ops,
1347e9f0d32SJagan Teki };
1357e9f0d32SJagan Teki 
136*e7447128SJagan Teki static const struct samsung_dsim_plat_data exynos4210_dsi_pdata = {
1377e9f0d32SJagan Teki 	.hw_type = DSIM_TYPE_EXYNOS4210,
138*e7447128SJagan Teki 	.host_ops = &exynos_dsi_exynos_host_ops,
1397e9f0d32SJagan Teki };
1407e9f0d32SJagan Teki 
141*e7447128SJagan Teki static const struct samsung_dsim_plat_data exynos5410_dsi_pdata = {
1427e9f0d32SJagan Teki 	.hw_type = DSIM_TYPE_EXYNOS5410,
143*e7447128SJagan Teki 	.host_ops = &exynos_dsi_exynos_host_ops,
1447e9f0d32SJagan Teki };
1457e9f0d32SJagan Teki 
146*e7447128SJagan Teki static const struct samsung_dsim_plat_data exynos5422_dsi_pdata = {
1477e9f0d32SJagan Teki 	.hw_type = DSIM_TYPE_EXYNOS5422,
148*e7447128SJagan Teki 	.host_ops = &exynos_dsi_exynos_host_ops,
1497e9f0d32SJagan Teki };
1507e9f0d32SJagan Teki 
151*e7447128SJagan Teki static const struct samsung_dsim_plat_data exynos5433_dsi_pdata = {
1527e9f0d32SJagan Teki 	.hw_type = DSIM_TYPE_EXYNOS5433,
153*e7447128SJagan Teki 	.host_ops = &exynos_dsi_exynos_host_ops,
1547e9f0d32SJagan Teki };
1557e9f0d32SJagan Teki 
1567e9f0d32SJagan Teki static const struct of_device_id exynos_dsi_of_match[] = {
1577e9f0d32SJagan Teki 	{
1587e9f0d32SJagan Teki 		.compatible = "samsung,exynos3250-mipi-dsi",
1597e9f0d32SJagan Teki 		.data = &exynos3250_dsi_pdata,
1607e9f0d32SJagan Teki 	},
1617e9f0d32SJagan Teki 	{
1627e9f0d32SJagan Teki 		.compatible = "samsung,exynos4210-mipi-dsi",
1637e9f0d32SJagan Teki 		.data = &exynos4210_dsi_pdata,
1647e9f0d32SJagan Teki 	},
1657e9f0d32SJagan Teki 	{
1667e9f0d32SJagan Teki 		.compatible = "samsung,exynos5410-mipi-dsi",
1677e9f0d32SJagan Teki 		.data = &exynos5410_dsi_pdata,
1687e9f0d32SJagan Teki 	},
1697e9f0d32SJagan Teki 	{
1707e9f0d32SJagan Teki 		.compatible = "samsung,exynos5422-mipi-dsi",
1717e9f0d32SJagan Teki 		.data = &exynos5422_dsi_pdata,
1727e9f0d32SJagan Teki 	},
1737e9f0d32SJagan Teki 	{
1747e9f0d32SJagan Teki 		.compatible = "samsung,exynos5433-mipi-dsi",
1757e9f0d32SJagan Teki 		.data = &exynos5433_dsi_pdata,
1767e9f0d32SJagan Teki 	},
1777e9f0d32SJagan Teki 	{ /* sentinel. */ }
1787e9f0d32SJagan Teki };
1797e9f0d32SJagan Teki MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
1807e9f0d32SJagan Teki 
1817eb8f069SAndrzej Hajda struct platform_driver dsi_driver = {
182*e7447128SJagan Teki 	.probe = samsung_dsim_probe,
183*e7447128SJagan Teki 	.remove = samsung_dsim_remove,
1847eb8f069SAndrzej Hajda 	.driver = {
1857eb8f069SAndrzej Hajda 		   .name = "exynos-dsi",
1867eb8f069SAndrzej Hajda 		   .owner = THIS_MODULE,
187*e7447128SJagan Teki 		   .pm = &samsung_dsim_pm_ops,
1887eb8f069SAndrzej Hajda 		   .of_match_table = exynos_dsi_of_match,
1897eb8f069SAndrzej Hajda 	},
1907eb8f069SAndrzej Hajda };
1917eb8f069SAndrzej Hajda 
1927eb8f069SAndrzej Hajda MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
1937eb8f069SAndrzej Hajda MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
1947eb8f069SAndrzej Hajda MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
1957eb8f069SAndrzej Hajda MODULE_LICENSE("GPL v2");
196