blob: c6896440c8cf35629561784d8c1ff45bca80b6bc [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001/*
2 * MediaTek display pulse-width-modulation controller driver.
3 * Copyright (c) 2015 MediaTek Inc.
4 * Author: YH Huang <yh.huang@mediatek.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/clk.h>
17#include <linux/err.h>
18#include <linux/io.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/of_device.h>
22#include <linux/platform_device.h>
23#include <linux/pwm.h>
24#include <linux/slab.h>
25#include <linux/of_address.h>
26#include <linux/delay.h>
27
28#define DISP_PWM_EN 0x00
29
30#define PWM_CLKDIV_SHIFT 16
31#define PWM_CLKDIV_MAX 0x3ff
32#define PWM_CLKDIV_MASK (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
33
34#define PWM_PERIOD_BIT_WIDTH 12
35#define PWM_PERIOD_MASK ((1 << PWM_PERIOD_BIT_WIDTH) - 1)
36
37#define PWM_HIGH_WIDTH_SHIFT 16
38#define PWM_HIGH_WIDTH_MASK (0x1fff << PWM_HIGH_WIDTH_SHIFT)
39
40#undef pr_fmt
41#define pr_fmt(fmt) "[mtk_disp_pwm]" fmt
42
43struct mtk_pwm_data {
44 u32 enable_mask;
45 unsigned int con0;
46 u32 con0_sel;
47 unsigned int con1;
48
49 bool has_commit;
50 unsigned int commit;
51 unsigned int commit_mask;
52
53 unsigned int bls_debug;
54 u32 bls_debug_mask;
55};
56
57struct mtk_disp_pwm {
58 struct pwm_chip chip;
59 const struct mtk_pwm_data *data;
60 struct clk *clk_main;
61 struct clk *clk_mm;
62 void __iomem *base;
63 void __iomem *pmw_src_addr;
64 bool pwm_src_enabled;
65};
66
67static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)
68{
69 return container_of(chip, struct mtk_disp_pwm, chip);
70}
71
72static void mtk_disp_pwm_update_bits(struct mtk_disp_pwm *mdp, u32 offset,
73 u32 mask, u32 data)
74{
75 void __iomem *address = mdp->base + offset;
76 u32 value;
77
78 value = readl(address);
79 value &= ~mask;
80 value |= data;
81 writel(value, address);
82}
83
84static int get_pwm_src_base(struct device *dev, struct mtk_disp_pwm *mdp)
85{
86 int ret = 0;
87 struct device_node *node;
88 void __iomem *pmw_src_base;
89 u32 addr_offset = 0;
90
91 node = of_parse_phandle(dev->of_node, "pwm_src_base", 0);
92 if (!node) {
93 dev_info(dev, "find pwm_src node failed\n");
94 return -1;
95 }
96 pmw_src_base = of_iomap(node, 0);
97 if (!pmw_src_base) {
98 dev_info(dev, "find pwm_src address failed\n");
99 of_node_put(node);
100 return -1;
101 }
102 ret = of_property_read_u32(dev->of_node, "pwm_src_addr", &addr_offset);
103 if (ret >= 0)
104 mdp->pmw_src_addr = pmw_src_base + addr_offset;
105
106 dev_info(dev, "get pwm_src_addr=%x\n", addr_offset);
107 of_node_put(node);
108 return ret;
109}
110
111static int pwm_src_power_on(struct mtk_disp_pwm *mdp)
112{
113 u32 regosc;
114
115 if (!mdp->pmw_src_addr || mdp->pwm_src_enabled)
116 return 0;
117
118 mdp->pwm_src_enabled = true;
119 regosc = readl(mdp->pmw_src_addr);
120
121 regosc = regosc | 0x1;
122 writel(regosc, mdp->pmw_src_addr);
123 udelay(150);
124
125 regosc = readl(mdp->pmw_src_addr);
126 regosc = regosc | 0x4;
127 writel(regosc, mdp->pmw_src_addr);
128 regosc = readl(mdp->pmw_src_addr);
129
130 return 0;
131}
132
133static int pwm_src_power_off(struct mtk_disp_pwm *mdp)
134{
135 u32 regosc;
136
137 if (!mdp->pmw_src_addr || !mdp->pwm_src_enabled)
138 return 0;
139
140 mdp->pwm_src_enabled = false;
141 regosc = readl(mdp->pmw_src_addr);
142
143 regosc = regosc & (~0x4);
144 writel(regosc, mdp->pmw_src_addr);
145
146 udelay(150);
147 regosc = readl(mdp->pmw_src_addr);
148
149 regosc = regosc & (~0x1);
150 writel(regosc, mdp->pmw_src_addr);
151 regosc = readl(mdp->pmw_src_addr);
152
153 return 0;
154}
155
156static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
157 int duty_ns, int period_ns)
158{
159 struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
160 u32 clk_div, period, high_width, value;
161 u64 div, rate;
162 int err;
163
164 /*
165 * Find period, high_width and clk_div to suit duty_ns and period_ns.
166 * Calculate proper div value to keep period value in the bound.
167 *
168 * period_ns = 10^9 * (clk_div + 1) * (period + 1) / PWM_CLK_RATE
169 * duty_ns = 10^9 * (clk_div + 1) * high_width / PWM_CLK_RATE
170 *
171 * period = (PWM_CLK_RATE * period_ns) / (10^9 * (clk_div + 1)) - 1
172 * high_width = (PWM_CLK_RATE * duty_ns) / (10^9 * (clk_div + 1))
173 */
174 dev_dbg(mdp->chip.dev, "duty=%d period=%d\n", duty_ns, period_ns);
175 rate = clk_get_rate(mdp->clk_main);
176 clk_div = div_u64(rate * period_ns, NSEC_PER_SEC) >>
177 PWM_PERIOD_BIT_WIDTH;
178 if (clk_div > PWM_CLKDIV_MAX)
179 return -EINVAL;
180 dev_dbg(mdp->chip.dev, "rate=%lld clk_div=%d\n", rate, clk_div);
181
182 div = NSEC_PER_SEC * (clk_div + 1);
183 period = div64_u64(rate * period_ns, div);
184 if (period > 0)
185 period--;
186
187 high_width = div64_u64(rate * duty_ns, div);
188 value = period | (high_width << PWM_HIGH_WIDTH_SHIFT);
189 dev_dbg(mdp->chip.dev, "high_width=%d period=%d\n",
190 high_width, period);
191
192 pwm_src_power_on(mdp);
193
194 err = clk_enable(mdp->clk_main);
195 if (err < 0)
196 return err;
197
198 err = clk_enable(mdp->clk_mm);
199 if (err < 0) {
200 clk_disable(mdp->clk_main);
201 return err;
202 }
203
204 mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
205 PWM_CLKDIV_MASK,
206 clk_div << PWM_CLKDIV_SHIFT);
207 mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
208 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
209 value);
210
211 if (mdp->data->has_commit) {
212 mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
213 mdp->data->commit_mask,
214 mdp->data->commit_mask);
215 mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
216 mdp->data->commit_mask,
217 0x0);
218 }
219
220 clk_disable(mdp->clk_mm);
221 clk_disable(mdp->clk_main);
222
223 return 0;
224}
225
226static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
227{
228 struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
229 int err;
230
231 dev_info(mdp->chip.dev, "%s\n", __func__);
232 err = clk_enable(mdp->clk_main);
233 if (err < 0)
234 return err;
235
236 err = clk_enable(mdp->clk_mm);
237 if (err < 0) {
238 clk_disable(mdp->clk_main);
239 return err;
240 }
241
242 pwm_src_power_on(mdp);
243
244 mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
245 mdp->data->enable_mask);
246
247 return 0;
248}
249
250static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
251{
252 struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
253
254 dev_dbg(mdp->chip.dev, "%s\n", __func__);
255 mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
256 0x0);
257
258 clk_disable(mdp->clk_mm);
259 clk_disable(mdp->clk_main);
260
261 pwm_src_power_off(mdp);
262}
263
264static const struct pwm_ops mtk_disp_pwm_ops = {
265 .config = mtk_disp_pwm_config,
266 .enable = mtk_disp_pwm_enable,
267 .disable = mtk_disp_pwm_disable,
268 .owner = THIS_MODULE,
269};
270
271static int mtk_disp_pwm_probe(struct platform_device *pdev)
272{
273 struct mtk_disp_pwm *mdp;
274 struct resource *r;
275 struct clk *pwm_src;
276 int ret;
277
278 mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL);
279 if (!mdp)
280 return -ENOMEM;
281
282 mdp->data = of_device_get_match_data(&pdev->dev);
283
284 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
285 mdp->base = devm_ioremap_resource(&pdev->dev, r);
286 if (IS_ERR(mdp->base))
287 return PTR_ERR(mdp->base);
288
289 mdp->clk_main = devm_clk_get(&pdev->dev, "main");
290 if (IS_ERR(mdp->clk_main))
291 return PTR_ERR(mdp->clk_main);
292
293 mdp->clk_mm = devm_clk_get(&pdev->dev, "mm");
294 if (IS_ERR(mdp->clk_mm))
295 return PTR_ERR(mdp->clk_mm);
296
297 ret = clk_prepare(mdp->clk_main);
298 if (ret < 0)
299 return ret;
300
301 ret = clk_prepare(mdp->clk_mm);
302 if (ret < 0)
303 goto disable_clk_main;
304
305 pwm_src = devm_clk_get(&pdev->dev, "pwm_src");
306 if (!IS_ERR(pwm_src) && get_pwm_src_base(&pdev->dev, mdp) >= 0) {
307 clk_enable(mdp->clk_mm);
308 clk_set_parent(mdp->clk_mm, pwm_src);
309 clk_disable(mdp->clk_mm);
310 dev_info(&pdev->dev, "select clk_mm with pwm_src\n");
311 }
312
313 mdp->chip.dev = &pdev->dev;
314 mdp->chip.ops = &mtk_disp_pwm_ops;
315 mdp->chip.base = -1;
316 mdp->chip.npwm = 1;
317
318 ret = pwmchip_add(&mdp->chip);
319 if (ret < 0) {
320 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
321 goto disable_clk_mm;
322 }
323
324 platform_set_drvdata(pdev, mdp);
325
326 /*
327 * For MT2701, disable double buffer before writing register
328 * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
329 */
330 if (!mdp->data->has_commit) {
331 mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,
332 mdp->data->bls_debug_mask,
333 mdp->data->bls_debug_mask);
334 mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
335 mdp->data->con0_sel,
336 mdp->data->con0_sel);
337 }
338
339 return 0;
340
341disable_clk_mm:
342 clk_unprepare(mdp->clk_mm);
343disable_clk_main:
344 clk_unprepare(mdp->clk_main);
345 return ret;
346}
347
348static int mtk_disp_pwm_remove(struct platform_device *pdev)
349{
350 struct mtk_disp_pwm *mdp = platform_get_drvdata(pdev);
351 int ret;
352
353 ret = pwmchip_remove(&mdp->chip);
354 clk_unprepare(mdp->clk_mm);
355 clk_unprepare(mdp->clk_main);
356
357 return ret;
358}
359
360static const struct mtk_pwm_data mt2701_pwm_data = {
361 .enable_mask = BIT(16),
362 .con0 = 0xa8,
363 .con0_sel = 0x2,
364 .con1 = 0xac,
365 .has_commit = false,
366 .bls_debug = 0xb0,
367 .bls_debug_mask = 0x3,
368};
369
370static const struct mtk_pwm_data mt8173_pwm_data = {
371 .enable_mask = BIT(0),
372 .con0 = 0x10,
373 .con0_sel = 0x0,
374 .con1 = 0x14,
375 .has_commit = true,
376 .commit = 0x8,
377 .commit_mask = 0x1,
378};
379
380static const struct mtk_pwm_data mt6799_pwm_data = {
381 .enable_mask = BIT(0),
382 .con0 = 0x18,
383 .con0_sel = 0x0,
384 .con1 = 0x1C,
385 .has_commit = true,
386 .commit = 0xC,
387 .commit_mask = 0x1,
388};
389
390static const struct of_device_id mtk_disp_pwm_of_match[] = {
391 {.compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data},
392 {.compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
393 {.compatible = "mediatek,mt6779-disp-pwm", .data = &mt6799_pwm_data},
394 {.compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
395 {.compatible = "mediatek,mt6880-disp-pwm", .data = &mt6799_pwm_data},
396 {.compatible = "mediatek,mt6890-disp-pwm", .data = &mt6799_pwm_data},
397 {}
398};
399MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
400
401static struct platform_driver mtk_disp_pwm_driver = {
402 .driver = {
403 .name = "mediatek-disp-pwm",
404 .of_match_table = mtk_disp_pwm_of_match,
405 },
406 .probe = mtk_disp_pwm_probe,
407 .remove = mtk_disp_pwm_remove,
408};
409module_platform_driver(mtk_disp_pwm_driver);
410
411MODULE_AUTHOR("YH Huang <yh.huang@mediatek.com>");
412MODULE_DESCRIPTION("MediaTek SoC display PWM driver");
413MODULE_LICENSE("GPL v2");