brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · bafd6b6 Raw
318 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * MediaTek display pulse-width-modulation controller driver.4 * Copyright (c) 2015 MediaTek Inc.5 * Author: YH Huang <yh.huang@mediatek.com>6 */7 8#include <linux/bitfield.h>9#include <linux/clk.h>10#include <linux/err.h>11#include <linux/io.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/pwm.h>16#include <linux/slab.h>17 18#define DISP_PWM_EN		0x0019 20#define PWM_CLKDIV_SHIFT	1621#define PWM_CLKDIV_MAX		0x3ff22#define PWM_CLKDIV_MASK		(PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)23 24#define PWM_PERIOD_BIT_WIDTH	1225#define PWM_PERIOD_MASK		((1 << PWM_PERIOD_BIT_WIDTH) - 1)26 27#define PWM_HIGH_WIDTH_SHIFT	1628#define PWM_HIGH_WIDTH_MASK	(0x1fff << PWM_HIGH_WIDTH_SHIFT)29 30struct mtk_pwm_data {31	u32 enable_mask;32	unsigned int con0;33	u32 con0_sel;34	unsigned int con1;35 36	bool has_commit;37	unsigned int commit;38	unsigned int commit_mask;39 40	unsigned int bls_debug;41	u32 bls_debug_mask;42};43 44struct mtk_disp_pwm {45	const struct mtk_pwm_data *data;46	struct clk *clk_main;47	struct clk *clk_mm;48	void __iomem *base;49	bool enabled;50};51 52static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)53{54	return pwmchip_get_drvdata(chip);55}56 57static void mtk_disp_pwm_update_bits(struct mtk_disp_pwm *mdp, u32 offset,58				     u32 mask, u32 data)59{60	void __iomem *address = mdp->base + offset;61	u32 value;62 63	value = readl(address);64	value &= ~mask;65	value |= data;66	writel(value, address);67}68 69static int mtk_disp_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,70			      const struct pwm_state *state)71{72	struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);73	u32 clk_div, period, high_width, value;74	u64 div, rate;75	int err;76 77	if (state->polarity != PWM_POLARITY_NORMAL)78		return -EINVAL;79 80	if (!state->enabled && mdp->enabled) {81		mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,82					 mdp->data->enable_mask, 0x0);83		clk_disable_unprepare(mdp->clk_mm);84		clk_disable_unprepare(mdp->clk_main);85 86		mdp->enabled = false;87		return 0;88	}89 90	if (!mdp->enabled) {91		err = clk_prepare_enable(mdp->clk_main);92		if (err < 0) {93			dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_main: %pe\n",94				ERR_PTR(err));95			return err;96		}97 98		err = clk_prepare_enable(mdp->clk_mm);99		if (err < 0) {100			dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_mm: %pe\n",101				ERR_PTR(err));102			clk_disable_unprepare(mdp->clk_main);103			return err;104		}105	}106 107	/*108	 * Find period, high_width and clk_div to suit duty_ns and period_ns.109	 * Calculate proper div value to keep period value in the bound.110	 *111	 * period_ns = 10^9 * (clk_div + 1) * (period + 1) / PWM_CLK_RATE112	 * duty_ns = 10^9 * (clk_div + 1) * high_width / PWM_CLK_RATE113	 *114	 * period = (PWM_CLK_RATE * period_ns) / (10^9 * (clk_div + 1)) - 1115	 * high_width = (PWM_CLK_RATE * duty_ns) / (10^9 * (clk_div + 1))116	 */117	rate = clk_get_rate(mdp->clk_main);118	clk_div = mul_u64_u64_div_u64(state->period, rate, NSEC_PER_SEC) >>119			  PWM_PERIOD_BIT_WIDTH;120	if (clk_div > PWM_CLKDIV_MAX) {121		if (!mdp->enabled) {122			clk_disable_unprepare(mdp->clk_mm);123			clk_disable_unprepare(mdp->clk_main);124		}125		return -EINVAL;126	}127 128	div = NSEC_PER_SEC * (clk_div + 1);129	period = mul_u64_u64_div_u64(state->period, rate, div);130	if (period > 0)131		period--;132 133	high_width = mul_u64_u64_div_u64(state->duty_cycle, rate, div);134	value = period | (high_width << PWM_HIGH_WIDTH_SHIFT);135 136	if (mdp->data->bls_debug && !mdp->data->has_commit) {137		/*138		 * For MT2701, disable double buffer before writing register139		 * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.140		 */141		mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,142					 mdp->data->bls_debug_mask,143					 mdp->data->bls_debug_mask);144		mtk_disp_pwm_update_bits(mdp, mdp->data->con0,145					 mdp->data->con0_sel,146					 mdp->data->con0_sel);147	}148 149	mtk_disp_pwm_update_bits(mdp, mdp->data->con0,150				 PWM_CLKDIV_MASK,151				 clk_div << PWM_CLKDIV_SHIFT);152	mtk_disp_pwm_update_bits(mdp, mdp->data->con1,153				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,154				 value);155 156	if (mdp->data->has_commit) {157		mtk_disp_pwm_update_bits(mdp, mdp->data->commit,158					 mdp->data->commit_mask,159					 mdp->data->commit_mask);160		mtk_disp_pwm_update_bits(mdp, mdp->data->commit,161					 mdp->data->commit_mask,162					 0x0);163	}164 165	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,166				 mdp->data->enable_mask);167	mdp->enabled = true;168 169	return 0;170}171 172static int mtk_disp_pwm_get_state(struct pwm_chip *chip,173				  struct pwm_device *pwm,174				  struct pwm_state *state)175{176	struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);177	u64 rate, period, high_width;178	u32 clk_div, pwm_en, con0, con1;179	int err;180 181	err = clk_prepare_enable(mdp->clk_main);182	if (err < 0) {183		dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_main: %pe\n", ERR_PTR(err));184		return err;185	}186 187	err = clk_prepare_enable(mdp->clk_mm);188	if (err < 0) {189		dev_err(pwmchip_parent(chip), "Can't enable mdp->clk_mm: %pe\n", ERR_PTR(err));190		clk_disable_unprepare(mdp->clk_main);191		return err;192	}193 194	/*195	 * Apply DISP_PWM_DEBUG settings to choose whether to enable or disable196	 * registers double buffer and manual commit to working register before197	 * performing any read/write operation198	 */199	if (mdp->data->bls_debug)200		mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,201					 mdp->data->bls_debug_mask,202					 mdp->data->bls_debug_mask);203 204	rate = clk_get_rate(mdp->clk_main);205	con0 = readl(mdp->base + mdp->data->con0);206	con1 = readl(mdp->base + mdp->data->con1);207	pwm_en = readl(mdp->base + DISP_PWM_EN);208	state->enabled = !!(pwm_en & mdp->data->enable_mask);209	clk_div = FIELD_GET(PWM_CLKDIV_MASK, con0);210	period = FIELD_GET(PWM_PERIOD_MASK, con1);211	/*212	 * period has 12 bits, clk_div 11 and NSEC_PER_SEC has 30,213	 * so period * (clk_div + 1) * NSEC_PER_SEC doesn't overflow.214	 */215	state->period = DIV64_U64_ROUND_UP(period * (clk_div + 1) * NSEC_PER_SEC, rate);216	high_width = FIELD_GET(PWM_HIGH_WIDTH_MASK, con1);217	state->duty_cycle = DIV64_U64_ROUND_UP(high_width * (clk_div + 1) * NSEC_PER_SEC,218					       rate);219	state->polarity = PWM_POLARITY_NORMAL;220	clk_disable_unprepare(mdp->clk_mm);221	clk_disable_unprepare(mdp->clk_main);222 223	return 0;224}225 226static const struct pwm_ops mtk_disp_pwm_ops = {227	.apply = mtk_disp_pwm_apply,228	.get_state = mtk_disp_pwm_get_state,229};230 231static int mtk_disp_pwm_probe(struct platform_device *pdev)232{233	struct pwm_chip *chip;234	struct mtk_disp_pwm *mdp;235	int ret;236 237	chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*mdp));238	if (IS_ERR(chip))239		return PTR_ERR(chip);240	mdp = to_mtk_disp_pwm(chip);241 242	mdp->data = of_device_get_match_data(&pdev->dev);243 244	mdp->base = devm_platform_ioremap_resource(pdev, 0);245	if (IS_ERR(mdp->base))246		return PTR_ERR(mdp->base);247 248	mdp->clk_main = devm_clk_get(&pdev->dev, "main");249	if (IS_ERR(mdp->clk_main))250		return dev_err_probe(&pdev->dev, PTR_ERR(mdp->clk_main),251				     "Failed to get main clock\n");252 253	mdp->clk_mm = devm_clk_get(&pdev->dev, "mm");254	if (IS_ERR(mdp->clk_mm))255		return dev_err_probe(&pdev->dev, PTR_ERR(mdp->clk_mm),256				     "Failed to get mm clock\n");257 258	chip->ops = &mtk_disp_pwm_ops;259 260	ret = devm_pwmchip_add(&pdev->dev, chip);261	if (ret < 0)262		return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");263 264	return 0;265}266 267static const struct mtk_pwm_data mt2701_pwm_data = {268	.enable_mask = BIT(16),269	.con0 = 0xa8,270	.con0_sel = 0x2,271	.con1 = 0xac,272	.has_commit = false,273	.bls_debug = 0xb0,274	.bls_debug_mask = 0x3,275};276 277static const struct mtk_pwm_data mt8173_pwm_data = {278	.enable_mask = BIT(0),279	.con0 = 0x10,280	.con0_sel = 0x0,281	.con1 = 0x14,282	.has_commit = true,283	.commit = 0x8,284	.commit_mask = 0x1,285};286 287static const struct mtk_pwm_data mt8183_pwm_data = {288	.enable_mask = BIT(0),289	.con0 = 0x18,290	.con0_sel = 0x0,291	.con1 = 0x1c,292	.has_commit = false,293	.bls_debug = 0x80,294	.bls_debug_mask = 0x3,295};296 297static const struct of_device_id mtk_disp_pwm_of_match[] = {298	{ .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data},299	{ .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},300	{ .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},301	{ .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data},302	{ }303};304MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);305 306static struct platform_driver mtk_disp_pwm_driver = {307	.driver = {308		.name = "mediatek-disp-pwm",309		.of_match_table = mtk_disp_pwm_of_match,310	},311	.probe = mtk_disp_pwm_probe,312};313module_platform_driver(mtk_disp_pwm_driver);314 315MODULE_AUTHOR("YH Huang <yh.huang@mediatek.com>");316MODULE_DESCRIPTION("MediaTek SoC display PWM driver");317MODULE_LICENSE("GPL v2");318