xref: /linux/drivers/gpu/drm/sitronix/st7571-i2c.c (revision e005fd94e2e5867f2a4e66e5df85069cda6f0db4)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Driver for Sitronix ST7571 connected via I2C bus.
4  *
5  * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
6  */
7 
8 #include <linux/i2c.h>
9 #include <linux/module.h>
10 #include <linux/regmap.h>
11 
12 #include "st7571.h"
13 
14 struct st7571_i2c_transport {
15 	struct i2c_client *client;
16 
17 	/*
18 	 * Depending on the hardware design, the acknowledge signal may be hard to
19 	 * recognize as a valid logic "0" level.
20 	 * Therefor, ignore NAK if possible to stay compatible with most hardware designs
21 	 * and off-the-shelf panels out there.
22 	 *
23 	 * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
24 	 *
25 	 * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
26 	 * I2C interface compatible.
27 	 * Separating acknowledge-output from serial data
28 	 * input is advantageous for chip-on-glass (COG) applications. In COG
29 	 * applications, the ITO resistance and the pull-up resistor will form a
30 	 * voltage  divider, which affects acknowledge-signal level. Larger ITO
31 	 * resistance will raise the acknowledged-signal level and system cannot
32 	 * recognize this level as a valid logic “0” level. By separating SDA_IN from
33 	 * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
34 	 * For applications which check acknowledge-bit, it is necessary to minimize
35 	 * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
36 	 *
37 	 */
38 	bool ignore_nak;
39 };
40 
41 static int st7571_i2c_regmap_write(void *context, const void *data, size_t count)
42 {
43 	struct st7571_i2c_transport *t = context;
44 	int ret;
45 
46 	struct i2c_msg msg = {
47 		.addr = t->client->addr,
48 		.flags = t->ignore_nak ? I2C_M_IGNORE_NAK : 0,
49 		.len = count,
50 		.buf = (u8 *)data
51 	};
52 
53 	ret = i2c_transfer(t->client->adapter, &msg, 1);
54 
55 	/*
56 	 * Unfortunately, there is no way to check if the transfer failed because of
57 	 * a NAK or something else as I2C bus drivers use different return values for NAK.
58 	 *
59 	 * However, if the transfer fails and ignore_nak is set, we know it is an error.
60 	 */
61 	if (ret < 0 && t->ignore_nak)
62 		return ret;
63 
64 	return 0;
65 }
66 
67 /* The st7571 driver does not read registers but regmap expects a .read */
68 static int st7571_i2c_regmap_read(void *context, const void *reg_buf,
69 				  size_t reg_size, void *val_buf, size_t val_size)
70 {
71 	return -EOPNOTSUPP;
72 }
73 
74 static const struct regmap_bus st7571_i2c_regmap_bus = {
75 	.read = st7571_i2c_regmap_read,
76 	.write = st7571_i2c_regmap_write,
77 };
78 
79 static const struct regmap_config st7571_i2c_regmap_config = {
80 	.reg_bits = 8,
81 	.val_bits = 8,
82 	.use_single_write = true,
83 };
84 
85 static int st7571_i2c_probe(struct i2c_client *client)
86 {
87 	struct st7571_device *st7571;
88 	struct st7571_i2c_transport *t;
89 	struct regmap *regmap;
90 
91 	t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
92 	if (!t)
93 		return -ENOMEM;
94 
95 	t->client = client;
96 
97 	/*
98 	 * The hardware design could make it hard to detect a NAK on the I2C bus.
99 	 * If the adapter does not support protocol mangling do
100 	 * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
101 	 * cruft in the logs.
102 	 */
103 	if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
104 		t->ignore_nak = true;
105 
106 	regmap = devm_regmap_init(&client->dev, &st7571_i2c_regmap_bus,
107 				  t, &st7571_i2c_regmap_config);
108 	if (IS_ERR(regmap)) {
109 		return dev_err_probe(&client->dev, PTR_ERR(regmap),
110 				     "Failed to initialize regmap\n");
111 	}
112 
113 	st7571 = st7571_probe(&client->dev, regmap);
114 	if (IS_ERR(st7571))
115 		return dev_err_probe(&client->dev, PTR_ERR(st7571),
116 				     "Failed to initialize regmap\n");
117 
118 	i2c_set_clientdata(client, st7571);
119 	return 0;
120 }
121 
122 static void st7571_i2c_remove(struct i2c_client *client)
123 {
124 	struct st7571_device *st7571 = i2c_get_clientdata(client);
125 
126 	st7571_remove(st7571);
127 }
128 
129 static const struct of_device_id st7571_of_match[] = {
130 	{ .compatible = "sitronix,st7567", .data = &st7567_config },
131 	{ .compatible = "sitronix,st7571", .data = &st7571_config },
132 	{},
133 };
134 MODULE_DEVICE_TABLE(of, st7571_of_match);
135 
136 static const struct i2c_device_id st7571_id[] = {
137 	{ "st7567", 0 },
138 	{ "st7571", 0 },
139 	{ }
140 };
141 MODULE_DEVICE_TABLE(i2c, st7571_id);
142 
143 static struct i2c_driver st7571_i2c_driver = {
144 	.driver = {
145 		.name = "st7571-i2c",
146 		.of_match_table = st7571_of_match,
147 	},
148 	.probe = st7571_i2c_probe,
149 	.remove = st7571_i2c_remove,
150 	.id_table = st7571_id,
151 };
152 
153 module_i2c_driver(st7571_i2c_driver);
154 
155 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
156 MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller (I2C)");
157 MODULE_LICENSE("GPL");
158 MODULE_IMPORT_NS("DRM_ST7571");
159