panel-samsung-s6e63m0-dsi.c (3259B)
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 19static 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 36static 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 92static 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 116static int s6e63m0_dsi_remove(struct mipi_dsi_device *dsi) 117{ 118 mipi_dsi_detach(dsi); 119 s6e63m0_remove(&dsi->dev); 120 return 0; 121} 122 123static const struct of_device_id s6e63m0_dsi_of_match[] = { 124 { .compatible = "samsung,s6e63m0" }, 125 { /* sentinel */ } 126}; 127MODULE_DEVICE_TABLE(of, s6e63m0_dsi_of_match); 128 129static struct mipi_dsi_driver s6e63m0_dsi_driver = { 130 .probe = s6e63m0_dsi_probe, 131 .remove = s6e63m0_dsi_remove, 132 .driver = { 133 .name = "panel-samsung-s6e63m0", 134 .of_match_table = s6e63m0_dsi_of_match, 135 }, 136}; 137module_mipi_dsi_driver(s6e63m0_dsi_driver); 138 139MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>"); 140MODULE_DESCRIPTION("s6e63m0 LCD DSI Driver"); 141MODULE_LICENSE("GPL v2");