1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Samsung MIPI DSIM glue for Exynos SoCs.
4  *
5  * Copyright (c) 2014 Samsung Electronics Co., Ltd
6  *
7  * Contacts: Tomasz Figa <t.figa@samsung.com>
8  */
9 
10 #include <linux/component.h>
11 #include <linux/of_device.h>
12 
13 #include <drm/bridge/samsung-dsim.h>
14 #include <drm/drm_probe_helper.h>
15 #include <drm/drm_simple_kms_helper.h>
16 
17 #include "exynos_drm_crtc.h"
18 #include "exynos_drm_drv.h"
19 
20 struct exynos_dsi {
21 	struct drm_encoder encoder;
22 };
23 
24 static irqreturn_t exynos_dsi_te_irq_handler(struct samsung_dsim *dsim)
25 {
26 	struct exynos_dsi *dsi = dsim->priv;
27 	struct drm_encoder *encoder = &dsi->encoder;
28 
29 	if (dsim->state & DSIM_STATE_VIDOUT_AVAILABLE)
30 		exynos_drm_crtc_te_handler(encoder->crtc);
31 
32 	return IRQ_HANDLED;
33 }
34 
35 static int exynos_dsi_host_attach(struct samsung_dsim *dsim,
36 				  struct mipi_dsi_device *device)
37 {
38 	struct exynos_dsi *dsi = dsim->priv;
39 	struct drm_encoder *encoder = &dsi->encoder;
40 	struct drm_device *drm = encoder->dev;
41 
42 	drm_bridge_attach(encoder, &dsim->bridge,
43 			  list_first_entry_or_null(&encoder->bridge_chain,
44 						   struct drm_bridge,
45 						   chain_node), 0);
46 
47 	mutex_lock(&drm->mode_config.mutex);
48 
49 	dsim->lanes = device->lanes;
50 	dsim->format = device->format;
51 	dsim->mode_flags = device->mode_flags;
52 	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
53 			!(dsim->mode_flags & MIPI_DSI_MODE_VIDEO);
54 
55 	mutex_unlock(&drm->mode_config.mutex);
56 
57 	if (drm->mode_config.poll_enabled)
58 		drm_kms_helper_hotplug_event(drm);
59 
60 	return 0;
61 }
62 
63 static void exynos_dsi_host_detach(struct samsung_dsim *dsim,
64 				   struct mipi_dsi_device *device)
65 {
66 	struct exynos_dsi *dsi = dsim->priv;
67 	struct drm_device *drm = dsi->encoder.dev;
68 
69 	if (drm->mode_config.poll_enabled)
70 		drm_kms_helper_hotplug_event(drm);
71 }
72 
73 static int exynos_dsi_bind(struct device *dev, struct device *master, void *data)
74 {
75 	struct samsung_dsim *dsim = dev_get_drvdata(dev);
76 	struct exynos_dsi *dsi = dsim->priv;
77 	struct drm_encoder *encoder = &dsi->encoder;
78 	struct drm_device *drm_dev = data;
79 	int ret;
80 
81 	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
82 
83 	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
84 	if (ret < 0)
85 		return ret;
86 
87 	return mipi_dsi_host_register(&dsim->dsi_host);
88 }
89 
90 static void exynos_dsi_unbind(struct device *dev, struct device *master, void *data)
91 {
92 	struct samsung_dsim *dsim = dev_get_drvdata(dev);
93 
94 	dsim->bridge.funcs->atomic_disable(&dsim->bridge, NULL);
95 
96 	mipi_dsi_host_unregister(&dsim->dsi_host);
97 }
98 
99 static const struct component_ops exynos_dsi_component_ops = {
100 	.bind	= exynos_dsi_bind,
101 	.unbind	= exynos_dsi_unbind,
102 };
103 
104 static int exynos_dsi_register_host(struct samsung_dsim *dsim)
105 {
106 	struct exynos_dsi *dsi;
107 
108 	dsi = devm_kzalloc(dsim->dev, sizeof(*dsi), GFP_KERNEL);
109 	if (!dsi)
110 		return -ENOMEM;
111 
112 	dsim->priv = dsi;
113 	dsim->bridge.pre_enable_prev_first = true;
114 
115 	return component_add(dsim->dev, &exynos_dsi_component_ops);
116 }
117 
118 static void exynos_dsi_unregister_host(struct samsung_dsim *dsim)
119 {
120 	component_del(dsim->dev, &exynos_dsi_component_ops);
121 }
122 
123 static const struct samsung_dsim_host_ops exynos_dsi_exynos_host_ops = {
124 	.register_host = exynos_dsi_register_host,
125 	.unregister_host = exynos_dsi_unregister_host,
126 	.attach = exynos_dsi_host_attach,
127 	.detach = exynos_dsi_host_detach,
128 	.te_irq_handler = exynos_dsi_te_irq_handler,
129 };
130 
131 static const struct samsung_dsim_plat_data exynos3250_dsi_pdata = {
132 	.hw_type = DSIM_TYPE_EXYNOS3250,
133 	.host_ops = &exynos_dsi_exynos_host_ops,
134 };
135 
136 static const struct samsung_dsim_plat_data exynos4210_dsi_pdata = {
137 	.hw_type = DSIM_TYPE_EXYNOS4210,
138 	.host_ops = &exynos_dsi_exynos_host_ops,
139 };
140 
141 static const struct samsung_dsim_plat_data exynos5410_dsi_pdata = {
142 	.hw_type = DSIM_TYPE_EXYNOS5410,
143 	.host_ops = &exynos_dsi_exynos_host_ops,
144 };
145 
146 static const struct samsung_dsim_plat_data exynos5422_dsi_pdata = {
147 	.hw_type = DSIM_TYPE_EXYNOS5422,
148 	.host_ops = &exynos_dsi_exynos_host_ops,
149 };
150 
151 static const struct samsung_dsim_plat_data exynos5433_dsi_pdata = {
152 	.hw_type = DSIM_TYPE_EXYNOS5433,
153 	.host_ops = &exynos_dsi_exynos_host_ops,
154 };
155 
156 static const struct of_device_id exynos_dsi_of_match[] = {
157 	{
158 		.compatible = "samsung,exynos3250-mipi-dsi",
159 		.data = &exynos3250_dsi_pdata,
160 	},
161 	{
162 		.compatible = "samsung,exynos4210-mipi-dsi",
163 		.data = &exynos4210_dsi_pdata,
164 	},
165 	{
166 		.compatible = "samsung,exynos5410-mipi-dsi",
167 		.data = &exynos5410_dsi_pdata,
168 	},
169 	{
170 		.compatible = "samsung,exynos5422-mipi-dsi",
171 		.data = &exynos5422_dsi_pdata,
172 	},
173 	{
174 		.compatible = "samsung,exynos5433-mipi-dsi",
175 		.data = &exynos5433_dsi_pdata,
176 	},
177 	{ /* sentinel. */ }
178 };
179 MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
180 
181 struct platform_driver dsi_driver = {
182 	.probe = samsung_dsim_probe,
183 	.remove = samsung_dsim_remove,
184 	.driver = {
185 		   .name = "exynos-dsi",
186 		   .owner = THIS_MODULE,
187 		   .pm = &samsung_dsim_pm_ops,
188 		   .of_match_table = exynos_dsi_of_match,
189 	},
190 };
191 
192 MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
193 MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
194 MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
195 MODULE_LICENSE("GPL v2");
196