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
21struct exynos_dsi {
22	struct drm_encoder encoder;
23};
24
25static 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
36static 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
64static 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
74static 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
91static 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
100static const struct component_ops exynos_dsi_component_ops = {
101	.bind	= exynos_dsi_bind,
102	.unbind	= exynos_dsi_unbind,
103};
104
105static 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
119static void exynos_dsi_unregister_host(struct samsung_dsim *dsim)
120{
121	component_del(dsim->dev, &exynos_dsi_component_ops);
122}
123
124static 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
132static 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
137static 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
142static 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
147static 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
152static 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
157static 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};
180MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
181
182struct platform_driver dsi_driver = {
183	.probe = samsung_dsim_probe,
184	.remove_new = 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
193MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
194MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
195MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
196MODULE_LICENSE("GPL v2");
197