1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * DSI interface to the Samsung S6E63M0 panel. 4 * (C) 2019 Linus Walleij 5 */ 6 7 #include <linux/module.h> 8 #include <linux/delay.h> 9 #include <linux/of_device.h> 10 11 #include <drm/drm_mipi_dsi.h> 12 #include <drm/drm_print.h> 13 14 #include "panel-samsung-s6e63m0.h" 15 16 #define MCS_GLOBAL_PARAM 0xb0 17 #define S6E63M0_DSI_MAX_CHUNK 15 /* CMD + 15 bytes max */ 18 19 static int s6e63m0_dsi_dcs_read(struct device *dev, void *trsp, 20 const u8 cmd, u8 *data) 21 { 22 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); 23 int ret; 24 25 ret = mipi_dsi_dcs_read(dsi, cmd, data, 1); 26 if (ret < 0) { 27 dev_err(dev, "could not read DCS CMD %02x\n", cmd); 28 return ret; 29 } 30 31 dev_dbg(dev, "DSI read CMD %02x = %02x\n", cmd, *data); 32 33 return 0; 34 } 35 36 static int s6e63m0_dsi_dcs_write(struct device *dev, void *trsp, 37 const u8 *data, size_t len) 38 { 39 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); 40 const u8 *seqp = data; 41 u8 cmd; 42 u8 cmdwritten; 43 int remain; 44 int chunk; 45 int ret; 46 47 dev_dbg(dev, "DSI writing dcs seq: %*ph\n", (int)len, data); 48 49 /* Pick out and skip past the DCS command */ 50 cmd = *seqp; 51 seqp++; 52 cmdwritten = 0; 53 remain = len - 1; 54 chunk = remain; 55 56 /* Send max S6E63M0_DSI_MAX_CHUNK bytes at a time */ 57 if (chunk > S6E63M0_DSI_MAX_CHUNK) 58 chunk = S6E63M0_DSI_MAX_CHUNK; 59 ret = mipi_dsi_dcs_write(dsi, cmd, seqp, chunk); 60 if (ret < 0) { 61 dev_err(dev, "error sending DCS command seq cmd %02x\n", cmd); 62 return ret; 63 } 64 cmdwritten += chunk; 65 seqp += chunk; 66 67 while (cmdwritten < remain) { 68 chunk = remain - cmdwritten; 69 if (chunk > S6E63M0_DSI_MAX_CHUNK) 70 chunk = S6E63M0_DSI_MAX_CHUNK; 71 ret = mipi_dsi_dcs_write(dsi, MCS_GLOBAL_PARAM, &cmdwritten, 1); 72 if (ret < 0) { 73 dev_err(dev, "error sending CMD %02x global param %02x\n", 74 cmd, cmdwritten); 75 return ret; 76 } 77 ret = mipi_dsi_dcs_write(dsi, cmd, seqp, chunk); 78 if (ret < 0) { 79 dev_err(dev, "error sending CMD %02x chunk\n", cmd); 80 return ret; 81 } 82 cmdwritten += chunk; 83 seqp += chunk; 84 } 85 dev_dbg(dev, "sent command %02x %02x bytes\n", cmd, cmdwritten); 86 87 usleep_range(8000, 9000); 88 89 return 0; 90 } 91 92 static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi) 93 { 94 struct device *dev = &dsi->dev; 95 int ret; 96 97 dsi->lanes = 2; 98 dsi->format = MIPI_DSI_FMT_RGB888; 99 dsi->hs_rate = 349440000; 100 dsi->lp_rate = 9600000; 101 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 102 MIPI_DSI_MODE_VIDEO_BURST; 103 104 ret = s6e63m0_probe(dev, NULL, s6e63m0_dsi_dcs_read, 105 s6e63m0_dsi_dcs_write, true); 106 if (ret) 107 return ret; 108 109 ret = mipi_dsi_attach(dsi); 110 if (ret < 0) 111 s6e63m0_remove(dev); 112 113 return ret; 114 } 115 116 static void s6e63m0_dsi_remove(struct mipi_dsi_device *dsi) 117 { 118 mipi_dsi_detach(dsi); 119 s6e63m0_remove(&dsi->dev); 120 } 121 122 static const struct of_device_id s6e63m0_dsi_of_match[] = { 123 { .compatible = "samsung,s6e63m0" }, 124 { /* sentinel */ } 125 }; 126 MODULE_DEVICE_TABLE(of, s6e63m0_dsi_of_match); 127 128 static struct mipi_dsi_driver s6e63m0_dsi_driver = { 129 .probe = s6e63m0_dsi_probe, 130 .remove = s6e63m0_dsi_remove, 131 .driver = { 132 .name = "panel-samsung-s6e63m0", 133 .of_match_table = s6e63m0_dsi_of_match, 134 }, 135 }; 136 module_mipi_dsi_driver(s6e63m0_dsi_driver); 137 138 MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); 139 MODULE_DESCRIPTION("s6e63m0 LCD DSI Driver"); 140 MODULE_LICENSE("GPL v2"); 141