1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
4 *
5 * Author: Zorro Liu <zorro.liu@rock-chips.com>
6 */
7
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/errno.h>
11 #include <linux/err.h>
12 #include <linux/io.h>
13 #include <linux/of.h>
14 #include <linux/slab.h>
15 #include <linux/device.h>
16 #include <linux/delay.h>
17 #include <linux/init.h>
18 #include <linux/interrupt.h>
19 #include <linux/platform_device.h>
20 #include <linux/clk.h>
21 #include <linux/irq.h>
22 #include <linux/pm_runtime.h>
23 #include <linux/mfd/syscon.h>
24 #include <linux/regmap.h>
25 #include "ebc_tcon.h"
26
27 #define HIWORD_UPDATE(x, l, h) (((x) << (l)) | (GENMASK(h, l) << 16))
28 #define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
29
30 /* eink register define */
31 #define EINK_IP_ENABLE 0x00
32 #define EINK_SFT_UPDATE 0x04
33 #define EINK_SIT_UPDATE 0x08
34 #define EINK_PRE_IMAGE_BUF_ADDR 0x0c
35 #define EINK_CUR_IMAGE_BUF_ADDR 0x10
36 #define EINK_IMAGE_PROCESS_BUF_ADDR 0x14
37 #define EINK_LINE_DATA_ADDR_OFFSET 0x18
38 #define EINK_IMAGE_WIDTH 0x1c
39 #define EINK_IMAGE_HEIGHT 0x20
40 #define EINK_DATA_FORMAT 0x24
41 #define EINK_IP_STATUS 0x28
42 #define EINK_IP_VERSION 0x2c
43 #define EINK_IP_CLR_INT 0x30
44 #define EINK_INT_SETTING0 0x34
45 #define EINK_INT_SETTING1 0x38
46 #define EINK_INT_SETTING2 0x3c
47 #define EINK_INT_SETTING3 0x40
48 #define EINK_INT_SETTING4 0x44
49 #define EINK_INT_SETTING5 0x48
50 #define EINK_INT_SETTING6 0x4c
51 #define EINK_INT_SETTING7 0x50
52 #define EINK_WF_SETTING0 0x54
53 #define EINK_WF_SETTING1 0x58
54 #define EINK_WF_SETTING2 0x5c
55 #define EINK_WF_SETTING3 0x60
56 #define EINK_WF_SETTING4 0x64
57 #define EINK_WF_SETTING5 0x68
58 #define EINK_WF_SETTING6 0x6c
59 #define EINK_WF_SETTING7 0x70
60
61 struct eink_reg_data {
62 int addr;
63 int value;
64 };
65
66 static const struct eink_reg_data PANEL_1200x825_INIT[] = {
67 { EINK_SFT_UPDATE, 0x00030001 },
68 { EINK_SIT_UPDATE, 0x00050000 },
69 { EINK_LINE_DATA_ADDR_OFFSET, 0x000004b0 }, //width
70 { EINK_IMAGE_WIDTH, 0x000004af }, //width - 1
71 { EINK_IMAGE_HEIGHT, 0x00000338 }, //height - 1
72
73 { EINK_INT_SETTING0, 0x0e56676f },
74 { EINK_INT_SETTING1, 0x40674408 },
75 { EINK_INT_SETTING2, 0xd7eb7743 },
76 { EINK_INT_SETTING3, 0x19414d35 },
77 { EINK_INT_SETTING4, 0x12561c00 },
78 { EINK_INT_SETTING5, 0x05552e0a },
79 { EINK_INT_SETTING6, 0x4a400e10 },
80 { EINK_INT_SETTING7, 0x15496e2b },
81
82 { EINK_WF_SETTING0, 0xb3f33a52 },
83 { EINK_WF_SETTING1, 0x2042b122 },
84 { EINK_WF_SETTING2, 0xbdb0f3be },
85 { EINK_WF_SETTING3, 0xe289a0ca },
86 { EINK_WF_SETTING4, 0xb0d3b2c8 },
87 { EINK_WF_SETTING5, 0x3a32ab20 },
88 { EINK_WF_SETTING6, 0xa69a634c },
89 { EINK_WF_SETTING7, 0xd87af2c0 },
90 };
91
92 static const struct eink_reg_data PANEL_1872x1404_INIT[] = {
93 { EINK_SFT_UPDATE, 0x00030001 },
94 { EINK_SIT_UPDATE, 0x00050000 },
95 { EINK_LINE_DATA_ADDR_OFFSET, 0x00000750 }, //width
96 { EINK_IMAGE_WIDTH, 0x0000074f }, //width - 1
97 { EINK_IMAGE_HEIGHT, 0x0000057c }, //height -1
98
99 { EINK_INT_SETTING0, 0x0e56676f },
100 { EINK_INT_SETTING1, 0x40674408 },
101 { EINK_INT_SETTING2, 0xb14a4643 },
102 { EINK_INT_SETTING3, 0x19414d35 },
103 { EINK_INT_SETTING4, 0x12561c00 },
104 { EINK_INT_SETTING5, 0x05552e0a },
105 { EINK_INT_SETTING6, 0x4a400e10 },
106 { EINK_INT_SETTING7, 0x15496e2b },
107
108 { EINK_WF_SETTING0, 0xb3f33a52 },
109 { EINK_WF_SETTING1, 0x2042b122 },
110 { EINK_WF_SETTING2, 0x34b0708b },
111 { EINK_WF_SETTING3, 0xe289a0ca },
112 { EINK_WF_SETTING4, 0xb0d3b2c8 },
113 { EINK_WF_SETTING5, 0x3a32ab20 },
114 { EINK_WF_SETTING6, 0x2f9ae079 },
115 { EINK_WF_SETTING7, 0xd87af2c0 },
116 };
117
tcon_write(struct eink_tcon * tcon,unsigned int reg,unsigned int value)118 static inline void tcon_write(struct eink_tcon *tcon, unsigned int reg,
119 unsigned int value)
120 {
121 regmap_write(tcon->regmap_base, reg, value);
122 }
123
tcon_read(struct eink_tcon * tcon,unsigned int reg)124 static inline unsigned int tcon_read(struct eink_tcon *tcon, unsigned int reg)
125 {
126 unsigned int value;
127
128 regmap_read(tcon->regmap_base, reg, &value);
129
130 return value;
131 }
132
tcon_update_bits(struct eink_tcon * tcon,unsigned int reg,unsigned int mask,unsigned int val)133 static inline void tcon_update_bits(struct eink_tcon *tcon, unsigned int reg,
134 unsigned int mask, unsigned int val)
135 {
136 regmap_update_bits(tcon->regmap_base, reg, mask, val);
137 }
138
tcon_enable(struct eink_tcon * tcon,struct ebc_panel * panel)139 static int tcon_enable(struct eink_tcon *tcon, struct ebc_panel *panel)
140 {
141 int reg_num = 0;
142 int i;
143 const struct eink_reg_data *pre_init_reg;
144
145 clk_prepare_enable(tcon->pclk);
146 clk_prepare_enable(tcon->hclk);
147 pm_runtime_get_sync(tcon->dev);
148
149 if ((panel->width == 1872) && (panel->height == 1404)) {
150 pre_init_reg = PANEL_1872x1404_INIT;
151 reg_num = ARRAY_SIZE(PANEL_1872x1404_INIT);
152 } else if ((panel->width == 1200) && (panel->height == 825)) {
153 pre_init_reg = PANEL_1200x825_INIT;
154 reg_num = ARRAY_SIZE(PANEL_1200x825_INIT);
155 } else {
156 pre_init_reg = PANEL_1872x1404_INIT;
157 reg_num = ARRAY_SIZE(PANEL_1872x1404_INIT);
158 }
159 for (i = 0; i < reg_num; i++) {
160 tcon_write(tcon, pre_init_reg[i].addr, pre_init_reg[i].value);
161 }
162
163 enable_irq(tcon->irq);
164
165 return 0;
166 }
167
tcon_disable(struct eink_tcon * tcon)168 static void tcon_disable(struct eink_tcon *tcon)
169 {
170 disable_irq(tcon->irq);
171
172 pm_runtime_put_sync(tcon->dev);
173 clk_disable_unprepare(tcon->hclk);
174 clk_disable_unprepare(tcon->pclk);
175 }
176
tcon_image_addr_set(struct eink_tcon * tcon,u32 pre_image_buf_addr,u32 cur_image_buf_addr,u32 image_process_buf_addr)177 static void tcon_image_addr_set(struct eink_tcon *tcon, u32 pre_image_buf_addr,
178 u32 cur_image_buf_addr, u32 image_process_buf_addr)
179 {
180 tcon_write(tcon, EINK_PRE_IMAGE_BUF_ADDR, pre_image_buf_addr);
181 tcon_write(tcon, EINK_CUR_IMAGE_BUF_ADDR, cur_image_buf_addr);
182 tcon_write(tcon, EINK_IMAGE_PROCESS_BUF_ADDR, image_process_buf_addr);
183 }
184
tcon_frame_start(struct eink_tcon * tcon)185 static void tcon_frame_start(struct eink_tcon *tcon)
186 {
187 tcon_write(tcon, EINK_IP_ENABLE, 1);
188 }
189
tcon_irq_hanlder(int irq,void * dev_id)190 static irqreturn_t tcon_irq_hanlder(int irq, void *dev_id)
191 {
192 struct eink_tcon *tcon = (struct eink_tcon *)dev_id;
193 u32 intr_status;
194
195 intr_status = tcon_read(tcon, EINK_IP_STATUS);
196
197 if (intr_status & 0x1) {
198 tcon_update_bits(tcon, EINK_IP_CLR_INT, 0x1, 0x1);
199
200 if (tcon->dsp_end_callback)
201 tcon->dsp_end_callback();
202 }
203
204 return IRQ_HANDLED;
205 }
206
207 static struct regmap_config eink_regmap_config = {
208 .reg_bits = 32,
209 .val_bits = 32,
210 .reg_stride = 4,
211 };
212
eink_tcon_probe(struct platform_device * pdev)213 static int eink_tcon_probe(struct platform_device *pdev)
214 {
215 struct device *dev = &pdev->dev;
216 struct eink_tcon *tcon;
217 struct resource *res;
218 int ret;
219
220 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
221 if (!tcon)
222 return -ENOMEM;
223
224 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
225 tcon->regs = devm_ioremap_resource(dev, res);
226 if (IS_ERR(tcon->regs))
227 return PTR_ERR(tcon->regs);
228
229 tcon->len = resource_size(res);
230 eink_regmap_config.max_register = resource_size(res) - 4;
231 eink_regmap_config.name = "rockchip,eink_tcon";
232 tcon->regmap_base = devm_regmap_init_mmio(dev, tcon->regs, &eink_regmap_config);
233 if (IS_ERR(tcon->regmap_base))
234 return PTR_ERR(tcon->regmap_base);
235
236 tcon->hclk = devm_clk_get(dev, "hclk");
237 if (IS_ERR(tcon->hclk)) {
238 ret = PTR_ERR(tcon->hclk);
239 dev_err(dev, "failed to get hclk clock: %d\n", ret);
240 return ret;
241 }
242
243 tcon->pclk = devm_clk_get(dev, "pclk");
244 if (IS_ERR(tcon->pclk)) {
245 ret = PTR_ERR(tcon->pclk);
246 dev_err(dev, "failed to get dclk clock: %d\n", ret);
247 return ret;
248 }
249
250 tcon->irq = platform_get_irq(pdev, 0);
251 if (tcon->irq < 0) {
252 dev_err(dev, "No IRQ resource!\n");
253 return tcon->irq;
254 }
255
256 irq_set_status_flags(tcon->irq, IRQ_NOAUTOEN);
257 ret = devm_request_irq(dev, tcon->irq, tcon_irq_hanlder,
258 0, dev_name(dev), tcon);
259 if (ret < 0) {
260 dev_err(dev, "failed to requeset irq: %d\n", ret);
261 return ret;
262 }
263
264 tcon->dev = dev;
265 tcon->enable = tcon_enable;
266 tcon->disable = tcon_disable;
267 tcon->image_addr_set = tcon_image_addr_set;
268 tcon->frame_start = tcon_frame_start;
269 platform_set_drvdata(pdev, tcon);
270
271 pm_runtime_enable(dev);
272
273 return 0;
274 }
275
eink_tcon_remove(struct platform_device * pdev)276 static int eink_tcon_remove(struct platform_device *pdev)
277 {
278 pm_runtime_disable(&pdev->dev);
279
280 return 0;
281 }
282
283 static const struct of_device_id eink_tcon_of_match[] = {
284 { .compatible = "rockchip,rk3568-eink-tcon" },
285 {}
286 };
287 MODULE_DEVICE_TABLE(of, eink_tcon_of_match);
288
289 static struct platform_driver eink_tcon_driver = {
290 .driver = {
291 .name = "rk-eink-tcon",
292 .of_match_table = eink_tcon_of_match,
293 },
294 .probe = eink_tcon_probe,
295 .remove = eink_tcon_remove,
296 };
297 module_platform_driver(eink_tcon_driver);
298
299 MODULE_AUTHOR("Zorro Liu <zorro.liu@rock-chips.com>");
300 MODULE_DESCRIPTION("ROCKCHIP EINK tcon driver");
301 MODULE_LICENSE("GPL v2");
302