blob: 1220826696c5f2ba2a50a69e0b81a183c847f6a3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * av7110_hw.c: av7110 low level hardware access and firmware interface
3 *
4 * Copyright (C) 1999-2002 Ralph Metzler
5 * & Marcus Metzler for convergence integrated media GmbH
6 *
7 * originally based on code by:
8 * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
24 *
25 * the project's page is at http://www.linuxtv.org/dvb/
26 */
27
28/* for debugging ARM communication: */
29//#define COM_DEBUG
30
31#include <stdarg.h>
32#include <linux/types.h>
33#include <linux/kernel.h>
34#include <linux/string.h>
35#include <linux/sched.h>
36#include <linux/delay.h>
37#include <linux/byteorder/swabb.h>
38#include <linux/smp_lock.h>
39#include <linux/fs.h>
40
41#include "av7110.h"
42#include "av7110_hw.h"
43
44/****************************************************************************
45 * DEBI functions
46 ****************************************************************************/
47
48/* This DEBI code is based on the Stradis driver
49 by Nathan Laredo <laredo@gnu.org> */
50
51int av7110_debiwrite(struct av7110 *av7110, u32 config,
52 int addr, u32 val, int count)
53{
54 struct saa7146_dev *dev = av7110->dev;
55
56 if (count <= 0 || count > 32764) {
57 printk("%s: invalid count %d\n", __FUNCTION__, count);
58 return -1;
59 }
60 if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
61 printk("%s: wait_for_debi_done failed\n", __FUNCTION__);
62 return -1;
63 }
64 saa7146_write(dev, DEBI_CONFIG, config);
65 if (count <= 4) /* immediate transfer */
66 saa7146_write(dev, DEBI_AD, val);
67 else /* block transfer */
68 saa7146_write(dev, DEBI_AD, av7110->debi_bus);
69 saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
70 saa7146_write(dev, MC2, (2 << 16) | 2);
71 return 0;
72}
73
74u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)
75{
76 struct saa7146_dev *dev = av7110->dev;
77 u32 result = 0;
78
79 if (count > 32764 || count <= 0) {
80 printk("%s: invalid count %d\n", __FUNCTION__, count);
81 return 0;
82 }
83 if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
84 printk("%s: wait_for_debi_done #1 failed\n", __FUNCTION__);
85 return 0;
86 }
87 saa7146_write(dev, DEBI_AD, av7110->debi_bus);
88 saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
89
90 saa7146_write(dev, DEBI_CONFIG, config);
91 saa7146_write(dev, MC2, (2 << 16) | 2);
92 if (count > 4)
93 return count;
94 if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
95 printk("%s: wait_for_debi_done #2 failed\n", __FUNCTION__);
96 return 0;
97 }
98
99 result = saa7146_read(dev, DEBI_AD);
100 result &= (0xffffffffUL >> ((4 - count) * 8));
101 return result;
102}
103
104
105
106/* av7110 ARM core boot stuff */
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700107#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108void av7110_reset_arm(struct av7110 *av7110)
109{
110 saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
111
112 /* Disable DEBI and GPIO irq */
113 SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
114 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
115
116 saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
117 msleep(30); /* the firmware needs some time to initialize */
118
119 ARM_ResetMailBox(av7110);
120
121 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
122 SAA7146_IER_ENABLE(av7110->dev, MASK_03);
123
124 av7110->arm_ready = 1;
125 dprintk(1, "reset ARM\n");
126}
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700127#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
129static int waitdebi(struct av7110 *av7110, int adr, int state)
130{
131 int k;
132
133 dprintk(4, "%p\n", av7110);
134
135 for (k = 0; k < 100; k++) {
136 if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)
137 return 0;
138 udelay(5);
139 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700140 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
143static int load_dram(struct av7110 *av7110, u32 *data, int len)
144{
145 int i;
146 int blocks, rest;
147 u32 base, bootblock = BOOT_BLOCK;
148
149 dprintk(4, "%p\n", av7110);
150
151 blocks = len / BOOT_MAX_SIZE;
152 rest = len % BOOT_MAX_SIZE;
153 base = DRAM_START_CODE;
154
155 for (i = 0; i < blocks; i++) {
156 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
157 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700158 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 }
160 dprintk(4, "writing DRAM block %d\n", i);
161 mwdebi(av7110, DEBISWAB, bootblock,
162 ((char*)data) + i * BOOT_MAX_SIZE, BOOT_MAX_SIZE);
163 bootblock ^= 0x1400;
164 iwdebi(av7110, DEBISWAB, BOOT_BASE, swab32(base), 4);
165 iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, BOOT_MAX_SIZE, 2);
166 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
167 base += BOOT_MAX_SIZE;
168 }
169
170 if (rest > 0) {
171 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
172 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700173 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 }
175 if (rest > 4)
176 mwdebi(av7110, DEBISWAB, bootblock,
177 ((char*)data) + i * BOOT_MAX_SIZE, rest);
178 else
179 mwdebi(av7110, DEBISWAB, bootblock,
180 ((char*)data) + i * BOOT_MAX_SIZE - 4, rest + 4);
181
182 iwdebi(av7110, DEBISWAB, BOOT_BASE, swab32(base), 4);
183 iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, rest, 2);
184 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
185 }
186 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
187 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700188 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 }
190 iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, 0, 2);
191 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
192 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BOOT_COMPLETE) < 0) {
193 printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700194 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
196 return 0;
197}
198
199
200/* we cannot write av7110 DRAM directly, so load a bootloader into
201 * the DPRAM which implements a simple boot protocol */
202static u8 bootcode[] = {
203 0xea, 0x00, 0x00, 0x0e, 0xe1, 0xb0, 0xf0, 0x0e, 0xe2, 0x5e, 0xf0, 0x04,
204 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x08, 0xe2, 0x5e, 0xf0, 0x04,
205 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04, 0x2c, 0x00, 0x00, 0x24,
206 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x34,
207 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0x5a, 0x5a, 0x00, 0x1f, 0x15, 0x55,
208 0x00, 0x00, 0x00, 0x09, 0xe5, 0x9f, 0xd0, 0x7c, 0xe5, 0x9f, 0x40, 0x74,
209 0xe3, 0xa0, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x04,
210 0xe5, 0x9f, 0x10, 0x70, 0xe5, 0x9f, 0x20, 0x70, 0xe5, 0x9f, 0x30, 0x64,
211 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe1, 0x51, 0x00, 0x02,
212 0xda, 0xff, 0xff, 0xfb, 0xe5, 0x9f, 0xf0, 0x50, 0xe1, 0xd4, 0x10, 0xb0,
213 0xe3, 0x51, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xfc, 0xe1, 0xa0, 0x10, 0x0d,
214 0xe5, 0x94, 0x30, 0x04, 0xe1, 0xd4, 0x20, 0xb2, 0xe2, 0x82, 0x20, 0x3f,
215 0xe1, 0xb0, 0x23, 0x22, 0x03, 0xa0, 0x00, 0x02, 0xe1, 0xc4, 0x00, 0xb0,
216 0x0a, 0xff, 0xff, 0xf4, 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0,
217 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe2, 0x52, 0x20, 0x01,
218 0x1a, 0xff, 0xff, 0xf9, 0xe2, 0x2d, 0xdb, 0x05, 0xea, 0xff, 0xff, 0xec,
219 0x2c, 0x00, 0x03, 0xf8, 0x2c, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x08, 0x00,
220 0x2c, 0x00, 0x00, 0x74, 0x2c, 0x00, 0x00, 0xc0
221};
222
223int av7110_bootarm(struct av7110 *av7110)
224{
225 struct saa7146_dev *dev = av7110->dev;
226 u32 ret;
227 int i;
228
229 dprintk(4, "%p\n", av7110);
230
231 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
232
233 /* Disable DEBI and GPIO irq */
234 SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
235 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
236
237 /* enable DEBI */
238 saa7146_write(av7110->dev, MC1, 0x08800880);
239 saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
240 saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
241
242 /* test DEBI */
243 iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
244 if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
245 printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: "
246 "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
247 ret, 0x10325476);
248 return -1;
249 }
250 for (i = 0; i < 8192; i += 4)
251 iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);
252 dprintk(2, "debi test OK\n");
253
254 /* boot */
255 dprintk(1, "load boot code\n");
256 saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
257 //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
258 //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
259
260 mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode));
261 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
262
263 if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
264 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
265 "saa7146_wait_for_debi_done() timed out\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700266 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
268 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
269 mdelay(1);
270
271 dprintk(1, "load dram code\n");
272 if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {
273 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
274 "load_dram() failed\n");
275 return -1;
276 }
277
278 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
279 mdelay(1);
280
281 dprintk(1, "load dpram code\n");
282 mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);
283
284 if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
285 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
286 "saa7146_wait_for_debi_done() timed out after loading DRAM\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700287 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 }
289 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
290 msleep(30); /* the firmware needs some time to initialize */
291
292 //ARM_ClearIrq(av7110);
293 ARM_ResetMailBox(av7110);
294 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
295 SAA7146_IER_ENABLE(av7110->dev, MASK_03);
296
297 av7110->arm_errors = 0;
298 av7110->arm_ready = 1;
299 return 0;
300}
301
302
303/****************************************************************************
304 * DEBI command polling
305 ****************************************************************************/
306
307int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
308{
309 unsigned long start;
310 u32 stat;
Oliver Endriss25de1922005-07-07 17:58:28 -0700311 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 if (FW_VERSION(av7110->arm_app) <= 0x261c) {
314 /* not supported by old firmware */
315 msleep(50);
316 return 0;
317 }
318
319 /* new firmware */
320 start = jiffies;
321 for (;;) {
Oliver Endriss25de1922005-07-07 17:58:28 -0700322 err = time_after(jiffies, start + ARM_WAIT_FREE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 if (down_interruptible(&av7110->dcomlock))
324 return -ERESTARTSYS;
325 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
326 up(&av7110->dcomlock);
Oliver Endriss25de1922005-07-07 17:58:28 -0700327 if ((stat & flags) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 break;
Oliver Endriss25de1922005-07-07 17:58:28 -0700329 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
331 __FUNCTION__, stat & flags);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700332 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334 msleep(1);
335 }
336 return 0;
337}
338
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700339static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 int i;
342 unsigned long start;
343 char *type = NULL;
344 u16 flags[2] = {0, 0};
345 u32 stat;
Oliver Endriss25de1922005-07-07 17:58:28 -0700346 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348// dprintk(4, "%p\n", av7110);
349
350 if (!av7110->arm_ready) {
351 dprintk(1, "arm not ready.\n");
352 return -ENXIO;
353 }
354
355 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700356 while (1) {
357 err = time_after(jiffies, start + ARM_WAIT_FREE);
358 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
359 break;
360 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __FUNCTION__);
362 return -ETIMEDOUT;
363 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700364 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
366
367 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);
368
369#ifndef _NOHANDSHAKE
370 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700371 while (1) {
372 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
373 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
374 break;
375 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
377 return -ETIMEDOUT;
378 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700379 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 }
381#endif
382
383 switch ((buf[0] >> 8) & 0xff) {
384 case COMTYPE_PIDFILTER:
385 case COMTYPE_ENCODER:
386 case COMTYPE_REC_PLAY:
387 case COMTYPE_MPEGDECODER:
388 type = "MSG";
389 flags[0] = GPMQOver;
390 flags[1] = GPMQFull;
391 break;
392 case COMTYPE_OSD:
393 type = "OSD";
394 flags[0] = OSDQOver;
395 flags[1] = OSDQFull;
396 break;
397 case COMTYPE_MISC:
398 if (FW_VERSION(av7110->arm_app) >= 0x261d) {
399 type = "MSG";
400 flags[0] = GPMQOver;
401 flags[1] = GPMQBusy;
402 }
403 break;
404 default:
405 break;
406 }
407
408 if (type != NULL) {
409 /* non-immediate COMMAND type */
410 start = jiffies;
411 for (;;) {
Oliver Endriss25de1922005-07-07 17:58:28 -0700412 err = time_after(jiffies, start + ARM_WAIT_FREE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
414 if (stat & flags[0]) {
415 printk(KERN_ERR "%s: %s QUEUE overflow\n",
416 __FUNCTION__, type);
417 return -1;
418 }
419 if ((stat & flags[1]) == 0)
420 break;
Oliver Endriss25de1922005-07-07 17:58:28 -0700421 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
423 __FUNCTION__, type);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700424 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 }
426 msleep(1);
427 }
428 }
429
430 for (i = 2; i < length; i++)
431 wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);
432
433 if (length)
434 wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
435 else
436 wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);
437
438 wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
439
440 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);
441
442#ifdef COM_DEBUG
443 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700444 while (1) {
445 err = time_after(jiffies, start + ARM_WAIT_FREE);
446 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
447 break;
448 if (err) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700449 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",
Oliver Endriss25de1922005-07-07 17:58:28 -0700450 __FUNCTION__, (buf[0] >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 return -ETIMEDOUT;
452 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700453 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 }
455
456 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
457 if (stat & GPMQOver) {
458 printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __FUNCTION__);
459 return -ENOSPC;
460 }
461 else if (stat & OSDQOver) {
462 printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __FUNCTION__);
463 return -ENOSPC;
464 }
465#endif
466
467 return 0;
468}
469
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700470static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471{
472 int ret;
473
474// dprintk(4, "%p\n", av7110);
475
476 if (!av7110->arm_ready) {
477 dprintk(1, "arm not ready.\n");
478 return -1;
479 }
480 if (down_interruptible(&av7110->dcomlock))
481 return -ERESTARTSYS;
482
483 ret = __av7110_send_fw_cmd(av7110, buf, length);
484 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700485 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
487 __FUNCTION__, ret);
488 return ret;
489}
490
491int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
492{
493 va_list args;
494 u16 buf[num + 2];
495 int i, ret;
496
497// dprintk(4, "%p\n", av7110);
498
499 buf[0] = ((type << 8) | com);
500 buf[1] = num;
501
502 if (num) {
503 va_start(args, num);
504 for (i = 0; i < num; i++)
505 buf[i + 2] = va_arg(args, u32);
506 va_end(args);
507 }
508
509 ret = av7110_send_fw_cmd(av7110, buf, num + 2);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700510 if (ret && ret != -ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
512 return ret;
513}
514
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700515#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
517{
518 int i, ret;
519 u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
520 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
521
522 dprintk(4, "%p\n", av7110);
523
524 for(i = 0; i < len && i < 32; i++)
525 {
526 if(i % 2 == 0)
527 cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
528 else
529 cmd[(i / 2) + 2] |= buf[i];
530 }
531
532 ret = av7110_send_fw_cmd(av7110, cmd, 18);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700533 if (ret && ret != -ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
535 return ret;
536}
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700537#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
540 int request_buf_len, u16 *reply_buf, int reply_buf_len)
541{
542 int err;
543 s16 i;
544 unsigned long start;
545#ifdef COM_DEBUG
546 u32 stat;
547#endif
548
549 dprintk(4, "%p\n", av7110);
550
551 if (!av7110->arm_ready) {
552 dprintk(1, "arm not ready.\n");
553 return -1;
554 }
555
556 if (down_interruptible(&av7110->dcomlock))
557 return -ERESTARTSYS;
558
559 if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {
560 up(&av7110->dcomlock);
561 printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
562 return err;
563 }
564
565 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700566 while (1) {
567 err = time_after(jiffies, start + ARM_WAIT_FREE);
568 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
569 break;
570 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __FUNCTION__);
572 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700573 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700575#ifdef _NOHANDSHAKE
576 msleep(1);
577#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579
580#ifndef _NOHANDSHAKE
581 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700582 while (1) {
583 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
584 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
585 break;
586 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
588 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700589 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700591 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 }
593#endif
594
595#ifdef COM_DEBUG
596 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
597 if (stat & GPMQOver) {
598 printk(KERN_ERR "%s: GPMQOver\n", __FUNCTION__);
599 up(&av7110->dcomlock);
600 return -1;
601 }
602 else if (stat & OSDQOver) {
603 printk(KERN_ERR "%s: OSDQOver\n", __FUNCTION__);
604 up(&av7110->dcomlock);
605 return -1;
606 }
607#endif
608
609 for (i = 0; i < reply_buf_len; i++)
610 reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);
611
612 up(&av7110->dcomlock);
613 return 0;
614}
615
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700616static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617{
618 int ret;
619 ret = av7110_fw_request(av7110, &tag, 0, buf, length);
620 if (ret)
621 printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
622 return ret;
623}
624
625
626/****************************************************************************
627 * Firmware commands
628 ****************************************************************************/
629
630/* get version of the firmware ROM, RTSL, video ucode and ARM application */
631int av7110_firmversion(struct av7110 *av7110)
632{
633 u16 buf[20];
634 u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
635
636 dprintk(4, "%p\n", av7110);
637
638 if (av7110_fw_query(av7110, tag, buf, 16)) {
639 printk("dvb-ttpci: failed to boot firmware @ card %d\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700640 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 return -EIO;
642 }
643
644 av7110->arm_fw = (buf[0] << 16) + buf[1];
645 av7110->arm_rtsl = (buf[2] << 16) + buf[3];
646 av7110->arm_vid = (buf[4] << 16) + buf[5];
647 av7110->arm_app = (buf[6] << 16) + buf[7];
648 av7110->avtype = (buf[8] << 16) + buf[9];
649
650 printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700651 av7110->dvb_adapter.num, av7110->arm_fw,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
653
654 /* print firmware capabilities */
655 if (FW_CI_LL_SUPPORT(av7110->arm_app))
656 printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700657 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 else
659 printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700660 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
662 return 0;
663}
664
665
666int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
667{
668 int i, ret;
669 u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
670 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
671
672 dprintk(4, "%p\n", av7110);
673
674 if (len > 10)
675 len = 10;
676
677 buf[1] = len + 2;
678 buf[2] = len;
679
680 if (burst != -1)
681 buf[3] = burst ? 0x01 : 0x00;
682 else
683 buf[3] = 0xffff;
684
685 for (i = 0; i < len; i++)
686 buf[i + 4] = msg[i];
687
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700688 ret = av7110_send_fw_cmd(av7110, buf, 18);
689 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700691 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692}
693
694
695#ifdef CONFIG_DVB_AV7110_OSD
696
697static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
698{
699 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);
700}
701
702static inline int SetBlend_(struct av7110 *av7110, u8 windownr,
703 enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
704{
705 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,
706 windownr, colordepth, index, blending);
707}
708
709static inline int SetColor_(struct av7110 *av7110, u8 windownr,
710 enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
711{
712 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,
713 windownr, colordepth, index, colorhi, colorlo);
714}
715
716static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
717 u16 colorfg, u16 colorbg)
718{
719 return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,
720 windownr, fontsize, colorfg, colorbg);
721}
722
723static int FlushText(struct av7110 *av7110)
724{
725 unsigned long start;
Oliver Endriss25de1922005-07-07 17:58:28 -0700726 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
728 if (down_interruptible(&av7110->dcomlock))
729 return -ERESTARTSYS;
730 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700731 while (1) {
732 err = time_after(jiffies, start + ARM_WAIT_OSD);
733 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
734 break;
735 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
737 __FUNCTION__);
738 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700739 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700741 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 }
743 up(&av7110->dcomlock);
744 return 0;
745}
746
747static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, u8* buf)
748{
749 int i, ret;
750 unsigned long start;
751 int length = strlen(buf) + 1;
752 u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
753
754 if (down_interruptible(&av7110->dcomlock))
755 return -ERESTARTSYS;
756
757 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700758 while (1) {
759 ret = time_after(jiffies, start + ARM_WAIT_OSD);
760 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
761 break;
762 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
764 __FUNCTION__);
765 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700766 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700768 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 }
770#ifndef _NOHANDSHAKE
771 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700772 while (1) {
773 ret = time_after(jiffies, start + ARM_WAIT_SHAKE);
774 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
775 break;
776 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
778 __FUNCTION__);
779 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700780 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700782 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 }
784#endif
785 for (i = 0; i < length / 2; i++)
786 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
787 swab16(*(u16 *)(buf + 2 * i)), 2);
788 if (length & 1)
789 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);
790 ret = __av7110_send_fw_cmd(av7110, cbuf, 5);
791 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700792 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
794 return ret;
795}
796
797static inline int DrawLine(struct av7110 *av7110, u8 windownr,
798 u16 x, u16 y, u16 dx, u16 dy, u16 color)
799{
800 return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6,
801 windownr, x, y, dx, dy, color);
802}
803
804static inline int DrawBlock(struct av7110 *av7110, u8 windownr,
805 u16 x, u16 y, u16 dx, u16 dy, u16 color)
806{
807 return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,
808 windownr, x, y, dx, dy, color);
809}
810
811static inline int HideWindow(struct av7110 *av7110, u8 windownr)
812{
813 return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);
814}
815
816static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
817{
818 return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);
819}
820
821static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
822{
823 return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);
824}
825
826static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
827{
828 return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);
829}
830
831static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
832 osd_raw_window_t disptype,
833 u16 width, u16 height)
834{
835 return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,
836 windownr, disptype, width, height);
837}
838
839
840static enum av7110_osd_palette_type bpp2pal[8] = {
841 Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
842};
843static osd_raw_window_t bpp2bit[8] = {
844 OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
845};
846
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700847static inline int WaitUntilBmpLoaded(struct av7110 *av7110)
848{
849 int ret = wait_event_interruptible_timeout(av7110->bmpq,
850 av7110->bmp_state != BMP_LOADING, 10*HZ);
851 if (ret == -ERESTARTSYS)
852 return ret;
853 if (ret == 0) {
854 printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
855 ret, av7110->bmp_state);
856 av7110->bmp_state = BMP_NONE;
857 return -ETIMEDOUT;
858 }
859 return 0;
860}
861
862static inline int LoadBitmap(struct av7110 *av7110,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 u16 dx, u16 dy, int inc, u8 __user * data)
864{
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700865 u16 format;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 int bpp;
867 int i;
868 int d, delta;
869 u8 c;
870 int ret;
871
872 dprintk(4, "%p\n", av7110);
873
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700874 format = bpp2bit[av7110->osdbpp[av7110->osdwin]];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876 av7110->bmp_state = BMP_LOADING;
877 if (format == OSD_BITMAP8) {
878 bpp=8; delta = 1;
879 } else if (format == OSD_BITMAP4) {
880 bpp=4; delta = 2;
881 } else if (format == OSD_BITMAP2) {
882 bpp=2; delta = 4;
883 } else if (format == OSD_BITMAP1) {
884 bpp=1; delta = 8;
885 } else {
886 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700887 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 }
889 av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
890 av7110->bmpp = 0;
891 if (av7110->bmplen > 32768) {
892 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700893 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 }
895 for (i = 0; i < dy; i++) {
896 if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {
897 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700898 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 }
900 }
901 if (format != OSD_BITMAP8) {
902 for (i = 0; i < dx * dy / delta; i++) {
903 c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
904 for (d = delta - 2; d >= 0; d--) {
905 c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
906 << ((delta - d - 1) * bpp));
907 ((u8 *)av7110->bmpbuf)[1024 + i] = c;
908 }
909 }
910 }
911 av7110->bmplen += 1024;
912 dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700913 ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
914 if (!ret)
915 ret = WaitUntilBmpLoaded(av7110);
916 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917}
918
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700919static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 dprintk(4, "%p\n", av7110);
922
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700923 return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924}
925
926static inline int ReleaseBitmap(struct av7110 *av7110)
927{
928 dprintk(4, "%p\n", av7110);
929
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700930 if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 return -1;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700932 if (av7110->bmp_state == BMP_LOADING)
933 dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 av7110->bmp_state = BMP_NONE;
935 return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);
936}
937
938static u32 RGB2YUV(u16 R, u16 G, u16 B)
939{
940 u16 y, u, v;
941 u16 Y, Cr, Cb;
942
943 y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */
944 u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */
945 v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */
946
947 Y = y / 256;
948 Cb = u / 16;
949 Cr = v / 16;
950
951 return Cr | (Cb << 16) | (Y << 8);
952}
953
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700954static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955{
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700956 int ret;
957
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 u16 ch, cl;
959 u32 yuv;
960
961 yuv = blend ? RGB2YUV(r,g,b) : 0;
962 cl = (yuv & 0xffff);
963 ch = ((yuv >> 16) & 0xffff);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700964 ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
965 color, ch, cl);
966 if (!ret)
967 ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
968 color, ((blend >> 4) & 0x0f));
969 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970}
971
972static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
973{
974 int i;
975 int length = last - first + 1;
976
977 if (length * 4 > DATA_BUFF3_SIZE)
978 return -EINVAL;
979
980 for (i = 0; i < length; i++) {
981 u32 color, blend, yuv;
982
983 if (get_user(color, colors + i))
984 return -EFAULT;
985 blend = (color & 0xF0000000) >> 4;
986 yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,
987 (color >> 16) & 0xFF) | blend : 0;
988 yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
989 wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);
990 }
991 return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,
992 av7110->osdwin,
993 bpp2pal[av7110->osdbpp[av7110->osdwin]],
994 first, last);
995}
996
997static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
998 int x1, int y1, int inc, u8 __user * data)
999{
1000 uint w, h, bpp, bpl, size, lpb, bnum, brest;
1001 int i;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001002 int rc,release_rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 w = x1 - x0 + 1;
1005 h = y1 - y0 + 1;
1006 if (inc <= 0)
1007 inc = w;
1008 if (w <= 0 || w > 720 || h <= 0 || h > 576)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001009 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 bpp = av7110->osdbpp[av7110->osdwin] + 1;
1011 bpl = ((w * bpp + 7) & ~7) / 8;
1012 size = h * bpl;
1013 lpb = (32 * 1024) / bpl;
1014 bnum = size / (lpb * bpl);
1015 brest = size - bnum * lpb * bpl;
1016
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001017 if (av7110->bmp_state == BMP_LOADING) {
1018 /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */
1019 BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e);
1020 rc = WaitUntilBmpLoaded(av7110);
1021 if (rc)
1022 return rc;
1023 /* just continue. This should work for all fw versions
1024 * if bnum==1 && !brest && LoadBitmap was successful
1025 */
1026 }
1027
1028 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 for (i = 0; i < bnum; i++) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001030 rc = LoadBitmap(av7110, w, lpb, inc, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 if (rc)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001032 break;
1033 rc = BlitBitmap(av7110, x0, y0 + i * lpb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 if (rc)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001035 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 data += lpb * inc;
1037 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001038 if (!rc && brest) {
1039 rc = LoadBitmap(av7110, w, brest / bpl, inc, data);
1040 if (!rc)
1041 rc = BlitBitmap(av7110, x0, y0 + bnum * lpb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001043 release_rc = ReleaseBitmap(av7110);
1044 if (!rc)
1045 rc = release_rc;
1046 if (rc)
1047 dprintk(1,"returns %d\n",rc);
1048 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049}
1050
1051int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
1052{
1053 int ret;
1054
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001055 if (down_interruptible(&av7110->osd_sema))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 return -ERESTARTSYS;
1057
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 switch (dc->cmd) {
1059 case OSD_Close:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001060 ret = DestroyOSDWindow(av7110, av7110->osdwin);
1061 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 case OSD_Open:
1063 av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001064 ret = CreateOSDWindow(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 bpp2bit[av7110->osdbpp[av7110->osdwin]],
1066 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001067 if (ret)
1068 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (!dc->data) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001070 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1071 if (ret)
1072 break;
1073 ret = SetColorBlend(av7110, av7110->osdwin);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001075 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 case OSD_Show:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001077 ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0);
1078 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 case OSD_Hide:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001080 ret = HideWindow(av7110, av7110->osdwin);
1081 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 case OSD_Clear:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001083 ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);
1084 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 case OSD_Fill:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001086 ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);
1087 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 case OSD_SetColor:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001089 ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);
1090 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 case OSD_SetPalette:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001092 if (FW_VERSION(av7110->arm_app) >= 0x2618)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001094 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 int i, len = dc->x0-dc->color+1;
1096 u8 __user *colors = (u8 __user *)dc->data;
1097 u8 r, g, b, blend;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001098 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 for (i = 0; i<len; i++) {
1100 if (get_user(r, colors + i * 4) ||
1101 get_user(g, colors + i * 4 + 1) ||
1102 get_user(b, colors + i * 4 + 2) ||
1103 get_user(blend, colors + i * 4 + 3)) {
1104 ret = -EFAULT;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001105 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001107 ret = OSDSetColor(av7110, dc->color + i, r, g, b, blend);
1108 if (ret)
1109 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 }
1111 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001112 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 case OSD_SetPixel:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001114 ret = DrawLine(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 dc->x0, dc->y0, 0, 0, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001116 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 case OSD_SetRow:
1118 dc->y1 = dc->y0;
1119 /* fall through */
1120 case OSD_SetBlock:
1121 ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001122 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 case OSD_FillRow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001124 ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 dc->x1-dc->x0+1, dc->y1, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001126 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 case OSD_FillBlock:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001128 ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001130 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 case OSD_Line:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001132 ret = DrawLine(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001134 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 case OSD_Text:
1136 {
1137 char textbuf[240];
1138
1139 if (strncpy_from_user(textbuf, dc->data, 240) < 0) {
1140 ret = -EFAULT;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001141 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 }
1143 textbuf[239] = 0;
1144 if (dc->x1 > 3)
1145 dc->x1 = 3;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001146 ret = SetFont(av7110, av7110->osdwin, dc->x1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 (u16) (dc->color & 0xffff), (u16) (dc->color >> 16));
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001148 if (!ret)
1149 ret = FlushText(av7110);
1150 if (!ret)
1151 ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);
1152 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 }
1154 case OSD_SetWindow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001155 if (dc->x0 < 1 || dc->x0 > 7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001157 else {
1158 av7110->osdwin = dc->x0;
1159 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001161 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 case OSD_MoveWindow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001163 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1164 if (!ret)
1165 ret = SetColorBlend(av7110, av7110->osdwin);
1166 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 case OSD_OpenRaw:
1168 if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
1169 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001170 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001172 if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001174 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 av7110->osdbpp[av7110->osdwin] = 0;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001176 ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001178 if (ret)
1179 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 if (!dc->data) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001181 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1182 if (!ret)
1183 ret = SetColorBlend(av7110, av7110->osdwin);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001185 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 default:
1187 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001188 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 }
1190
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 up(&av7110->osd_sema);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001192 if (ret==-ERESTARTSYS)
1193 dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);
1194 else if (ret)
1195 dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);
1196
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 return ret;
1198}
1199
1200int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
1201{
1202 switch (cap->cmd) {
1203 case OSD_CAP_MEMSIZE:
1204 if (FW_4M_SDRAM(av7110->arm_app))
1205 cap->val = 1000000;
1206 else
1207 cap->val = 92000;
1208 return 0;
1209 default:
1210 return -EINVAL;
1211 }
1212}
1213#endif /* CONFIG_DVB_AV7110_OSD */