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/list.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mod_devicetable.h> |
15 | #include <linux/mutex.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/slab.h> |
19 | |
20 | #include <media/rcar-fcp.h> |
21 | |
22 | struct rcar_fcp_device { |
23 | struct list_head list; |
24 | struct device *dev; |
25 | }; |
26 | |
27 | static LIST_HEAD(fcp_devices); |
28 | static DEFINE_MUTEX(fcp_lock); |
29 | |
30 | /* ----------------------------------------------------------------------------- |
31 | * Public API |
32 | */ |
33 | |
34 | /** |
35 | * rcar_fcp_get - Find and acquire a reference to an FCP instance |
36 | * @np: Device node of the FCP instance |
37 | * |
38 | * Search the list of registered FCP instances for the instance corresponding to |
39 | * the given device node. |
40 | * |
41 | * Return a pointer to the FCP instance, or an ERR_PTR if the instance can't be |
42 | * found. |
43 | */ |
44 | struct rcar_fcp_device *rcar_fcp_get(const struct device_node *np) |
45 | { |
46 | struct rcar_fcp_device *fcp; |
47 | |
48 | mutex_lock(&fcp_lock); |
49 | |
50 | list_for_each_entry(fcp, &fcp_devices, list) { |
51 | if (fcp->dev->of_node != np) |
52 | continue; |
53 | |
54 | get_device(dev: fcp->dev); |
55 | goto done; |
56 | } |
57 | |
58 | fcp = ERR_PTR(error: -EPROBE_DEFER); |
59 | |
60 | done: |
61 | mutex_unlock(lock: &fcp_lock); |
62 | return fcp; |
63 | } |
64 | EXPORT_SYMBOL_GPL(rcar_fcp_get); |
65 | |
66 | /** |
67 | * rcar_fcp_put - Release a reference to an FCP instance |
68 | * @fcp: The FCP instance |
69 | * |
70 | * Release the FCP instance acquired by a call to rcar_fcp_get(). |
71 | */ |
72 | void rcar_fcp_put(struct rcar_fcp_device *fcp) |
73 | { |
74 | if (fcp) |
75 | put_device(dev: fcp->dev); |
76 | } |
77 | EXPORT_SYMBOL_GPL(rcar_fcp_put); |
78 | |
79 | struct device *rcar_fcp_get_device(struct rcar_fcp_device *fcp) |
80 | { |
81 | return fcp->dev; |
82 | } |
83 | EXPORT_SYMBOL_GPL(rcar_fcp_get_device); |
84 | |
85 | /** |
86 | * rcar_fcp_enable - Enable an FCP |
87 | * @fcp: The FCP instance |
88 | * |
89 | * Before any memory access through an FCP is performed by a module, the FCP |
90 | * must be enabled by a call to this function. The enable calls are reference |
91 | * counted, each successful call must be followed by one rcar_fcp_disable() |
92 | * call when no more memory transfer can occur through the FCP. |
93 | * |
94 | * Return 0 on success or a negative error code if an error occurs. The enable |
95 | * reference count isn't increased when this function returns an error. |
96 | */ |
97 | int rcar_fcp_enable(struct rcar_fcp_device *fcp) |
98 | { |
99 | if (!fcp) |
100 | return 0; |
101 | |
102 | return pm_runtime_resume_and_get(dev: fcp->dev); |
103 | } |
104 | EXPORT_SYMBOL_GPL(rcar_fcp_enable); |
105 | |
106 | /** |
107 | * rcar_fcp_disable - Disable an FCP |
108 | * @fcp: The FCP instance |
109 | * |
110 | * This function is the counterpart of rcar_fcp_enable(). As enable calls are |
111 | * reference counted a disable call may not disable the FCP synchronously. |
112 | */ |
113 | void rcar_fcp_disable(struct rcar_fcp_device *fcp) |
114 | { |
115 | if (fcp) |
116 | pm_runtime_put(dev: fcp->dev); |
117 | } |
118 | EXPORT_SYMBOL_GPL(rcar_fcp_disable); |
119 | |
120 | /* ----------------------------------------------------------------------------- |
121 | * Platform Driver |
122 | */ |
123 | |
124 | static int rcar_fcp_probe(struct platform_device *pdev) |
125 | { |
126 | struct rcar_fcp_device *fcp; |
127 | |
128 | fcp = devm_kzalloc(dev: &pdev->dev, size: sizeof(*fcp), GFP_KERNEL); |
129 | if (fcp == NULL) |
130 | return -ENOMEM; |
131 | |
132 | fcp->dev = &pdev->dev; |
133 | |
134 | dma_set_max_seg_size(dev: fcp->dev, UINT_MAX); |
135 | |
136 | pm_runtime_enable(dev: &pdev->dev); |
137 | |
138 | mutex_lock(&fcp_lock); |
139 | list_add_tail(new: &fcp->list, head: &fcp_devices); |
140 | mutex_unlock(lock: &fcp_lock); |
141 | |
142 | platform_set_drvdata(pdev, data: fcp); |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | static void rcar_fcp_remove(struct platform_device *pdev) |
148 | { |
149 | struct rcar_fcp_device *fcp = platform_get_drvdata(pdev); |
150 | |
151 | mutex_lock(&fcp_lock); |
152 | list_del(entry: &fcp->list); |
153 | mutex_unlock(lock: &fcp_lock); |
154 | |
155 | pm_runtime_disable(dev: &pdev->dev); |
156 | } |
157 | |
158 | static const struct of_device_id rcar_fcp_of_match[] = { |
159 | { .compatible = "renesas,fcpf" }, |
160 | { .compatible = "renesas,fcpv" }, |
161 | { }, |
162 | }; |
163 | MODULE_DEVICE_TABLE(of, rcar_fcp_of_match); |
164 | |
165 | static struct platform_driver rcar_fcp_platform_driver = { |
166 | .probe = rcar_fcp_probe, |
167 | .remove_new = rcar_fcp_remove, |
168 | .driver = { |
169 | .name = "rcar-fcp" , |
170 | .of_match_table = rcar_fcp_of_match, |
171 | .suppress_bind_attrs = true, |
172 | }, |
173 | }; |
174 | |
175 | module_platform_driver(rcar_fcp_platform_driver); |
176 | |
177 | MODULE_ALIAS("rcar-fcp" ); |
178 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>" ); |
179 | MODULE_DESCRIPTION("Renesas FCP Driver" ); |
180 | MODULE_LICENSE("GPL" ); |
181 | |