xref: /openbmc/linux/drivers/gpu/drm/fsl-dcu/fsl_tcon.c (revision 2874c5fd)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2fb127b79SStefan Agner /*
3fb127b79SStefan Agner  * Copyright 2015 Toradex AG
4fb127b79SStefan Agner  *
5fb127b79SStefan Agner  * Stefan Agner <stefan@agner.ch>
6fb127b79SStefan Agner  *
7fb127b79SStefan Agner  * Freescale TCON device driver
8fb127b79SStefan Agner  */
9fb127b79SStefan Agner 
10fb127b79SStefan Agner #include <linux/clk.h>
11fb127b79SStefan Agner #include <linux/io.h>
12fb127b79SStefan Agner #include <linux/mm.h>
13fb127b79SStefan Agner #include <linux/of_address.h>
14fb127b79SStefan Agner #include <linux/platform_device.h>
15fb127b79SStefan Agner #include <linux/regmap.h>
16fb127b79SStefan Agner 
17fb127b79SStefan Agner #include "fsl_tcon.h"
18fb127b79SStefan Agner 
fsl_tcon_bypass_disable(struct fsl_tcon * tcon)19fb127b79SStefan Agner void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
20fb127b79SStefan Agner {
21fb127b79SStefan Agner 	regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
22fb127b79SStefan Agner 			   FSL_TCON_CTRL1_TCON_BYPASS, 0);
23fb127b79SStefan Agner }
24fb127b79SStefan Agner 
fsl_tcon_bypass_enable(struct fsl_tcon * tcon)25fb127b79SStefan Agner void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
26fb127b79SStefan Agner {
27fb127b79SStefan Agner 	regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
28fb127b79SStefan Agner 			   FSL_TCON_CTRL1_TCON_BYPASS,
29fb127b79SStefan Agner 			   FSL_TCON_CTRL1_TCON_BYPASS);
30fb127b79SStefan Agner }
31fb127b79SStefan Agner 
32fb127b79SStefan Agner static struct regmap_config fsl_tcon_regmap_config = {
33fb127b79SStefan Agner 	.reg_bits = 32,
34fb127b79SStefan Agner 	.reg_stride = 4,
35fb127b79SStefan Agner 	.val_bits = 32,
36fb127b79SStefan Agner 
37fb127b79SStefan Agner 	.name = "tcon",
38fb127b79SStefan Agner };
39fb127b79SStefan Agner 
fsl_tcon_init_regmap(struct device * dev,struct fsl_tcon * tcon,struct device_node * np)40fb127b79SStefan Agner static int fsl_tcon_init_regmap(struct device *dev,
41fb127b79SStefan Agner 				struct fsl_tcon *tcon,
42fb127b79SStefan Agner 				struct device_node *np)
43fb127b79SStefan Agner {
44fb127b79SStefan Agner 	struct resource res;
45fb127b79SStefan Agner 	void __iomem *regs;
46fb127b79SStefan Agner 
47fb127b79SStefan Agner 	if (of_address_to_resource(np, 0, &res))
48fb127b79SStefan Agner 		return -EINVAL;
49fb127b79SStefan Agner 
50fb127b79SStefan Agner 	regs = devm_ioremap_resource(dev, &res);
51fb127b79SStefan Agner 	if (IS_ERR(regs))
52fb127b79SStefan Agner 		return PTR_ERR(regs);
53fb127b79SStefan Agner 
54fb127b79SStefan Agner 	tcon->regs = devm_regmap_init_mmio(dev, regs,
55fb127b79SStefan Agner 					   &fsl_tcon_regmap_config);
56acd4d615SWei Yongjun 	return PTR_ERR_OR_ZERO(tcon->regs);
57fb127b79SStefan Agner }
58fb127b79SStefan Agner 
fsl_tcon_init(struct device * dev)59fb127b79SStefan Agner struct fsl_tcon *fsl_tcon_init(struct device *dev)
60fb127b79SStefan Agner {
61fb127b79SStefan Agner 	struct fsl_tcon *tcon;
62fb127b79SStefan Agner 	struct device_node *np;
63fb127b79SStefan Agner 	int ret;
64fb127b79SStefan Agner 
65fb127b79SStefan Agner 	/* TCON node is not mandatory, some devices do not provide TCON */
66fb127b79SStefan Agner 	np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
67fb127b79SStefan Agner 	if (!np)
68fb127b79SStefan Agner 		return NULL;
69fb127b79SStefan Agner 
70fb127b79SStefan Agner 	tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
715d2883d5SFabio Estevam 	if (!tcon)
72fb127b79SStefan Agner 		goto err_node_put;
73fb127b79SStefan Agner 
74fb127b79SStefan Agner 	ret = fsl_tcon_init_regmap(dev, tcon, np);
75fb127b79SStefan Agner 	if (ret) {
76fb127b79SStefan Agner 		dev_err(dev, "Couldn't create the TCON regmap\n");
77fb127b79SStefan Agner 		goto err_node_put;
78fb127b79SStefan Agner 	}
79fb127b79SStefan Agner 
80fb127b79SStefan Agner 	tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
81fb127b79SStefan Agner 	if (IS_ERR(tcon->ipg_clk)) {
82fb127b79SStefan Agner 		dev_err(dev, "Couldn't get the TCON bus clock\n");
83fb127b79SStefan Agner 		goto err_node_put;
84fb127b79SStefan Agner 	}
85fb127b79SStefan Agner 
86ef15d361SFabio Estevam 	ret = clk_prepare_enable(tcon->ipg_clk);
87ef15d361SFabio Estevam 	if (ret) {
88ef15d361SFabio Estevam 		dev_err(dev, "Couldn't enable the TCON clock\n");
89ef15d361SFabio Estevam 		goto err_node_put;
90ef15d361SFabio Estevam 	}
91fb127b79SStefan Agner 
92ef15d361SFabio Estevam 	of_node_put(np);
93fb127b79SStefan Agner 	dev_info(dev, "Using TCON in bypass mode\n");
94fb127b79SStefan Agner 
95fb127b79SStefan Agner 	return tcon;
96fb127b79SStefan Agner 
97fb127b79SStefan Agner err_node_put:
98fb127b79SStefan Agner 	of_node_put(np);
99fb127b79SStefan Agner 	return NULL;
100fb127b79SStefan Agner }
101fb127b79SStefan Agner 
fsl_tcon_free(struct fsl_tcon * tcon)102fb127b79SStefan Agner void fsl_tcon_free(struct fsl_tcon *tcon)
103fb127b79SStefan Agner {
104fb127b79SStefan Agner 	clk_disable_unprepare(tcon->ipg_clk);
105fb127b79SStefan Agner 	clk_put(tcon->ipg_clk);
106fb127b79SStefan Agner }
107fb127b79SStefan Agner 
108