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
19struct attached_list {
20 struct videocodec *codec;
21 struct attached_list *next;
22};
23
24struct codec_list {
25 const struct videocodec *codec;
26 int attached;
27 struct attached_list *list;
28 struct codec_list *next;
29};
30
31static struct codec_list *codeclist_top;
32
33/* ================================================= */
34/* function prototypes of the master/slave interface */
35/* ================================================= */
36
37struct videocodec *videocodec_attach(struct videocodec_master *master)
38{
39 struct codec_list *h = codeclist_top;
40 struct zoran *zr;
41 struct attached_list *a, *ptr;
42 struct videocodec *codec;
43 int res;
44
45 if (!master) {
46 pr_err("%s: no data\n", __func__);
47 return NULL;
48 }
49
50 zr = videocodec_master_to_zoran(master);
51
52 zrdev_dbg(zr, "%s: '%s', flags %lx, magic %lx\n", __func__,
53 master->name, master->flags, master->magic);
54
55 if (!h) {
56 zrdev_err(zr, "%s: no device available\n", __func__);
57 return NULL;
58 }
59
60 while (h) {
61 // attach only if the slave has at least the flags
62 // expected by the master
63 if ((master->flags & h->codec->flags) == master->flags) {
64 zrdev_dbg(zr, "%s: try '%s'\n", __func__, h->codec->name);
65
66 codec = kmemdup(p: h->codec, size: sizeof(struct videocodec), GFP_KERNEL);
67 if (!codec)
68 goto out_kfree;
69
70 res = strlen(codec->name);
71 snprintf(buf: codec->name + res, size: sizeof(codec->name) - res, fmt: "[%d]", h->attached);
72 codec->master_data = master;
73 res = codec->setup(codec);
74 if (res == 0) {
75 zrdev_dbg(zr, "%s: '%s'\n", __func__, codec->name);
76 ptr = kzalloc(size: sizeof(*ptr), GFP_KERNEL);
77 if (!ptr)
78 goto out_kfree;
79 ptr->codec = codec;
80
81 a = h->list;
82 if (!a) {
83 h->list = ptr;
84 zrdev_dbg(zr, "videocodec: first element\n");
85 } else {
86 while (a->next)
87 a = a->next; // find end
88 a->next = ptr;
89 zrdev_dbg(zr, "videocodec: in after '%s'\n",
90 h->codec->name);
91 }
92
93 h->attached += 1;
94 return codec;
95 }
96 kfree(objp: codec);
97 }
98 h = h->next;
99 }
100
101 zrdev_err(zr, "%s: no codec found!\n", __func__);
102 return NULL;
103
104 out_kfree:
105 kfree(objp: codec);
106 return NULL;
107}
108
109int videocodec_detach(struct videocodec *codec)
110{
111 struct codec_list *h = codeclist_top;
112 struct zoran *zr;
113 struct attached_list *a, *prev;
114 int res;
115
116 if (!codec) {
117 pr_err("%s: no data\n", __func__);
118 return -EINVAL;
119 }
120
121 zr = videocodec_to_zoran(codec);
122
123 zrdev_dbg(zr, "%s: '%s', type: %x, flags %lx, magic %lx\n", __func__,
124 codec->name, codec->type, codec->flags, codec->magic);
125
126 if (!h) {
127 zrdev_err(zr, "%s: no device left...\n", __func__);
128 return -ENXIO;
129 }
130
131 while (h) {
132 a = h->list;
133 prev = NULL;
134 while (a) {
135 if (codec == a->codec) {
136 res = a->codec->unset(a->codec);
137 if (res >= 0) {
138 zrdev_dbg(zr, "%s: '%s'\n", __func__,
139 a->codec->name);
140 a->codec->master_data = NULL;
141 } else {
142 zrdev_err(zr, "%s: '%s'\n", __func__, a->codec->name);
143 a->codec->master_data = NULL;
144 }
145 if (!prev) {
146 h->list = a->next;
147 zrdev_dbg(zr, "videocodec: delete first\n");
148 } else {
149 prev->next = a->next;
150 zrdev_dbg(zr, "videocodec: delete middle\n");
151 }
152 kfree(objp: a->codec);
153 kfree(objp: a);
154 h->attached -= 1;
155 return 0;
156 }
157 prev = a;
158 a = a->next;
159 }
160 h = h->next;
161 }
162
163 zrdev_err(zr, "%s: given codec not found!\n", __func__);
164 return -EINVAL;
165}
166
167int videocodec_register(const struct videocodec *codec)
168{
169 struct codec_list *ptr, *h = codeclist_top;
170 struct zoran *zr;
171
172 if (!codec) {
173 pr_err("%s: no data!\n", __func__);
174 return -EINVAL;
175 }
176
177 zr = videocodec_to_zoran(codec: (struct videocodec *)codec);
178
179 zrdev_dbg(zr,
180 "videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
181 codec->name, codec->type, codec->flags, codec->magic);
182
183 ptr = kzalloc(size: sizeof(*ptr), GFP_KERNEL);
184 if (!ptr)
185 return -ENOMEM;
186 ptr->codec = codec;
187
188 if (!h) {
189 codeclist_top = ptr;
190 zrdev_dbg(zr, "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 zrdev_dbg(zr, "videocodec: hooked in after '%s'\n",
196 h->codec->name);
197 }
198
199 return 0;
200}
201
202int videocodec_unregister(const struct videocodec *codec)
203{
204 struct codec_list *prev = NULL, *h = codeclist_top;
205 struct zoran *zr;
206
207 if (!codec) {
208 pr_err("%s: no data!\n", __func__);
209 return -EINVAL;
210 }
211
212 zr = videocodec_to_zoran(codec: (struct videocodec *)codec);
213
214 zrdev_dbg(zr,
215 "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
216 codec->name, codec->type, codec->flags, codec->magic);
217
218 if (!h) {
219 zrdev_err(zr, "%s: no device left...\n", __func__);
220 return -ENXIO;
221 }
222
223 while (h) {
224 if (codec == h->codec) {
225 if (h->attached) {
226 zrdev_err(zr, "videocodec: '%s' is used\n",
227 h->codec->name);
228 return -EBUSY;
229 }
230 zrdev_dbg(zr, "videocodec: unregister '%s' is ok.\n",
231 h->codec->name);
232 if (!prev) {
233 codeclist_top = h->next;
234 zrdev_dbg(zr,
235 "videocodec: delete first element\n");
236 } else {
237 prev->next = h->next;
238 zrdev_dbg(zr,
239 "videocodec: delete middle element\n");
240 }
241 kfree(objp: h);
242 return 0;
243 }
244 prev = h;
245 h = h->next;
246 }
247
248 zrdev_err(zr, "%s: given codec not found!\n", __func__);
249 return -EINVAL;
250}
251
252int videocodec_debugfs_show(struct seq_file *m)
253{
254 struct codec_list *h = codeclist_top;
255 struct attached_list *a;
256
257 seq_puts(m, s: "<S>lave or attached <M>aster name type flags magic ");
258 seq_puts(m, s: "(connected as)\n");
259
260 while (h) {
261 seq_printf(m, fmt: "S %32s %04x %08lx %08lx (TEMPLATE)\n",
262 h->codec->name, h->codec->type,
263 h->codec->flags, h->codec->magic);
264 a = h->list;
265 while (a) {
266 seq_printf(m, fmt: "M %32s %04x %08lx %08lx (%s)\n",
267 a->codec->master_data->name,
268 a->codec->master_data->type,
269 a->codec->master_data->flags,
270 a->codec->master_data->magic,
271 a->codec->name);
272 a = a->next;
273 }
274 h = h->next;
275 }
276
277 return 0;
278}
279

source code of linux/drivers/media/pci/zoran/videocodec.c