Path: blob/master/drivers/media/dvb/ttpci/av7110_hw.c
15112 views
/*1* av7110_hw.c: av7110 low level hardware access and firmware interface2*3* Copyright (C) 1999-2002 Ralph Metzler4* & Marcus Metzler for convergence integrated media GmbH5*6* originally based on code by:7* Copyright (C) 1998,1999 Christian Theiss <[email protected]>8*9* This program is free software; you can redistribute it and/or10* modify it under the terms of the GNU General Public License11* as published by the Free Software Foundation; either version 212* of the License, or (at your option) any later version.13*14* This program is distributed in the hope that it will be useful,15* but WITHOUT ANY WARRANTY; without even the implied warranty of16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17* GNU General Public License for more details.18*19* You should have received a copy of the GNU General Public License20* along with this program; if not, write to the Free Software21* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.22* Or, point your browser to http://www.gnu.org/copyleft/gpl.html23*24* the project's page is at http://www.linuxtv.org/25*/2627/* for debugging ARM communication: */28//#define COM_DEBUG2930#include <stdarg.h>31#include <linux/types.h>32#include <linux/kernel.h>33#include <linux/string.h>34#include <linux/delay.h>35#include <linux/fs.h>3637#include "av7110.h"38#include "av7110_hw.h"3940#define _NOHANDSHAKE4142/****************************************************************************43* DEBI functions44****************************************************************************/4546/* This DEBI code is based on the Stradis driver47by Nathan Laredo <[email protected]> */4849int av7110_debiwrite(struct av7110 *av7110, u32 config,50int addr, u32 val, int count)51{52struct saa7146_dev *dev = av7110->dev;5354if (count <= 0 || count > 32764) {55printk("%s: invalid count %d\n", __func__, count);56return -1;57}58if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {59printk("%s: wait_for_debi_done failed\n", __func__);60return -1;61}62saa7146_write(dev, DEBI_CONFIG, config);63if (count <= 4) /* immediate transfer */64saa7146_write(dev, DEBI_AD, val);65else /* block transfer */66saa7146_write(dev, DEBI_AD, av7110->debi_bus);67saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));68saa7146_write(dev, MC2, (2 << 16) | 2);69return 0;70}7172u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)73{74struct saa7146_dev *dev = av7110->dev;75u32 result = 0;7677if (count > 32764 || count <= 0) {78printk("%s: invalid count %d\n", __func__, count);79return 0;80}81if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {82printk("%s: wait_for_debi_done #1 failed\n", __func__);83return 0;84}85saa7146_write(dev, DEBI_AD, av7110->debi_bus);86saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));8788saa7146_write(dev, DEBI_CONFIG, config);89saa7146_write(dev, MC2, (2 << 16) | 2);90if (count > 4)91return count;92if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {93printk("%s: wait_for_debi_done #2 failed\n", __func__);94return 0;95}9697result = saa7146_read(dev, DEBI_AD);98result &= (0xffffffffUL >> ((4 - count) * 8));99return result;100}101102103104/* av7110 ARM core boot stuff */105#if 0106void av7110_reset_arm(struct av7110 *av7110)107{108saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);109110/* Disable DEBI and GPIO irq */111SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);112SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);113114saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);115msleep(30); /* the firmware needs some time to initialize */116117ARM_ResetMailBox(av7110);118119SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);120SAA7146_IER_ENABLE(av7110->dev, MASK_03);121122av7110->arm_ready = 1;123dprintk(1, "reset ARM\n");124}125#endif /* 0 */126127static int waitdebi(struct av7110 *av7110, int adr, int state)128{129int k;130131dprintk(4, "%p\n", av7110);132133for (k = 0; k < 100; k++) {134if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)135return 0;136udelay(5);137}138return -ETIMEDOUT;139}140141static int load_dram(struct av7110 *av7110, u32 *data, int len)142{143int i;144int blocks, rest;145u32 base, bootblock = AV7110_BOOT_BLOCK;146147dprintk(4, "%p\n", av7110);148149blocks = len / AV7110_BOOT_MAX_SIZE;150rest = len % AV7110_BOOT_MAX_SIZE;151base = DRAM_START_CODE;152153for (i = 0; i < blocks; i++) {154if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {155printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);156return -ETIMEDOUT;157}158dprintk(4, "writing DRAM block %d\n", i);159mwdebi(av7110, DEBISWAB, bootblock,160((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE);161bootblock ^= 0x1400;162iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4);163iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2);164iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);165base += AV7110_BOOT_MAX_SIZE;166}167168if (rest > 0) {169if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {170printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");171return -ETIMEDOUT;172}173if (rest > 4)174mwdebi(av7110, DEBISWAB, bootblock,175((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest);176else177mwdebi(av7110, DEBISWAB, bootblock,178((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4);179180iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4);181iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2);182iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);183}184if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {185printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");186return -ETIMEDOUT;187}188iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2);189iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);190if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) {191printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");192return -ETIMEDOUT;193}194return 0;195}196197198/* we cannot write av7110 DRAM directly, so load a bootloader into199* the DPRAM which implements a simple boot protocol */200int av7110_bootarm(struct av7110 *av7110)201{202const struct firmware *fw;203const char *fw_name = "av7110/bootcode.bin";204struct saa7146_dev *dev = av7110->dev;205u32 ret;206int i;207208dprintk(4, "%p\n", av7110);209210av7110->arm_ready = 0;211212saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);213214/* Disable DEBI and GPIO irq */215SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);216SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);217218/* enable DEBI */219saa7146_write(av7110->dev, MC1, 0x08800880);220saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);221saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));222223/* test DEBI */224iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);225/* FIXME: Why does Nexus CA require 2x iwdebi for first init? */226iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);227228if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {229printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: "230"%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",231ret, 0x10325476);232return -1;233}234for (i = 0; i < 8192; i += 4)235iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);236dprintk(2, "debi test OK\n");237238/* boot */239dprintk(1, "load boot code\n");240saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);241//saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);242//saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);243244ret = request_firmware(&fw, fw_name, &dev->pci->dev);245if (ret) {246printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n",247fw_name);248return ret;249}250251mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size);252release_firmware(fw);253iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);254255if (saa7146_wait_for_debi_done(av7110->dev, 1)) {256printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "257"saa7146_wait_for_debi_done() timed out\n");258return -ETIMEDOUT;259}260saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);261mdelay(1);262263dprintk(1, "load dram code\n");264if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {265printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "266"load_dram() failed\n");267return -1;268}269270saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);271mdelay(1);272273dprintk(1, "load dpram code\n");274mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);275276if (saa7146_wait_for_debi_done(av7110->dev, 1)) {277printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "278"saa7146_wait_for_debi_done() timed out after loading DRAM\n");279return -ETIMEDOUT;280}281saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);282msleep(30); /* the firmware needs some time to initialize */283284//ARM_ClearIrq(av7110);285ARM_ResetMailBox(av7110);286SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);287SAA7146_IER_ENABLE(av7110->dev, MASK_03);288289av7110->arm_errors = 0;290av7110->arm_ready = 1;291return 0;292}293MODULE_FIRMWARE("av7110/bootcode.bin");294295/****************************************************************************296* DEBI command polling297****************************************************************************/298299int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)300{301unsigned long start;302u32 stat;303int err;304305if (FW_VERSION(av7110->arm_app) <= 0x261c) {306/* not supported by old firmware */307msleep(50);308return 0;309}310311/* new firmware */312start = jiffies;313for (;;) {314err = time_after(jiffies, start + ARM_WAIT_FREE);315if (mutex_lock_interruptible(&av7110->dcomlock))316return -ERESTARTSYS;317stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);318mutex_unlock(&av7110->dcomlock);319if ((stat & flags) == 0)320break;321if (err) {322printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",323__func__, stat & flags);324return -ETIMEDOUT;325}326msleep(1);327}328return 0;329}330331static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)332{333int i;334unsigned long start;335char *type = NULL;336u16 flags[2] = {0, 0};337u32 stat;338int err;339340// dprintk(4, "%p\n", av7110);341342if (!av7110->arm_ready) {343dprintk(1, "arm not ready.\n");344return -ENXIO;345}346347start = jiffies;348while (1) {349err = time_after(jiffies, start + ARM_WAIT_FREE);350if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)351break;352if (err) {353printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__);354av7110->arm_errors++;355return -ETIMEDOUT;356}357msleep(1);358}359360if (FW_VERSION(av7110->arm_app) <= 0x261f)361wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);362363#ifndef _NOHANDSHAKE364start = jiffies;365while (1) {366err = time_after(jiffies, start + ARM_WAIT_SHAKE);367if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)368break;369if (err) {370printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__);371return -ETIMEDOUT;372}373msleep(1);374}375#endif376377switch ((buf[0] >> 8) & 0xff) {378case COMTYPE_PIDFILTER:379case COMTYPE_ENCODER:380case COMTYPE_REC_PLAY:381case COMTYPE_MPEGDECODER:382type = "MSG";383flags[0] = GPMQOver;384flags[1] = GPMQFull;385break;386case COMTYPE_OSD:387type = "OSD";388flags[0] = OSDQOver;389flags[1] = OSDQFull;390break;391case COMTYPE_MISC:392if (FW_VERSION(av7110->arm_app) >= 0x261d) {393type = "MSG";394flags[0] = GPMQOver;395flags[1] = GPMQBusy;396}397break;398default:399break;400}401402if (type != NULL) {403/* non-immediate COMMAND type */404start = jiffies;405for (;;) {406err = time_after(jiffies, start + ARM_WAIT_FREE);407stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);408if (stat & flags[0]) {409printk(KERN_ERR "%s: %s QUEUE overflow\n",410__func__, type);411return -1;412}413if ((stat & flags[1]) == 0)414break;415if (err) {416printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",417__func__, type);418av7110->arm_errors++;419return -ETIMEDOUT;420}421msleep(1);422}423}424425for (i = 2; i < length; i++)426wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);427428if (length)429wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);430else431wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);432433wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);434435if (FW_VERSION(av7110->arm_app) <= 0x261f)436wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);437438#ifdef COM_DEBUG439start = jiffies;440while (1) {441err = time_after(jiffies, start + ARM_WAIT_FREE);442if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)443break;444if (err) {445printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n",446__func__, (buf[0] >> 8) & 0xff);447return -ETIMEDOUT;448}449msleep(1);450}451452stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);453if (stat & GPMQOver) {454printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__);455return -ENOSPC;456}457else if (stat & OSDQOver) {458printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__);459return -ENOSPC;460}461#endif462463return 0;464}465466static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)467{468int ret;469470// dprintk(4, "%p\n", av7110);471472if (!av7110->arm_ready) {473dprintk(1, "arm not ready.\n");474return -1;475}476if (mutex_lock_interruptible(&av7110->dcomlock))477return -ERESTARTSYS;478479ret = __av7110_send_fw_cmd(av7110, buf, length);480mutex_unlock(&av7110->dcomlock);481if (ret && ret!=-ERESTARTSYS)482printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",483__func__, ret);484return ret;485}486487int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)488{489va_list args;490u16 buf[num + 2];491int i, ret;492493// dprintk(4, "%p\n", av7110);494495buf[0] = ((type << 8) | com);496buf[1] = num;497498if (num) {499va_start(args, num);500for (i = 0; i < num; i++)501buf[i + 2] = va_arg(args, u32);502va_end(args);503}504505ret = av7110_send_fw_cmd(av7110, buf, num + 2);506if (ret && ret != -ERESTARTSYS)507printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);508return ret;509}510511#if 0512int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)513{514int i, ret;515u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),51616, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };517518dprintk(4, "%p\n", av7110);519520for(i = 0; i < len && i < 32; i++)521{522if(i % 2 == 0)523cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;524else525cmd[(i / 2) + 2] |= buf[i];526}527528ret = av7110_send_fw_cmd(av7110, cmd, 18);529if (ret && ret != -ERESTARTSYS)530printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);531return ret;532}533#endif /* 0 */534535int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,536int request_buf_len, u16 *reply_buf, int reply_buf_len)537{538int err;539s16 i;540unsigned long start;541#ifdef COM_DEBUG542u32 stat;543#endif544545dprintk(4, "%p\n", av7110);546547if (!av7110->arm_ready) {548dprintk(1, "arm not ready.\n");549return -1;550}551552if (mutex_lock_interruptible(&av7110->dcomlock))553return -ERESTARTSYS;554555if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {556mutex_unlock(&av7110->dcomlock);557printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);558return err;559}560561start = jiffies;562while (1) {563err = time_after(jiffies, start + ARM_WAIT_FREE);564if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0)565break;566if (err) {567printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__);568mutex_unlock(&av7110->dcomlock);569return -ETIMEDOUT;570}571#ifdef _NOHANDSHAKE572msleep(1);573#endif574}575576#ifndef _NOHANDSHAKE577start = jiffies;578while (1) {579err = time_after(jiffies, start + ARM_WAIT_SHAKE);580if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)581break;582if (err) {583printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__);584mutex_unlock(&av7110->dcomlock);585return -ETIMEDOUT;586}587msleep(1);588}589#endif590591#ifdef COM_DEBUG592stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);593if (stat & GPMQOver) {594printk(KERN_ERR "%s: GPMQOver\n", __func__);595mutex_unlock(&av7110->dcomlock);596return -1;597}598else if (stat & OSDQOver) {599printk(KERN_ERR "%s: OSDQOver\n", __func__);600mutex_unlock(&av7110->dcomlock);601return -1;602}603#endif604605for (i = 0; i < reply_buf_len; i++)606reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);607608mutex_unlock(&av7110->dcomlock);609return 0;610}611612static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)613{614int ret;615ret = av7110_fw_request(av7110, &tag, 0, buf, length);616if (ret)617printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);618return ret;619}620621622/****************************************************************************623* Firmware commands624****************************************************************************/625626/* get version of the firmware ROM, RTSL, video ucode and ARM application */627int av7110_firmversion(struct av7110 *av7110)628{629u16 buf[20];630u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);631632dprintk(4, "%p\n", av7110);633634if (av7110_fw_query(av7110, tag, buf, 16)) {635printk("dvb-ttpci: failed to boot firmware @ card %d\n",636av7110->dvb_adapter.num);637return -EIO;638}639640av7110->arm_fw = (buf[0] << 16) + buf[1];641av7110->arm_rtsl = (buf[2] << 16) + buf[3];642av7110->arm_vid = (buf[4] << 16) + buf[5];643av7110->arm_app = (buf[6] << 16) + buf[7];644av7110->avtype = (buf[8] << 16) + buf[9];645646printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",647av7110->dvb_adapter.num, av7110->arm_fw,648av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);649650/* print firmware capabilities */651if (FW_CI_LL_SUPPORT(av7110->arm_app))652printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",653av7110->dvb_adapter.num);654else655printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",656av7110->dvb_adapter.num);657658return 0;659}660661662int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)663{664int i, ret;665u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),66616, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };667668dprintk(4, "%p\n", av7110);669670if (len > 10)671len = 10;672673buf[1] = len + 2;674buf[2] = len;675676if (burst != -1)677buf[3] = burst ? 0x01 : 0x00;678else679buf[3] = 0xffff;680681for (i = 0; i < len; i++)682buf[i + 4] = msg[i];683684ret = av7110_send_fw_cmd(av7110, buf, 18);685if (ret && ret!=-ERESTARTSYS)686printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);687return ret;688}689690691#ifdef CONFIG_DVB_AV7110_OSD692693static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)694{695return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);696}697698static inline int SetBlend_(struct av7110 *av7110, u8 windownr,699enum av7110_osd_palette_type colordepth, u16 index, u8 blending)700{701return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,702windownr, colordepth, index, blending);703}704705static inline int SetColor_(struct av7110 *av7110, u8 windownr,706enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)707{708return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,709windownr, colordepth, index, colorhi, colorlo);710}711712static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,713u16 colorfg, u16 colorbg)714{715return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,716windownr, fontsize, colorfg, colorbg);717}718719static int FlushText(struct av7110 *av7110)720{721unsigned long start;722int err;723724if (mutex_lock_interruptible(&av7110->dcomlock))725return -ERESTARTSYS;726start = jiffies;727while (1) {728err = time_after(jiffies, start + ARM_WAIT_OSD);729if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)730break;731if (err) {732printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",733__func__);734mutex_unlock(&av7110->dcomlock);735return -ETIMEDOUT;736}737msleep(1);738}739mutex_unlock(&av7110->dcomlock);740return 0;741}742743static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf)744{745int i, ret;746unsigned long start;747int length = strlen(buf) + 1;748u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };749750if (mutex_lock_interruptible(&av7110->dcomlock))751return -ERESTARTSYS;752753start = jiffies;754while (1) {755ret = time_after(jiffies, start + ARM_WAIT_OSD);756if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0)757break;758if (ret) {759printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",760__func__);761mutex_unlock(&av7110->dcomlock);762return -ETIMEDOUT;763}764msleep(1);765}766#ifndef _NOHANDSHAKE767start = jiffies;768while (1) {769ret = time_after(jiffies, start + ARM_WAIT_SHAKE);770if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0)771break;772if (ret) {773printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",774__func__);775mutex_unlock(&av7110->dcomlock);776return -ETIMEDOUT;777}778msleep(1);779}780#endif781for (i = 0; i < length / 2; i++)782wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,783swab16(*(u16 *)(buf + 2 * i)), 2);784if (length & 1)785wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);786ret = __av7110_send_fw_cmd(av7110, cbuf, 5);787mutex_unlock(&av7110->dcomlock);788if (ret && ret!=-ERESTARTSYS)789printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);790return ret;791}792793static inline int DrawLine(struct av7110 *av7110, u8 windownr,794u16 x, u16 y, u16 dx, u16 dy, u16 color)795{796return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6,797windownr, x, y, dx, dy, color);798}799800static inline int DrawBlock(struct av7110 *av7110, u8 windownr,801u16 x, u16 y, u16 dx, u16 dy, u16 color)802{803return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,804windownr, x, y, dx, dy, color);805}806807static inline int HideWindow(struct av7110 *av7110, u8 windownr)808{809return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);810}811812static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)813{814return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);815}816817static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)818{819return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);820}821822static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)823{824return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);825}826827static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,828osd_raw_window_t disptype,829u16 width, u16 height)830{831return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,832windownr, disptype, width, height);833}834835836static enum av7110_osd_palette_type bpp2pal[8] = {837Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit838};839static osd_raw_window_t bpp2bit[8] = {840OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8841};842843static inline int WaitUntilBmpLoaded(struct av7110 *av7110)844{845int ret = wait_event_timeout(av7110->bmpq,846av7110->bmp_state != BMP_LOADING, 10*HZ);847if (ret == 0) {848printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",849ret, av7110->bmp_state);850av7110->bmp_state = BMP_NONE;851return -ETIMEDOUT;852}853return 0;854}855856static inline int LoadBitmap(struct av7110 *av7110,857u16 dx, u16 dy, int inc, u8 __user * data)858{859u16 format;860int bpp;861int i;862int d, delta;863u8 c;864int ret;865866dprintk(4, "%p\n", av7110);867868format = bpp2bit[av7110->osdbpp[av7110->osdwin]];869870av7110->bmp_state = BMP_LOADING;871if (format == OSD_BITMAP8) {872bpp=8; delta = 1;873} else if (format == OSD_BITMAP4) {874bpp=4; delta = 2;875} else if (format == OSD_BITMAP2) {876bpp=2; delta = 4;877} else if (format == OSD_BITMAP1) {878bpp=1; delta = 8;879} else {880av7110->bmp_state = BMP_NONE;881return -EINVAL;882}883av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;884av7110->bmpp = 0;885if (av7110->bmplen > 32768) {886av7110->bmp_state = BMP_NONE;887return -EINVAL;888}889for (i = 0; i < dy; i++) {890if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {891av7110->bmp_state = BMP_NONE;892return -EINVAL;893}894}895if (format != OSD_BITMAP8) {896for (i = 0; i < dx * dy / delta; i++) {897c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];898for (d = delta - 2; d >= 0; d--) {899c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]900<< ((delta - d - 1) * bpp));901((u8 *)av7110->bmpbuf)[1024 + i] = c;902}903}904}905av7110->bmplen += 1024;906dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);907ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);908if (!ret)909ret = WaitUntilBmpLoaded(av7110);910return ret;911}912913static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y)914{915dprintk(4, "%p\n", av7110);916917return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0);918}919920static inline int ReleaseBitmap(struct av7110 *av7110)921{922dprintk(4, "%p\n", av7110);923924if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e)925return -1;926if (av7110->bmp_state == BMP_LOADING)927dprintk(1,"ReleaseBitmap called while BMP_LOADING\n");928av7110->bmp_state = BMP_NONE;929return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);930}931932static u32 RGB2YUV(u16 R, u16 G, u16 B)933{934u16 y, u, v;935u16 Y, Cr, Cb;936937y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */938u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */939v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */940941Y = y / 256;942Cb = u / 16;943Cr = v / 16;944945return Cr | (Cb << 16) | (Y << 8);946}947948static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)949{950int ret;951952u16 ch, cl;953u32 yuv;954955yuv = blend ? RGB2YUV(r,g,b) : 0;956cl = (yuv & 0xffff);957ch = ((yuv >> 16) & 0xffff);958ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],959color, ch, cl);960if (!ret)961ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],962color, ((blend >> 4) & 0x0f));963return ret;964}965966static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)967{968int i;969int length = last - first + 1;970971if (length * 4 > DATA_BUFF3_SIZE)972return -EINVAL;973974for (i = 0; i < length; i++) {975u32 color, blend, yuv;976977if (get_user(color, colors + i))978return -EFAULT;979blend = (color & 0xF0000000) >> 4;980yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,981(color >> 16) & 0xFF) | blend : 0;982yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);983wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);984}985return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,986av7110->osdwin,987bpp2pal[av7110->osdbpp[av7110->osdwin]],988first, last);989}990991static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,992int x1, int y1, int inc, u8 __user * data)993{994uint w, h, bpp, bpl, size, lpb, bnum, brest;995int i;996int rc,release_rc;997998w = x1 - x0 + 1;999h = y1 - y0 + 1;1000if (inc <= 0)1001inc = w;1002if (w <= 0 || w > 720 || h <= 0 || h > 576)1003return -EINVAL;1004bpp = av7110->osdbpp[av7110->osdwin] + 1;1005bpl = ((w * bpp + 7) & ~7) / 8;1006size = h * bpl;1007lpb = (32 * 1024) / bpl;1008bnum = size / (lpb * bpl);1009brest = size - bnum * lpb * bpl;10101011if (av7110->bmp_state == BMP_LOADING) {1012/* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */1013BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e);1014rc = WaitUntilBmpLoaded(av7110);1015if (rc)1016return rc;1017/* just continue. This should work for all fw versions1018* if bnum==1 && !brest && LoadBitmap was successful1019*/1020}10211022rc = 0;1023for (i = 0; i < bnum; i++) {1024rc = LoadBitmap(av7110, w, lpb, inc, data);1025if (rc)1026break;1027rc = BlitBitmap(av7110, x0, y0 + i * lpb);1028if (rc)1029break;1030data += lpb * inc;1031}1032if (!rc && brest) {1033rc = LoadBitmap(av7110, w, brest / bpl, inc, data);1034if (!rc)1035rc = BlitBitmap(av7110, x0, y0 + bnum * lpb);1036}1037release_rc = ReleaseBitmap(av7110);1038if (!rc)1039rc = release_rc;1040if (rc)1041dprintk(1,"returns %d\n",rc);1042return rc;1043}10441045int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)1046{1047int ret;10481049if (mutex_lock_interruptible(&av7110->osd_mutex))1050return -ERESTARTSYS;10511052switch (dc->cmd) {1053case OSD_Close:1054ret = DestroyOSDWindow(av7110, av7110->osdwin);1055break;1056case OSD_Open:1057av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;1058ret = CreateOSDWindow(av7110, av7110->osdwin,1059bpp2bit[av7110->osdbpp[av7110->osdwin]],1060dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);1061if (ret)1062break;1063if (!dc->data) {1064ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);1065if (ret)1066break;1067ret = SetColorBlend(av7110, av7110->osdwin);1068}1069break;1070case OSD_Show:1071ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0);1072break;1073case OSD_Hide:1074ret = HideWindow(av7110, av7110->osdwin);1075break;1076case OSD_Clear:1077ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);1078break;1079case OSD_Fill:1080ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);1081break;1082case OSD_SetColor:1083ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);1084break;1085case OSD_SetPalette:1086if (FW_VERSION(av7110->arm_app) >= 0x2618)1087ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);1088else {1089int i, len = dc->x0-dc->color+1;1090u8 __user *colors = (u8 __user *)dc->data;1091u8 r, g = 0, b = 0, blend = 0;1092ret = 0;1093for (i = 0; i<len; i++) {1094if (get_user(r, colors + i * 4) ||1095get_user(g, colors + i * 4 + 1) ||1096get_user(b, colors + i * 4 + 2) ||1097get_user(blend, colors + i * 4 + 3)) {1098ret = -EFAULT;1099break;1100}1101ret = OSDSetColor(av7110, dc->color + i, r, g, b, blend);1102if (ret)1103break;1104}1105}1106break;1107case OSD_SetPixel:1108ret = DrawLine(av7110, av7110->osdwin,1109dc->x0, dc->y0, 0, 0, dc->color);1110break;1111case OSD_SetRow:1112dc->y1 = dc->y0;1113/* fall through */1114case OSD_SetBlock:1115ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);1116break;1117case OSD_FillRow:1118ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,1119dc->x1-dc->x0+1, dc->y1, dc->color);1120break;1121case OSD_FillBlock:1122ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,1123dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);1124break;1125case OSD_Line:1126ret = DrawLine(av7110, av7110->osdwin,1127dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);1128break;1129case OSD_Text:1130{1131char textbuf[240];11321133if (strncpy_from_user(textbuf, dc->data, 240) < 0) {1134ret = -EFAULT;1135break;1136}1137textbuf[239] = 0;1138if (dc->x1 > 3)1139dc->x1 = 3;1140ret = SetFont(av7110, av7110->osdwin, dc->x1,1141(u16) (dc->color & 0xffff), (u16) (dc->color >> 16));1142if (!ret)1143ret = FlushText(av7110);1144if (!ret)1145ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);1146break;1147}1148case OSD_SetWindow:1149if (dc->x0 < 1 || dc->x0 > 7)1150ret = -EINVAL;1151else {1152av7110->osdwin = dc->x0;1153ret = 0;1154}1155break;1156case OSD_MoveWindow:1157ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);1158if (!ret)1159ret = SetColorBlend(av7110, av7110->osdwin);1160break;1161case OSD_OpenRaw:1162if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {1163ret = -EINVAL;1164break;1165}1166if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR)1167av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;1168else1169av7110->osdbpp[av7110->osdwin] = 0;1170ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,1171dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);1172if (ret)1173break;1174if (!dc->data) {1175ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);1176if (!ret)1177ret = SetColorBlend(av7110, av7110->osdwin);1178}1179break;1180default:1181ret = -EINVAL;1182break;1183}11841185mutex_unlock(&av7110->osd_mutex);1186if (ret==-ERESTARTSYS)1187dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd);1188else if (ret)1189dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret);11901191return ret;1192}11931194int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)1195{1196switch (cap->cmd) {1197case OSD_CAP_MEMSIZE:1198if (FW_4M_SDRAM(av7110->arm_app))1199cap->val = 1000000;1200else1201cap->val = 92000;1202return 0;1203default:1204return -EINVAL;1205}1206}1207#endif /* CONFIG_DVB_AV7110_OSD */120812091210