xref: /linux/drivers/media/platform/renesas/rcar-fcp.c (revision d2c9a99135da931377240942d44f3dea104cedb8)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * rcar-fcp.c  --  R-Car Frame Compression Processor Driver
4  *
5  * Copyright (C) 2016 Renesas Electronics Corporation
6  *
7  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8  */
9 
10 #include <linux/device.h>
11 #include <linux/dma-mapping.h>
12 #include <linux/io.h>
13 #include <linux/iopoll.h>
14 #include <linux/list.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/platform_device.h>
18 #include <linux/pm_runtime.h>
19 #include <linux/slab.h>
20 
21 #include <media/rcar-fcp.h>
22 
23 #define RCAR_FCP_REG_RST		0x0010
24 #define RCAR_FCP_REG_RST_SOFTRST	BIT(0)
25 #define RCAR_FCP_REG_STA		0x0018
26 #define RCAR_FCP_REG_STA_ACT		BIT(0)
27 
28 struct rcar_fcp_device {
29 	struct list_head list;
30 	struct device *dev;
31 	void __iomem *base;
32 };
33 
34 static LIST_HEAD(fcp_devices);
35 static DEFINE_MUTEX(fcp_lock);
36 
rcar_fcp_write(struct rcar_fcp_device * fcp,u32 reg,u32 val)37 static inline void rcar_fcp_write(struct rcar_fcp_device *fcp, u32 reg, u32 val)
38 {
39 	iowrite32(val, fcp->base + reg);
40 }
41 
42 /* -----------------------------------------------------------------------------
43  * Public API
44  */
45 
46 /**
47  * rcar_fcp_get - Find and acquire a reference to an FCP instance
48  * @np: Device node of the FCP instance
49  *
50  * Search the list of registered FCP instances for the instance corresponding to
51  * the given device node.
52  *
53  * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be
54  * found.
55  */
rcar_fcp_get(const struct device_node * np)56 struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np)
57 {
58 	struct rcar_fcp_device *fcp;
59 
60 	mutex_lock(&fcp_lock);
61 
62 	list_for_each_entry(fcp, &fcp_devices, list) {
63 		if (fcp->dev->of_node != np)
64 			continue;
65 
66 		get_device(fcp->dev);
67 		goto done;
68 	}
69 
70 	fcp = ERR_PTR(-EPROBE_DEFER);
71 
72 done:
73 	mutex_unlock(&fcp_lock);
74 	return fcp;
75 }
76 EXPORT_SYMBOL_GPL(rcar_fcp_get);
77 
78 /**
79  * rcar_fcp_put - Release a reference to an FCP instance
80  * @fcp: The FCP instance
81  *
82  * Release the FCP instance acquired by a call to rcar_fcp_get().
83  */
rcar_fcp_put(struct rcar_fcp_device * fcp)84 void rcar_fcp_put(struct rcar_fcp_device *fcp)
85 {
86 	if (fcp)
87 		put_device(fcp->dev);
88 }
89 EXPORT_SYMBOL_GPL(rcar_fcp_put);
90 
rcar_fcp_get_device(struct rcar_fcp_device * fcp)91 struct device *rcar_fcp_get_device(struct rcar_fcp_device *fcp)
92 {
93 	return fcp->dev;
94 }
95 EXPORT_SYMBOL_GPL(rcar_fcp_get_device);
96 
97 /**
98  * rcar_fcp_enable - Enable an FCP
99  * @fcp: The FCP instance
100  *
101  * Before any memory access through an FCP is performed by a module, the FCP
102  * must be enabled by a call to this function. The enable calls are reference
103  * counted, each successful call must be followed by one rcar_fcp_disable()
104  * call when no more memory transfer can occur through the FCP.
105  *
106  * Return 0 on success or a negative error code if an error occurs. The enable
107  * reference count isn't increased when this function returns an error.
108  */
rcar_fcp_enable(struct rcar_fcp_device * fcp)109 int rcar_fcp_enable(struct rcar_fcp_device *fcp)
110 {
111 	if (!fcp)
112 		return 0;
113 
114 	return pm_runtime_resume_and_get(fcp->dev);
115 }
116 EXPORT_SYMBOL_GPL(rcar_fcp_enable);
117 
118 /**
119  * rcar_fcp_disable - Disable an FCP
120  * @fcp: The FCP instance
121  *
122  * This function is the counterpart of rcar_fcp_enable(). As enable calls are
123  * reference counted a disable call may not disable the FCP synchronously.
124  */
rcar_fcp_disable(struct rcar_fcp_device * fcp)125 void rcar_fcp_disable(struct rcar_fcp_device *fcp)
126 {
127 	if (fcp)
128 		pm_runtime_put(fcp->dev);
129 }
130 EXPORT_SYMBOL_GPL(rcar_fcp_disable);
131 
rcar_fcp_soft_reset(struct rcar_fcp_device * fcp)132 int rcar_fcp_soft_reset(struct rcar_fcp_device *fcp)
133 {
134 	u32 value;
135 	int ret;
136 
137 	if (!fcp)
138 		return 0;
139 
140 	rcar_fcp_write(fcp, RCAR_FCP_REG_RST, RCAR_FCP_REG_RST_SOFTRST);
141 	ret = readl_poll_timeout(fcp->base + RCAR_FCP_REG_STA,
142 				 value, !(value & RCAR_FCP_REG_STA_ACT),
143 				 1, 100);
144 	if (ret)
145 		dev_err(fcp->dev, "Failed to soft-reset\n");
146 
147 	return ret;
148 }
149 EXPORT_SYMBOL_GPL(rcar_fcp_soft_reset);
150 
151 /* -----------------------------------------------------------------------------
152  * Platform Driver
153  */
154 
rcar_fcp_probe(struct platform_device * pdev)155 static int rcar_fcp_probe(struct platform_device *pdev)
156 {
157 	struct rcar_fcp_device *fcp;
158 
159 	fcp = devm_kzalloc(&pdev->dev, sizeof(*fcp), GFP_KERNEL);
160 	if (fcp == NULL)
161 		return -ENOMEM;
162 
163 	fcp->dev = &pdev->dev;
164 
165 	fcp->base = devm_platform_ioremap_resource(pdev, 0);
166 	if (IS_ERR(fcp->base))
167 		return PTR_ERR(fcp->base);
168 
169 	dma_set_max_seg_size(fcp->dev, UINT_MAX);
170 
171 	pm_runtime_enable(&pdev->dev);
172 
173 	mutex_lock(&fcp_lock);
174 	list_add_tail(&fcp->list, &fcp_devices);
175 	mutex_unlock(&fcp_lock);
176 
177 	platform_set_drvdata(pdev, fcp);
178 
179 	return 0;
180 }
181 
rcar_fcp_remove(struct platform_device * pdev)182 static void rcar_fcp_remove(struct platform_device *pdev)
183 {
184 	struct rcar_fcp_device *fcp = platform_get_drvdata(pdev);
185 
186 	mutex_lock(&fcp_lock);
187 	list_del(&fcp->list);
188 	mutex_unlock(&fcp_lock);
189 
190 	pm_runtime_disable(&pdev->dev);
191 }
192 
193 static const struct of_device_id rcar_fcp_of_match[] = {
194 	{ .compatible = "renesas,fcpf" },
195 	{ .compatible = "renesas,fcpv" },
196 	{ },
197 };
198 MODULE_DEVICE_TABLE(of, rcar_fcp_of_match);
199 
200 static struct platform_driver rcar_fcp_platform_driver = {
201 	.probe		= rcar_fcp_probe,
202 	.remove		= rcar_fcp_remove,
203 	.driver		= {
204 		.name	= "rcar-fcp",
205 		.of_match_table = rcar_fcp_of_match,
206 		.suppress_bind_attrs = true,
207 	},
208 };
209 
210 module_platform_driver(rcar_fcp_platform_driver);
211 
212 MODULE_ALIAS("rcar-fcp");
213 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
214 MODULE_DESCRIPTION("Renesas FCP Driver");
215 MODULE_LICENSE("GPL");
216