1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2020-2021 NXP |
4 | */ |
5 | |
6 | #include <linux/init.h> |
7 | #include <linux/interconnect.h> |
8 | #include <linux/ioctl.h> |
9 | #include <linux/list.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/dma-map-ops.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_platform.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/types.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/videodev2.h> |
20 | #include <linux/of_reserved_mem.h> |
21 | #include <media/v4l2-device.h> |
22 | #include <media/videobuf2-v4l2.h> |
23 | #include <media/v4l2-mem2mem.h> |
24 | #include <media/v4l2-ioctl.h> |
25 | #include <linux/debugfs.h> |
26 | #include "vpu.h" |
27 | #include "vpu_imx8q.h" |
28 | |
29 | bool debug; |
30 | module_param(debug, bool, 0644); |
31 | |
32 | void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val) |
33 | { |
34 | writel(val, addr: vpu->base + reg); |
35 | } |
36 | |
37 | u32 vpu_readl(struct vpu_dev *vpu, u32 reg) |
38 | { |
39 | return readl(addr: vpu->base + reg); |
40 | } |
41 | |
42 | static void vpu_dev_get(struct vpu_dev *vpu) |
43 | { |
44 | if (atomic_inc_return(v: &vpu->ref_vpu) == 1 && vpu->res->setup) |
45 | vpu->res->setup(vpu); |
46 | } |
47 | |
48 | static void vpu_dev_put(struct vpu_dev *vpu) |
49 | { |
50 | atomic_dec(v: &vpu->ref_vpu); |
51 | } |
52 | |
53 | static void vpu_enc_get(struct vpu_dev *vpu) |
54 | { |
55 | if (atomic_inc_return(v: &vpu->ref_enc) == 1 && vpu->res->setup_encoder) |
56 | vpu->res->setup_encoder(vpu); |
57 | } |
58 | |
59 | static void vpu_enc_put(struct vpu_dev *vpu) |
60 | { |
61 | atomic_dec(v: &vpu->ref_enc); |
62 | } |
63 | |
64 | static void vpu_dec_get(struct vpu_dev *vpu) |
65 | { |
66 | if (atomic_inc_return(v: &vpu->ref_dec) == 1 && vpu->res->setup_decoder) |
67 | vpu->res->setup_decoder(vpu); |
68 | } |
69 | |
70 | static void vpu_dec_put(struct vpu_dev *vpu) |
71 | { |
72 | atomic_dec(v: &vpu->ref_dec); |
73 | } |
74 | |
75 | static int vpu_init_media_device(struct vpu_dev *vpu) |
76 | { |
77 | vpu->mdev.dev = vpu->dev; |
78 | strscpy(vpu->mdev.model, "amphion-vpu" , sizeof(vpu->mdev.model)); |
79 | strscpy(vpu->mdev.bus_info, "platform: amphion-vpu" , sizeof(vpu->mdev.bus_info)); |
80 | media_device_init(mdev: &vpu->mdev); |
81 | vpu->v4l2_dev.mdev = &vpu->mdev; |
82 | |
83 | return 0; |
84 | } |
85 | |
86 | static int vpu_probe(struct platform_device *pdev) |
87 | { |
88 | struct device *dev = &pdev->dev; |
89 | struct vpu_dev *vpu; |
90 | int ret; |
91 | |
92 | dev_dbg(dev, "probe\n" ); |
93 | vpu = devm_kzalloc(dev, size: sizeof(*vpu), GFP_KERNEL); |
94 | if (!vpu) |
95 | return -ENOMEM; |
96 | |
97 | vpu->pdev = pdev; |
98 | vpu->dev = dev; |
99 | mutex_init(&vpu->lock); |
100 | INIT_LIST_HEAD(list: &vpu->cores); |
101 | platform_set_drvdata(pdev, data: vpu); |
102 | atomic_set(v: &vpu->ref_vpu, i: 0); |
103 | atomic_set(v: &vpu->ref_enc, i: 0); |
104 | atomic_set(v: &vpu->ref_dec, i: 0); |
105 | vpu->get_vpu = vpu_dev_get; |
106 | vpu->put_vpu = vpu_dev_put; |
107 | vpu->get_enc = vpu_enc_get; |
108 | vpu->put_enc = vpu_enc_put; |
109 | vpu->get_dec = vpu_dec_get; |
110 | vpu->put_dec = vpu_dec_put; |
111 | |
112 | vpu->base = devm_platform_ioremap_resource(pdev, index: 0); |
113 | if (IS_ERR(ptr: vpu->base)) |
114 | return PTR_ERR(ptr: vpu->base); |
115 | |
116 | vpu->res = of_device_get_match_data(dev); |
117 | if (!vpu->res) |
118 | return -ENODEV; |
119 | |
120 | pm_runtime_enable(dev); |
121 | |
122 | ret = v4l2_device_register(dev, v4l2_dev: &vpu->v4l2_dev); |
123 | if (ret) |
124 | goto err_vpu_deinit; |
125 | |
126 | vpu_init_media_device(vpu); |
127 | vpu->encoder.type = VPU_CORE_TYPE_ENC; |
128 | vpu->encoder.function = MEDIA_ENT_F_PROC_VIDEO_ENCODER; |
129 | vpu->decoder.type = VPU_CORE_TYPE_DEC; |
130 | vpu->decoder.function = MEDIA_ENT_F_PROC_VIDEO_DECODER; |
131 | ret = vpu_add_func(vpu, func: &vpu->decoder); |
132 | if (ret) |
133 | goto err_add_decoder; |
134 | ret = vpu_add_func(vpu, func: &vpu->encoder); |
135 | if (ret) |
136 | goto err_add_encoder; |
137 | ret = media_device_register(&vpu->mdev); |
138 | if (ret) |
139 | goto err_vpu_media; |
140 | vpu->debugfs = debugfs_create_dir(name: "amphion_vpu" , NULL); |
141 | |
142 | of_platform_populate(root: dev->of_node, NULL, NULL, parent: dev); |
143 | |
144 | return 0; |
145 | |
146 | err_vpu_media: |
147 | vpu_remove_func(func: &vpu->encoder); |
148 | err_add_encoder: |
149 | vpu_remove_func(func: &vpu->decoder); |
150 | err_add_decoder: |
151 | media_device_cleanup(mdev: &vpu->mdev); |
152 | v4l2_device_unregister(v4l2_dev: &vpu->v4l2_dev); |
153 | err_vpu_deinit: |
154 | pm_runtime_set_suspended(dev); |
155 | pm_runtime_disable(dev); |
156 | |
157 | return ret; |
158 | } |
159 | |
160 | static void vpu_remove(struct platform_device *pdev) |
161 | { |
162 | struct vpu_dev *vpu = platform_get_drvdata(pdev); |
163 | struct device *dev = &pdev->dev; |
164 | |
165 | debugfs_remove_recursive(dentry: vpu->debugfs); |
166 | vpu->debugfs = NULL; |
167 | |
168 | pm_runtime_disable(dev); |
169 | |
170 | media_device_unregister(mdev: &vpu->mdev); |
171 | vpu_remove_func(func: &vpu->decoder); |
172 | vpu_remove_func(func: &vpu->encoder); |
173 | media_device_cleanup(mdev: &vpu->mdev); |
174 | v4l2_device_unregister(v4l2_dev: &vpu->v4l2_dev); |
175 | mutex_destroy(lock: &vpu->lock); |
176 | } |
177 | |
178 | static int __maybe_unused vpu_runtime_resume(struct device *dev) |
179 | { |
180 | return 0; |
181 | } |
182 | |
183 | static int __maybe_unused vpu_runtime_suspend(struct device *dev) |
184 | { |
185 | return 0; |
186 | } |
187 | |
188 | static int __maybe_unused vpu_resume(struct device *dev) |
189 | { |
190 | return 0; |
191 | } |
192 | |
193 | static int __maybe_unused vpu_suspend(struct device *dev) |
194 | { |
195 | return 0; |
196 | } |
197 | |
198 | static const struct dev_pm_ops vpu_pm_ops = { |
199 | SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL) |
200 | SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume) |
201 | }; |
202 | |
203 | static struct vpu_resources imx8qxp_res = { |
204 | .plat_type = IMX8QXP, |
205 | .mreg_base = 0x40000000, |
206 | .setup = vpu_imx8q_setup, |
207 | .setup_encoder = vpu_imx8q_setup_enc, |
208 | .setup_decoder = vpu_imx8q_setup_dec, |
209 | .reset = vpu_imx8q_reset |
210 | }; |
211 | |
212 | static struct vpu_resources imx8qm_res = { |
213 | .plat_type = IMX8QM, |
214 | .mreg_base = 0x40000000, |
215 | .setup = vpu_imx8q_setup, |
216 | .setup_encoder = vpu_imx8q_setup_enc, |
217 | .setup_decoder = vpu_imx8q_setup_dec, |
218 | .reset = vpu_imx8q_reset |
219 | }; |
220 | |
221 | static const struct of_device_id vpu_dt_match[] = { |
222 | { .compatible = "nxp,imx8qxp-vpu" , .data = &imx8qxp_res }, |
223 | { .compatible = "nxp,imx8qm-vpu" , .data = &imx8qm_res }, |
224 | {} |
225 | }; |
226 | MODULE_DEVICE_TABLE(of, vpu_dt_match); |
227 | |
228 | static struct platform_driver amphion_vpu_driver = { |
229 | .probe = vpu_probe, |
230 | .remove_new = vpu_remove, |
231 | .driver = { |
232 | .name = "amphion-vpu" , |
233 | .of_match_table = vpu_dt_match, |
234 | .pm = &vpu_pm_ops, |
235 | }, |
236 | }; |
237 | |
238 | static int __init vpu_driver_init(void) |
239 | { |
240 | int ret; |
241 | |
242 | ret = platform_driver_register(&hion_vpu_driver); |
243 | if (ret) |
244 | return ret; |
245 | |
246 | ret = vpu_core_driver_init(); |
247 | if (ret) |
248 | platform_driver_unregister(&hion_vpu_driver); |
249 | |
250 | return ret; |
251 | } |
252 | |
253 | static void __exit vpu_driver_exit(void) |
254 | { |
255 | vpu_core_driver_exit(); |
256 | platform_driver_unregister(&hion_vpu_driver); |
257 | } |
258 | module_init(vpu_driver_init); |
259 | module_exit(vpu_driver_exit); |
260 | |
261 | MODULE_AUTHOR("Freescale Semiconductor, Inc." ); |
262 | MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX8Q" ); |
263 | MODULE_LICENSE("GPL v2" ); |
264 | |