GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / staging / media / zoran / videocodec.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * VIDEO MOTION CODECs internal API for video devices
4  *
5  * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
6  * bound to a master device.
7  *
8  * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/types.h>
15 #include <linux/slab.h>
16
17 #include "videocodec.h"
18
19 static int videocodec_debug;
20 module_param(videocodec_debug, int, 0);
21 MODULE_PARM_DESC(videocodec_debug, "Debug level (0-4)");
22
23 #define dprintk(num, format, args...) \
24         do { \
25                 if (videocodec_debug >= num) \
26                         printk(format, ##args); \
27         } while (0)
28
29 struct attached_list {
30         struct videocodec *codec;
31         struct attached_list *next;
32 };
33
34 struct codec_list {
35         const struct videocodec *codec;
36         int attached;
37         struct attached_list *list;
38         struct codec_list *next;
39 };
40
41 static struct codec_list *codeclist_top;
42
43 /* ================================================= */
44 /* function prototypes of the master/slave interface */
45 /* ================================================= */
46
47 struct videocodec *videocodec_attach(struct videocodec_master *master)
48 {
49         struct codec_list *h = codeclist_top;
50         struct attached_list *a, *ptr;
51         struct videocodec *codec;
52         int res;
53
54         if (!master) {
55                 pr_err("%s: no data\n", __func__);
56                 return NULL;
57         }
58
59         dprintk(2, "%s: '%s', flags %lx, magic %lx\n", __func__,
60                 master->name, master->flags, master->magic);
61
62         if (!h) {
63                 pr_err("%s: no device available\n", __func__);
64                 return NULL;
65         }
66
67         while (h) {
68                 // attach only if the slave has at least the flags
69                 // expected by the master
70                 if ((master->flags & h->codec->flags) == master->flags) {
71                         dprintk(4, "%s: try '%s'\n", __func__, h->codec->name);
72
73                         codec = kmemdup(h->codec, sizeof(struct videocodec), GFP_KERNEL);
74                         if (!codec)
75                                 goto out_kfree;
76
77                         res = strlen(codec->name);
78                         snprintf(codec->name + res, sizeof(codec->name) - res, "[%d]", h->attached);
79                         codec->master_data = master;
80                         res = codec->setup(codec);
81                         if (res == 0) {
82                                 dprintk(3, "%s: '%s'\n", __func__, codec->name);
83                                 ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
84                                 if (!ptr)
85                                         goto out_kfree;
86                                 ptr->codec = codec;
87
88                                 a = h->list;
89                                 if (!a) {
90                                         h->list = ptr;
91                                         dprintk(4, "videocodec: first element\n");
92                                 } else {
93                                         while (a->next)
94                                                 a = a->next;    // find end
95                                         a->next = ptr;
96                                         dprintk(4, "videocodec: in after '%s'\n", h->codec->name);
97                                 }
98
99                                 h->attached += 1;
100                                 return codec;
101                         } else {
102                                 kfree(codec);
103                         }
104                 }
105                 h = h->next;
106         }
107
108         pr_err("%s: no codec found!\n", __func__);
109         return NULL;
110
111  out_kfree:
112         kfree(codec);
113         return NULL;
114 }
115
116 int videocodec_detach(struct videocodec *codec)
117 {
118         struct codec_list *h = codeclist_top;
119         struct attached_list *a, *prev;
120         int res;
121
122         if (!codec) {
123                 pr_err("%s: no data\n", __func__);
124                 return -EINVAL;
125         }
126
127         dprintk(2, "%s: '%s', type: %x, flags %lx, magic %lx\n", __func__,
128                 codec->name, codec->type, codec->flags, codec->magic);
129
130         if (!h) {
131                 pr_err("%s: no device left...\n", __func__);
132                 return -ENXIO;
133         }
134
135         while (h) {
136                 a = h->list;
137                 prev = NULL;
138                 while (a) {
139                         if (codec == a->codec) {
140                                 res = a->codec->unset(a->codec);
141                                 if (res >= 0) {
142                                         dprintk(3, "%s: '%s'\n", __func__, a->codec->name);
143                                         a->codec->master_data = NULL;
144                                 } else {
145                                         pr_err("%s: '%s'\n", __func__, a->codec->name);
146                                         a->codec->master_data = NULL;
147                                 }
148                                 if (!prev) {
149                                         h->list = a->next;
150                                         dprintk(4, "videocodec: delete first\n");
151                                 } else {
152                                         prev->next = a->next;
153                                         dprintk(4, "videocodec: delete middle\n");
154                                 }
155                                 kfree(a->codec);
156                                 kfree(a);
157                                 h->attached -= 1;
158                                 return 0;
159                         }
160                         prev = a;
161                         a = a->next;
162                 }
163                 h = h->next;
164         }
165
166         pr_err("%s: given codec not found!\n", __func__);
167         return -EINVAL;
168 }
169
170 int videocodec_register(const struct videocodec *codec)
171 {
172         struct codec_list *ptr, *h = codeclist_top;
173
174         if (!codec) {
175                 pr_err("%s: no data!\n", __func__);
176                 return -EINVAL;
177         }
178
179         dprintk(2,
180                 "videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
181                 codec->name, codec->type, codec->flags, codec->magic);
182
183         ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
184         if (!ptr)
185                 return -ENOMEM;
186         ptr->codec = codec;
187
188         if (!h) {
189                 codeclist_top = ptr;
190                 dprintk(4, "videocodec: hooked in as first element\n");
191         } else {
192                 while (h->next)
193                         h = h->next;    // find the end
194                 h->next = ptr;
195                 dprintk(4, "videocodec: hooked in after '%s'\n",
196                         h->codec->name);
197         }
198
199         return 0;
200 }
201
202 int videocodec_unregister(const struct videocodec *codec)
203 {
204         struct codec_list *prev = NULL, *h = codeclist_top;
205
206         if (!codec) {
207                 pr_err("%s: no data!\n", __func__);
208                 return -EINVAL;
209         }
210
211         dprintk(2,
212                 "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
213                 codec->name, codec->type, codec->flags, codec->magic);
214
215         if (!h) {
216                 pr_err("%s: no device left...\n", __func__);
217                 return -ENXIO;
218         }
219
220         while (h) {
221                 if (codec == h->codec) {
222                         if (h->attached) {
223                                 pr_err("videocodec: '%s' is used\n", h->codec->name);
224                                 return -EBUSY;
225                         }
226                         dprintk(3, "videocodec: unregister '%s' is ok.\n",
227                                 h->codec->name);
228                         if (!prev) {
229                                 codeclist_top = h->next;
230                                 dprintk(4,
231                                         "videocodec: delete first element\n");
232                         } else {
233                                 prev->next = h->next;
234                                 dprintk(4,
235                                         "videocodec: delete middle element\n");
236                         }
237                         kfree(h);
238                         return 0;
239                 }
240                 prev = h;
241                 h = h->next;
242         }
243
244         pr_err("%s: given codec not found!\n", __func__);
245         return -EINVAL;
246 }
247
248 int videocodec_debugfs_show(struct seq_file *m)
249 {
250         struct codec_list *h = codeclist_top;
251         struct attached_list *a;
252
253         seq_printf(m, "<S>lave or attached <M>aster name  type flags    magic    ");
254         seq_printf(m, "(connected as)\n");
255
256         while (h) {
257                 seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n",
258                            h->codec->name, h->codec->type,
259                               h->codec->flags, h->codec->magic);
260                 a = h->list;
261                 while (a) {
262                         seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n",
263                                    a->codec->master_data->name,
264                                       a->codec->master_data->type,
265                                       a->codec->master_data->flags,
266                                       a->codec->master_data->magic,
267                                       a->codec->name);
268                         a = a->next;
269                 }
270                 h = h->next;
271         }
272
273         return 0;
274 }