1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * av7110_hw.c: av7110 low level hardware access and firmware interface
4 *
5 * Copyright (C) 1999-2002 Ralph Metzler
6 * & Marcus Metzler for convergence integrated media GmbH
7 *
8 * originally based on code by:
9 * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
10 *
11 * the project's page is at https://linuxtv.org
12 */
13
14/* for debugging ARM communication: */
15//#define COM_DEBUG
16
17#include <linux/types.h>
18#include <linux/kernel.h>
19#include <linux/string.h>
20#include <linux/delay.h>
21#include <linux/fs.h>
22
23#include "av7110.h"
24#include "av7110_hw.h"
25
26#define _NOHANDSHAKE
27
28/*
29 * Max transfer size done by av7110_fw_cmd()
30 *
31 * The maximum size passed to this function is 6 bytes. The buffer also
32 * uses two additional ones for type and size. So, 8 bytes is enough.
33 */
34#define MAX_XFER_SIZE 8
35
36/****************************************************************************
37 * DEBI functions
38 ****************************************************************************/
39
40/* This DEBI code is based on the Stradis driver
41 by Nathan Laredo <laredo@gnu.org> */
42
43int av7110_debiwrite(struct av7110 *av7110, u32 config,
44 int addr, u32 val, unsigned int count)
45{
46 struct saa7146_dev *dev = av7110->dev;
47
48 if (count > 32764) {
49 printk("%s: invalid count %d\n", __func__, count);
50 return -1;
51 }
52 if (saa7146_wait_for_debi_done(dev: av7110->dev, nobusyloop: 0) < 0) {
53 printk("%s: wait_for_debi_done failed\n", __func__);
54 return -1;
55 }
56 saa7146_write(dev, DEBI_CONFIG, config);
57 if (count <= 4) /* immediate transfer */
58 saa7146_write(dev, DEBI_AD, val);
59 else /* block transfer */
60 saa7146_write(dev, DEBI_AD, av7110->debi_bus);
61 saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
62 saa7146_write(dev, MC2, (2 << 16) | 2);
63 return 0;
64}
65
66u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count)
67{
68 struct saa7146_dev *dev = av7110->dev;
69 u32 result = 0;
70
71 if (count > 32764) {
72 printk("%s: invalid count %d\n", __func__, count);
73 return 0;
74 }
75 if (saa7146_wait_for_debi_done(dev: av7110->dev, nobusyloop: 0) < 0) {
76 printk("%s: wait_for_debi_done #1 failed\n", __func__);
77 return 0;
78 }
79 saa7146_write(dev, DEBI_AD, av7110->debi_bus);
80 saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
81
82 saa7146_write(dev, DEBI_CONFIG, config);
83 saa7146_write(dev, MC2, (2 << 16) | 2);
84 if (count > 4)
85 return count;
86 if (saa7146_wait_for_debi_done(dev: av7110->dev, nobusyloop: 0) < 0) {
87 printk("%s: wait_for_debi_done #2 failed\n", __func__);
88 return 0;
89 }
90
91 result = saa7146_read(dev, DEBI_AD);
92 result &= (0xffffffffUL >> ((4 - count) * 8));
93 return result;
94}
95
96
97
98/* av7110 ARM core boot stuff */
99#if 0
100void av7110_reset_arm(struct av7110 *av7110)
101{
102 saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
103
104 /* Disable DEBI and GPIO irq */
105 SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
106 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
107
108 saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
109 msleep(30); /* the firmware needs some time to initialize */
110
111 ARM_ResetMailBox(av7110);
112
113 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
114 SAA7146_IER_ENABLE(av7110->dev, MASK_03);
115
116 av7110->arm_ready = 1;
117 dprintk(1, "reset ARM\n");
118}
119#endif /* 0 */
120
121static int waitdebi(struct av7110 *av7110, int adr, int state)
122{
123 int k;
124
125 dprintk(4, "%p\n", av7110);
126
127 for (k = 0; k < 100; k++) {
128 if (irdebi(av7110, DEBINOSWAP, addr: adr, val: 0, count: 2) == state)
129 return 0;
130 udelay(5);
131 }
132 return -ETIMEDOUT;
133}
134
135static int load_dram(struct av7110 *av7110, u32 *data, int len)
136{
137 int i;
138 int blocks, rest;
139 u32 base, bootblock = AV7110_BOOT_BLOCK;
140
141 dprintk(4, "%p\n", av7110);
142
143 blocks = len / AV7110_BOOT_MAX_SIZE;
144 rest = len % AV7110_BOOT_MAX_SIZE;
145 base = DRAM_START_CODE;
146
147 for (i = 0; i < blocks; i++) {
148 if (waitdebi(av7110, AV7110_BOOT_STATE, state: BOOTSTATE_BUFFER_EMPTY) < 0) {
149 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);
150 return -ETIMEDOUT;
151 }
152 dprintk(4, "writing DRAM block %d\n", i);
153 mwdebi(av7110, DEBISWAB, addr: bootblock,
154 val: ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE);
155 bootblock ^= 0x1400;
156 iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), count: 4);
157 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, count: 2);
158 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, val: BOOTSTATE_BUFFER_FULL, count: 2);
159 base += AV7110_BOOT_MAX_SIZE;
160 }
161
162 if (rest > 0) {
163 if (waitdebi(av7110, AV7110_BOOT_STATE, state: BOOTSTATE_BUFFER_EMPTY) < 0) {
164 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");
165 return -ETIMEDOUT;
166 }
167 if (rest > 4)
168 mwdebi(av7110, DEBISWAB, addr: bootblock,
169 val: ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, count: rest);
170 else
171 mwdebi(av7110, DEBISWAB, addr: bootblock,
172 val: ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, count: rest + 4);
173
174 iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), count: 4);
175 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, val: rest, count: 2);
176 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, val: BOOTSTATE_BUFFER_FULL, count: 2);
177 }
178 if (waitdebi(av7110, AV7110_BOOT_STATE, state: BOOTSTATE_BUFFER_EMPTY) < 0) {
179 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");
180 return -ETIMEDOUT;
181 }
182 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, val: 0, count: 2);
183 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, val: BOOTSTATE_BUFFER_FULL, count: 2);
184 if (waitdebi(av7110, AV7110_BOOT_STATE, state: BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) {
185 printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");
186 return -ETIMEDOUT;
187 }
188 return 0;
189}
190
191
192/* we cannot write av7110 DRAM directly, so load a bootloader into
193 * the DPRAM which implements a simple boot protocol */
194int av7110_bootarm(struct av7110 *av7110)
195{
196 const struct firmware *fw;
197 const char *fw_name = "av7110/bootcode.bin";
198 struct saa7146_dev *dev = av7110->dev;
199 u32 ret;
200 int i;
201
202 dprintk(4, "%p\n", av7110);
203
204 av7110->arm_ready = 0;
205
206 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
207
208 /* Disable DEBI and GPIO irq */
209 SAA7146_IER_DISABLE(x: av7110->dev, MASK_03 | MASK_19);
210 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
211
212 /* enable DEBI */
213 saa7146_write(av7110->dev, MC1, 0x08800880);
214 saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
215 saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
216
217 /* test DEBI */
218 iwdebi(av7110, DEBISWAP, DPRAM_BASE, val: 0x76543210, count: 4);
219 /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */
220 iwdebi(av7110, DEBISWAP, DPRAM_BASE, val: 0x76543210, count: 4);
221
222 if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, val: 0, count: 4)) != 0x10325476) {
223 printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
224 ret, 0x10325476);
225 return -1;
226 }
227 for (i = 0; i < 8192; i += 4)
228 iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, val: 0x00, count: 4);
229 dprintk(2, "debi test OK\n");
230
231 /* boot */
232 dprintk(1, "load boot code\n");
233 saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
234 //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
235 //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
236
237 ret = request_firmware(fw: &fw, name: fw_name, device: &dev->pci->dev);
238 if (ret) {
239 printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n",
240 fw_name);
241 return ret;
242 }
243
244 mwdebi(av7110, DEBISWAB, DPRAM_BASE, val: fw->data, count: fw->size);
245 release_firmware(fw);
246 iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, val: BOOTSTATE_BUFFER_FULL, count: 2);
247
248 if (saa7146_wait_for_debi_done(dev: av7110->dev, nobusyloop: 1)) {
249 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out\n");
250 return -ETIMEDOUT;
251 }
252 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
253 mdelay(1);
254
255 dprintk(1, "load dram code\n");
256 if (load_dram(av7110, data: (u32 *)av7110->bin_root, len: av7110->size_root) < 0) {
257 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): load_dram() failed\n");
258 return -1;
259 }
260
261 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
262 mdelay(1);
263
264 dprintk(1, "load dpram code\n");
265 mwdebi(av7110, DEBISWAB, DPRAM_BASE, val: av7110->bin_dpram, count: av7110->size_dpram);
266
267 if (saa7146_wait_for_debi_done(dev: av7110->dev, nobusyloop: 1)) {
268 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out after loading DRAM\n");
269 return -ETIMEDOUT;
270 }
271 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
272 msleep(msecs: 30); /* the firmware needs some time to initialize */
273
274 //ARM_ClearIrq(av7110);
275 ARM_ResetMailBox(av7110);
276 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
277 SAA7146_IER_ENABLE(x: av7110->dev, MASK_03);
278
279 av7110->arm_errors = 0;
280 av7110->arm_ready = 1;
281 return 0;
282}
283MODULE_FIRMWARE("av7110/bootcode.bin");
284
285/****************************************************************************
286 * DEBI command polling
287 ****************************************************************************/
288
289int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
290{
291 unsigned long start;
292 u32 stat;
293 int err;
294
295 if (FW_VERSION(av7110->arm_app) <= 0x261c) {
296 /* not supported by old firmware */
297 msleep(msecs: 50);
298 return 0;
299 }
300
301 /* new firmware */
302 start = jiffies;
303 for (;;) {
304 err = time_after(jiffies, start + ARM_WAIT_FREE);
305 if (mutex_lock_interruptible(&av7110->dcomlock))
306 return -ERESTARTSYS;
307 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, val: 0, count: 2);
308 mutex_unlock(lock: &av7110->dcomlock);
309 if ((stat & flags) == 0)
310 break;
311 if (err) {
312 printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
313 __func__, stat & flags);
314 return -ETIMEDOUT;
315 }
316 msleep(msecs: 1);
317 }
318 return 0;
319}
320
321static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
322{
323 int i;
324 unsigned long start;
325 char *type = NULL;
326 u16 flags[2] = {0, 0};
327 u32 stat;
328 int err;
329
330// dprintk(4, "%p\n", av7110);
331
332 if (!av7110->arm_ready) {
333 dprintk(1, "arm not ready.\n");
334 return -ENXIO;
335 }
336
337 start = jiffies;
338 while (1) {
339 err = time_after(jiffies, start + ARM_WAIT_FREE);
340 if (rdebi(av7110, DEBINOSWAP, COMMAND, val: 0, count: 2) == 0)
341 break;
342 if (err) {
343 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__);
344 av7110->arm_errors++;
345 return -ETIMEDOUT;
346 }
347 msleep(msecs: 1);
348 }
349
350 if (FW_VERSION(av7110->arm_app) <= 0x261f)
351 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, val: 0xffff, count: 2);
352
353#ifndef _NOHANDSHAKE
354 start = jiffies;
355 while (1) {
356 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
357 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
358 break;
359 if (err) {
360 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__);
361 return -ETIMEDOUT;
362 }
363 msleep(1);
364 }
365#endif
366
367 switch ((buf[0] >> 8) & 0xff) {
368 case COMTYPE_PIDFILTER:
369 case COMTYPE_ENCODER:
370 case COMTYPE_REC_PLAY:
371 case COMTYPE_MPEGDECODER:
372 type = "MSG";
373 flags[0] = GPMQOver;
374 flags[1] = GPMQFull;
375 break;
376 case COMTYPE_OSD:
377 type = "OSD";
378 flags[0] = OSDQOver;
379 flags[1] = OSDQFull;
380 break;
381 case COMTYPE_MISC:
382 if (FW_VERSION(av7110->arm_app) >= 0x261d) {
383 type = "MSG";
384 flags[0] = GPMQOver;
385 flags[1] = GPMQBusy;
386 }
387 break;
388 default:
389 break;
390 }
391
392 if (type != NULL) {
393 /* non-immediate COMMAND type */
394 start = jiffies;
395 for (;;) {
396 err = time_after(jiffies, start + ARM_WAIT_FREE);
397 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, val: 0, count: 2);
398 if (stat & flags[0]) {
399 printk(KERN_ERR "%s: %s QUEUE overflow\n",
400 __func__, type);
401 return -1;
402 }
403 if ((stat & flags[1]) == 0)
404 break;
405 if (err) {
406 printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
407 __func__, type);
408 av7110->arm_errors++;
409 return -ETIMEDOUT;
410 }
411 msleep(msecs: 1);
412 }
413 }
414
415 for (i = 2; i < length; i++)
416 wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, val: (u32) buf[i], count: 2);
417
418 if (length)
419 wdebi(av7110, DEBINOSWAP, COMMAND + 2, val: (u32) buf[1], count: 2);
420 else
421 wdebi(av7110, DEBINOSWAP, COMMAND + 2, val: 0, count: 2);
422
423 wdebi(av7110, DEBINOSWAP, COMMAND, val: (u32) buf[0], count: 2);
424
425 if (FW_VERSION(av7110->arm_app) <= 0x261f)
426 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, val: 0x0000, count: 2);
427
428#ifdef COM_DEBUG
429 start = jiffies;
430 while (1) {
431 err = time_after(jiffies, start + ARM_WAIT_FREE);
432 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
433 break;
434 if (err) {
435 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",
436 __func__, (buf[0] >> 8) & 0xff);
437 return -ETIMEDOUT;
438 }
439 msleep(1);
440 }
441
442 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
443 if (stat & GPMQOver) {
444 printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__);
445 return -ENOSPC;
446 }
447 else if (stat & OSDQOver) {
448 printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__);
449 return -ENOSPC;
450 }
451#endif
452
453 return 0;
454}
455
456static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
457{
458 int ret;
459
460// dprintk(4, "%p\n", av7110);
461
462 if (!av7110->arm_ready) {
463 dprintk(1, "arm not ready.\n");
464 return -1;
465 }
466 if (mutex_lock_interruptible(&av7110->dcomlock))
467 return -ERESTARTSYS;
468
469 ret = __av7110_send_fw_cmd(av7110, buf, length);
470 mutex_unlock(lock: &av7110->dcomlock);
471 if (ret && ret!=-ERESTARTSYS)
472 printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
473 __func__, ret);
474 return ret;
475}
476
477int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
478{
479 va_list args;
480 u16 buf[MAX_XFER_SIZE];
481 int i, ret;
482
483// dprintk(4, "%p\n", av7110);
484
485 if (2 + num > ARRAY_SIZE(buf)) {
486 printk(KERN_WARNING
487 "%s: %s len=%d is too big!\n",
488 KBUILD_MODNAME, __func__, num);
489 return -EINVAL;
490 }
491
492 buf[0] = ((type << 8) | com);
493 buf[1] = num;
494
495 if (num) {
496 va_start(args, num);
497 for (i = 0; i < num; i++)
498 buf[i + 2] = va_arg(args, u32);
499 va_end(args);
500 }
501
502 ret = av7110_send_fw_cmd(av7110, buf, length: num + 2);
503 if (ret && ret != -ERESTARTSYS)
504 printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
505 return ret;
506}
507
508#if 0
509int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
510{
511 int i, ret;
512 u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
513 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
514
515 dprintk(4, "%p\n", av7110);
516
517 for(i = 0; i < len && i < 32; i++)
518 {
519 if(i % 2 == 0)
520 cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
521 else
522 cmd[(i / 2) + 2] |= buf[i];
523 }
524
525 ret = av7110_send_fw_cmd(av7110, cmd, 18);
526 if (ret && ret != -ERESTARTSYS)
527 printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
528 return ret;
529}
530#endif /* 0 */
531
532int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
533 int request_buf_len, u16 *reply_buf, int reply_buf_len)
534{
535 int err;
536 s16 i;
537 unsigned long start;
538#ifdef COM_DEBUG
539 u32 stat;
540#endif
541
542 dprintk(4, "%p\n", av7110);
543
544 if (!av7110->arm_ready) {
545 dprintk(1, "arm not ready.\n");
546 return -1;
547 }
548
549 if (mutex_lock_interruptible(&av7110->dcomlock))
550 return -ERESTARTSYS;
551
552 if ((err = __av7110_send_fw_cmd(av7110, buf: request_buf, length: request_buf_len)) < 0) {
553 mutex_unlock(lock: &av7110->dcomlock);
554 printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
555 return err;
556 }
557
558 start = jiffies;
559 while (1) {
560 err = time_after(jiffies, start + ARM_WAIT_FREE);
561 if (rdebi(av7110, DEBINOSWAP, COMMAND, val: 0, count: 2) == 0)
562 break;
563 if (err) {
564 printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__);
565 mutex_unlock(lock: &av7110->dcomlock);
566 return -ETIMEDOUT;
567 }
568#ifdef _NOHANDSHAKE
569 msleep(msecs: 1);
570#endif
571 }
572
573#ifndef _NOHANDSHAKE
574 start = jiffies;
575 while (1) {
576 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
577 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
578 break;
579 if (err) {
580 printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__);
581 mutex_unlock(&av7110->dcomlock);
582 return -ETIMEDOUT;
583 }
584 msleep(1);
585 }
586#endif
587
588#ifdef COM_DEBUG
589 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
590 if (stat & GPMQOver) {
591 printk(KERN_ERR "%s: GPMQOver\n", __func__);
592 mutex_unlock(&av7110->dcomlock);
593 return -1;
594 }
595 else if (stat & OSDQOver) {
596 printk(KERN_ERR "%s: OSDQOver\n", __func__);
597 mutex_unlock(&av7110->dcomlock);
598 return -1;
599 }
600#endif
601
602 for (i = 0; i < reply_buf_len; i++)
603 reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, val: 0, count: 2);
604
605 mutex_unlock(lock: &av7110->dcomlock);
606 return 0;
607}
608
609static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
610{
611 int ret;
612 ret = av7110_fw_request(av7110, request_buf: &tag, request_buf_len: 0, reply_buf: buf, reply_buf_len: length);
613 if (ret)
614 printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
615 return ret;
616}
617
618
619/****************************************************************************
620 * Firmware commands
621 ****************************************************************************/
622
623/* get version of the firmware ROM, RTSL, video ucode and ARM application */
624int av7110_firmversion(struct av7110 *av7110)
625{
626 u16 buf[20];
627 u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
628
629 dprintk(4, "%p\n", av7110);
630
631 if (av7110_fw_query(av7110, tag, buf, length: 16)) {
632 printk("dvb-ttpci: failed to boot firmware @ card %d\n",
633 av7110->dvb_adapter.num);
634 return -EIO;
635 }
636
637 av7110->arm_fw = (buf[0] << 16) + buf[1];
638 av7110->arm_rtsl = (buf[2] << 16) + buf[3];
639 av7110->arm_vid = (buf[4] << 16) + buf[5];
640 av7110->arm_app = (buf[6] << 16) + buf[7];
641 av7110->avtype = (buf[8] << 16) + buf[9];
642
643 printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
644 av7110->dvb_adapter.num, av7110->arm_fw,
645 av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
646
647 /* print firmware capabilities */
648 if (FW_CI_LL_SUPPORT(av7110->arm_app))
649 printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
650 av7110->dvb_adapter.num);
651 else
652 printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
653 av7110->dvb_adapter.num);
654
655 return 0;
656}
657
658
659int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
660{
661 int i, ret;
662 u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
663 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
664
665 dprintk(4, "%p\n", av7110);
666
667 if (len > 10)
668 len = 10;
669
670 buf[1] = len + 2;
671 buf[2] = len;
672
673 if (burst != -1)
674 buf[3] = burst ? 0x01 : 0x00;
675 else
676 buf[3] = 0xffff;
677
678 for (i = 0; i < len; i++)
679 buf[i + 4] = msg[i];
680
681 ret = av7110_send_fw_cmd(av7110, buf, length: 18);
682 if (ret && ret!=-ERESTARTSYS)
683 printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
684 return ret;
685}
686
687
688#ifdef CONFIG_DVB_AV7110_OSD
689
690static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
691{
692 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: SetCBlend, num: 1, windownr);
693}
694
695static inline int SetBlend_(struct av7110 *av7110, u8 windownr,
696 enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
697{
698 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: SetBlend, num: 4,
699 windownr, colordepth, index, blending);
700}
701
702static inline int SetColor_(struct av7110 *av7110, u8 windownr,
703 enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
704{
705 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: SetColor, num: 5,
706 windownr, colordepth, index, colorhi, colorlo);
707}
708
709static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
710 u16 colorfg, u16 colorbg)
711{
712 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: Set_Font, num: 4,
713 windownr, fontsize, colorfg, colorbg);
714}
715
716static int FlushText(struct av7110 *av7110)
717{
718 unsigned long start;
719 int err;
720
721 if (mutex_lock_interruptible(&av7110->dcomlock))
722 return -ERESTARTSYS;
723 start = jiffies;
724 while (1) {
725 err = time_after(jiffies, start + ARM_WAIT_OSD);
726 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, val: 0, count: 2) == 0)
727 break;
728 if (err) {
729 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
730 __func__);
731 mutex_unlock(lock: &av7110->dcomlock);
732 return -ETIMEDOUT;
733 }
734 msleep(msecs: 1);
735 }
736 mutex_unlock(lock: &av7110->dcomlock);
737 return 0;
738}
739
740static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf)
741{
742 int i, ret;
743 unsigned long start;
744 int length = strlen(buf) + 1;
745 u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
746
747 if (mutex_lock_interruptible(&av7110->dcomlock))
748 return -ERESTARTSYS;
749
750 start = jiffies;
751 while (1) {
752 ret = time_after(jiffies, start + ARM_WAIT_OSD);
753 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, val: 0, count: 2) == 0)
754 break;
755 if (ret) {
756 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
757 __func__);
758 mutex_unlock(lock: &av7110->dcomlock);
759 return -ETIMEDOUT;
760 }
761 msleep(msecs: 1);
762 }
763#ifndef _NOHANDSHAKE
764 start = jiffies;
765 while (1) {
766 ret = time_after(jiffies, start + ARM_WAIT_SHAKE);
767 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
768 break;
769 if (ret) {
770 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
771 __func__);
772 mutex_unlock(&av7110->dcomlock);
773 return -ETIMEDOUT;
774 }
775 msleep(1);
776 }
777#endif
778 for (i = 0; i < length / 2; i++)
779 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
780 swab16(*(u16 *)(buf + 2 * i)), count: 2);
781 if (length & 1)
782 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, val: 0, count: 2);
783 ret = __av7110_send_fw_cmd(av7110, buf: cbuf, length: 5);
784 mutex_unlock(lock: &av7110->dcomlock);
785 if (ret && ret!=-ERESTARTSYS)
786 printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
787 return ret;
788}
789
790static inline int DrawLine(struct av7110 *av7110, u8 windownr,
791 u16 x, u16 y, u16 dx, u16 dy, u16 color)
792{
793 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: DLine, num: 6,
794 windownr, x, y, dx, dy, color);
795}
796
797static inline int DrawBlock(struct av7110 *av7110, u8 windownr,
798 u16 x, u16 y, u16 dx, u16 dy, u16 color)
799{
800 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: DBox, num: 6,
801 windownr, x, y, dx, dy, color);
802}
803
804static inline int HideWindow(struct av7110 *av7110, u8 windownr)
805{
806 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: WHide, num: 1, windownr);
807}
808
809static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
810{
811 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: WMoveD, num: 3, windownr, x, y);
812}
813
814static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
815{
816 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: WMoveA, num: 3, windownr, x, y);
817}
818
819static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
820{
821 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: WDestroy, num: 1, windownr);
822}
823
824static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
825 osd_raw_window_t disptype,
826 u16 width, u16 height)
827{
828 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: WCreate, num: 4,
829 windownr, disptype, width, height);
830}
831
832
833static enum av7110_osd_palette_type bpp2pal[8] = {
834 Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
835};
836static osd_raw_window_t bpp2bit[8] = {
837 OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
838};
839
840static inline int WaitUntilBmpLoaded(struct av7110 *av7110)
841{
842 int ret = wait_event_timeout(av7110->bmpq,
843 av7110->bmp_state != BMP_LOADING, 10*HZ);
844 if (ret == 0) {
845 printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
846 ret, av7110->bmp_state);
847 av7110->bmp_state = BMP_NONE;
848 return -ETIMEDOUT;
849 }
850 return 0;
851}
852
853static inline int LoadBitmap(struct av7110 *av7110,
854 u16 dx, u16 dy, int inc, u8 __user * data)
855{
856 u16 format;
857 int bpp;
858 int i;
859 int d, delta;
860 u8 c;
861 int ret;
862
863 dprintk(4, "%p\n", av7110);
864
865 format = bpp2bit[av7110->osdbpp[av7110->osdwin]];
866
867 av7110->bmp_state = BMP_LOADING;
868 if (format == OSD_BITMAP8) {
869 bpp=8; delta = 1;
870 } else if (format == OSD_BITMAP4) {
871 bpp=4; delta = 2;
872 } else if (format == OSD_BITMAP2) {
873 bpp=2; delta = 4;
874 } else if (format == OSD_BITMAP1) {
875 bpp=1; delta = 8;
876 } else {
877 av7110->bmp_state = BMP_NONE;
878 return -EINVAL;
879 }
880 av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
881 av7110->bmpp = 0;
882 if (av7110->bmplen > 32768) {
883 av7110->bmp_state = BMP_NONE;
884 return -EINVAL;
885 }
886 for (i = 0; i < dy; i++) {
887 if (copy_from_user(to: av7110->bmpbuf + 1024 + i * dx, from: data + i * inc, n: dx)) {
888 av7110->bmp_state = BMP_NONE;
889 return -EINVAL;
890 }
891 }
892 if (format != OSD_BITMAP8) {
893 for (i = 0; i < dx * dy / delta; i++) {
894 c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
895 for (d = delta - 2; d >= 0; d--) {
896 c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
897 << ((delta - d - 1) * bpp));
898 ((u8 *)av7110->bmpbuf)[1024 + i] = c;
899 }
900 }
901 }
902 av7110->bmplen += 1024;
903 dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
904 ret = av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: LoadBmp, num: 3, format, dx, dy);
905 if (!ret)
906 ret = WaitUntilBmpLoaded(av7110);
907 return ret;
908}
909
910static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)
911{
912 dprintk(4, "%p\n", av7110);
913
914 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: BlitBmp, num: 4, av7110->osdwin, x, y, 0);
915}
916
917static inline int ReleaseBitmap(struct av7110 *av7110)
918{
919 dprintk(4, "%p\n", av7110);
920
921 if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)
922 return -1;
923 if (av7110->bmp_state == BMP_LOADING)
924 dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");
925 av7110->bmp_state = BMP_NONE;
926 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: ReleaseBmp, num: 0);
927}
928
929static u32 RGB2YUV(u16 R, u16 G, u16 B)
930{
931 u16 y, u, v;
932 u16 Y, Cr, Cb;
933
934 y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */
935 u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */
936 v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */
937
938 Y = y / 256;
939 Cb = u / 16;
940 Cr = v / 16;
941
942 return Cr | (Cb << 16) | (Y << 8);
943}
944
945static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
946{
947 int ret;
948
949 u16 ch, cl;
950 u32 yuv;
951
952 yuv = blend ? RGB2YUV(R: r,G: g,B: b) : 0;
953 cl = (yuv & 0xffff);
954 ch = ((yuv >> 16) & 0xffff);
955 ret = SetColor_(av7110, windownr: av7110->osdwin, colordepth: bpp2pal[av7110->osdbpp[av7110->osdwin]],
956 index: color, colorhi: ch, colorlo: cl);
957 if (!ret)
958 ret = SetBlend_(av7110, windownr: av7110->osdwin, colordepth: bpp2pal[av7110->osdbpp[av7110->osdwin]],
959 index: color, blending: ((blend >> 4) & 0x0f));
960 return ret;
961}
962
963static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
964{
965 int i;
966 int length = last - first + 1;
967
968 if (length * 4 > DATA_BUFF3_SIZE)
969 return -EINVAL;
970
971 for (i = 0; i < length; i++) {
972 u32 color, blend, yuv;
973
974 if (get_user(color, colors + i))
975 return -EFAULT;
976 blend = (color & 0xF0000000) >> 4;
977 yuv = blend ? RGB2YUV(R: color & 0xFF, G: (color >> 8) & 0xFF,
978 B: (color >> 16) & 0xFF) | blend : 0;
979 yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
980 wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, val: yuv, count: 4);
981 }
982 return av7110_fw_cmd(av7110, type: COMTYPE_OSD, com: Set_Palette, num: 4,
983 av7110->osdwin,
984 bpp2pal[av7110->osdbpp[av7110->osdwin]],
985 first, last);
986}
987
988static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
989 int x1, int y1, int inc, u8 __user * data)
990{
991 uint w, h, bpp, bpl, size, lpb, bnum, brest;
992 int i;
993 int rc,release_rc;
994
995 w = x1 - x0 + 1;
996 h = y1 - y0 + 1;
997 if (inc <= 0)
998 inc = w;
999 if (w <= 0 || w > 720 || h <= 0 || h > 576)
1000 return -EINVAL;
1001 bpp = av7110->osdbpp[av7110->osdwin] + 1;
1002 bpl = ((w * bpp + 7) & ~7) / 8;
1003 size = h * bpl;
1004 lpb = (32 * 1024) / bpl;
1005 bnum = size / (lpb * bpl);
1006 brest = size - bnum * lpb * bpl;
1007
1008 if (av7110->bmp_state == BMP_LOADING) {
1009 /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */
1010 if (WARN_ON(FW_VERSION(av7110->arm_app) >= 0x261e))
1011 return -EIO;
1012 rc = WaitUntilBmpLoaded(av7110);
1013 if (rc)
1014 return rc;
1015 /* just continue. This should work for all fw versions
1016 * if bnum==1 && !brest && LoadBitmap was successful
1017 */
1018 }
1019
1020 rc = 0;
1021 for (i = 0; i < bnum; i++) {
1022 rc = LoadBitmap(av7110, dx: w, dy: lpb, inc, data);
1023 if (rc)
1024 break;
1025 rc = BlitBitmap(av7110, x: x0, y: y0 + i * lpb);
1026 if (rc)
1027 break;
1028 data += lpb * inc;
1029 }
1030 if (!rc && brest) {
1031 rc = LoadBitmap(av7110, dx: w, dy: brest / bpl, inc, data);
1032 if (!rc)
1033 rc = BlitBitmap(av7110, x: x0, y: y0 + bnum * lpb);
1034 }
1035 release_rc = ReleaseBitmap(av7110);
1036 if (!rc)
1037 rc = release_rc;
1038 if (rc)
1039 dprintk(1,"returns %d\n",rc);
1040 return rc;
1041}
1042
1043int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
1044{
1045 int ret;
1046
1047 if (mutex_lock_interruptible(&av7110->osd_mutex))
1048 return -ERESTARTSYS;
1049
1050 switch (dc->cmd) {
1051 case OSD_Close:
1052 ret = DestroyOSDWindow(av7110, windownr: av7110->osdwin);
1053 break;
1054 case OSD_Open:
1055 av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
1056 ret = CreateOSDWindow(av7110, windownr: av7110->osdwin,
1057 disptype: bpp2bit[av7110->osdbpp[av7110->osdwin]],
1058 width: dc->x1 - dc->x0 + 1, height: dc->y1 - dc->y0 + 1);
1059 if (ret)
1060 break;
1061 if (!dc->data) {
1062 ret = MoveWindowAbs(av7110, windownr: av7110->osdwin, x: dc->x0, y: dc->y0);
1063 if (ret)
1064 break;
1065 ret = SetColorBlend(av7110, windownr: av7110->osdwin);
1066 }
1067 break;
1068 case OSD_Show:
1069 ret = MoveWindowRel(av7110, windownr: av7110->osdwin, x: 0, y: 0);
1070 break;
1071 case OSD_Hide:
1072 ret = HideWindow(av7110, windownr: av7110->osdwin);
1073 break;
1074 case OSD_Clear:
1075 ret = DrawBlock(av7110, windownr: av7110->osdwin, x: 0, y: 0, dx: 720, dy: 576, color: 0);
1076 break;
1077 case OSD_Fill:
1078 ret = DrawBlock(av7110, windownr: av7110->osdwin, x: 0, y: 0, dx: 720, dy: 576, color: dc->color);
1079 break;
1080 case OSD_SetColor:
1081 ret = OSDSetColor(av7110, color: dc->color, r: dc->x0, g: dc->y0, b: dc->x1, blend: dc->y1);
1082 break;
1083 case OSD_SetPalette:
1084 if (FW_VERSION(av7110->arm_app) >= 0x2618)
1085 ret = OSDSetPalette(av7110, colors: dc->data, first: dc->color, last: dc->x0);
1086 else {
1087 int i, len = dc->x0-dc->color+1;
1088 u8 __user *colors = (u8 __user *)dc->data;
1089 u8 r, g = 0, b = 0, blend = 0;
1090 ret = 0;
1091 for (i = 0; i<len; i++) {
1092 if (get_user(r, colors + i * 4) ||
1093 get_user(g, colors + i * 4 + 1) ||
1094 get_user(b, colors + i * 4 + 2) ||
1095 get_user(blend, colors + i * 4 + 3)) {
1096 ret = -EFAULT;
1097 break;
1098 }
1099 ret = OSDSetColor(av7110, color: dc->color + i, r, g, b, blend);
1100 if (ret)
1101 break;
1102 }
1103 }
1104 break;
1105 case OSD_SetPixel:
1106 ret = DrawLine(av7110, windownr: av7110->osdwin,
1107 x: dc->x0, y: dc->y0, dx: 0, dy: 0, color: dc->color);
1108 break;
1109 case OSD_SetRow:
1110 dc->y1 = dc->y0;
1111 fallthrough;
1112 case OSD_SetBlock:
1113 ret = OSDSetBlock(av7110, x0: dc->x0, y0: dc->y0, x1: dc->x1, y1: dc->y1, inc: dc->color, data: dc->data);
1114 break;
1115 case OSD_FillRow:
1116 ret = DrawBlock(av7110, windownr: av7110->osdwin, x: dc->x0, y: dc->y0,
1117 dx: dc->x1-dc->x0+1, dy: dc->y1, color: dc->color);
1118 break;
1119 case OSD_FillBlock:
1120 ret = DrawBlock(av7110, windownr: av7110->osdwin, x: dc->x0, y: dc->y0,
1121 dx: dc->x1 - dc->x0 + 1, dy: dc->y1 - dc->y0 + 1, color: dc->color);
1122 break;
1123 case OSD_Line:
1124 ret = DrawLine(av7110, windownr: av7110->osdwin,
1125 x: dc->x0, y: dc->y0, dx: dc->x1 - dc->x0, dy: dc->y1 - dc->y0, color: dc->color);
1126 break;
1127 case OSD_Text:
1128 {
1129 char textbuf[240];
1130
1131 if (strncpy_from_user(dst: textbuf, src: dc->data, count: 240) < 0) {
1132 ret = -EFAULT;
1133 break;
1134 }
1135 textbuf[239] = 0;
1136 if (dc->x1 > 3)
1137 dc->x1 = 3;
1138 ret = SetFont(av7110, windownr: av7110->osdwin, fontsize: dc->x1,
1139 colorfg: (u16) (dc->color & 0xffff), colorbg: (u16) (dc->color >> 16));
1140 if (!ret)
1141 ret = FlushText(av7110);
1142 if (!ret)
1143 ret = WriteText(av7110, win: av7110->osdwin, x: dc->x0, y: dc->y0, buf: textbuf);
1144 break;
1145 }
1146 case OSD_SetWindow:
1147 if (dc->x0 < 1 || dc->x0 > 7)
1148 ret = -EINVAL;
1149 else {
1150 av7110->osdwin = dc->x0;
1151 ret = 0;
1152 }
1153 break;
1154 case OSD_MoveWindow:
1155 ret = MoveWindowAbs(av7110, windownr: av7110->osdwin, x: dc->x0, y: dc->y0);
1156 if (!ret)
1157 ret = SetColorBlend(av7110, windownr: av7110->osdwin);
1158 break;
1159 case OSD_OpenRaw:
1160 if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
1161 ret = -EINVAL;
1162 break;
1163 }
1164 if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)
1165 av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
1166 else
1167 av7110->osdbpp[av7110->osdwin] = 0;
1168 ret = CreateOSDWindow(av7110, windownr: av7110->osdwin, disptype: (osd_raw_window_t)dc->color,
1169 width: dc->x1 - dc->x0 + 1, height: dc->y1 - dc->y0 + 1);
1170 if (ret)
1171 break;
1172 if (!dc->data) {
1173 ret = MoveWindowAbs(av7110, windownr: av7110->osdwin, x: dc->x0, y: dc->y0);
1174 if (!ret)
1175 ret = SetColorBlend(av7110, windownr: av7110->osdwin);
1176 }
1177 break;
1178 default:
1179 ret = -EINVAL;
1180 break;
1181 }
1182
1183 mutex_unlock(lock: &av7110->osd_mutex);
1184 if (ret==-ERESTARTSYS)
1185 dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);
1186 else if (ret)
1187 dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);
1188
1189 return ret;
1190}
1191
1192int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
1193{
1194 switch (cap->cmd) {
1195 case OSD_CAP_MEMSIZE:
1196 if (FW_4M_SDRAM(av7110->arm_app))
1197 cap->val = 1000000;
1198 else
1199 cap->val = 92000;
1200 return 0;
1201 default:
1202 return -EINVAL;
1203 }
1204}
1205#endif /* CONFIG_DVB_AV7110_OSD */
1206

source code of linux/drivers/staging/media/av7110/av7110_hw.c