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