blob: cb377452b57dcfef26ffa9cd070588f060fe55f2 [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
Oliver Endriss66190a22006-01-09 15:32:42 -0200233 av7110->arm_ready = 0;
234
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
236
237 /* Disable DEBI and GPIO irq */
238 SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
239 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
240
241 /* enable DEBI */
242 saa7146_write(av7110->dev, MC1, 0x08800880);
243 saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
244 saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
245
246 /* test DEBI */
247 iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
248 if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
249 printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: "
250 "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
251 ret, 0x10325476);
252 return -1;
253 }
254 for (i = 0; i < 8192; i += 4)
255 iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);
256 dprintk(2, "debi test OK\n");
257
258 /* boot */
259 dprintk(1, "load boot code\n");
260 saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
261 //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
262 //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
263
264 mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode));
265 iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
266
267 if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
268 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
269 "saa7146_wait_for_debi_done() timed out\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700270 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 }
272 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
273 mdelay(1);
274
275 dprintk(1, "load dram code\n");
276 if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {
277 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
278 "load_dram() failed\n");
279 return -1;
280 }
281
282 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
283 mdelay(1);
284
285 dprintk(1, "load dpram code\n");
286 mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);
287
288 if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
289 printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
290 "saa7146_wait_for_debi_done() timed out after loading DRAM\n");
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700291 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
293 saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
294 msleep(30); /* the firmware needs some time to initialize */
295
296 //ARM_ClearIrq(av7110);
297 ARM_ResetMailBox(av7110);
298 SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
299 SAA7146_IER_ENABLE(av7110->dev, MASK_03);
300
301 av7110->arm_errors = 0;
302 av7110->arm_ready = 1;
303 return 0;
304}
305
306
307/****************************************************************************
308 * DEBI command polling
309 ****************************************************************************/
310
311int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
312{
313 unsigned long start;
314 u32 stat;
Oliver Endriss25de1922005-07-07 17:58:28 -0700315 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 if (FW_VERSION(av7110->arm_app) <= 0x261c) {
318 /* not supported by old firmware */
319 msleep(50);
320 return 0;
321 }
322
323 /* new firmware */
324 start = jiffies;
325 for (;;) {
Oliver Endriss25de1922005-07-07 17:58:28 -0700326 err = time_after(jiffies, start + ARM_WAIT_FREE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 if (down_interruptible(&av7110->dcomlock))
328 return -ERESTARTSYS;
329 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
330 up(&av7110->dcomlock);
Oliver Endriss25de1922005-07-07 17:58:28 -0700331 if ((stat & flags) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 break;
Oliver Endriss25de1922005-07-07 17:58:28 -0700333 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
335 __FUNCTION__, stat & flags);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700336 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 }
338 msleep(1);
339 }
340 return 0;
341}
342
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700343static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
345 int i;
346 unsigned long start;
347 char *type = NULL;
348 u16 flags[2] = {0, 0};
349 u32 stat;
Oliver Endriss25de1922005-07-07 17:58:28 -0700350 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
352// dprintk(4, "%p\n", av7110);
353
354 if (!av7110->arm_ready) {
355 dprintk(1, "arm not ready.\n");
356 return -ENXIO;
357 }
358
359 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700360 while (1) {
361 err = time_after(jiffies, start + ARM_WAIT_FREE);
362 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
363 break;
364 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __FUNCTION__);
Oliver Endriss66190a22006-01-09 15:32:42 -0200366 av7110->arm_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 return -ETIMEDOUT;
368 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700369 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
371
Oliver Endriss9a7b1022005-09-09 13:03:11 -0700372 if (FW_VERSION(av7110->arm_app) <= 0x261f)
373 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
375#ifndef _NOHANDSHAKE
376 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700377 while (1) {
378 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
379 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
380 break;
381 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
383 return -ETIMEDOUT;
384 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700385 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 }
387#endif
388
389 switch ((buf[0] >> 8) & 0xff) {
390 case COMTYPE_PIDFILTER:
391 case COMTYPE_ENCODER:
392 case COMTYPE_REC_PLAY:
393 case COMTYPE_MPEGDECODER:
394 type = "MSG";
395 flags[0] = GPMQOver;
396 flags[1] = GPMQFull;
397 break;
398 case COMTYPE_OSD:
399 type = "OSD";
400 flags[0] = OSDQOver;
401 flags[1] = OSDQFull;
402 break;
403 case COMTYPE_MISC:
404 if (FW_VERSION(av7110->arm_app) >= 0x261d) {
405 type = "MSG";
406 flags[0] = GPMQOver;
407 flags[1] = GPMQBusy;
408 }
409 break;
410 default:
411 break;
412 }
413
414 if (type != NULL) {
415 /* non-immediate COMMAND type */
416 start = jiffies;
417 for (;;) {
Oliver Endriss25de1922005-07-07 17:58:28 -0700418 err = time_after(jiffies, start + ARM_WAIT_FREE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
420 if (stat & flags[0]) {
421 printk(KERN_ERR "%s: %s QUEUE overflow\n",
422 __FUNCTION__, type);
423 return -1;
424 }
425 if ((stat & flags[1]) == 0)
426 break;
Oliver Endriss25de1922005-07-07 17:58:28 -0700427 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
429 __FUNCTION__, type);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700430 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 }
432 msleep(1);
433 }
434 }
435
436 for (i = 2; i < length; i++)
437 wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);
438
439 if (length)
440 wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
441 else
442 wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);
443
444 wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
445
Oliver Endriss9a7b1022005-09-09 13:03:11 -0700446 if (FW_VERSION(av7110->arm_app) <= 0x261f)
447 wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448
449#ifdef COM_DEBUG
450 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700451 while (1) {
452 err = time_after(jiffies, start + ARM_WAIT_FREE);
453 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
454 break;
455 if (err) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700456 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",
Oliver Endriss25de1922005-07-07 17:58:28 -0700457 __FUNCTION__, (buf[0] >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 return -ETIMEDOUT;
459 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700460 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 }
462
463 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
464 if (stat & GPMQOver) {
465 printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __FUNCTION__);
466 return -ENOSPC;
467 }
468 else if (stat & OSDQOver) {
469 printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __FUNCTION__);
470 return -ENOSPC;
471 }
472#endif
473
474 return 0;
475}
476
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700477static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
479 int ret;
480
481// dprintk(4, "%p\n", av7110);
482
483 if (!av7110->arm_ready) {
484 dprintk(1, "arm not ready.\n");
485 return -1;
486 }
487 if (down_interruptible(&av7110->dcomlock))
488 return -ERESTARTSYS;
489
490 ret = __av7110_send_fw_cmd(av7110, buf, length);
491 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700492 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
494 __FUNCTION__, ret);
495 return ret;
496}
497
498int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
499{
500 va_list args;
501 u16 buf[num + 2];
502 int i, ret;
503
504// dprintk(4, "%p\n", av7110);
505
506 buf[0] = ((type << 8) | com);
507 buf[1] = num;
508
509 if (num) {
510 va_start(args, num);
511 for (i = 0; i < num; i++)
512 buf[i + 2] = va_arg(args, u32);
513 va_end(args);
514 }
515
516 ret = av7110_send_fw_cmd(av7110, buf, num + 2);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700517 if (ret && ret != -ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
519 return ret;
520}
521
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700522#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
524{
525 int i, ret;
526 u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
527 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
528
529 dprintk(4, "%p\n", av7110);
530
531 for(i = 0; i < len && i < 32; i++)
532 {
533 if(i % 2 == 0)
534 cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
535 else
536 cmd[(i / 2) + 2] |= buf[i];
537 }
538
539 ret = av7110_send_fw_cmd(av7110, cmd, 18);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700540 if (ret && ret != -ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
542 return ret;
543}
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700544#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
546int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
547 int request_buf_len, u16 *reply_buf, int reply_buf_len)
548{
549 int err;
550 s16 i;
551 unsigned long start;
552#ifdef COM_DEBUG
553 u32 stat;
554#endif
555
556 dprintk(4, "%p\n", av7110);
557
558 if (!av7110->arm_ready) {
559 dprintk(1, "arm not ready.\n");
560 return -1;
561 }
562
563 if (down_interruptible(&av7110->dcomlock))
564 return -ERESTARTSYS;
565
566 if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {
567 up(&av7110->dcomlock);
568 printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
569 return err;
570 }
571
572 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700573 while (1) {
574 err = time_after(jiffies, start + ARM_WAIT_FREE);
575 if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
576 break;
577 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __FUNCTION__);
579 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700580 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700582#ifdef _NOHANDSHAKE
583 msleep(1);
584#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 }
586
587#ifndef _NOHANDSHAKE
588 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700589 while (1) {
590 err = time_after(jiffies, start + ARM_WAIT_SHAKE);
591 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
592 break;
593 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
595 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700596 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700598 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 }
600#endif
601
602#ifdef COM_DEBUG
603 stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
604 if (stat & GPMQOver) {
605 printk(KERN_ERR "%s: GPMQOver\n", __FUNCTION__);
606 up(&av7110->dcomlock);
607 return -1;
608 }
609 else if (stat & OSDQOver) {
610 printk(KERN_ERR "%s: OSDQOver\n", __FUNCTION__);
611 up(&av7110->dcomlock);
612 return -1;
613 }
614#endif
615
616 for (i = 0; i < reply_buf_len; i++)
617 reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);
618
619 up(&av7110->dcomlock);
620 return 0;
621}
622
Johannes Stezenbachd91b7302005-05-16 21:54:38 -0700623static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
625 int ret;
626 ret = av7110_fw_request(av7110, &tag, 0, buf, length);
627 if (ret)
628 printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
629 return ret;
630}
631
632
633/****************************************************************************
634 * Firmware commands
635 ****************************************************************************/
636
637/* get version of the firmware ROM, RTSL, video ucode and ARM application */
638int av7110_firmversion(struct av7110 *av7110)
639{
640 u16 buf[20];
641 u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
642
643 dprintk(4, "%p\n", av7110);
644
645 if (av7110_fw_query(av7110, tag, buf, 16)) {
646 printk("dvb-ttpci: failed to boot firmware @ card %d\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700647 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 return -EIO;
649 }
650
651 av7110->arm_fw = (buf[0] << 16) + buf[1];
652 av7110->arm_rtsl = (buf[2] << 16) + buf[3];
653 av7110->arm_vid = (buf[4] << 16) + buf[5];
654 av7110->arm_app = (buf[6] << 16) + buf[7];
655 av7110->avtype = (buf[8] << 16) + buf[9];
656
657 printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700658 av7110->dvb_adapter.num, av7110->arm_fw,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
660
661 /* print firmware capabilities */
662 if (FW_CI_LL_SUPPORT(av7110->arm_app))
663 printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700664 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 else
666 printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
Johannes Stezenbachfdc53a62005-05-16 21:54:39 -0700667 av7110->dvb_adapter.num);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 return 0;
670}
671
672
673int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
674{
675 int i, ret;
676 u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
677 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
678
679 dprintk(4, "%p\n", av7110);
680
681 if (len > 10)
682 len = 10;
683
684 buf[1] = len + 2;
685 buf[2] = len;
686
687 if (burst != -1)
688 buf[3] = burst ? 0x01 : 0x00;
689 else
690 buf[3] = 0xffff;
691
692 for (i = 0; i < len; i++)
693 buf[i + 4] = msg[i];
694
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700695 ret = av7110_send_fw_cmd(av7110, buf, 18);
696 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700698 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699}
700
701
702#ifdef CONFIG_DVB_AV7110_OSD
703
704static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
705{
706 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);
707}
708
709static inline int SetBlend_(struct av7110 *av7110, u8 windownr,
710 enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
711{
712 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,
713 windownr, colordepth, index, blending);
714}
715
716static inline int SetColor_(struct av7110 *av7110, u8 windownr,
717 enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
718{
719 return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,
720 windownr, colordepth, index, colorhi, colorlo);
721}
722
723static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
724 u16 colorfg, u16 colorbg)
725{
726 return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,
727 windownr, fontsize, colorfg, colorbg);
728}
729
730static int FlushText(struct av7110 *av7110)
731{
732 unsigned long start;
Oliver Endriss25de1922005-07-07 17:58:28 -0700733 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
735 if (down_interruptible(&av7110->dcomlock))
736 return -ERESTARTSYS;
737 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700738 while (1) {
739 err = time_after(jiffies, start + ARM_WAIT_OSD);
740 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
741 break;
742 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
744 __FUNCTION__);
745 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700746 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700748 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 }
750 up(&av7110->dcomlock);
751 return 0;
752}
753
754static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, u8* buf)
755{
756 int i, ret;
757 unsigned long start;
758 int length = strlen(buf) + 1;
759 u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
760
761 if (down_interruptible(&av7110->dcomlock))
762 return -ERESTARTSYS;
763
764 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700765 while (1) {
766 ret = time_after(jiffies, start + ARM_WAIT_OSD);
767 if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
768 break;
769 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
771 __FUNCTION__);
772 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700773 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700775 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 }
777#ifndef _NOHANDSHAKE
778 start = jiffies;
Oliver Endriss25de1922005-07-07 17:58:28 -0700779 while (1) {
780 ret = time_after(jiffies, start + ARM_WAIT_SHAKE);
781 if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
782 break;
783 if (ret) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
785 __FUNCTION__);
786 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700787 return -ETIMEDOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 }
Johannes Stezenbach7d87bc32005-07-07 17:57:56 -0700789 msleep(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 }
791#endif
792 for (i = 0; i < length / 2; i++)
793 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
794 swab16(*(u16 *)(buf + 2 * i)), 2);
795 if (length & 1)
796 wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);
797 ret = __av7110_send_fw_cmd(av7110, cbuf, 5);
798 up(&av7110->dcomlock);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700799 if (ret && ret!=-ERESTARTSYS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
801 return ret;
802}
803
804static inline int DrawLine(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, DLine, 6,
808 windownr, x, y, dx, dy, color);
809}
810
811static inline int DrawBlock(struct av7110 *av7110, u8 windownr,
812 u16 x, u16 y, u16 dx, u16 dy, u16 color)
813{
814 return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,
815 windownr, x, y, dx, dy, color);
816}
817
818static inline int HideWindow(struct av7110 *av7110, u8 windownr)
819{
820 return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);
821}
822
823static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
824{
825 return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);
826}
827
828static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
829{
830 return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);
831}
832
833static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
834{
835 return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);
836}
837
838static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
839 osd_raw_window_t disptype,
840 u16 width, u16 height)
841{
842 return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,
843 windownr, disptype, width, height);
844}
845
846
847static enum av7110_osd_palette_type bpp2pal[8] = {
848 Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
849};
850static osd_raw_window_t bpp2bit[8] = {
851 OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
852};
853
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700854static inline int WaitUntilBmpLoaded(struct av7110 *av7110)
855{
856 int ret = wait_event_interruptible_timeout(av7110->bmpq,
857 av7110->bmp_state != BMP_LOADING, 10*HZ);
858 if (ret == -ERESTARTSYS)
859 return ret;
860 if (ret == 0) {
861 printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
862 ret, av7110->bmp_state);
863 av7110->bmp_state = BMP_NONE;
864 return -ETIMEDOUT;
865 }
866 return 0;
867}
868
869static inline int LoadBitmap(struct av7110 *av7110,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 u16 dx, u16 dy, int inc, u8 __user * data)
871{
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700872 u16 format;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 int bpp;
874 int i;
875 int d, delta;
876 u8 c;
877 int ret;
878
879 dprintk(4, "%p\n", av7110);
880
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700881 format = bpp2bit[av7110->osdbpp[av7110->osdwin]];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882
883 av7110->bmp_state = BMP_LOADING;
884 if (format == OSD_BITMAP8) {
885 bpp=8; delta = 1;
886 } else if (format == OSD_BITMAP4) {
887 bpp=4; delta = 2;
888 } else if (format == OSD_BITMAP2) {
889 bpp=2; delta = 4;
890 } else if (format == OSD_BITMAP1) {
891 bpp=1; delta = 8;
892 } else {
893 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700894 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 }
896 av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
897 av7110->bmpp = 0;
898 if (av7110->bmplen > 32768) {
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 for (i = 0; i < dy; i++) {
903 if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {
904 av7110->bmp_state = BMP_NONE;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700905 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 }
907 }
908 if (format != OSD_BITMAP8) {
909 for (i = 0; i < dx * dy / delta; i++) {
910 c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
911 for (d = delta - 2; d >= 0; d--) {
912 c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
913 << ((delta - d - 1) * bpp));
914 ((u8 *)av7110->bmpbuf)[1024 + i] = c;
915 }
916 }
917 }
918 av7110->bmplen += 1024;
919 dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700920 ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
921 if (!ret)
922 ret = WaitUntilBmpLoaded(av7110);
923 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924}
925
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700926static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 dprintk(4, "%p\n", av7110);
929
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700930 return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931}
932
933static inline int ReleaseBitmap(struct av7110 *av7110)
934{
935 dprintk(4, "%p\n", av7110);
936
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700937 if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 return -1;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700939 if (av7110->bmp_state == BMP_LOADING)
940 dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 av7110->bmp_state = BMP_NONE;
942 return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);
943}
944
945static u32 RGB2YUV(u16 R, u16 G, u16 B)
946{
947 u16 y, u, v;
948 u16 Y, Cr, Cb;
949
950 y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */
951 u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */
952 v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */
953
954 Y = y / 256;
955 Cb = u / 16;
956 Cr = v / 16;
957
958 return Cr | (Cb << 16) | (Y << 8);
959}
960
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700961static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962{
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700963 int ret;
964
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 u16 ch, cl;
966 u32 yuv;
967
968 yuv = blend ? RGB2YUV(r,g,b) : 0;
969 cl = (yuv & 0xffff);
970 ch = ((yuv >> 16) & 0xffff);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -0700971 ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
972 color, ch, cl);
973 if (!ret)
974 ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
975 color, ((blend >> 4) & 0x0f));
976 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977}
978
979static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
980{
981 int i;
982 int length = last - first + 1;
983
984 if (length * 4 > DATA_BUFF3_SIZE)
985 return -EINVAL;
986
987 for (i = 0; i < length; i++) {
988 u32 color, blend, yuv;
989
990 if (get_user(color, colors + i))
991 return -EFAULT;
992 blend = (color & 0xF0000000) >> 4;
993 yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,
994 (color >> 16) & 0xFF) | blend : 0;
995 yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
996 wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);
997 }
998 return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,
999 av7110->osdwin,
1000 bpp2pal[av7110->osdbpp[av7110->osdwin]],
1001 first, last);
1002}
1003
1004static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
1005 int x1, int y1, int inc, u8 __user * data)
1006{
1007 uint w, h, bpp, bpl, size, lpb, bnum, brest;
1008 int i;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001009 int rc,release_rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
1011 w = x1 - x0 + 1;
1012 h = y1 - y0 + 1;
1013 if (inc <= 0)
1014 inc = w;
1015 if (w <= 0 || w > 720 || h <= 0 || h > 576)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001016 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 bpp = av7110->osdbpp[av7110->osdwin] + 1;
1018 bpl = ((w * bpp + 7) & ~7) / 8;
1019 size = h * bpl;
1020 lpb = (32 * 1024) / bpl;
1021 bnum = size / (lpb * bpl);
1022 brest = size - bnum * lpb * bpl;
1023
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001024 if (av7110->bmp_state == BMP_LOADING) {
1025 /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */
1026 BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e);
1027 rc = WaitUntilBmpLoaded(av7110);
1028 if (rc)
1029 return rc;
1030 /* just continue. This should work for all fw versions
1031 * if bnum==1 && !brest && LoadBitmap was successful
1032 */
1033 }
1034
1035 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 for (i = 0; i < bnum; i++) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001037 rc = LoadBitmap(av7110, w, lpb, inc, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 if (rc)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001039 break;
1040 rc = BlitBitmap(av7110, x0, y0 + i * lpb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 if (rc)
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001042 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 data += lpb * inc;
1044 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001045 if (!rc && brest) {
1046 rc = LoadBitmap(av7110, w, brest / bpl, inc, data);
1047 if (!rc)
1048 rc = BlitBitmap(av7110, x0, y0 + bnum * lpb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001050 release_rc = ReleaseBitmap(av7110);
1051 if (!rc)
1052 rc = release_rc;
1053 if (rc)
1054 dprintk(1,"returns %d\n",rc);
1055 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056}
1057
1058int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
1059{
1060 int ret;
1061
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001062 if (down_interruptible(&av7110->osd_sema))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 return -ERESTARTSYS;
1064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 switch (dc->cmd) {
1066 case OSD_Close:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001067 ret = DestroyOSDWindow(av7110, av7110->osdwin);
1068 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 case OSD_Open:
1070 av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001071 ret = CreateOSDWindow(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 bpp2bit[av7110->osdbpp[av7110->osdwin]],
1073 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001074 if (ret)
1075 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 if (!dc->data) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001077 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1078 if (ret)
1079 break;
1080 ret = SetColorBlend(av7110, av7110->osdwin);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001082 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 case OSD_Show:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001084 ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0);
1085 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 case OSD_Hide:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001087 ret = HideWindow(av7110, av7110->osdwin);
1088 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 case OSD_Clear:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001090 ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);
1091 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 case OSD_Fill:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001093 ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);
1094 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 case OSD_SetColor:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001096 ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);
1097 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 case OSD_SetPalette:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001099 if (FW_VERSION(av7110->arm_app) >= 0x2618)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001101 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 int i, len = dc->x0-dc->color+1;
1103 u8 __user *colors = (u8 __user *)dc->data;
1104 u8 r, g, b, blend;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001105 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 for (i = 0; i<len; i++) {
1107 if (get_user(r, colors + i * 4) ||
1108 get_user(g, colors + i * 4 + 1) ||
1109 get_user(b, colors + i * 4 + 2) ||
1110 get_user(blend, colors + i * 4 + 3)) {
1111 ret = -EFAULT;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001112 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001114 ret = OSDSetColor(av7110, dc->color + i, r, g, b, blend);
1115 if (ret)
1116 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 }
1118 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001119 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 case OSD_SetPixel:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001121 ret = DrawLine(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 dc->x0, dc->y0, 0, 0, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001123 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 case OSD_SetRow:
1125 dc->y1 = dc->y0;
1126 /* fall through */
1127 case OSD_SetBlock:
1128 ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001129 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 case OSD_FillRow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001131 ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 dc->x1-dc->x0+1, dc->y1, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001133 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 case OSD_FillBlock:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001135 ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001137 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 case OSD_Line:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001139 ret = DrawLine(av7110, av7110->osdwin,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001141 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 case OSD_Text:
1143 {
1144 char textbuf[240];
1145
1146 if (strncpy_from_user(textbuf, dc->data, 240) < 0) {
1147 ret = -EFAULT;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001148 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
1150 textbuf[239] = 0;
1151 if (dc->x1 > 3)
1152 dc->x1 = 3;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001153 ret = SetFont(av7110, av7110->osdwin, dc->x1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 (u16) (dc->color & 0xffff), (u16) (dc->color >> 16));
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001155 if (!ret)
1156 ret = FlushText(av7110);
1157 if (!ret)
1158 ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);
1159 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161 case OSD_SetWindow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001162 if (dc->x0 < 1 || dc->x0 > 7)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001164 else {
1165 av7110->osdwin = dc->x0;
1166 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001168 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 case OSD_MoveWindow:
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001170 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1171 if (!ret)
1172 ret = SetColorBlend(av7110, av7110->osdwin);
1173 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 case OSD_OpenRaw:
1175 if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
1176 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001177 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001179 if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001181 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 av7110->osdbpp[av7110->osdwin] = 0;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001183 ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001185 if (ret)
1186 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 if (!dc->data) {
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001188 ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
1189 if (!ret)
1190 ret = SetColorBlend(av7110, av7110->osdwin);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001192 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 default:
1194 ret = -EINVAL;
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001195 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 }
1197
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 up(&av7110->osd_sema);
Wolfgang Rohdewaldc9090eb2005-07-07 17:57:55 -07001199 if (ret==-ERESTARTSYS)
1200 dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);
1201 else if (ret)
1202 dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);
1203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 return ret;
1205}
1206
1207int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
1208{
Mauro Carvalho Chehab9101e622005-12-12 00:37:24 -08001209 switch (cap->cmd) {
1210 case OSD_CAP_MEMSIZE:
1211 if (FW_4M_SDRAM(av7110->arm_app))
Michael Krufky50c25ff2006-01-09 15:25:34 -02001212 cap->val = 1000000;
Mauro Carvalho Chehab9101e622005-12-12 00:37:24 -08001213 else
Michael Krufky50c25ff2006-01-09 15:25:34 -02001214 cap->val = 92000;
Mauro Carvalho Chehab9101e622005-12-12 00:37:24 -08001215 return 0;
1216 default:
1217 return -EINVAL;
1218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219}
1220#endif /* CONFIG_DVB_AV7110_OSD */