blob: 456d529cb3811ac7d880bf75ee89f7e38eb70ae6 [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
Johannes Stezenbachce7d3c12005-09-09 13:03:10 -070044#define _NOHANDSHAKE
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046/****************************************************************************
47 * DEBI functions
48 ****************************************************************************/
49
50/* This DEBI code is based on the Stradis driver
51 by Nathan Laredo <laredo@gnu.org> */
52
53int av7110_debiwrite(struct av7110 *av7110, u32 config,
54 int addr, u32 val, int count)
55{
56 struct saa7146_dev *dev = av7110->dev;
57
58 if (count <= 0 || count > 32764) {
59 printk("%s: invalid count %d\n", __FUNCTION__, count);
60 return -1;
61 }
62 if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
63 printk("%s: wait_for_debi_done failed\n", __FUNCTION__);
64 return -1;
65 }
66 saa7146_write(dev, DEBI_CONFIG, config);
67 if (count <= 4) /* immediate transfer */
68 saa7146_write(dev, DEBI_AD, val);
69 else /* block transfer */
70 saa7146_write(dev, DEBI_AD, av7110->debi_bus);
71 saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
72 saa7146_write(dev, MC2, (2 << 16) | 2);
73 return 0;
74}
75
76u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)
77{
78 struct saa7146_dev *dev = av7110->dev;
79 u32 result = 0;
80
81 if (count > 32764 || count <= 0) {
82 printk("%s: invalid count %d\n", __FUNCTION__, count);
83 return 0;
84 }
85 if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
86 printk("%s: wait_for_debi_done #1 failed\n", __FUNCTION__);
87 return 0;
88 }
89 saa7146_write(dev, DEBI_AD, av7110->debi_bus);
90 saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
91
92 saa7146_write(dev, DEBI_CONFIG, config);
93 saa7146_write(dev, MC2, (2 << 16) | 2);
94 if (count > 4)
95 return count;
96 if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
97 printk("%s: wait_for_debi_done #2 failed\n", __FUNCTION__);
98 return 0;
99 }
100
101 result = saa7146_read(dev, DEBI_AD);
102 result &= (0xffffffffUL >> ((4 - count) * 8));
103 return result;
104}
105
106
107
108/* av7110 ARM core boot stuff */
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700109#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110void av7110_reset_arm(struct av7110 *av7110)
111{
112 saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
113
114 /* Disable DEBI and GPIO irq */
115 SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
116 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
117
118 saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
119 msleep(30); /* the firmware needs some time to initialize */
120
121 ARM_ResetMailBox(av7110);
122
123 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
124 SAA7146_IER_ENABLE(av7110->dev, MASK_03);
125
126 av7110->arm_ready = 1;
127 dprintk(1, "reset ARM\n");
128}
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700129#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
131static int waitdebi(struct av7110 *av7110, int adr, int state)
132{
133 int k;
134
135 dprintk(4, "%p\n", av7110);
136
137 for (k = 0; k < 100; k++) {
138 if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)
139 return 0;
140 udelay(5);
141 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700142 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143}
144
145static int load_dram(struct av7110 *av7110, u32 *data, int len)
146{
147 int i;
148 int blocks, rest;
149 u32 base, bootblock = BOOT_BLOCK;
150
151 dprintk(4, "%p\n", av7110);
152
153 blocks = len / BOOT_MAX_SIZE;
154 rest = len % BOOT_MAX_SIZE;
155 base = DRAM_START_CODE;
156
157 for (i = 0; i < blocks; i++) {
158 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
159 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700160 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 }
162 dprintk(4, "writing DRAM block %d\n", i);
163 mwdebi(av7110, DEBISWAB, bootblock,
164 ((char*)data) + i * BOOT_MAX_SIZE, BOOT_MAX_SIZE);
165 bootblock ^= 0x1400;
166 iwdebi(av7110, DEBISWAB, BOOT_BASE, swab32(base), 4);
167 iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, BOOT_MAX_SIZE, 2);
168 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
169 base += BOOT_MAX_SIZE;
170 }
171
172 if (rest > 0) {
173 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
174 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700175 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 }
177 if (rest > 4)
178 mwdebi(av7110, DEBISWAB, bootblock,
179 ((char*)data) + i * BOOT_MAX_SIZE, rest);
180 else
181 mwdebi(av7110, DEBISWAB, bootblock,
182 ((char*)data) + i * BOOT_MAX_SIZE - 4, rest + 4);
183
184 iwdebi(av7110, DEBISWAB, BOOT_BASE, swab32(base), 4);
185 iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, rest, 2);
186 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
187 }
188 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
189 printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700190 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
192 iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, 0, 2);
193 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
194 if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BOOT_COMPLETE) < 0) {
195 printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700196 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 }
198 return 0;
199}
200
201
202/* we cannot write av7110 DRAM directly, so load a bootloader into
203 * the DPRAM which implements a simple boot protocol */
204static u8 bootcode[] = {
205 0xea, 0x00, 0x00, 0x0e, 0xe1, 0xb0, 0xf0, 0x0e, 0xe2, 0x5e, 0xf0, 0x04,
206 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x08, 0xe2, 0x5e, 0xf0, 0x04,
207 0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04, 0x2c, 0x00, 0x00, 0x24,
208 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x34,
209 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0x5a, 0x5a, 0x00, 0x1f, 0x15, 0x55,
210 0x00, 0x00, 0x00, 0x09, 0xe5, 0x9f, 0xd0, 0x7c, 0xe5, 0x9f, 0x40, 0x74,
211 0xe3, 0xa0, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x04,
212 0xe5, 0x9f, 0x10, 0x70, 0xe5, 0x9f, 0x20, 0x70, 0xe5, 0x9f, 0x30, 0x64,
213 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe1, 0x51, 0x00, 0x02,
214 0xda, 0xff, 0xff, 0xfb, 0xe5, 0x9f, 0xf0, 0x50, 0xe1, 0xd4, 0x10, 0xb0,
215 0xe3, 0x51, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xfc, 0xe1, 0xa0, 0x10, 0x0d,
216 0xe5, 0x94, 0x30, 0x04, 0xe1, 0xd4, 0x20, 0xb2, 0xe2, 0x82, 0x20, 0x3f,
217 0xe1, 0xb0, 0x23, 0x22, 0x03, 0xa0, 0x00, 0x02, 0xe1, 0xc4, 0x00, 0xb0,
218 0x0a, 0xff, 0xff, 0xf4, 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0,
219 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe2, 0x52, 0x20, 0x01,
220 0x1a, 0xff, 0xff, 0xf9, 0xe2, 0x2d, 0xdb, 0x05, 0xea, 0xff, 0xff, 0xec,
221 0x2c, 0x00, 0x03, 0xf8, 0x2c, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x08, 0x00,
222 0x2c, 0x00, 0x00, 0x74, 0x2c, 0x00, 0x00, 0xc0
223};
224
225int av7110_bootarm(struct av7110 *av7110)
226{
227 struct saa7146_dev *dev = av7110->dev;
228 u32 ret;
229 int i;
230
231 dprintk(4, "%p\n", av7110);
232
233 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
234
235 /* Disable DEBI and GPIO irq */
236 SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
237 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
238
239 /* enable DEBI */
240 saa7146_write(av7110->dev, MC1, 0x08800880);
241 saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
242 saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
243
244 /* test DEBI */
245 iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
246 if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
247 printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: "
248 "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
249 ret, 0x10325476);
250 return -1;
251 }
252 for (i = 0; i < 8192; i += 4)
253 iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);
254 dprintk(2, "debi test OK\n");
255
256 /* boot */
257 dprintk(1, "load boot code\n");
258 saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
259 //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
260 //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
261
262 mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode));
263 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
264
265 if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
266 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
267 "saa7146_wait_for_debi_done() timed out\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700268 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 }
270 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
271 mdelay(1);
272
273 dprintk(1, "load dram code\n");
274 if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {
275 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
276 "load_dram() failed\n");
277 return -1;
278 }
279
280 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
281 mdelay(1);
282
283 dprintk(1, "load dpram code\n");
284 mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);
285
286 if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
287 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
288 "saa7146_wait_for_debi_done() timed out after loading DRAM\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700289 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
291 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
292 msleep(30); /* the firmware needs some time to initialize */
293
294 //ARM_ClearIrq(av7110);
295 ARM_ResetMailBox(av7110);
296 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
297 SAA7146_IER_ENABLE(av7110->dev, MASK_03);
298
299 av7110->arm_errors = 0;
300 av7110->arm_ready = 1;
301 return 0;
302}
303
304
305/****************************************************************************
306 * DEBI command polling
307 ****************************************************************************/
308
309int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
310{
311 unsigned long start;
312 u32 stat;
Oliver Endriss25de1922005-07-07 17:58:28 -0700313 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315 if (FW_VERSION(av7110->arm_app) <= 0x261c) {
316 /* not supported by old firmware */
317 msleep(50);
318 return 0;
319 }
320
321 /* new firmware */
322 start = jiffies;
323 for (;;) {
Oliver Endriss25de1922005-07-07 17:58:28 -0700324 err = time_after(jiffies, start + ARM_WAIT_FREE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 if (down_interruptible(&av7110->dcomlock))
326 return -ERESTARTSYS;
327 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
328 up(&av7110->dcomlock);
Oliver Endriss25de1922005-07-07 17:58:28 -0700329 if ((stat & flags) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 break;
Oliver Endriss25de1922005-07-07 17:58:28 -0700331 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
333 __FUNCTION__, stat & flags);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700334 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }
336 msleep(1);
337 }
338 return 0;
339}
340
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700341static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
343 int i;
344 unsigned long start;
345 char *type = NULL;
346 u16 flags[2] = {0, 0};
347 u32 stat;
Oliver Endriss25de1922005-07-07 17:58:28 -0700348 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
350// dprintk(4, "%p\n", av7110);
351
352 if (!av7110->arm_ready) {
353 dprintk(1, "arm not ready.\n");
354 return -ENXIO;
355 }
356
357 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700358 while (1) {
359 err = time_after(jiffies, start + ARM_WAIT_FREE);
360 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
361 break;
362 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __FUNCTION__);
364 return -ETIMEDOUT;
365 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700366 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 }
368
369 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);
370
371#ifndef _NOHANDSHAKE
372 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700373 while (1) {
374 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
375 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
376 break;
377 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
379 return -ETIMEDOUT;
380 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700381 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
383#endif
384
385 switch ((buf[0] >> 8) & 0xff) {
386 case COMTYPE_PIDFILTER:
387 case COMTYPE_ENCODER:
388 case COMTYPE_REC_PLAY:
389 case COMTYPE_MPEGDECODER:
390 type = "MSG";
391 flags[0] = GPMQOver;
392 flags[1] = GPMQFull;
393 break;
394 case COMTYPE_OSD:
395 type = "OSD";
396 flags[0] = OSDQOver;
397 flags[1] = OSDQFull;
398 break;
399 case COMTYPE_MISC:
400 if (FW_VERSION(av7110->arm_app) >= 0x261d) {
401 type = "MSG";
402 flags[0] = GPMQOver;
403 flags[1] = GPMQBusy;
404 }
405 break;
406 default:
407 break;
408 }
409
410 if (type != NULL) {
411 /* non-immediate COMMAND type */
412 start = jiffies;
413 for (;;) {
Oliver Endriss25de1922005-07-07 17:58:28 -0700414 err = time_after(jiffies, start + ARM_WAIT_FREE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
416 if (stat & flags[0]) {
417 printk(KERN_ERR "%s: %s QUEUE overflow\n",
418 __FUNCTION__, type);
419 return -1;
420 }
421 if ((stat & flags[1]) == 0)
422 break;
Oliver Endriss25de1922005-07-07 17:58:28 -0700423 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
425 __FUNCTION__, type);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700426 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 }
428 msleep(1);
429 }
430 }
431
432 for (i = 2; i < length; i++)
433 wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);
434
435 if (length)
436 wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
437 else
438 wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);
439
440 wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
441
442 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);
443
444#ifdef COM_DEBUG
445 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700446 while (1) {
447 err = time_after(jiffies, start + ARM_WAIT_FREE);
448 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
449 break;
450 if (err) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700451 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",
Oliver Endriss25de1922005-07-07 17:58:28 -0700452 __FUNCTION__, (buf[0] >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 return -ETIMEDOUT;
454 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700455 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 }
457
458 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
459 if (stat & GPMQOver) {
460 printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __FUNCTION__);
461 return -ENOSPC;
462 }
463 else if (stat & OSDQOver) {
464 printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __FUNCTION__);
465 return -ENOSPC;
466 }
467#endif
468
469 return 0;
470}
471
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700472static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473{
474 int ret;
475
476// dprintk(4, "%p\n", av7110);
477
478 if (!av7110->arm_ready) {
479 dprintk(1, "arm not ready.\n");
480 return -1;
481 }
482 if (down_interruptible(&av7110->dcomlock))
483 return -ERESTARTSYS;
484
485 ret = __av7110_send_fw_cmd(av7110, buf, length);
486 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700487 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
489 __FUNCTION__, ret);
490 return ret;
491}
492
493int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
494{
495 va_list args;
496 u16 buf[num + 2];
497 int i, ret;
498
499// dprintk(4, "%p\n", av7110);
500
501 buf[0] = ((type << 8) | com);
502 buf[1] = num;
503
504 if (num) {
505 va_start(args, num);
506 for (i = 0; i < num; i++)
507 buf[i + 2] = va_arg(args, u32);
508 va_end(args);
509 }
510
511 ret = av7110_send_fw_cmd(av7110, buf, num + 2);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700512 if (ret && ret != -ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
514 return ret;
515}
516
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700517#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
519{
520 int i, ret;
521 u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
522 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
523
524 dprintk(4, "%p\n", av7110);
525
526 for(i = 0; i < len && i < 32; i++)
527 {
528 if(i % 2 == 0)
529 cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
530 else
531 cmd[(i / 2) + 2] |= buf[i];
532 }
533
534 ret = av7110_send_fw_cmd(av7110, cmd, 18);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700535 if (ret && ret != -ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
537 return ret;
538}
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700539#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
542 int request_buf_len, u16 *reply_buf, int reply_buf_len)
543{
544 int err;
545 s16 i;
546 unsigned long start;
547#ifdef COM_DEBUG
548 u32 stat;
549#endif
550
551 dprintk(4, "%p\n", av7110);
552
553 if (!av7110->arm_ready) {
554 dprintk(1, "arm not ready.\n");
555 return -1;
556 }
557
558 if (down_interruptible(&av7110->dcomlock))
559 return -ERESTARTSYS;
560
561 if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {
562 up(&av7110->dcomlock);
563 printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
564 return err;
565 }
566
567 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700568 while (1) {
569 err = time_after(jiffies, start + ARM_WAIT_FREE);
570 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
571 break;
572 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __FUNCTION__);
574 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700575 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700577#ifdef _NOHANDSHAKE
578 msleep(1);
579#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581
582#ifndef _NOHANDSHAKE
583 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700584 while (1) {
585 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
586 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
587 break;
588 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
590 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700591 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700593 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 }
595#endif
596
597#ifdef COM_DEBUG
598 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
599 if (stat & GPMQOver) {
600 printk(KERN_ERR "%s: GPMQOver\n", __FUNCTION__);
601 up(&av7110->dcomlock);
602 return -1;
603 }
604 else if (stat & OSDQOver) {
605 printk(KERN_ERR "%s: OSDQOver\n", __FUNCTION__);
606 up(&av7110->dcomlock);
607 return -1;
608 }
609#endif
610
611 for (i = 0; i < reply_buf_len; i++)
612 reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);
613
614 up(&av7110->dcomlock);
615 return 0;
616}
617
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700618static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
620 int ret;
621 ret = av7110_fw_request(av7110, &tag, 0, buf, length);
622 if (ret)
623 printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
624 return ret;
625}
626
627
628/****************************************************************************
629 * Firmware commands
630 ****************************************************************************/
631
632/* get version of the firmware ROM, RTSL, video ucode and ARM application */
633int av7110_firmversion(struct av7110 *av7110)
634{
635 u16 buf[20];
636 u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
637
638 dprintk(4, "%p\n", av7110);
639
640 if (av7110_fw_query(av7110, tag, buf, 16)) {
641 printk("dvb-ttpci: failed to boot firmware @ card %d\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700642 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return -EIO;
644 }
645
646 av7110->arm_fw = (buf[0] << 16) + buf[1];
647 av7110->arm_rtsl = (buf[2] << 16) + buf[3];
648 av7110->arm_vid = (buf[4] << 16) + buf[5];
649 av7110->arm_app = (buf[6] << 16) + buf[7];
650 av7110->avtype = (buf[8] << 16) + buf[9];
651
652 printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700653 av7110->dvb_adapter.num, av7110->arm_fw,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
655
656 /* print firmware capabilities */
657 if (FW_CI_LL_SUPPORT(av7110->arm_app))
658 printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700659 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 else
661 printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700662 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 return 0;
665}
666
667
668int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
669{
670 int i, ret;
671 u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
672 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
673
674 dprintk(4, "%p\n", av7110);
675
676 if (len > 10)
677 len = 10;
678
679 buf[1] = len + 2;
680 buf[2] = len;
681
682 if (burst != -1)
683 buf[3] = burst ? 0x01 : 0x00;
684 else
685 buf[3] = 0xffff;
686
687 for (i = 0; i < len; i++)
688 buf[i + 4] = msg[i];
689
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700690 ret = av7110_send_fw_cmd(av7110, buf, 18);
691 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700693 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694}
695
696
697#ifdef CONFIG_DVB_AV7110_OSD
698
699static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
700{
701 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);
702}
703
704static inline int SetBlend_(struct av7110 *av7110, u8 windownr,
705 enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
706{
707 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,
708 windownr, colordepth, index, blending);
709}
710
711static inline int SetColor_(struct av7110 *av7110, u8 windownr,
712 enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
713{
714 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,
715 windownr, colordepth, index, colorhi, colorlo);
716}
717
718static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
719 u16 colorfg, u16 colorbg)
720{
721 return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,
722 windownr, fontsize, colorfg, colorbg);
723}
724
725static int FlushText(struct av7110 *av7110)
726{
727 unsigned long start;
Oliver Endriss25de1922005-07-07 17:58:28 -0700728 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729
730 if (down_interruptible(&av7110->dcomlock))
731 return -ERESTARTSYS;
732 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700733 while (1) {
734 err = time_after(jiffies, start + ARM_WAIT_OSD);
735 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
736 break;
737 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
739 __FUNCTION__);
740 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700741 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700743 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 }
745 up(&av7110->dcomlock);
746 return 0;
747}
748
749static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, u8* buf)
750{
751 int i, ret;
752 unsigned long start;
753 int length = strlen(buf) + 1;
754 u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
755
756 if (down_interruptible(&av7110->dcomlock))
757 return -ERESTARTSYS;
758
759 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700760 while (1) {
761 ret = time_after(jiffies, start + ARM_WAIT_OSD);
762 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
763 break;
764 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
766 __FUNCTION__);
767 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700768 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700770 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 }
772#ifndef _NOHANDSHAKE
773 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700774 while (1) {
775 ret = time_after(jiffies, start + ARM_WAIT_SHAKE);
776 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
777 break;
778 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
780 __FUNCTION__);
781 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700782 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700784 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 }
786#endif
787 for (i = 0; i < length / 2; i++)
788 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
789 swab16(*(u16 *)(buf + 2 * i)), 2);
790 if (length & 1)
791 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);
792 ret = __av7110_send_fw_cmd(av7110, cbuf, 5);
793 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700794 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
796 return ret;
797}
798
799static inline int DrawLine(struct av7110 *av7110, u8 windownr,
800 u16 x, u16 y, u16 dx, u16 dy, u16 color)
801{
802 return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6,
803 windownr, x, y, dx, dy, color);
804}
805
806static inline int DrawBlock(struct av7110 *av7110, u8 windownr,
807 u16 x, u16 y, u16 dx, u16 dy, u16 color)
808{
809 return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,
810 windownr, x, y, dx, dy, color);
811}
812
813static inline int HideWindow(struct av7110 *av7110, u8 windownr)
814{
815 return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);
816}
817
818static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
819{
820 return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);
821}
822
823static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
824{
825 return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);
826}
827
828static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
829{
830 return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);
831}
832
833static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
834 osd_raw_window_t disptype,
835 u16 width, u16 height)
836{
837 return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,
838 windownr, disptype, width, height);
839}
840
841
842static enum av7110_osd_palette_type bpp2pal[8] = {
843 Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
844};
845static osd_raw_window_t bpp2bit[8] = {
846 OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
847};
848
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700849static inline int WaitUntilBmpLoaded(struct av7110 *av7110)
850{
851 int ret = wait_event_interruptible_timeout(av7110->bmpq,
852 av7110->bmp_state != BMP_LOADING, 10*HZ);
853 if (ret == -ERESTARTSYS)
854 return ret;
855 if (ret == 0) {
856 printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
857 ret, av7110->bmp_state);
858 av7110->bmp_state = BMP_NONE;
859 return -ETIMEDOUT;
860 }
861 return 0;
862}
863
864static inline int LoadBitmap(struct av7110 *av7110,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 u16 dx, u16 dy, int inc, u8 __user * data)
866{
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700867 u16 format;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 int bpp;
869 int i;
870 int d, delta;
871 u8 c;
872 int ret;
873
874 dprintk(4, "%p\n", av7110);
875
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700876 format = bpp2bit[av7110->osdbpp[av7110->osdwin]];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
878 av7110->bmp_state = BMP_LOADING;
879 if (format == OSD_BITMAP8) {
880 bpp=8; delta = 1;
881 } else if (format == OSD_BITMAP4) {
882 bpp=4; delta = 2;
883 } else if (format == OSD_BITMAP2) {
884 bpp=2; delta = 4;
885 } else if (format == OSD_BITMAP1) {
886 bpp=1; delta = 8;
887 } else {
888 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700889 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 }
891 av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
892 av7110->bmpp = 0;
893 if (av7110->bmplen > 32768) {
894 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700895 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 }
897 for (i = 0; i < dy; i++) {
898 if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {
899 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700900 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 }
902 }
903 if (format != OSD_BITMAP8) {
904 for (i = 0; i < dx * dy / delta; i++) {
905 c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
906 for (d = delta - 2; d >= 0; d--) {
907 c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
908 << ((delta - d - 1) * bpp));
909 ((u8 *)av7110->bmpbuf)[1024 + i] = c;
910 }
911 }
912 }
913 av7110->bmplen += 1024;
914 dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700915 ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
916 if (!ret)
917 ret = WaitUntilBmpLoaded(av7110);
918 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919}
920
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700921static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 dprintk(4, "%p\n", av7110);
924
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700925 return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926}
927
928static inline int ReleaseBitmap(struct av7110 *av7110)
929{
930 dprintk(4, "%p\n", av7110);
931
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700932 if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 return -1;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700934 if (av7110->bmp_state == BMP_LOADING)
935 dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 av7110->bmp_state = BMP_NONE;
937 return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);
938}
939
940static u32 RGB2YUV(u16 R, u16 G, u16 B)
941{
942 u16 y, u, v;
943 u16 Y, Cr, Cb;
944
945 y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */
946 u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */
947 v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */
948
949 Y = y / 256;
950 Cb = u / 16;
951 Cr = v / 16;
952
953 return Cr | (Cb << 16) | (Y << 8);
954}
955
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700956static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957{
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700958 int ret;
959
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 u16 ch, cl;
961 u32 yuv;
962
963 yuv = blend ? RGB2YUV(r,g,b) : 0;
964 cl = (yuv & 0xffff);
965 ch = ((yuv >> 16) & 0xffff);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700966 ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
967 color, ch, cl);
968 if (!ret)
969 ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
970 color, ((blend >> 4) & 0x0f));
971 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972}
973
974static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
975{
976 int i;
977 int length = last - first + 1;
978
979 if (length * 4 > DATA_BUFF3_SIZE)
980 return -EINVAL;
981
982 for (i = 0; i < length; i++) {
983 u32 color, blend, yuv;
984
985 if (get_user(color, colors + i))
986 return -EFAULT;
987 blend = (color & 0xF0000000) >> 4;
988 yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,
989 (color >> 16) & 0xFF) | blend : 0;
990 yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
991 wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);
992 }
993 return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,
994 av7110->osdwin,
995 bpp2pal[av7110->osdbpp[av7110->osdwin]],
996 first, last);
997}
998
999static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
1000 int x1, int y1, int inc, u8 __user * data)
1001{
1002 uint w, h, bpp, bpl, size, lpb, bnum, brest;
1003 int i;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001004 int rc,release_rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
1006 w = x1 - x0 + 1;
1007 h = y1 - y0 + 1;
1008 if (inc <= 0)
1009 inc = w;
1010 if (w <= 0 || w > 720 || h <= 0 || h > 576)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001011 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 bpp = av7110->osdbpp[av7110->osdwin] + 1;
1013 bpl = ((w * bpp + 7) & ~7) / 8;
1014 size = h * bpl;
1015 lpb = (32 * 1024) / bpl;
1016 bnum = size / (lpb * bpl);
1017 brest = size - bnum * lpb * bpl;
1018
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001019 if (av7110->bmp_state == BMP_LOADING) {
1020 /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */
1021 BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e);
1022 rc = WaitUntilBmpLoaded(av7110);
1023 if (rc)
1024 return rc;
1025 /* just continue. This should work for all fw versions
1026 * if bnum==1 && !brest && LoadBitmap was successful
1027 */
1028 }
1029
1030 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 for (i = 0; i < bnum; i++) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001032 rc = LoadBitmap(av7110, w, lpb, inc, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 if (rc)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001034 break;
1035 rc = BlitBitmap(av7110, x0, y0 + i * lpb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 if (rc)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001037 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 data += lpb * inc;
1039 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001040 if (!rc && brest) {
1041 rc = LoadBitmap(av7110, w, brest / bpl, inc, data);
1042 if (!rc)
1043 rc = BlitBitmap(av7110, x0, y0 + bnum * lpb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001045 release_rc = ReleaseBitmap(av7110);
1046 if (!rc)
1047 rc = release_rc;
1048 if (rc)
1049 dprintk(1,"returns %d\n",rc);
1050 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051}
1052
1053int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
1054{
1055 int ret;
1056
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001057 if (down_interruptible(&av7110->osd_sema))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 return -ERESTARTSYS;
1059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 switch (dc->cmd) {
1061 case OSD_Close:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001062 ret = DestroyOSDWindow(av7110, av7110->osdwin);
1063 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 case OSD_Open:
1065 av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001066 ret = CreateOSDWindow(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 bpp2bit[av7110->osdbpp[av7110->osdwin]],
1068 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001069 if (ret)
1070 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (!dc->data) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001072 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1073 if (ret)
1074 break;
1075 ret = SetColorBlend(av7110, av7110->osdwin);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001077 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 case OSD_Show:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001079 ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0);
1080 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 case OSD_Hide:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001082 ret = HideWindow(av7110, av7110->osdwin);
1083 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 case OSD_Clear:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001085 ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);
1086 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 case OSD_Fill:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001088 ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);
1089 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 case OSD_SetColor:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001091 ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);
1092 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 case OSD_SetPalette:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001094 if (FW_VERSION(av7110->arm_app) >= 0x2618)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001096 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 int i, len = dc->x0-dc->color+1;
1098 u8 __user *colors = (u8 __user *)dc->data;
1099 u8 r, g, b, blend;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001100 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 for (i = 0; i<len; i++) {
1102 if (get_user(r, colors + i * 4) ||
1103 get_user(g, colors + i * 4 + 1) ||
1104 get_user(b, colors + i * 4 + 2) ||
1105 get_user(blend, colors + i * 4 + 3)) {
1106 ret = -EFAULT;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001107 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001109 ret = OSDSetColor(av7110, dc->color + i, r, g, b, blend);
1110 if (ret)
1111 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 }
1113 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001114 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 case OSD_SetPixel:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001116 ret = DrawLine(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 dc->x0, dc->y0, 0, 0, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001118 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 case OSD_SetRow:
1120 dc->y1 = dc->y0;
1121 /* fall through */
1122 case OSD_SetBlock:
1123 ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001124 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 case OSD_FillRow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001126 ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 dc->x1-dc->x0+1, dc->y1, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001128 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 case OSD_FillBlock:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001130 ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001132 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 case OSD_Line:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001134 ret = DrawLine(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001136 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 case OSD_Text:
1138 {
1139 char textbuf[240];
1140
1141 if (strncpy_from_user(textbuf, dc->data, 240) < 0) {
1142 ret = -EFAULT;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001143 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 }
1145 textbuf[239] = 0;
1146 if (dc->x1 > 3)
1147 dc->x1 = 3;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001148 ret = SetFont(av7110, av7110->osdwin, dc->x1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 (u16) (dc->color & 0xffff), (u16) (dc->color >> 16));
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001150 if (!ret)
1151 ret = FlushText(av7110);
1152 if (!ret)
1153 ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);
1154 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 }
1156 case OSD_SetWindow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001157 if (dc->x0 < 1 || dc->x0 > 7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001159 else {
1160 av7110->osdwin = dc->x0;
1161 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001163 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 case OSD_MoveWindow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001165 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1166 if (!ret)
1167 ret = SetColorBlend(av7110, av7110->osdwin);
1168 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 case OSD_OpenRaw:
1170 if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
1171 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001172 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001174 if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001176 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 av7110->osdbpp[av7110->osdwin] = 0;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001178 ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001180 if (ret)
1181 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 if (!dc->data) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001183 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1184 if (!ret)
1185 ret = SetColorBlend(av7110, av7110->osdwin);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001187 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 default:
1189 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001190 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
1192
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 up(&av7110->osd_sema);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001194 if (ret==-ERESTARTSYS)
1195 dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);
1196 else if (ret)
1197 dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);
1198
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 return ret;
1200}
1201
1202int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
1203{
1204 switch (cap->cmd) {
1205 case OSD_CAP_MEMSIZE:
1206 if (FW_4M_SDRAM(av7110->arm_app))
1207 cap->val = 1000000;
1208 else
1209 cap->val = 92000;
1210 return 0;
1211 default:
1212 return -EINVAL;
1213 }
1214}
1215#endif /* CONFIG_DVB_AV7110_OSD */