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