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 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 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 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 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 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 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 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_new = samsung_dsim_remove, 185 .driver = { 186 .name = "exynos-dsi", 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