diff options
Diffstat (limited to 'drivers/pwm/pwm-mtk-disp.c')
-rw-r--r-- | drivers/pwm/pwm-mtk-disp.c | 87 |
1 files changed, 72 insertions, 15 deletions
diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 0ad3385298c0..893940d45f0d 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c | |||
@@ -18,30 +18,40 @@ | |||
18 | #include <linux/io.h> | 18 | #include <linux/io.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_device.h> | ||
21 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
22 | #include <linux/pwm.h> | 23 | #include <linux/pwm.h> |
23 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
24 | 25 | ||
25 | #define DISP_PWM_EN 0x00 | 26 | #define DISP_PWM_EN 0x00 |
26 | #define PWM_ENABLE_MASK BIT(0) | ||
27 | 27 | ||
28 | #define DISP_PWM_COMMIT 0x08 | ||
29 | #define PWM_COMMIT_MASK BIT(0) | ||
30 | |||
31 | #define DISP_PWM_CON_0 0x10 | ||
32 | #define PWM_CLKDIV_SHIFT 16 | 28 | #define PWM_CLKDIV_SHIFT 16 |
33 | #define PWM_CLKDIV_MAX 0x3ff | 29 | #define PWM_CLKDIV_MAX 0x3ff |
34 | #define PWM_CLKDIV_MASK (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT) | 30 | #define PWM_CLKDIV_MASK (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT) |
35 | 31 | ||
36 | #define DISP_PWM_CON_1 0x14 | ||
37 | #define PWM_PERIOD_BIT_WIDTH 12 | 32 | #define PWM_PERIOD_BIT_WIDTH 12 |
38 | #define PWM_PERIOD_MASK ((1 << PWM_PERIOD_BIT_WIDTH) - 1) | 33 | #define PWM_PERIOD_MASK ((1 << PWM_PERIOD_BIT_WIDTH) - 1) |
39 | 34 | ||
40 | #define PWM_HIGH_WIDTH_SHIFT 16 | 35 | #define PWM_HIGH_WIDTH_SHIFT 16 |
41 | #define PWM_HIGH_WIDTH_MASK (0x1fff << PWM_HIGH_WIDTH_SHIFT) | 36 | #define PWM_HIGH_WIDTH_MASK (0x1fff << PWM_HIGH_WIDTH_SHIFT) |
42 | 37 | ||
38 | struct mtk_pwm_data { | ||
39 | u32 enable_mask; | ||
40 | unsigned int con0; | ||
41 | u32 con0_sel; | ||
42 | unsigned int con1; | ||
43 | |||
44 | bool has_commit; | ||
45 | unsigned int commit; | ||
46 | unsigned int commit_mask; | ||
47 | |||
48 | unsigned int bls_debug; | ||
49 | u32 bls_debug_mask; | ||
50 | }; | ||
51 | |||
43 | struct mtk_disp_pwm { | 52 | struct mtk_disp_pwm { |
44 | struct pwm_chip chip; | 53 | struct pwm_chip chip; |
54 | const struct mtk_pwm_data *data; | ||
45 | struct clk *clk_main; | 55 | struct clk *clk_main; |
46 | struct clk *clk_mm; | 56 | struct clk *clk_mm; |
47 | void __iomem *base; | 57 | void __iomem *base; |
@@ -106,12 +116,21 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
106 | return err; | 116 | return err; |
107 | } | 117 | } |
108 | 118 | ||
109 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK, | 119 | mtk_disp_pwm_update_bits(mdp, mdp->data->con0, |
120 | PWM_CLKDIV_MASK, | ||
110 | clk_div << PWM_CLKDIV_SHIFT); | 121 | clk_div << PWM_CLKDIV_SHIFT); |
111 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1, | 122 | mtk_disp_pwm_update_bits(mdp, mdp->data->con1, |
112 | PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value); | 123 | PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, |
113 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1); | 124 | value); |
114 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0); | 125 | |
126 | if (mdp->data->has_commit) { | ||
127 | mtk_disp_pwm_update_bits(mdp, mdp->data->commit, | ||
128 | mdp->data->commit_mask, | ||
129 | mdp->data->commit_mask); | ||
130 | mtk_disp_pwm_update_bits(mdp, mdp->data->commit, | ||
131 | mdp->data->commit_mask, | ||
132 | 0x0); | ||
133 | } | ||
115 | 134 | ||
116 | clk_disable(mdp->clk_mm); | 135 | clk_disable(mdp->clk_mm); |
117 | clk_disable(mdp->clk_main); | 136 | clk_disable(mdp->clk_main); |
@@ -134,7 +153,8 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
134 | return err; | 153 | return err; |
135 | } | 154 | } |
136 | 155 | ||
137 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1); | 156 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, |
157 | mdp->data->enable_mask); | ||
138 | 158 | ||
139 | return 0; | 159 | return 0; |
140 | } | 160 | } |
@@ -143,7 +163,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
143 | { | 163 | { |
144 | struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip); | 164 | struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip); |
145 | 165 | ||
146 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0); | 166 | mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, |
167 | 0x0); | ||
147 | 168 | ||
148 | clk_disable(mdp->clk_mm); | 169 | clk_disable(mdp->clk_mm); |
149 | clk_disable(mdp->clk_main); | 170 | clk_disable(mdp->clk_main); |
@@ -166,6 +187,8 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev) | |||
166 | if (!mdp) | 187 | if (!mdp) |
167 | return -ENOMEM; | 188 | return -ENOMEM; |
168 | 189 | ||
190 | mdp->data = of_device_get_match_data(&pdev->dev); | ||
191 | |||
169 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 192 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
170 | mdp->base = devm_ioremap_resource(&pdev->dev, r); | 193 | mdp->base = devm_ioremap_resource(&pdev->dev, r); |
171 | if (IS_ERR(mdp->base)) | 194 | if (IS_ERR(mdp->base)) |
@@ -200,6 +223,19 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev) | |||
200 | 223 | ||
201 | platform_set_drvdata(pdev, mdp); | 224 | platform_set_drvdata(pdev, mdp); |
202 | 225 | ||
226 | /* | ||
227 | * For MT2701, disable double buffer before writing register | ||
228 | * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH. | ||
229 | */ | ||
230 | if (!mdp->data->has_commit) { | ||
231 | mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug, | ||
232 | mdp->data->bls_debug_mask, | ||
233 | mdp->data->bls_debug_mask); | ||
234 | mtk_disp_pwm_update_bits(mdp, mdp->data->con0, | ||
235 | mdp->data->con0_sel, | ||
236 | mdp->data->con0_sel); | ||
237 | } | ||
238 | |||
203 | return 0; | 239 | return 0; |
204 | 240 | ||
205 | disable_clk_mm: | 241 | disable_clk_mm: |
@@ -221,9 +257,30 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev) | |||
221 | return ret; | 257 | return ret; |
222 | } | 258 | } |
223 | 259 | ||
260 | static const struct mtk_pwm_data mt2701_pwm_data = { | ||
261 | .enable_mask = BIT(16), | ||
262 | .con0 = 0xa8, | ||
263 | .con0_sel = 0x2, | ||
264 | .con1 = 0xac, | ||
265 | .has_commit = false, | ||
266 | .bls_debug = 0xb0, | ||
267 | .bls_debug_mask = 0x3, | ||
268 | }; | ||
269 | |||
270 | static const struct mtk_pwm_data mt8173_pwm_data = { | ||
271 | .enable_mask = BIT(0), | ||
272 | .con0 = 0x10, | ||
273 | .con0_sel = 0x0, | ||
274 | .con1 = 0x14, | ||
275 | .has_commit = true, | ||
276 | .commit = 0x8, | ||
277 | .commit_mask = 0x1, | ||
278 | }; | ||
279 | |||
224 | static const struct of_device_id mtk_disp_pwm_of_match[] = { | 280 | static const struct of_device_id mtk_disp_pwm_of_match[] = { |
225 | { .compatible = "mediatek,mt8173-disp-pwm" }, | 281 | { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data}, |
226 | { .compatible = "mediatek,mt6595-disp-pwm" }, | 282 | { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data}, |
283 | { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data}, | ||
227 | { } | 284 | { } |
228 | }; | 285 | }; |
229 | MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); | 286 | MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); |