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 | |
43 | int 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 | |
66 | u32 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 |
100 | void 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 | |
121 | static 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 | |
135 | static 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 */ |
194 | int 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 | } |
283 | MODULE_FIRMWARE("av7110/bootcode.bin" ); |
284 | |
285 | /**************************************************************************** |
286 | * DEBI command polling |
287 | ****************************************************************************/ |
288 | |
289 | int 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 | |
321 | static 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 | |
456 | static 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 | |
477 | int 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 |
509 | int 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 | |
532 | int 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 | |
609 | static 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 */ |
624 | int 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 | |
659 | int 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 | |
690 | static 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 | |
695 | static 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 | |
702 | static 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 | |
709 | static 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 | |
716 | static 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 | |
740 | static 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 | |
790 | static 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 | |
797 | static 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 | |
804 | static 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 | |
809 | static 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 | |
814 | static 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 | |
819 | static 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 | |
824 | static 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 | |
833 | static enum av7110_osd_palette_type bpp2pal[8] = { |
834 | Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit |
835 | }; |
836 | static osd_raw_window_t bpp2bit[8] = { |
837 | OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 |
838 | }; |
839 | |
840 | static 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 | |
853 | static 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 | |
910 | static 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 | |
917 | static 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 | |
929 | static 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 | |
945 | static 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 | |
963 | static 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 | |
988 | static 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 | |
1043 | int 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 | |
1192 | int 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 | |