drm: bridge: Generalize Exynos-DSI driver into a Samsung DSIM bridge
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / exynos / exynos_drm_dsi.c
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");