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