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