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"); 196