18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * av7110_hw.c: av7110 low level hardware access and firmware interface
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1999-2002 Ralph  Metzler
68c2ecf20Sopenharmony_ci *                       & Marcus Metzler for convergence integrated media GmbH
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * originally based on code by:
98c2ecf20Sopenharmony_ci * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * the project's page is at https://linuxtv.org
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/* for debugging ARM communication: */
158c2ecf20Sopenharmony_ci//#define COM_DEBUG
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/string.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/fs.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "av7110.h"
248c2ecf20Sopenharmony_ci#include "av7110_hw.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define _NOHANDSHAKE
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Max transfer size done by av7110_fw_cmd()
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * The maximum size passed to this function is 6 bytes. The buffer also
328c2ecf20Sopenharmony_ci * uses two additional ones for type and size. So, 8 bytes is enough.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE  8
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/****************************************************************************
378c2ecf20Sopenharmony_ci * DEBI functions
388c2ecf20Sopenharmony_ci ****************************************************************************/
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* This DEBI code is based on the Stradis driver
418c2ecf20Sopenharmony_ci   by Nathan Laredo <laredo@gnu.org> */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciint av7110_debiwrite(struct av7110 *av7110, u32 config,
448c2ecf20Sopenharmony_ci		     int addr, u32 val, unsigned int count)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = av7110->dev;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (count > 32764) {
498c2ecf20Sopenharmony_ci		printk("%s: invalid count %d\n", __func__, count);
508c2ecf20Sopenharmony_ci		return -1;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
538c2ecf20Sopenharmony_ci		printk("%s: wait_for_debi_done failed\n", __func__);
548c2ecf20Sopenharmony_ci		return -1;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	saa7146_write(dev, DEBI_CONFIG, config);
578c2ecf20Sopenharmony_ci	if (count <= 4)		/* immediate transfer */
588c2ecf20Sopenharmony_ci		saa7146_write(dev, DEBI_AD, val);
598c2ecf20Sopenharmony_ci	else			/* block transfer */
608c2ecf20Sopenharmony_ci		saa7146_write(dev, DEBI_AD, av7110->debi_bus);
618c2ecf20Sopenharmony_ci	saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
628c2ecf20Sopenharmony_ci	saa7146_write(dev, MC2, (2 << 16) | 2);
638c2ecf20Sopenharmony_ci	return 0;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ciu32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = av7110->dev;
698c2ecf20Sopenharmony_ci	u32 result = 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (count > 32764) {
728c2ecf20Sopenharmony_ci		printk("%s: invalid count %d\n", __func__, count);
738c2ecf20Sopenharmony_ci		return 0;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
768c2ecf20Sopenharmony_ci		printk("%s: wait_for_debi_done #1 failed\n", __func__);
778c2ecf20Sopenharmony_ci		return 0;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	saa7146_write(dev, DEBI_AD, av7110->debi_bus);
808c2ecf20Sopenharmony_ci	saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	saa7146_write(dev, DEBI_CONFIG, config);
838c2ecf20Sopenharmony_ci	saa7146_write(dev, MC2, (2 << 16) | 2);
848c2ecf20Sopenharmony_ci	if (count > 4)
858c2ecf20Sopenharmony_ci		return count;
868c2ecf20Sopenharmony_ci	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
878c2ecf20Sopenharmony_ci		printk("%s: wait_for_debi_done #2 failed\n", __func__);
888c2ecf20Sopenharmony_ci		return 0;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	result = saa7146_read(dev, DEBI_AD);
928c2ecf20Sopenharmony_ci	result &= (0xffffffffUL >> ((4 - count) * 8));
938c2ecf20Sopenharmony_ci	return result;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* av7110 ARM core boot stuff */
998c2ecf20Sopenharmony_ci#if 0
1008c2ecf20Sopenharmony_civoid av7110_reset_arm(struct av7110 *av7110)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/* Disable DEBI and GPIO irq */
1058c2ecf20Sopenharmony_ci	SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
1068c2ecf20Sopenharmony_ci	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
1098c2ecf20Sopenharmony_ci	msleep(30);	/* the firmware needs some time to initialize */
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	ARM_ResetMailBox(av7110);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
1148c2ecf20Sopenharmony_ci	SAA7146_IER_ENABLE(av7110->dev, MASK_03);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	av7110->arm_ready = 1;
1178c2ecf20Sopenharmony_ci	dprintk(1, "reset ARM\n");
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci#endif  /*  0  */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int waitdebi(struct av7110 *av7110, int adr, int state)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int k;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	for (k = 0; k < 100; k++) {
1288c2ecf20Sopenharmony_ci		if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)
1298c2ecf20Sopenharmony_ci			return 0;
1308c2ecf20Sopenharmony_ci		udelay(5);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int load_dram(struct av7110 *av7110, u32 *data, int len)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	int i;
1388c2ecf20Sopenharmony_ci	int blocks, rest;
1398c2ecf20Sopenharmony_ci	u32 base, bootblock = AV7110_BOOT_BLOCK;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	blocks = len / AV7110_BOOT_MAX_SIZE;
1448c2ecf20Sopenharmony_ci	rest = len % AV7110_BOOT_MAX_SIZE;
1458c2ecf20Sopenharmony_ci	base = DRAM_START_CODE;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	for (i = 0; i < blocks; i++) {
1488c2ecf20Sopenharmony_ci		if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
1498c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);
1508c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
1518c2ecf20Sopenharmony_ci		}
1528c2ecf20Sopenharmony_ci		dprintk(4, "writing DRAM block %d\n", i);
1538c2ecf20Sopenharmony_ci		mwdebi(av7110, DEBISWAB, bootblock,
1548c2ecf20Sopenharmony_ci		       ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE);
1558c2ecf20Sopenharmony_ci		bootblock ^= 0x1400;
1568c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4);
1578c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2);
1588c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
1598c2ecf20Sopenharmony_ci		base += AV7110_BOOT_MAX_SIZE;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (rest > 0) {
1638c2ecf20Sopenharmony_ci		if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
1648c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");
1658c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci		if (rest > 4)
1688c2ecf20Sopenharmony_ci			mwdebi(av7110, DEBISWAB, bootblock,
1698c2ecf20Sopenharmony_ci			       ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest);
1708c2ecf20Sopenharmony_ci		else
1718c2ecf20Sopenharmony_ci			mwdebi(av7110, DEBISWAB, bootblock,
1728c2ecf20Sopenharmony_ci			       ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4);
1758c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2);
1768c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
1798c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");
1808c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci	iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2);
1838c2ecf20Sopenharmony_ci	iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
1848c2ecf20Sopenharmony_ci	if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) {
1858c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");
1868c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* we cannot write av7110 DRAM directly, so load a bootloader into
1938c2ecf20Sopenharmony_ci * the DPRAM which implements a simple boot protocol */
1948c2ecf20Sopenharmony_ciint av7110_bootarm(struct av7110 *av7110)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	const struct firmware *fw;
1978c2ecf20Sopenharmony_ci	const char *fw_name = "av7110/bootcode.bin";
1988c2ecf20Sopenharmony_ci	struct saa7146_dev *dev = av7110->dev;
1998c2ecf20Sopenharmony_ci	u32 ret;
2008c2ecf20Sopenharmony_ci	int i;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	av7110->arm_ready = 0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Disable DEBI and GPIO irq */
2098c2ecf20Sopenharmony_ci	SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
2108c2ecf20Sopenharmony_ci	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* enable DEBI */
2138c2ecf20Sopenharmony_ci	saa7146_write(av7110->dev, MC1, 0x08800880);
2148c2ecf20Sopenharmony_ci	saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
2158c2ecf20Sopenharmony_ci	saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* test DEBI */
2188c2ecf20Sopenharmony_ci	iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
2198c2ecf20Sopenharmony_ci	/* FIXME: Why does Nexus CA require 2x iwdebi for first init? */
2208c2ecf20Sopenharmony_ci	iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
2238c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
2248c2ecf20Sopenharmony_ci		       ret, 0x10325476);
2258c2ecf20Sopenharmony_ci		return -1;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	for (i = 0; i < 8192; i += 4)
2288c2ecf20Sopenharmony_ci		iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);
2298c2ecf20Sopenharmony_ci	dprintk(2, "debi test OK\n");
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* boot */
2328c2ecf20Sopenharmony_ci	dprintk(1, "load boot code\n");
2338c2ecf20Sopenharmony_ci	saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
2348c2ecf20Sopenharmony_ci	//saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
2358c2ecf20Sopenharmony_ci	//saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	ret = request_firmware(&fw, fw_name, &dev->pci->dev);
2388c2ecf20Sopenharmony_ci	if (ret) {
2398c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n",
2408c2ecf20Sopenharmony_ci			fw_name);
2418c2ecf20Sopenharmony_ci		return ret;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size);
2458c2ecf20Sopenharmony_ci	release_firmware(fw);
2468c2ecf20Sopenharmony_ci	iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
2498c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out\n");
2508c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
2538c2ecf20Sopenharmony_ci	mdelay(1);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	dprintk(1, "load dram code\n");
2568c2ecf20Sopenharmony_ci	if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {
2578c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): load_dram() failed\n");
2588c2ecf20Sopenharmony_ci		return -1;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
2628c2ecf20Sopenharmony_ci	mdelay(1);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	dprintk(1, "load dpram code\n");
2658c2ecf20Sopenharmony_ci	mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
2688c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out after loading DRAM\n");
2698c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
2728c2ecf20Sopenharmony_ci	msleep(30);	/* the firmware needs some time to initialize */
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	//ARM_ClearIrq(av7110);
2758c2ecf20Sopenharmony_ci	ARM_ResetMailBox(av7110);
2768c2ecf20Sopenharmony_ci	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
2778c2ecf20Sopenharmony_ci	SAA7146_IER_ENABLE(av7110->dev, MASK_03);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	av7110->arm_errors = 0;
2808c2ecf20Sopenharmony_ci	av7110->arm_ready = 1;
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ciMODULE_FIRMWARE("av7110/bootcode.bin");
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci/****************************************************************************
2868c2ecf20Sopenharmony_ci * DEBI command polling
2878c2ecf20Sopenharmony_ci ****************************************************************************/
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ciint av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	unsigned long start;
2928c2ecf20Sopenharmony_ci	u32 stat;
2938c2ecf20Sopenharmony_ci	int err;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (FW_VERSION(av7110->arm_app) <= 0x261c) {
2968c2ecf20Sopenharmony_ci		/* not supported by old firmware */
2978c2ecf20Sopenharmony_ci		msleep(50);
2988c2ecf20Sopenharmony_ci		return 0;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* new firmware */
3028c2ecf20Sopenharmony_ci	start = jiffies;
3038c2ecf20Sopenharmony_ci	for (;;) {
3048c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_FREE);
3058c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&av7110->dcomlock))
3068c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
3078c2ecf20Sopenharmony_ci		stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
3088c2ecf20Sopenharmony_ci		mutex_unlock(&av7110->dcomlock);
3098c2ecf20Sopenharmony_ci		if ((stat & flags) == 0)
3108c2ecf20Sopenharmony_ci			break;
3118c2ecf20Sopenharmony_ci		if (err) {
3128c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
3138c2ecf20Sopenharmony_ci				__func__, stat & flags);
3148c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci		msleep(1);
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	int i;
3248c2ecf20Sopenharmony_ci	unsigned long start;
3258c2ecf20Sopenharmony_ci	char *type = NULL;
3268c2ecf20Sopenharmony_ci	u16 flags[2] = {0, 0};
3278c2ecf20Sopenharmony_ci	u32 stat;
3288c2ecf20Sopenharmony_ci	int err;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci//	dprintk(4, "%p\n", av7110);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (!av7110->arm_ready) {
3338c2ecf20Sopenharmony_ci		dprintk(1, "arm not ready.\n");
3348c2ecf20Sopenharmony_ci		return -ENXIO;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	start = jiffies;
3388c2ecf20Sopenharmony_ci	while (1) {
3398c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_FREE);
3408c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
3418c2ecf20Sopenharmony_ci			break;
3428c2ecf20Sopenharmony_ci		if (err) {
3438c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__);
3448c2ecf20Sopenharmony_ci			av7110->arm_errors++;
3458c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci		msleep(1);
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (FW_VERSION(av7110->arm_app) <= 0x261f)
3518c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci#ifndef _NOHANDSHAKE
3548c2ecf20Sopenharmony_ci	start = jiffies;
3558c2ecf20Sopenharmony_ci	while (1) {
3568c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_SHAKE);
3578c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
3588c2ecf20Sopenharmony_ci			break;
3598c2ecf20Sopenharmony_ci		if (err) {
3608c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__);
3618c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
3628c2ecf20Sopenharmony_ci		}
3638c2ecf20Sopenharmony_ci		msleep(1);
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci#endif
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	switch ((buf[0] >> 8) & 0xff) {
3688c2ecf20Sopenharmony_ci	case COMTYPE_PIDFILTER:
3698c2ecf20Sopenharmony_ci	case COMTYPE_ENCODER:
3708c2ecf20Sopenharmony_ci	case COMTYPE_REC_PLAY:
3718c2ecf20Sopenharmony_ci	case COMTYPE_MPEGDECODER:
3728c2ecf20Sopenharmony_ci		type = "MSG";
3738c2ecf20Sopenharmony_ci		flags[0] = GPMQOver;
3748c2ecf20Sopenharmony_ci		flags[1] = GPMQFull;
3758c2ecf20Sopenharmony_ci		break;
3768c2ecf20Sopenharmony_ci	case COMTYPE_OSD:
3778c2ecf20Sopenharmony_ci		type = "OSD";
3788c2ecf20Sopenharmony_ci		flags[0] = OSDQOver;
3798c2ecf20Sopenharmony_ci		flags[1] = OSDQFull;
3808c2ecf20Sopenharmony_ci		break;
3818c2ecf20Sopenharmony_ci	case COMTYPE_MISC:
3828c2ecf20Sopenharmony_ci		if (FW_VERSION(av7110->arm_app) >= 0x261d) {
3838c2ecf20Sopenharmony_ci			type = "MSG";
3848c2ecf20Sopenharmony_ci			flags[0] = GPMQOver;
3858c2ecf20Sopenharmony_ci			flags[1] = GPMQBusy;
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci		break;
3888c2ecf20Sopenharmony_ci	default:
3898c2ecf20Sopenharmony_ci		break;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (type != NULL) {
3938c2ecf20Sopenharmony_ci		/* non-immediate COMMAND type */
3948c2ecf20Sopenharmony_ci		start = jiffies;
3958c2ecf20Sopenharmony_ci		for (;;) {
3968c2ecf20Sopenharmony_ci			err = time_after(jiffies, start + ARM_WAIT_FREE);
3978c2ecf20Sopenharmony_ci			stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
3988c2ecf20Sopenharmony_ci			if (stat & flags[0]) {
3998c2ecf20Sopenharmony_ci				printk(KERN_ERR "%s: %s QUEUE overflow\n",
4008c2ecf20Sopenharmony_ci					__func__, type);
4018c2ecf20Sopenharmony_ci				return -1;
4028c2ecf20Sopenharmony_ci			}
4038c2ecf20Sopenharmony_ci			if ((stat & flags[1]) == 0)
4048c2ecf20Sopenharmony_ci				break;
4058c2ecf20Sopenharmony_ci			if (err) {
4068c2ecf20Sopenharmony_ci				printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
4078c2ecf20Sopenharmony_ci					__func__, type);
4088c2ecf20Sopenharmony_ci				av7110->arm_errors++;
4098c2ecf20Sopenharmony_ci				return -ETIMEDOUT;
4108c2ecf20Sopenharmony_ci			}
4118c2ecf20Sopenharmony_ci			msleep(1);
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	for (i = 2; i < length; i++)
4168c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (length)
4198c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
4208c2ecf20Sopenharmony_ci	else
4218c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (FW_VERSION(av7110->arm_app) <= 0x261f)
4268c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci#ifdef COM_DEBUG
4298c2ecf20Sopenharmony_ci	start = jiffies;
4308c2ecf20Sopenharmony_ci	while (1) {
4318c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_FREE);
4328c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
4338c2ecf20Sopenharmony_ci			break;
4348c2ecf20Sopenharmony_ci		if (err) {
4358c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",
4368c2ecf20Sopenharmony_ci			       __func__, (buf[0] >> 8) & 0xff);
4378c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci		msleep(1);
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
4438c2ecf20Sopenharmony_ci	if (stat & GPMQOver) {
4448c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__);
4458c2ecf20Sopenharmony_ci		return -ENOSPC;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci	else if (stat & OSDQOver) {
4488c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__);
4498c2ecf20Sopenharmony_ci		return -ENOSPC;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci#endif
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	int ret;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci//	dprintk(4, "%p\n", av7110);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (!av7110->arm_ready) {
4638c2ecf20Sopenharmony_ci		dprintk(1, "arm not ready.\n");
4648c2ecf20Sopenharmony_ci		return -1;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&av7110->dcomlock))
4678c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	ret = __av7110_send_fw_cmd(av7110, buf, length);
4708c2ecf20Sopenharmony_ci	mutex_unlock(&av7110->dcomlock);
4718c2ecf20Sopenharmony_ci	if (ret && ret!=-ERESTARTSYS)
4728c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
4738c2ecf20Sopenharmony_ci		       __func__, ret);
4748c2ecf20Sopenharmony_ci	return ret;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ciint av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	va_list args;
4808c2ecf20Sopenharmony_ci	u16 buf[MAX_XFER_SIZE];
4818c2ecf20Sopenharmony_ci	int i, ret;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci//	dprintk(4, "%p\n", av7110);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (2 + num > ARRAY_SIZE(buf)) {
4868c2ecf20Sopenharmony_ci		printk(KERN_WARNING
4878c2ecf20Sopenharmony_ci		       "%s: %s len=%d is too big!\n",
4888c2ecf20Sopenharmony_ci		       KBUILD_MODNAME, __func__, num);
4898c2ecf20Sopenharmony_ci		return -EINVAL;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	buf[0] = ((type << 8) | com);
4938c2ecf20Sopenharmony_ci	buf[1] = num;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (num) {
4968c2ecf20Sopenharmony_ci		va_start(args, num);
4978c2ecf20Sopenharmony_ci		for (i = 0; i < num; i++)
4988c2ecf20Sopenharmony_ci			buf[i + 2] = va_arg(args, u32);
4998c2ecf20Sopenharmony_ci		va_end(args);
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	ret = av7110_send_fw_cmd(av7110, buf, num + 2);
5038c2ecf20Sopenharmony_ci	if (ret && ret != -ERESTARTSYS)
5048c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
5058c2ecf20Sopenharmony_ci	return ret;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci#if 0
5098c2ecf20Sopenharmony_ciint av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	int i, ret;
5128c2ecf20Sopenharmony_ci	u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
5138c2ecf20Sopenharmony_ci		16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	for(i = 0; i < len && i < 32; i++)
5188c2ecf20Sopenharmony_ci	{
5198c2ecf20Sopenharmony_ci		if(i % 2 == 0)
5208c2ecf20Sopenharmony_ci			cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
5218c2ecf20Sopenharmony_ci		else
5228c2ecf20Sopenharmony_ci			cmd[(i / 2) + 2] |= buf[i];
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	ret = av7110_send_fw_cmd(av7110, cmd, 18);
5268c2ecf20Sopenharmony_ci	if (ret && ret != -ERESTARTSYS)
5278c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
5288c2ecf20Sopenharmony_ci	return ret;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci#endif  /*  0  */
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ciint av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
5338c2ecf20Sopenharmony_ci		      int request_buf_len, u16 *reply_buf, int reply_buf_len)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	int err;
5368c2ecf20Sopenharmony_ci	s16 i;
5378c2ecf20Sopenharmony_ci	unsigned long start;
5388c2ecf20Sopenharmony_ci#ifdef COM_DEBUG
5398c2ecf20Sopenharmony_ci	u32 stat;
5408c2ecf20Sopenharmony_ci#endif
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (!av7110->arm_ready) {
5458c2ecf20Sopenharmony_ci		dprintk(1, "arm not ready.\n");
5468c2ecf20Sopenharmony_ci		return -1;
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&av7110->dcomlock))
5508c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {
5538c2ecf20Sopenharmony_ci		mutex_unlock(&av7110->dcomlock);
5548c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
5558c2ecf20Sopenharmony_ci		return err;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	start = jiffies;
5598c2ecf20Sopenharmony_ci	while (1) {
5608c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_FREE);
5618c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)
5628c2ecf20Sopenharmony_ci			break;
5638c2ecf20Sopenharmony_ci		if (err) {
5648c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__);
5658c2ecf20Sopenharmony_ci			mutex_unlock(&av7110->dcomlock);
5668c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci#ifdef _NOHANDSHAKE
5698c2ecf20Sopenharmony_ci		msleep(1);
5708c2ecf20Sopenharmony_ci#endif
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci#ifndef _NOHANDSHAKE
5748c2ecf20Sopenharmony_ci	start = jiffies;
5758c2ecf20Sopenharmony_ci	while (1) {
5768c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_SHAKE);
5778c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
5788c2ecf20Sopenharmony_ci			break;
5798c2ecf20Sopenharmony_ci		if (err) {
5808c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__);
5818c2ecf20Sopenharmony_ci			mutex_unlock(&av7110->dcomlock);
5828c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci		msleep(1);
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci#endif
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci#ifdef COM_DEBUG
5898c2ecf20Sopenharmony_ci	stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
5908c2ecf20Sopenharmony_ci	if (stat & GPMQOver) {
5918c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: GPMQOver\n", __func__);
5928c2ecf20Sopenharmony_ci		mutex_unlock(&av7110->dcomlock);
5938c2ecf20Sopenharmony_ci		return -1;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci	else if (stat & OSDQOver) {
5968c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: OSDQOver\n", __func__);
5978c2ecf20Sopenharmony_ci		mutex_unlock(&av7110->dcomlock);
5988c2ecf20Sopenharmony_ci		return -1;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci#endif
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	for (i = 0; i < reply_buf_len; i++)
6038c2ecf20Sopenharmony_ci		reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	mutex_unlock(&av7110->dcomlock);
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	int ret;
6128c2ecf20Sopenharmony_ci	ret = av7110_fw_request(av7110, &tag, 0, buf, length);
6138c2ecf20Sopenharmony_ci	if (ret)
6148c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
6158c2ecf20Sopenharmony_ci	return ret;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/****************************************************************************
6208c2ecf20Sopenharmony_ci * Firmware commands
6218c2ecf20Sopenharmony_ci ****************************************************************************/
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/* get version of the firmware ROM, RTSL, video ucode and ARM application  */
6248c2ecf20Sopenharmony_ciint av7110_firmversion(struct av7110 *av7110)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	u16 buf[20];
6278c2ecf20Sopenharmony_ci	u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (av7110_fw_query(av7110, tag, buf, 16)) {
6328c2ecf20Sopenharmony_ci		printk("dvb-ttpci: failed to boot firmware @ card %d\n",
6338c2ecf20Sopenharmony_ci		       av7110->dvb_adapter.num);
6348c2ecf20Sopenharmony_ci		return -EIO;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	av7110->arm_fw = (buf[0] << 16) + buf[1];
6388c2ecf20Sopenharmony_ci	av7110->arm_rtsl = (buf[2] << 16) + buf[3];
6398c2ecf20Sopenharmony_ci	av7110->arm_vid = (buf[4] << 16) + buf[5];
6408c2ecf20Sopenharmony_ci	av7110->arm_app = (buf[6] << 16) + buf[7];
6418c2ecf20Sopenharmony_ci	av7110->avtype = (buf[8] << 16) + buf[9];
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
6448c2ecf20Sopenharmony_ci	       av7110->dvb_adapter.num, av7110->arm_fw,
6458c2ecf20Sopenharmony_ci	       av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/* print firmware capabilities */
6488c2ecf20Sopenharmony_ci	if (FW_CI_LL_SUPPORT(av7110->arm_app))
6498c2ecf20Sopenharmony_ci		printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
6508c2ecf20Sopenharmony_ci		       av7110->dvb_adapter.num);
6518c2ecf20Sopenharmony_ci	else
6528c2ecf20Sopenharmony_ci		printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
6538c2ecf20Sopenharmony_ci		       av7110->dvb_adapter.num);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	return 0;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ciint av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	int i, ret;
6628c2ecf20Sopenharmony_ci	u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
6638c2ecf20Sopenharmony_ci			16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (len > 10)
6688c2ecf20Sopenharmony_ci		len = 10;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	buf[1] = len + 2;
6718c2ecf20Sopenharmony_ci	buf[2] = len;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	if (burst != -1)
6748c2ecf20Sopenharmony_ci		buf[3] = burst ? 0x01 : 0x00;
6758c2ecf20Sopenharmony_ci	else
6768c2ecf20Sopenharmony_ci		buf[3] = 0xffff;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
6798c2ecf20Sopenharmony_ci		buf[i + 4] = msg[i];
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	ret = av7110_send_fw_cmd(av7110, buf, 18);
6828c2ecf20Sopenharmony_ci	if (ret && ret!=-ERESTARTSYS)
6838c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
6848c2ecf20Sopenharmony_ci	return ret;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci#ifdef CONFIG_DVB_AV7110_OSD
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic inline int SetBlend_(struct av7110 *av7110, u8 windownr,
6968c2ecf20Sopenharmony_ci		     enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,
6998c2ecf20Sopenharmony_ci			     windownr, colordepth, index, blending);
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic inline int SetColor_(struct av7110 *av7110, u8 windownr,
7038c2ecf20Sopenharmony_ci		     enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,
7068c2ecf20Sopenharmony_ci			     windownr, colordepth, index, colorhi, colorlo);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_cistatic inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
7108c2ecf20Sopenharmony_ci			  u16 colorfg, u16 colorbg)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,
7138c2ecf20Sopenharmony_ci			     windownr, fontsize, colorfg, colorbg);
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int FlushText(struct av7110 *av7110)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	unsigned long start;
7198c2ecf20Sopenharmony_ci	int err;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&av7110->dcomlock))
7228c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
7238c2ecf20Sopenharmony_ci	start = jiffies;
7248c2ecf20Sopenharmony_ci	while (1) {
7258c2ecf20Sopenharmony_ci		err = time_after(jiffies, start + ARM_WAIT_OSD);
7268c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
7278c2ecf20Sopenharmony_ci			break;
7288c2ecf20Sopenharmony_ci		if (err) {
7298c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
7308c2ecf20Sopenharmony_ci			       __func__);
7318c2ecf20Sopenharmony_ci			mutex_unlock(&av7110->dcomlock);
7328c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci		msleep(1);
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci	mutex_unlock(&av7110->dcomlock);
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	int i, ret;
7438c2ecf20Sopenharmony_ci	unsigned long start;
7448c2ecf20Sopenharmony_ci	int length = strlen(buf) + 1;
7458c2ecf20Sopenharmony_ci	u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&av7110->dcomlock))
7488c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	start = jiffies;
7518c2ecf20Sopenharmony_ci	while (1) {
7528c2ecf20Sopenharmony_ci		ret = time_after(jiffies, start + ARM_WAIT_OSD);
7538c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)
7548c2ecf20Sopenharmony_ci			break;
7558c2ecf20Sopenharmony_ci		if (ret) {
7568c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
7578c2ecf20Sopenharmony_ci			       __func__);
7588c2ecf20Sopenharmony_ci			mutex_unlock(&av7110->dcomlock);
7598c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
7608c2ecf20Sopenharmony_ci		}
7618c2ecf20Sopenharmony_ci		msleep(1);
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci#ifndef _NOHANDSHAKE
7648c2ecf20Sopenharmony_ci	start = jiffies;
7658c2ecf20Sopenharmony_ci	while (1) {
7668c2ecf20Sopenharmony_ci		ret = time_after(jiffies, start + ARM_WAIT_SHAKE);
7678c2ecf20Sopenharmony_ci		if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)
7688c2ecf20Sopenharmony_ci			break;
7698c2ecf20Sopenharmony_ci		if (ret) {
7708c2ecf20Sopenharmony_ci			printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
7718c2ecf20Sopenharmony_ci			       __func__);
7728c2ecf20Sopenharmony_ci			mutex_unlock(&av7110->dcomlock);
7738c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
7748c2ecf20Sopenharmony_ci		}
7758c2ecf20Sopenharmony_ci		msleep(1);
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci#endif
7788c2ecf20Sopenharmony_ci	for (i = 0; i < length / 2; i++)
7798c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
7808c2ecf20Sopenharmony_ci		      swab16(*(u16 *)(buf + 2 * i)), 2);
7818c2ecf20Sopenharmony_ci	if (length & 1)
7828c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);
7838c2ecf20Sopenharmony_ci	ret = __av7110_send_fw_cmd(av7110, cbuf, 5);
7848c2ecf20Sopenharmony_ci	mutex_unlock(&av7110->dcomlock);
7858c2ecf20Sopenharmony_ci	if (ret && ret!=-ERESTARTSYS)
7868c2ecf20Sopenharmony_ci		printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
7878c2ecf20Sopenharmony_ci	return ret;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic inline int DrawLine(struct av7110 *av7110, u8 windownr,
7918c2ecf20Sopenharmony_ci			   u16 x, u16 y, u16 dx, u16 dy, u16 color)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6,
7948c2ecf20Sopenharmony_ci			     windownr, x, y, dx, dy, color);
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic inline int DrawBlock(struct av7110 *av7110, u8 windownr,
7988c2ecf20Sopenharmony_ci			    u16 x, u16 y, u16 dx, u16 dy, u16 color)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,
8018c2ecf20Sopenharmony_ci			     windownr, x, y, dx, dy, color);
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic inline int HideWindow(struct av7110 *av7110, u8 windownr)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_cistatic inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cistatic inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
8258c2ecf20Sopenharmony_ci				  osd_raw_window_t disptype,
8268c2ecf20Sopenharmony_ci				  u16 width, u16 height)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,
8298c2ecf20Sopenharmony_ci			     windownr, disptype, width, height);
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic enum av7110_osd_palette_type bpp2pal[8] = {
8348c2ecf20Sopenharmony_ci	Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
8358c2ecf20Sopenharmony_ci};
8368c2ecf20Sopenharmony_cistatic osd_raw_window_t bpp2bit[8] = {
8378c2ecf20Sopenharmony_ci	OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
8388c2ecf20Sopenharmony_ci};
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic inline int WaitUntilBmpLoaded(struct av7110 *av7110)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	int ret = wait_event_timeout(av7110->bmpq,
8438c2ecf20Sopenharmony_ci				av7110->bmp_state != BMP_LOADING, 10*HZ);
8448c2ecf20Sopenharmony_ci	if (ret == 0) {
8458c2ecf20Sopenharmony_ci		printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
8468c2ecf20Sopenharmony_ci		       ret, av7110->bmp_state);
8478c2ecf20Sopenharmony_ci		av7110->bmp_state = BMP_NONE;
8488c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci	return 0;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic inline int LoadBitmap(struct av7110 *av7110,
8548c2ecf20Sopenharmony_ci			     u16 dx, u16 dy, int inc, u8 __user * data)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	u16 format;
8578c2ecf20Sopenharmony_ci	int bpp;
8588c2ecf20Sopenharmony_ci	int i;
8598c2ecf20Sopenharmony_ci	int d, delta;
8608c2ecf20Sopenharmony_ci	u8 c;
8618c2ecf20Sopenharmony_ci	int ret;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	format = bpp2bit[av7110->osdbpp[av7110->osdwin]];
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	av7110->bmp_state = BMP_LOADING;
8688c2ecf20Sopenharmony_ci	if	(format == OSD_BITMAP8) {
8698c2ecf20Sopenharmony_ci		bpp=8; delta = 1;
8708c2ecf20Sopenharmony_ci	} else if (format == OSD_BITMAP4) {
8718c2ecf20Sopenharmony_ci		bpp=4; delta = 2;
8728c2ecf20Sopenharmony_ci	} else if (format == OSD_BITMAP2) {
8738c2ecf20Sopenharmony_ci		bpp=2; delta = 4;
8748c2ecf20Sopenharmony_ci	} else if (format == OSD_BITMAP1) {
8758c2ecf20Sopenharmony_ci		bpp=1; delta = 8;
8768c2ecf20Sopenharmony_ci	} else {
8778c2ecf20Sopenharmony_ci		av7110->bmp_state = BMP_NONE;
8788c2ecf20Sopenharmony_ci		return -EINVAL;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci	av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
8818c2ecf20Sopenharmony_ci	av7110->bmpp = 0;
8828c2ecf20Sopenharmony_ci	if (av7110->bmplen > 32768) {
8838c2ecf20Sopenharmony_ci		av7110->bmp_state = BMP_NONE;
8848c2ecf20Sopenharmony_ci		return -EINVAL;
8858c2ecf20Sopenharmony_ci	}
8868c2ecf20Sopenharmony_ci	for (i = 0; i < dy; i++) {
8878c2ecf20Sopenharmony_ci		if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {
8888c2ecf20Sopenharmony_ci			av7110->bmp_state = BMP_NONE;
8898c2ecf20Sopenharmony_ci			return -EINVAL;
8908c2ecf20Sopenharmony_ci		}
8918c2ecf20Sopenharmony_ci	}
8928c2ecf20Sopenharmony_ci	if (format != OSD_BITMAP8) {
8938c2ecf20Sopenharmony_ci		for (i = 0; i < dx * dy / delta; i++) {
8948c2ecf20Sopenharmony_ci			c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
8958c2ecf20Sopenharmony_ci			for (d = delta - 2; d >= 0; d--) {
8968c2ecf20Sopenharmony_ci				c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
8978c2ecf20Sopenharmony_ci				      << ((delta - d - 1) * bpp));
8988c2ecf20Sopenharmony_ci				((u8 *)av7110->bmpbuf)[1024 + i] = c;
8998c2ecf20Sopenharmony_ci			}
9008c2ecf20Sopenharmony_ci		}
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci	av7110->bmplen += 1024;
9038c2ecf20Sopenharmony_ci	dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
9048c2ecf20Sopenharmony_ci	ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
9058c2ecf20Sopenharmony_ci	if (!ret)
9068c2ecf20Sopenharmony_ci		ret = WaitUntilBmpLoaded(av7110);
9078c2ecf20Sopenharmony_ci	return ret;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0);
9158c2ecf20Sopenharmony_ci}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_cistatic inline int ReleaseBitmap(struct av7110 *av7110)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	dprintk(4, "%p\n", av7110);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)
9228c2ecf20Sopenharmony_ci		return -1;
9238c2ecf20Sopenharmony_ci	if (av7110->bmp_state == BMP_LOADING)
9248c2ecf20Sopenharmony_ci		dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");
9258c2ecf20Sopenharmony_ci	av7110->bmp_state = BMP_NONE;
9268c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic u32 RGB2YUV(u16 R, u16 G, u16 B)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	u16 y, u, v;
9328c2ecf20Sopenharmony_ci	u16 Y, Cr, Cb;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	y = R * 77 + G * 150 + B * 29;	/* Luma=0.299R+0.587G+0.114B 0..65535 */
9358c2ecf20Sopenharmony_ci	u = 2048 + B * 8 -(y >> 5);	/* Cr 0..4095 */
9368c2ecf20Sopenharmony_ci	v = 2048 + R * 8 -(y >> 5);	/* Cb 0..4095 */
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	Y = y / 256;
9398c2ecf20Sopenharmony_ci	Cb = u / 16;
9408c2ecf20Sopenharmony_ci	Cr = v / 16;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	return Cr | (Cb << 16) | (Y << 8);
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistatic int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
9468c2ecf20Sopenharmony_ci{
9478c2ecf20Sopenharmony_ci	int ret;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	u16 ch, cl;
9508c2ecf20Sopenharmony_ci	u32 yuv;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	yuv = blend ? RGB2YUV(r,g,b) : 0;
9538c2ecf20Sopenharmony_ci	cl = (yuv & 0xffff);
9548c2ecf20Sopenharmony_ci	ch = ((yuv >> 16) & 0xffff);
9558c2ecf20Sopenharmony_ci	ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
9568c2ecf20Sopenharmony_ci			color, ch, cl);
9578c2ecf20Sopenharmony_ci	if (!ret)
9588c2ecf20Sopenharmony_ci		ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
9598c2ecf20Sopenharmony_ci				color, ((blend >> 4) & 0x0f));
9608c2ecf20Sopenharmony_ci	return ret;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_cistatic int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	int i;
9668c2ecf20Sopenharmony_ci	int length = last - first + 1;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	if (length * 4 > DATA_BUFF3_SIZE)
9698c2ecf20Sopenharmony_ci		return -EINVAL;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	for (i = 0; i < length; i++) {
9728c2ecf20Sopenharmony_ci		u32 color, blend, yuv;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci		if (get_user(color, colors + i))
9758c2ecf20Sopenharmony_ci			return -EFAULT;
9768c2ecf20Sopenharmony_ci		blend = (color & 0xF0000000) >> 4;
9778c2ecf20Sopenharmony_ci		yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,
9788c2ecf20Sopenharmony_ci				     (color >> 16) & 0xFF) | blend : 0;
9798c2ecf20Sopenharmony_ci		yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
9808c2ecf20Sopenharmony_ci		wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci	return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,
9838c2ecf20Sopenharmony_ci			    av7110->osdwin,
9848c2ecf20Sopenharmony_ci			    bpp2pal[av7110->osdbpp[av7110->osdwin]],
9858c2ecf20Sopenharmony_ci			    first, last);
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
9898c2ecf20Sopenharmony_ci		       int x1, int y1, int inc, u8 __user * data)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	uint w, h, bpp, bpl, size, lpb, bnum, brest;
9928c2ecf20Sopenharmony_ci	int i;
9938c2ecf20Sopenharmony_ci	int rc,release_rc;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	w = x1 - x0 + 1;
9968c2ecf20Sopenharmony_ci	h = y1 - y0 + 1;
9978c2ecf20Sopenharmony_ci	if (inc <= 0)
9988c2ecf20Sopenharmony_ci		inc = w;
9998c2ecf20Sopenharmony_ci	if (w <= 0 || w > 720 || h <= 0 || h > 576)
10008c2ecf20Sopenharmony_ci		return -EINVAL;
10018c2ecf20Sopenharmony_ci	bpp = av7110->osdbpp[av7110->osdwin] + 1;
10028c2ecf20Sopenharmony_ci	bpl = ((w * bpp + 7) & ~7) / 8;
10038c2ecf20Sopenharmony_ci	size = h * bpl;
10048c2ecf20Sopenharmony_ci	lpb = (32 * 1024) / bpl;
10058c2ecf20Sopenharmony_ci	bnum = size / (lpb * bpl);
10068c2ecf20Sopenharmony_ci	brest = size - bnum * lpb * bpl;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	if (av7110->bmp_state == BMP_LOADING) {
10098c2ecf20Sopenharmony_ci		/* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */
10108c2ecf20Sopenharmony_ci		BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e);
10118c2ecf20Sopenharmony_ci		rc = WaitUntilBmpLoaded(av7110);
10128c2ecf20Sopenharmony_ci		if (rc)
10138c2ecf20Sopenharmony_ci			return rc;
10148c2ecf20Sopenharmony_ci		/* just continue. This should work for all fw versions
10158c2ecf20Sopenharmony_ci		 * if bnum==1 && !brest && LoadBitmap was successful
10168c2ecf20Sopenharmony_ci		 */
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	rc = 0;
10208c2ecf20Sopenharmony_ci	for (i = 0; i < bnum; i++) {
10218c2ecf20Sopenharmony_ci		rc = LoadBitmap(av7110, w, lpb, inc, data);
10228c2ecf20Sopenharmony_ci		if (rc)
10238c2ecf20Sopenharmony_ci			break;
10248c2ecf20Sopenharmony_ci		rc = BlitBitmap(av7110, x0, y0 + i * lpb);
10258c2ecf20Sopenharmony_ci		if (rc)
10268c2ecf20Sopenharmony_ci			break;
10278c2ecf20Sopenharmony_ci		data += lpb * inc;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci	if (!rc && brest) {
10308c2ecf20Sopenharmony_ci		rc = LoadBitmap(av7110, w, brest / bpl, inc, data);
10318c2ecf20Sopenharmony_ci		if (!rc)
10328c2ecf20Sopenharmony_ci			rc = BlitBitmap(av7110, x0, y0 + bnum * lpb);
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci	release_rc = ReleaseBitmap(av7110);
10358c2ecf20Sopenharmony_ci	if (!rc)
10368c2ecf20Sopenharmony_ci		rc = release_rc;
10378c2ecf20Sopenharmony_ci	if (rc)
10388c2ecf20Sopenharmony_ci		dprintk(1,"returns %d\n",rc);
10398c2ecf20Sopenharmony_ci	return rc;
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ciint av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	int ret;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&av7110->osd_mutex))
10478c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	switch (dc->cmd) {
10508c2ecf20Sopenharmony_ci	case OSD_Close:
10518c2ecf20Sopenharmony_ci		ret = DestroyOSDWindow(av7110, av7110->osdwin);
10528c2ecf20Sopenharmony_ci		break;
10538c2ecf20Sopenharmony_ci	case OSD_Open:
10548c2ecf20Sopenharmony_ci		av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
10558c2ecf20Sopenharmony_ci		ret = CreateOSDWindow(av7110, av7110->osdwin,
10568c2ecf20Sopenharmony_ci				bpp2bit[av7110->osdbpp[av7110->osdwin]],
10578c2ecf20Sopenharmony_ci				dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
10588c2ecf20Sopenharmony_ci		if (ret)
10598c2ecf20Sopenharmony_ci			break;
10608c2ecf20Sopenharmony_ci		if (!dc->data) {
10618c2ecf20Sopenharmony_ci			ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
10628c2ecf20Sopenharmony_ci			if (ret)
10638c2ecf20Sopenharmony_ci				break;
10648c2ecf20Sopenharmony_ci			ret = SetColorBlend(av7110, av7110->osdwin);
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci		break;
10678c2ecf20Sopenharmony_ci	case OSD_Show:
10688c2ecf20Sopenharmony_ci		ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0);
10698c2ecf20Sopenharmony_ci		break;
10708c2ecf20Sopenharmony_ci	case OSD_Hide:
10718c2ecf20Sopenharmony_ci		ret = HideWindow(av7110, av7110->osdwin);
10728c2ecf20Sopenharmony_ci		break;
10738c2ecf20Sopenharmony_ci	case OSD_Clear:
10748c2ecf20Sopenharmony_ci		ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);
10758c2ecf20Sopenharmony_ci		break;
10768c2ecf20Sopenharmony_ci	case OSD_Fill:
10778c2ecf20Sopenharmony_ci		ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);
10788c2ecf20Sopenharmony_ci		break;
10798c2ecf20Sopenharmony_ci	case OSD_SetColor:
10808c2ecf20Sopenharmony_ci		ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);
10818c2ecf20Sopenharmony_ci		break;
10828c2ecf20Sopenharmony_ci	case OSD_SetPalette:
10838c2ecf20Sopenharmony_ci		if (FW_VERSION(av7110->arm_app) >= 0x2618)
10848c2ecf20Sopenharmony_ci			ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);
10858c2ecf20Sopenharmony_ci		else {
10868c2ecf20Sopenharmony_ci			int i, len = dc->x0-dc->color+1;
10878c2ecf20Sopenharmony_ci			u8 __user *colors = (u8 __user *)dc->data;
10888c2ecf20Sopenharmony_ci			u8 r, g = 0, b = 0, blend = 0;
10898c2ecf20Sopenharmony_ci			ret = 0;
10908c2ecf20Sopenharmony_ci			for (i = 0; i<len; i++) {
10918c2ecf20Sopenharmony_ci				if (get_user(r, colors + i * 4) ||
10928c2ecf20Sopenharmony_ci				    get_user(g, colors + i * 4 + 1) ||
10938c2ecf20Sopenharmony_ci				    get_user(b, colors + i * 4 + 2) ||
10948c2ecf20Sopenharmony_ci				    get_user(blend, colors + i * 4 + 3)) {
10958c2ecf20Sopenharmony_ci					ret = -EFAULT;
10968c2ecf20Sopenharmony_ci					break;
10978c2ecf20Sopenharmony_ci				    }
10988c2ecf20Sopenharmony_ci				ret = OSDSetColor(av7110, dc->color + i, r, g, b, blend);
10998c2ecf20Sopenharmony_ci				if (ret)
11008c2ecf20Sopenharmony_ci					break;
11018c2ecf20Sopenharmony_ci			}
11028c2ecf20Sopenharmony_ci		}
11038c2ecf20Sopenharmony_ci		break;
11048c2ecf20Sopenharmony_ci	case OSD_SetPixel:
11058c2ecf20Sopenharmony_ci		ret = DrawLine(av7110, av7110->osdwin,
11068c2ecf20Sopenharmony_ci			 dc->x0, dc->y0, 0, 0, dc->color);
11078c2ecf20Sopenharmony_ci		break;
11088c2ecf20Sopenharmony_ci	case OSD_SetRow:
11098c2ecf20Sopenharmony_ci		dc->y1 = dc->y0;
11108c2ecf20Sopenharmony_ci		fallthrough;
11118c2ecf20Sopenharmony_ci	case OSD_SetBlock:
11128c2ecf20Sopenharmony_ci		ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);
11138c2ecf20Sopenharmony_ci		break;
11148c2ecf20Sopenharmony_ci	case OSD_FillRow:
11158c2ecf20Sopenharmony_ci		ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
11168c2ecf20Sopenharmony_ci			  dc->x1-dc->x0+1, dc->y1, dc->color);
11178c2ecf20Sopenharmony_ci		break;
11188c2ecf20Sopenharmony_ci	case OSD_FillBlock:
11198c2ecf20Sopenharmony_ci		ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
11208c2ecf20Sopenharmony_ci			  dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);
11218c2ecf20Sopenharmony_ci		break;
11228c2ecf20Sopenharmony_ci	case OSD_Line:
11238c2ecf20Sopenharmony_ci		ret = DrawLine(av7110, av7110->osdwin,
11248c2ecf20Sopenharmony_ci			 dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);
11258c2ecf20Sopenharmony_ci		break;
11268c2ecf20Sopenharmony_ci	case OSD_Text:
11278c2ecf20Sopenharmony_ci	{
11288c2ecf20Sopenharmony_ci		char textbuf[240];
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci		if (strncpy_from_user(textbuf, dc->data, 240) < 0) {
11318c2ecf20Sopenharmony_ci			ret = -EFAULT;
11328c2ecf20Sopenharmony_ci			break;
11338c2ecf20Sopenharmony_ci		}
11348c2ecf20Sopenharmony_ci		textbuf[239] = 0;
11358c2ecf20Sopenharmony_ci		if (dc->x1 > 3)
11368c2ecf20Sopenharmony_ci			dc->x1 = 3;
11378c2ecf20Sopenharmony_ci		ret = SetFont(av7110, av7110->osdwin, dc->x1,
11388c2ecf20Sopenharmony_ci			(u16) (dc->color & 0xffff), (u16) (dc->color >> 16));
11398c2ecf20Sopenharmony_ci		if (!ret)
11408c2ecf20Sopenharmony_ci			ret = FlushText(av7110);
11418c2ecf20Sopenharmony_ci		if (!ret)
11428c2ecf20Sopenharmony_ci			ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);
11438c2ecf20Sopenharmony_ci		break;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci	case OSD_SetWindow:
11468c2ecf20Sopenharmony_ci		if (dc->x0 < 1 || dc->x0 > 7)
11478c2ecf20Sopenharmony_ci			ret = -EINVAL;
11488c2ecf20Sopenharmony_ci		else {
11498c2ecf20Sopenharmony_ci			av7110->osdwin = dc->x0;
11508c2ecf20Sopenharmony_ci			ret = 0;
11518c2ecf20Sopenharmony_ci		}
11528c2ecf20Sopenharmony_ci		break;
11538c2ecf20Sopenharmony_ci	case OSD_MoveWindow:
11548c2ecf20Sopenharmony_ci		ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
11558c2ecf20Sopenharmony_ci		if (!ret)
11568c2ecf20Sopenharmony_ci			ret = SetColorBlend(av7110, av7110->osdwin);
11578c2ecf20Sopenharmony_ci		break;
11588c2ecf20Sopenharmony_ci	case OSD_OpenRaw:
11598c2ecf20Sopenharmony_ci		if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
11608c2ecf20Sopenharmony_ci			ret = -EINVAL;
11618c2ecf20Sopenharmony_ci			break;
11628c2ecf20Sopenharmony_ci		}
11638c2ecf20Sopenharmony_ci		if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)
11648c2ecf20Sopenharmony_ci			av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
11658c2ecf20Sopenharmony_ci		else
11668c2ecf20Sopenharmony_ci			av7110->osdbpp[av7110->osdwin] = 0;
11678c2ecf20Sopenharmony_ci		ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,
11688c2ecf20Sopenharmony_ci				dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
11698c2ecf20Sopenharmony_ci		if (ret)
11708c2ecf20Sopenharmony_ci			break;
11718c2ecf20Sopenharmony_ci		if (!dc->data) {
11728c2ecf20Sopenharmony_ci			ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
11738c2ecf20Sopenharmony_ci			if (!ret)
11748c2ecf20Sopenharmony_ci				ret = SetColorBlend(av7110, av7110->osdwin);
11758c2ecf20Sopenharmony_ci		}
11768c2ecf20Sopenharmony_ci		break;
11778c2ecf20Sopenharmony_ci	default:
11788c2ecf20Sopenharmony_ci		ret = -EINVAL;
11798c2ecf20Sopenharmony_ci		break;
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	mutex_unlock(&av7110->osd_mutex);
11838c2ecf20Sopenharmony_ci	if (ret==-ERESTARTSYS)
11848c2ecf20Sopenharmony_ci		dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);
11858c2ecf20Sopenharmony_ci	else if (ret)
11868c2ecf20Sopenharmony_ci		dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	return ret;
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ciint av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
11928c2ecf20Sopenharmony_ci{
11938c2ecf20Sopenharmony_ci	switch (cap->cmd) {
11948c2ecf20Sopenharmony_ci	case OSD_CAP_MEMSIZE:
11958c2ecf20Sopenharmony_ci		if (FW_4M_SDRAM(av7110->arm_app))
11968c2ecf20Sopenharmony_ci			cap->val = 1000000;
11978c2ecf20Sopenharmony_ci		else
11988c2ecf20Sopenharmony_ci			cap->val = 92000;
11998c2ecf20Sopenharmony_ci		return 0;
12008c2ecf20Sopenharmony_ci	default:
12018c2ecf20Sopenharmony_ci		return -EINVAL;
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci#endif /* CONFIG_DVB_AV7110_OSD */
1205