1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Zoran ZR36016 basic configuration functions |
4 | * |
5 | * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/init.h> |
10 | #include <linux/slab.h> |
11 | |
12 | /* headerfile of this module */ |
13 | #include "zr36016.h" |
14 | |
15 | /* codec io API */ |
16 | #include "videocodec.h" |
17 | |
18 | /* |
19 | * it doesn't make sense to have more than 20 or so, |
20 | * just to prevent some unwanted loops |
21 | */ |
22 | #define MAX_CODECS 20 |
23 | |
24 | /* amount of chips attached via this driver */ |
25 | static int zr36016_codecs; |
26 | |
27 | /* |
28 | * Local hardware I/O functions: read/write via codec layer |
29 | * (registers are located in the master device) |
30 | */ |
31 | |
32 | /* read and write functions */ |
33 | static u8 zr36016_read(struct zr36016 *ptr, u16 reg) |
34 | { |
35 | u8 value = 0; |
36 | struct zoran *zr = videocodec_to_zoran(codec: ptr->codec); |
37 | |
38 | /* just in case something is wrong... */ |
39 | if (ptr->codec->master_data->readreg) |
40 | value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; |
41 | else |
42 | zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n" , ptr->name); |
43 | |
44 | zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n" , ptr->name, reg, value); |
45 | |
46 | return value; |
47 | } |
48 | |
49 | static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value) |
50 | { |
51 | struct zoran *zr = videocodec_to_zoran(codec: ptr->codec); |
52 | |
53 | zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n" , ptr->name, value, reg); |
54 | |
55 | // just in case something is wrong... |
56 | if (ptr->codec->master_data->writereg) |
57 | ptr->codec->master_data->writereg(ptr->codec, reg, value); |
58 | else |
59 | zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n" , ptr->name); |
60 | } |
61 | |
62 | /* |
63 | * indirect read and write functions |
64 | * |
65 | * the 016 supports auto-addr-increment, but |
66 | * writing it all time cost not much and is safer... |
67 | */ |
68 | static u8 zr36016_readi(struct zr36016 *ptr, u16 reg) |
69 | { |
70 | u8 value = 0; |
71 | struct zoran *zr = videocodec_to_zoran(codec: ptr->codec); |
72 | |
73 | /* just in case something is wrong... */ |
74 | if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) { |
75 | ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); |
76 | value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; |
77 | } else { |
78 | zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n" , ptr->name); |
79 | } |
80 | |
81 | zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n" , |
82 | ptr->name, reg, value); |
83 | return value; |
84 | } |
85 | |
86 | static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value) |
87 | { |
88 | struct zoran *zr = videocodec_to_zoran(codec: ptr->codec); |
89 | |
90 | zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n" , ptr->name, |
91 | value, reg); |
92 | |
93 | /* just in case something is wrong... */ |
94 | if (ptr->codec->master_data->writereg) { |
95 | ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); |
96 | ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); |
97 | } else { |
98 | zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n" , ptr->name); |
99 | } |
100 | } |
101 | |
102 | /* Local helper function: version read */ |
103 | |
104 | /* version kept in datastructure */ |
105 | static u8 zr36016_read_version(struct zr36016 *ptr) |
106 | { |
107 | ptr->version = zr36016_read(ptr, reg: 0) >> 4; |
108 | return ptr->version; |
109 | } |
110 | |
111 | /* |
112 | * Local helper function: basic test of "connectivity", writes/reads |
113 | * to/from PAX-Lo register |
114 | */ |
115 | |
116 | static int zr36016_basic_test(struct zr36016 *ptr) |
117 | { |
118 | struct zoran *zr = videocodec_to_zoran(codec: ptr->codec); |
119 | |
120 | if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) { |
121 | int i; |
122 | |
123 | zr36016_writei(ptr, ZR016I_PAX_LO, value: 0x55); |
124 | zrdev_dbg(zr, "%s: registers: " , ptr->name); |
125 | for (i = 0; i <= 0x0b; i++) |
126 | zrdev_dbg(zr, "%02x " , zr36016_readi(ptr, i)); |
127 | zrdev_dbg(zr, "\n" ); |
128 | } |
129 | // for testing just write 0, then the default value to a register and read |
130 | // it back in both cases |
131 | zr36016_writei(ptr, ZR016I_PAX_LO, value: 0x00); |
132 | if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { |
133 | zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n" , ptr->name); |
134 | return -ENXIO; |
135 | } |
136 | zr36016_writei(ptr, ZR016I_PAX_LO, value: 0x0d0); |
137 | if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { |
138 | zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n" , ptr->name); |
139 | return -ENXIO; |
140 | } |
141 | // we allow version numbers from 0-3, should be enough, though |
142 | zr36016_read_version(ptr); |
143 | if (ptr->version & 0x0c) { |
144 | zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n" , ptr->name, |
145 | ptr->version); |
146 | return -ENXIO; |
147 | } |
148 | |
149 | return 0; /* looks good! */ |
150 | } |
151 | |
152 | /* Basic datasets & init */ |
153 | |
154 | static void zr36016_init(struct zr36016 *ptr) |
155 | { |
156 | // stop any processing |
157 | zr36016_write(ptr, ZR016_GOSTOP, value: 0); |
158 | |
159 | // mode setup (yuv422 in and out, compression/expansuon due to mode) |
160 | zr36016_write(ptr, ZR016_MODE, |
161 | ZR016_YUV422 | ZR016_YUV422_YUV422 | |
162 | (ptr->mode == CODEC_DO_COMPRESSION ? |
163 | ZR016_COMPRESSION : ZR016_EXPANSION)); |
164 | |
165 | // misc setup |
166 | zr36016_writei(ptr, ZR016I_SETUP1, |
167 | value: (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | |
168 | (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); |
169 | zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); |
170 | |
171 | // Window setup |
172 | // (no extra offset for now, norm defines offset, default width height) |
173 | zr36016_writei(ptr, ZR016I_PAX_HI, value: ptr->width >> 8); |
174 | zr36016_writei(ptr, ZR016I_PAX_LO, value: ptr->width & 0xFF); |
175 | zr36016_writei(ptr, ZR016I_PAY_HI, value: ptr->height >> 8); |
176 | zr36016_writei(ptr, ZR016I_PAY_LO, value: ptr->height & 0xFF); |
177 | zr36016_writei(ptr, ZR016I_NAX_HI, value: ptr->xoff >> 8); |
178 | zr36016_writei(ptr, ZR016I_NAX_LO, value: ptr->xoff & 0xFF); |
179 | zr36016_writei(ptr, ZR016I_NAY_HI, value: ptr->yoff >> 8); |
180 | zr36016_writei(ptr, ZR016I_NAY_LO, value: ptr->yoff & 0xFF); |
181 | |
182 | /* shall we continue now, please? */ |
183 | zr36016_write(ptr, ZR016_GOSTOP, value: 1); |
184 | } |
185 | |
186 | /* |
187 | * CODEC API FUNCTIONS |
188 | * |
189 | * These functions are accessed by the master via the API structure |
190 | */ |
191 | |
192 | /* |
193 | * set compression/expansion mode and launches codec - |
194 | * this should be the last call from the master before starting processing |
195 | */ |
196 | static int zr36016_set_mode(struct videocodec *codec, int mode) |
197 | { |
198 | struct zr36016 *ptr = (struct zr36016 *)codec->data; |
199 | struct zoran *zr = videocodec_to_zoran(codec); |
200 | |
201 | zrdev_dbg(zr, "%s: set_mode %d call\n" , ptr->name, mode); |
202 | |
203 | if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) |
204 | return -EINVAL; |
205 | |
206 | ptr->mode = mode; |
207 | zr36016_init(ptr); |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | /* set picture size */ |
213 | static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm, |
214 | struct vfe_settings *cap, struct vfe_polarity *pol) |
215 | { |
216 | struct zr36016 *ptr = (struct zr36016 *)codec->data; |
217 | struct zoran *zr = videocodec_to_zoran(codec); |
218 | |
219 | zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n" , |
220 | ptr->name, norm->h_start, norm->v_start, |
221 | cap->x, cap->y, cap->width, cap->height, |
222 | cap->decimation); |
223 | |
224 | /* |
225 | * if () return -EINVAL; |
226 | * trust the master driver that it knows what it does - so |
227 | * we allow invalid startx/y for now ... |
228 | */ |
229 | ptr->width = cap->width; |
230 | ptr->height = cap->height; |
231 | /* |
232 | * (Ronald) This is ugly. zoran_device.c, line 387 |
233 | * already mentions what happens if h_start is even |
234 | * (blue faces, etc., cr/cb inversed). There's probably |
235 | * some good reason why h_start is 0 instead of 1, so I'm |
236 | * leaving it to this for now, but really... This can be |
237 | * done a lot simpler |
238 | */ |
239 | ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x; |
240 | /* |
241 | * Something to note here (I don't understand it), setting |
242 | * v_start too high will cause the codec to 'not work'. I |
243 | * really don't get it. values of 16 (v_start) already break |
244 | * it here. Just '0' seems to work. More testing needed! |
245 | */ |
246 | ptr->yoff = norm->v_start + cap->y; |
247 | /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ |
248 | ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; |
249 | ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | /* additional control functions */ |
255 | static int zr36016_control(struct videocodec *codec, int type, int size, void *data) |
256 | { |
257 | struct zr36016 *ptr = (struct zr36016 *)codec->data; |
258 | struct zoran *zr = videocodec_to_zoran(codec); |
259 | int *ival = (int *)data; |
260 | |
261 | zrdev_dbg(zr, "%s: control %d call with %d byte\n" , |
262 | ptr->name, type, size); |
263 | |
264 | switch (type) { |
265 | case CODEC_G_STATUS: /* get last status - we don't know it ... */ |
266 | if (size != sizeof(int)) |
267 | return -EFAULT; |
268 | *ival = 0; |
269 | break; |
270 | |
271 | case CODEC_G_CODEC_MODE: |
272 | if (size != sizeof(int)) |
273 | return -EFAULT; |
274 | *ival = 0; |
275 | break; |
276 | |
277 | case CODEC_S_CODEC_MODE: |
278 | if (size != sizeof(int)) |
279 | return -EFAULT; |
280 | if (*ival != 0) |
281 | return -EINVAL; |
282 | /* not needed, do nothing */ |
283 | return 0; |
284 | |
285 | case CODEC_G_VFE: |
286 | case CODEC_S_VFE: |
287 | return 0; |
288 | |
289 | case CODEC_S_MMAP: |
290 | /* not available, give an error */ |
291 | return -ENXIO; |
292 | |
293 | default: |
294 | return -EINVAL; |
295 | } |
296 | |
297 | return size; |
298 | } |
299 | |
300 | /* |
301 | * Exit and unregister function: |
302 | * |
303 | * Deinitializes Zoran's JPEG processor |
304 | */ |
305 | |
306 | static int zr36016_unset(struct videocodec *codec) |
307 | { |
308 | struct zr36016 *ptr = codec->data; |
309 | struct zoran *zr = videocodec_to_zoran(codec); |
310 | |
311 | if (ptr) { |
312 | /* do wee need some codec deinit here, too ???? */ |
313 | |
314 | zrdev_dbg(zr, "%s: finished codec #%d\n" , ptr->name, ptr->num); |
315 | kfree(objp: ptr); |
316 | codec->data = NULL; |
317 | |
318 | zr36016_codecs--; |
319 | return 0; |
320 | } |
321 | |
322 | return -EFAULT; |
323 | } |
324 | |
325 | /* |
326 | * Setup and registry function: |
327 | * |
328 | * Initializes Zoran's JPEG processor |
329 | * |
330 | * Also sets pixel size, average code size, mode (compr./decompr.) |
331 | * (the given size is determined by the processor with the video interface) |
332 | */ |
333 | |
334 | static int zr36016_setup(struct videocodec *codec) |
335 | { |
336 | struct zr36016 *ptr; |
337 | struct zoran *zr = videocodec_to_zoran(codec); |
338 | int res; |
339 | |
340 | zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n" , zr36016_codecs); |
341 | |
342 | if (zr36016_codecs == MAX_CODECS) { |
343 | zrdev_err(zr, "zr36016: Can't attach more codecs!\n" ); |
344 | return -ENOSPC; |
345 | } |
346 | //mem structure init |
347 | ptr = kzalloc(size: sizeof(*ptr), GFP_KERNEL); |
348 | codec->data = ptr; |
349 | if (!ptr) |
350 | return -ENOMEM; |
351 | |
352 | snprintf(buf: ptr->name, size: sizeof(ptr->name), fmt: "zr36016[%d]" , zr36016_codecs); |
353 | ptr->num = zr36016_codecs++; |
354 | ptr->codec = codec; |
355 | |
356 | //testing |
357 | res = zr36016_basic_test(ptr); |
358 | if (res < 0) { |
359 | zr36016_unset(codec); |
360 | return res; |
361 | } |
362 | //final setup |
363 | ptr->mode = CODEC_DO_COMPRESSION; |
364 | ptr->width = 768; |
365 | ptr->height = 288; |
366 | ptr->xdec = 1; |
367 | ptr->ydec = 0; |
368 | zr36016_init(ptr); |
369 | |
370 | zrdev_dbg(zr, "%s: codec v%d attached and running\n" , |
371 | ptr->name, ptr->version); |
372 | |
373 | return 0; |
374 | } |
375 | |
376 | static const struct videocodec zr36016_codec = { |
377 | .name = "zr36016" , |
378 | .magic = 0L, /* magic not used */ |
379 | .flags = |
380 | CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | |
381 | CODEC_FLAG_DECODER, |
382 | .type = CODEC_TYPE_ZR36016, |
383 | .setup = zr36016_setup, /* functionality */ |
384 | .unset = zr36016_unset, |
385 | .set_mode = zr36016_set_mode, |
386 | .set_video = zr36016_set_video, |
387 | .control = zr36016_control, |
388 | /* others are not used */ |
389 | }; |
390 | |
391 | /* HOOK IN DRIVER AS KERNEL MODULE */ |
392 | |
393 | int zr36016_init_module(void) |
394 | { |
395 | zr36016_codecs = 0; |
396 | return videocodec_register(codec: &zr36016_codec); |
397 | } |
398 | |
399 | void zr36016_cleanup_module(void) |
400 | { |
401 | if (zr36016_codecs) { |
402 | pr_debug("zr36016: something's wrong - %d codecs left somehow.\n" , |
403 | zr36016_codecs); |
404 | } |
405 | videocodec_unregister(codec: &zr36016_codec); |
406 | } |
407 | |