diff options
author | Johannes Stezenbach <devnull@localhost> | 2004-01-05 16:25:22 +0000 |
---|---|---|
committer | Johannes Stezenbach <devnull@localhost> | 2004-01-05 16:25:22 +0000 |
commit | 3612bc6917d94cb4178e4301fa2ac6d27289c704 (patch) | |
tree | 12e66af7cbc8d82e8ede7b5c828a34ba74b83f28 /linux/drivers/media/dvb/ttpci/av7110_av.c | |
parent | 539fe957e343be686be416ccf137396bca953e07 (diff) | |
download | mediapointer-dvb-s2-3612bc6917d94cb4178e4301fa2ac6d27289c704.tar.gz mediapointer-dvb-s2-3612bc6917d94cb4178e4301fa2ac6d27289c704.tar.bz2 |
Split av7110.c into a few separate modules:
- av7110.c: initialization and demux stuff
- av7110_hw.c: lowlevel hardware access and firmware interface
- av7110_ca.c: CI and ECD
- av7110_av.c: audio/video MPEG decoder and remuxing stuff
- av7110_v4l.c: v4l interface
It's all still ugly and needs lots of namespace and coding-style
cleanups. It's probably broken, too. But it's a start.
(My main motivation is easier porting to the V4 API).
Diffstat (limited to 'linux/drivers/media/dvb/ttpci/av7110_av.c')
-rw-r--r-- | linux/drivers/media/dvb/ttpci/av7110_av.c | 1457 |
1 files changed, 1457 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/ttpci/av7110_av.c b/linux/drivers/media/dvb/ttpci/av7110_av.c new file mode 100644 index 000000000..cf291c10b --- /dev/null +++ b/linux/drivers/media/dvb/ttpci/av7110_av.c @@ -0,0 +1,1457 @@ +/* + * av7110_av.c: audio and video MPEG decoder stuff + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * originally based on code by: + * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + * + * the project's page is at http://www.linuxtv.org/dvb/ + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/byteorder/swabb.h> +#include <linux/smp_lock.h> +#include <linux/fs.h> + +#define DEBUG_VARIABLE av7110_debug +extern int av7110_debug; + +#include "dvb_functions.h" +#include "av7110.h" +#include "av7110_hw.h" +#include "av7110_av.h" +#include "av7110_ipack.h" + +#define PROG_STREAM_MAP 0xBC +#define PRIVATE_STREAM1 0xBD +#define PADDING_STREAM 0xBE +#define PRIVATE_STREAM2 0xBF +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define PTS_DTS_FLAGS 0xC0 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +static void p_to_t(u8 const *buf, long int length, u16 pid, + u8 *counter, struct dvb_demux_feed *feed); + + +int record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) +{ + struct dvb_demux_feed *dvbdmxfeed=(struct dvb_demux_feed *) p2t->priv; + +// DEB_EE(("struct dvb_filter_pes2ts:%p\n",p2t)); + + if (!(dvbdmxfeed->ts_type & TS_PACKET)) + return 0; + if (buf[3]==0xe0) // video PES do not have a length in TS + buf[4]=buf[5]=0; + if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) + return dvbdmxfeed->cb.ts(buf, len, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + else + return dvb_filter_pes2ts(p2t, buf, len, 1); +} + +static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) +{ + struct dvb_demux_feed *dvbdmxfeed=(struct dvb_demux_feed *) priv; + +// DEB_EE(("dvb_demux_feed:%p\n",dvbdmxfeed)); + + dvbdmxfeed->cb.ts(data, 188, 0, 0, + &dvbdmxfeed->feed.ts, + DMX_OK); + return 0; +} + +int AV_StartRecord(struct av7110 *av7110, int av, + struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx=dvbdmxfeed->demux; + + DEB_EE(("av7110: %p, dvb_demux_feed:%p\n",av7110,dvbdmxfeed)); + + if (av7110->playing||(av7110->rec_mode&av)) + return -EBUSY; + outcom(av7110, COMTYPE_REC_PLAY, __Stop, 0); + dvbdmx->recording=1; + av7110->rec_mode|=av; + + switch (av7110->rec_mode) { + case RP_AUDIO: + dvb_filter_pes2ts_init (&av7110->p2t[0], + dvbdmx->pesfilter[0]->pid, + dvb_filter_pes2ts_cb, + (void *)dvbdmx->pesfilter[0]); + outcom(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + break; + + case RP_VIDEO: + dvb_filter_pes2ts_init (&av7110->p2t[1], + dvbdmx->pesfilter[1]->pid, + dvb_filter_pes2ts_cb, + (void *)dvbdmx->pesfilter[1]); + outcom(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + break; + + case RP_AV: + dvb_filter_pes2ts_init (&av7110->p2t[0], + dvbdmx->pesfilter[0]->pid, + dvb_filter_pes2ts_cb, + (void *)dvbdmx->pesfilter[0]); + dvb_filter_pes2ts_init (&av7110->p2t[1], + dvbdmx->pesfilter[1]->pid, + dvb_filter_pes2ts_cb, + (void *)dvbdmx->pesfilter[1]); + outcom(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); + break; + } + return 0; +} + +int AV_StartPlay(struct av7110 *av7110, int av) +{ + DEB_EE(("av7110: %p\n",av7110)); + + if (av7110->rec_mode) + return -EBUSY; + if (av7110->playing&av) + return -EBUSY; + + outcom(av7110, COMTYPE_REC_PLAY, __Stop, 0); + + if (av7110->playing == RP_NONE) { + av7110_ipack_reset(&av7110->ipack[0]); + av7110_ipack_reset(&av7110->ipack[1]); + } + + av7110->playing|=av; + switch (av7110->playing) { + case RP_AUDIO: + outcom(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + break; + case RP_VIDEO: + outcom(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + av7110->sinfo=0; + break; + case RP_AV: + av7110->sinfo=0; + outcom(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); + break; + } + return av7110->playing; +} + +void AV_Stop(struct av7110 *av7110, int av) +{ + DEB_EE(("av7110: %p\n",av7110)); + + if (!(av7110->playing&av) && !(av7110->rec_mode&av)) + return; + + outcom(av7110, COMTYPE_REC_PLAY, __Stop, 0); + if (av7110->playing) { + av7110->playing&=~av; + switch (av7110->playing) { + case RP_AUDIO: + outcom(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); + break; + case RP_VIDEO: + outcom(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); + break; + case RP_NONE: + SetMode(av7110, av7110->vidmode); + break; + } + } else { + av7110->rec_mode&=~av; + switch (av7110->rec_mode) { + case RP_AUDIO: + outcom(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); + break; + case RP_VIDEO: + outcom(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); + break; + case RP_NONE: + break; + } + } +} + + +u8 pshead[0x26] = { + 0x00, 0x00, 0x01, 0xba, 0x5f, 0xff, 0xfe, 0xe6, + 0xc4, 0x01, 0x01, 0x89, 0xc3, 0xf8, 0x00, 0x00, + 0x01, 0xbb, 0x00, 0x12, 0x80, 0xc4, 0xe1, 0x00, + 0xe1, 0xff, 0xb9, 0xe0, 0xe8, 0xb8, 0xc0, 0x20, + 0xbd, 0xe0, 0x44, 0xbf, 0xe0, 0x02, +}; + + +int pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) +{ + int len; + u32 sync; + u16 blen; + + DEB_EE(("dvb_ring_buffer_t: %p\n",buf)); + + if (!dlen) { + wake_up(&buf->queue); + return -1; + } + while (1) { + if ((len=dvb_ringbuffer_avail(buf)) < 6) + return -1; + sync= DVB_RINGBUFFER_PEEK(buf,0)<<24; + sync|=DVB_RINGBUFFER_PEEK(buf,1)<<16; + sync|=DVB_RINGBUFFER_PEEK(buf,2)<<8; + sync|=DVB_RINGBUFFER_PEEK(buf,3); + + if (((sync&~0x0f)==0x000001e0) || + ((sync&~0x1f)==0x000001c0) || + (sync==0x000001bd)) + break; + printk("resync\n"); + DVB_RINGBUFFER_SKIP(buf,1); + } + blen= DVB_RINGBUFFER_PEEK(buf,4)<<8; + blen|=DVB_RINGBUFFER_PEEK(buf,5); + blen+=6; + if (len<blen || blen>dlen) { + //printk("buffer empty - avail %d blen %u dlen %d\n",len,blen,dlen); + wake_up(&buf->queue); + return -1; + } + + (void)dvb_ringbuffer_read(buf,dest,(size_t)blen,0); + + DEB_S(("pread=0x%08lx, pwrite=0x%08lx\n",(unsigned long)buf->pread, (unsigned long)buf->pwrite)); + wake_up(&buf->queue); + return blen; +} + + +int SetVolume(struct av7110 *av7110, int volleft, int volright) +{ + int err, vol, val, balance = 0; + + DEB_EE(("av7110: %p\n",av7110)); + + switch (av7110->adac_type) { + case DVB_ADAC_TI: + volleft = (volleft * 256) / 1036; + volright = (volright * 256) / 1036; + if (volleft > 0x3f) + volleft = 0x3f; + if (volright > 0x3f) + volright = 0x3f; + if ((err = SendDAC(av7110, 3, 0x80 + volleft))) + return err; + return SendDAC(av7110, 4, volright); + + case DVB_ADAC_CRYSTAL: + volleft=127-volleft/2; + volright=127-volright/2; + i2c_writereg(av7110, 0x20, 0x03, volleft); + i2c_writereg(av7110, 0x20, 0x04, volright); + return 0; + + case DVB_ADAC_MSP: + vol = (volleft > volright) ? volleft : volright; + val = (vol * 0x73 / 255) << 8; + if (vol > 0) { + balance = ((volright-volleft) * 127) / vol; + } + msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); + msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ + msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ + return 0; + } + return 0; +} + +void SetMode(struct av7110 *av7110, int mode) +{ + DEB_EE(("av7110: %p\n",av7110)); + + outcom(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); + + if (!av7110->playing) { + ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], + av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_TELETEXT], + 0, av7110->pids[DMX_PES_PCR]); + outcom(av7110, COMTYPE_PIDFILTER, Scan, 0); + } +} + + +static int sw2mode[16] = { + VIDEO_MODE_PAL, VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL, + VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL, VIDEO_MODE_NTSC, + VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, + VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, +}; + +static void get_video_format(struct av7110 *av7110, u8 *buf, int count) +{ + int i; + int hsize,vsize; + int sw; + u8 *p; + + DEB_EE(("av7110: %p\n",av7110)); + + if (av7110->sinfo) + return; + for (i=7; i<count-10; i++) { + p=buf+i; + if (p[0] || p[1] || p[2]!=0x01 || p[3]!=0xb3) + continue; + p+=4; + hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); + vsize = ((p[1] &0x0F) << 8) | (p[2]); + sw = (p[3]&0x0F); + SetMode(av7110, sw2mode[sw]); + DEB_S(("dvb: playback %dx%d fr=%d\n", hsize, vsize, sw)); + av7110->sinfo=1; + break; + } +} + + +/**************************************************************************** + * I/O buffer management and control + ****************************************************************************/ + +static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, + const char *buf, unsigned long count) +{ + unsigned long todo = count; + int free; + + while (todo > 0) { + if (dvb_ringbuffer_free(rbuf)<2048) { + if (wait_event_interruptible(rbuf->queue, + (dvb_ringbuffer_free(rbuf)>=2048))) + return count-todo; + } + free = dvb_ringbuffer_free(rbuf); + if (free > todo) + free = todo; + (void)dvb_ringbuffer_write(rbuf,buf,free,0); + todo -= free; + buf += free; + } + + return count-todo; +} + +static void play_video_cb(u8 *buf, int count, void *priv) +{ + struct av7110 *av7110=(struct av7110 *) priv; + DEB_EE(("av7110: %p\n",av7110)); + + if ((buf[3]&0xe0)==0xe0) { + get_video_format(av7110, buf, count); + aux_ring_buffer_write(&av7110->avout, buf, count); + } else + aux_ring_buffer_write(&av7110->aout, buf, count); +} + +static void play_audio_cb(u8 *buf, int count, void *priv) +{ + struct av7110 *av7110=(struct av7110 *) priv; + DEB_EE(("av7110: %p\n",av7110)); + + aux_ring_buffer_write(&av7110->aout, buf, count); +} + +#define FREE_COND (dvb_ringbuffer_free(&av7110->avout)>=20*1024 && \ + dvb_ringbuffer_free(&av7110->aout)>=20*1024) + +static ssize_t dvb_play(struct av7110 *av7110, const u8 *buf, + unsigned long count, int nonblock, int type, int umem) +{ + unsigned long todo = count, n; + DEB_EE(("av7110: %p\n",av7110)); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + + if (nonblock && !FREE_COND) + return -EWOULDBLOCK; + + while (todo>0) { + if (!FREE_COND) { + if (nonblock) + return count-todo; + if (wait_event_interruptible(av7110->avout.queue, + FREE_COND)) + return count-todo; + } + n=todo; + if (n>IPACKS*2) + n=IPACKS*2; + if (umem) { + if (copy_from_user(av7110->kbuf[type], buf, n)) + return -EFAULT; + av7110_ipack_instant_repack(av7110->kbuf[type], n, + &av7110->ipack[type]); + } else { + av7110_ipack_instant_repack(buf, n, + &av7110->ipack[type]); + } + todo -= n; + buf += n; + } + return count-todo; +} + +static ssize_t dvb_aplay(struct av7110 *av7110, const u8 *buf, + unsigned long count, int nonblock, int type) +{ + unsigned long todo = count, n; + DEB_EE(("av7110: %p\n",av7110)); + + if (!av7110->kbuf[type]) + return -ENOBUFS; + if (nonblock && dvb_ringbuffer_free(&av7110->aout)<20*1024) + return -EWOULDBLOCK; + + while (todo>0) { + if (dvb_ringbuffer_free(&av7110->aout)<20*1024) { + if (nonblock) + return count-todo; + if (wait_event_interruptible(av7110->aout.queue, + (dvb_ringbuffer_free(&av7110->aout)>= + 20*1024))) + return count-todo; + } + n=todo; + if (n>IPACKS*2) + n=IPACKS*2; + if (copy_from_user(av7110->kbuf[type], buf, n)) + return -EFAULT; + av7110_ipack_instant_repack(av7110->kbuf[type], n, + &av7110->ipack[type]); +// memcpy(dvb->kbuf[type], buf, n); + todo -= n; + buf += n; + } + return count-todo; +} + +void init_p2t(struct av7110_p2t *p, struct dvb_demux_feed *feed) +{ + memset(p->pes,0,TS_SIZE); + p->counter = 0; + p->pos = 0; + p->frags = 0; + if (feed) p->feed = feed; +} + +void clear_p2t(struct av7110_p2t *p) +{ + memset(p->pes,0,TS_SIZE); +// p->counter = 0; + p->pos = 0; + p->frags = 0; +} + + +long int find_pes_header(u8 const *buf, long int length, int *frags) +{ + int c = 0; + int found = 0; + + *frags = 0; + + while (c < length-3 && !found) { + if (buf[c] == 0x00 && buf[c+1] == 0x00 && + buf[c+2] == 0x01) { + switch ( buf[c+3] ) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + found = 1; + break; + + default: + c++; + break; + } + } else c++; + } + if (c == length-3 && !found){ + if (buf[length-1] == 0x00) *frags = 1; + if (buf[length-2] == 0x00 && + buf[length-1] == 0x00) *frags = 2; + if (buf[length-3] == 0x00 && + buf[length-2] == 0x00 && + buf[length-1] == 0x01) *frags = 3; + return -1; + } + + return c; +} + +void pes_to_ts( u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) +{ + int c,c2,l,add; + int check,rest; + + c = 0; + c2 = 0; + if (p->frags){ + check = 0; + switch(p->frags){ + case 1: + if ( buf[c] == 0x00 && buf[c+1] == 0x01 ){ + check = 1; + c += 2; + } + break; + case 2: + if ( buf[c] == 0x01 ){ + check = 1; + c++; + } + break; + case 3: + check = 1; + } + if(check){ + switch ( buf[c] ) { + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + p->pes[0] = 0x00; + p->pes[1] = 0x00; + p->pes[2] = 0x01; + p->pes[3] = buf[c]; + p->pos=4; + memcpy(p->pes+p->pos,buf+c,(TS_SIZE-4)-p->pos); + c += (TS_SIZE-4)-p->pos; + p_to_t(p->pes,(TS_SIZE-4),pid,&p->counter, + p->feed); + clear_p2t(p); + break; + + default: + c=0; + break; + } + } + p->frags = 0; + } + + if (p->pos){ + c2 = find_pes_header(buf+c,length-c,&p->frags); + if (c2 >= 0 && c2 < (TS_SIZE-4)-p->pos){ + l = c2+c; + } else l = (TS_SIZE-4)-p->pos; + memcpy(p->pes+p->pos,buf,l); + c += l; + p->pos += l; + p_to_t(p->pes,p->pos,pid,&p->counter, p->feed); + clear_p2t(p); + } + + add = 0; + while (c < length){ + c2 = find_pes_header(buf+c+add,length-c-add,&p->frags); + if (c2 >= 0) { + c2 += c+add; + if (c2 > c){ + p_to_t(buf+c,c2-c,pid,&p->counter, + p->feed); + c = c2; + clear_p2t(p); + add = 0; + } else add = 1; + } else { + l = length-c; + rest = l % (TS_SIZE-4); + l -= rest; + p_to_t(buf+c,l,pid,&p->counter, + p->feed); + memcpy(p->pes,buf+c+l,rest); + p->pos = rest; + c = length; + } + } +} + + +int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) +{ + int i; + int c = 0; + int fill; + u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10}; + + fill = (TS_SIZE-4)-length; + if (pes_start) tshead[1] = 0x40; + if (fill) tshead[3] = 0x30; + tshead[1] |= (u8)((pid & 0x1F00) >> 8); + tshead[2] |= (u8)(pid & 0x00FF); + tshead[3] |= ((*counter)++ & 0x0F) ; + memcpy(buf,tshead,4); + c+=4; + + + if (fill){ + buf[4] = fill-1; + c++; + if (fill >1){ + buf[5] = 0x00; + c++; + } + for ( i = 6; i < fill+4; i++){ + buf[i] = 0xFF; + c++; + } + } + + return c; +} + + +static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, + struct dvb_demux_feed *feed) +{ + int l, pes_start; + u8 obuf[TS_SIZE]; + long int c = 0; + + pes_start = 0; + if ( length > 3 && + buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01 ) + switch (buf[3]){ + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + pes_start = 1; + break; + + default: + break; + } + + while ( c < length ){ + memset(obuf,0,TS_SIZE); + if (length - c >= (TS_SIZE-4)){ + l = write_ts_header2(pid, counter, pes_start + , obuf, (TS_SIZE-4)); + memcpy(obuf+l, buf+c, TS_SIZE-l); + c += TS_SIZE-l; + } else { + l = write_ts_header2(pid, counter, pes_start + , obuf, length-c); + memcpy(obuf+l, buf+c, TS_SIZE-l); + c = length; + } + feed->cb.ts(obuf, 188, 0, 0, &feed->feed.ts, DMX_OK); + pes_start = 0; + } +} + + +int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) +{ + struct dvb_demux *demux = feed->demux; + struct av7110 *av7110 = (struct av7110 *) demux->priv; + struct ipack *ipack = &av7110->ipack[feed->pes_type]; + + DEB_EE(("av7110: %p\n",av7110)); + + switch (feed->pes_type) { + case 0: + if (av7110->audiostate.stream_source==AUDIO_SOURCE_MEMORY) + return -EINVAL; + break; + case 1: + if (av7110->videostate.stream_source==VIDEO_SOURCE_MEMORY) + return -EINVAL; + break; + default: + return -1; + } + + if (!(buf[3] & 0x10)) { // no payload? + return -1; + } + if (buf[1] & 0x40) + av7110_ipack_flush(ipack); + + if (buf[3] & 0x20) { // adaptation field? + len -= buf[4]+1; + buf += buf[4]+1; + if (!len) + return 0; + } + + av7110_ipack_instant_repack(buf+4, len-4, &av7110->ipack[feed->pes_type]); + return 0; +} + + + +/****************************************************************************** + * Video MPEG decoder events + ******************************************************************************/ +void dvb_video_add_event (struct av7110 *av7110, struct video_event *event) +{ + struct dvb_video_events *events = &av7110->video_events; + int wp; + + DEB_D(("\n")); + + spin_lock_bh(&events->lock); + + wp = (events->eventw + 1) % MAX_VIDEO_EVENT; + + if (wp == events->eventr) { + events->overflow = 1; + events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; + } + + //FIXME: timestamp? + memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); + + events->eventw = wp; + + spin_unlock_bh(&events->lock); + + wake_up_interruptible (&events->wait_queue); +} + + +static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) +{ + struct dvb_video_events *events = &av7110->video_events; + + DEB_D(("\n")); + + if (events->overflow) { + events->overflow = 0; + return -EOVERFLOW; + } + + if (events->eventw == events->eventr) { + int ret; + + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible (events->wait_queue, + events->eventw != events->eventr); + if (ret < 0) + return ret; + } + + spin_lock_bh(&events->lock); + + memcpy (event, &events->events[events->eventr], + sizeof(struct video_event)); + + events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; + + spin_unlock_bh(&events->lock); + + return 0; +} + + +/****************************************************************************** + * DVB device file operations + ******************************************************************************/ + +static unsigned int dvb_video_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; + struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + unsigned int mask = 0; + + DEB_EE(("av7110: %p\n",av7110)); + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + poll_wait(file, &av7110->avout.queue, wait); + } + + poll_wait(file, &av7110->video_events.wait_queue, wait); + + if (av7110->video_events.eventw != av7110->video_events.eventr) + mask = POLLPRI; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (av7110->playing) { + if (FREE_COND) + mask |= (POLLOUT | POLLWRNORM); + } else /* if not playing: may play if asked for */ + mask |= (POLLOUT | POLLWRNORM); + } + + return mask; +} + +static ssize_t dvb_video_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + + DEB_EE(("av7110: %p\n",av7110)); + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + return -EPERM; + } + + if (av7110->videostate.stream_source!=VIDEO_SOURCE_MEMORY) + return -EPERM; + + return dvb_play(av7110, buf, count, file->f_flags&O_NONBLOCK, 1, 1); +} + +static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) +{ + struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; + struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + unsigned int mask = 0; + + DEB_EE(("av7110: %p\n",av7110)); + + poll_wait(file, &av7110->aout.queue, wait); + + if (av7110->playing) { + if (dvb_ringbuffer_free(&av7110->aout)>=20*1024) + mask |= (POLLOUT | POLLWRNORM); + } else /* if not playing: may play if asked for */ + mask = (POLLOUT | POLLWRNORM); + + return mask; +} + +static ssize_t dvb_audio_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + + DEB_EE(("av7110: %p\n",av7110)); + + if (av7110->audiostate.stream_source!=AUDIO_SOURCE_MEMORY) { + printk(KERN_ERR "not audio source memory\n"); + return -EPERM; + } + return dvb_aplay(av7110, buf, count, file->f_flags&O_NONBLOCK, 0); +} + +u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; + +#define MIN_IFRAME 400000 + +static int play_iframe(struct av7110 *av7110, u8 *buf, unsigned int len, int nonblock) +{ + int i, n=1; + + DEB_EE(("av7110: %p\n",av7110)); + + if (!(av7110->playing&RP_VIDEO)) { + if (AV_StartPlay(av7110, RP_VIDEO) < 0) + return -EBUSY; + n=MIN_IFRAME/len+1; + } + + /* setting n always > 1, fixes problems when playing stillframes + consisting of I- and P-Frames */ + n=MIN_IFRAME/len+1; + + /* FIXME: nonblock? */ + dvb_play(av7110, iframe_header, sizeof(iframe_header), 0, 1, 0); + + for (i=0; i<n; i++) + dvb_play(av7110, buf, len, 0, 1, 1); + + av7110_ipack_flush(&av7110->ipack[1]); + return 0; +} + + +static int dvb_video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + unsigned long arg=(unsigned long) parg; + int ret=0; + + DEB_EE(("av7110: %p\n",av7110)); + + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + if ( cmd!=VIDEO_GET_STATUS && cmd!=VIDEO_GET_EVENT && + cmd!=VIDEO_GET_SIZE ) { + return -EPERM; + } + } + + switch (cmd) { + case VIDEO_STOP: + av7110->videostate.play_state=VIDEO_STOPPED; + if (av7110->videostate.stream_source==VIDEO_SOURCE_MEMORY) + AV_Stop(av7110, RP_VIDEO); + else + vidcom(av7110, 0x000e, + av7110->videostate.video_blank ? 0 : 1); + av7110->trickmode=TRICK_NONE; + break; + + case VIDEO_PLAY: + av7110->trickmode=TRICK_NONE; + if (av7110->videostate.play_state==VIDEO_FREEZED) { + av7110->videostate.play_state=VIDEO_PLAYING; + vidcom(av7110, 0x000d, 0); + } + + if (av7110->videostate.stream_source==VIDEO_SOURCE_MEMORY) { + if (av7110->playing==RP_AV) { + outcom(av7110, COMTYPE_REC_PLAY, __Stop, 0); + av7110->playing&=~RP_VIDEO; + } + AV_StartPlay(av7110,RP_VIDEO); + vidcom(av7110, 0x000d, 0); + } else { + //AV_Stop(av7110, RP_VIDEO); + vidcom(av7110, 0x000d, 0); + } + av7110->videostate.play_state=VIDEO_PLAYING; + break; + + case VIDEO_FREEZE: + av7110->videostate.play_state=VIDEO_FREEZED; + if (av7110->playing&RP_VIDEO) + outcom(av7110, COMTYPE_REC_PLAY, __Pause, 0); + else + vidcom(av7110, 0x0102, 1); + av7110->trickmode=TRICK_FREEZE; + break; + + case VIDEO_CONTINUE: + if (av7110->playing&RP_VIDEO) + outcom(av7110, COMTYPE_REC_PLAY, __Continue, 0); + vidcom(av7110, 0x000d, 0); + av7110->videostate.play_state=VIDEO_PLAYING; + av7110->trickmode=TRICK_NONE; + break; + + case VIDEO_SELECT_SOURCE: + av7110->videostate.stream_source=(video_stream_source_t) arg; + break; + + case VIDEO_SET_BLANK: + av7110->videostate.video_blank=(int) arg; + break; + + case VIDEO_GET_STATUS: + memcpy(parg, &av7110->videostate, sizeof(struct video_status)); + break; + + case VIDEO_GET_EVENT: + ret=dvb_video_get_event(av7110, parg, file->f_flags); + break; + + case VIDEO_GET_SIZE: + memcpy(parg, &av7110->video_size, sizeof(video_size_t)); + break; + + case VIDEO_SET_DISPLAY_FORMAT: + { + video_displayformat_t format=(video_displayformat_t) arg; + u16 val=0; + + switch(format) { + case VIDEO_PAN_SCAN: + val=VID_PAN_SCAN_PREF; + break; + + case VIDEO_LETTER_BOX: + val=VID_VC_AND_PS_PREF; + break; + + case VIDEO_CENTER_CUT_OUT: + val=VID_CENTRE_CUT_PREF; + break; + + default: + ret=-EINVAL; + break; + } + if (ret<0) + break; + av7110->videostate.video_format=format; + ret=outcom(av7110, COMTYPE_ENCODER, SetPanScanType, + 1, (u16) val); + break; + } + + case VIDEO_SET_FORMAT: + if (arg>1) { + ret=-EINVAL; + break; + } + av7110->display_ar=arg; + ret=outcom(av7110, COMTYPE_ENCODER, SetMonitorType, + 1, (u16) arg); + break; + + case VIDEO_STILLPICTURE: + { + struct video_still_picture *pic= + (struct video_still_picture *) parg; + av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + ret = play_iframe(av7110, pic->iFrame, pic->size, + file->f_flags&O_NONBLOCK); + break; + } + + case VIDEO_FAST_FORWARD: + //note: arg is ignored by firmware + if (av7110->playing&RP_VIDEO) + outcom(av7110, COMTYPE_REC_PLAY, + __Scan_I, 2, AV_PES, 0); + else + vidcom(av7110, 0x16, arg); + av7110->trickmode=TRICK_FAST; + av7110->videostate.play_state=VIDEO_PLAYING; + break; + + case VIDEO_SLOWMOTION: + if (av7110->playing&RP_VIDEO) { + outcom(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + vidcom(av7110, 0x22, arg); + } else { + vidcom(av7110, 0x0d, 0); + vidcom(av7110, 0x0e, 0); + vidcom(av7110, 0x22, arg); + } + av7110->trickmode=TRICK_SLOW; + av7110->videostate.play_state=VIDEO_PLAYING; + break; + + case VIDEO_GET_CAPABILITIES: + *(int *)parg=VIDEO_CAP_MPEG1| + VIDEO_CAP_MPEG2| + VIDEO_CAP_SYS| + VIDEO_CAP_PROG; + break; + + case VIDEO_CLEAR_BUFFER: + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + av7110_ipack_reset(&av7110->ipack[1]); + + if (av7110->playing==RP_AV) { + outcom(av7110, COMTYPE_REC_PLAY, + __Play, 2, AV_PES, 0); + if (av7110->trickmode==TRICK_FAST) + outcom(av7110, COMTYPE_REC_PLAY, + __Scan_I, 2, AV_PES, 0); + if (av7110->trickmode==TRICK_SLOW) { + outcom(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); + vidcom(av7110, 0x22, arg); + } + if (av7110->trickmode==TRICK_FREEZE) + vidcom(av7110, 0x000e, 1); + } + break; + + case VIDEO_SET_STREAMTYPE: + + break; + + default: + ret=-ENOIOCTLCMD; + break; + } + return ret; +} + +static int dvb_audio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + unsigned long arg=(unsigned long) parg; + int ret=0; + + DEB_EE(("av7110: %p\n",av7110)); + + if (((file->f_flags&O_ACCMODE)==O_RDONLY) && + (cmd!=AUDIO_GET_STATUS)) + return -EPERM; + + switch (cmd) { + case AUDIO_STOP: + if (av7110->audiostate.stream_source==AUDIO_SOURCE_MEMORY) + AV_Stop(av7110, RP_AUDIO); + else + audcom(av7110, 1); + av7110->audiostate.play_state=AUDIO_STOPPED; + break; + + case AUDIO_PLAY: + if (av7110->audiostate.stream_source==AUDIO_SOURCE_MEMORY) + AV_StartPlay(av7110, RP_AUDIO); + audcom(av7110, 2); + av7110->audiostate.play_state=AUDIO_PLAYING; + break; + + case AUDIO_PAUSE: + audcom(av7110, 1); + av7110->audiostate.play_state=AUDIO_PAUSED; + break; + + case AUDIO_CONTINUE: + if (av7110->audiostate.play_state==AUDIO_PAUSED) { + av7110->audiostate.play_state=AUDIO_PLAYING; + audcom(av7110, 0x12); + } + break; + + case AUDIO_SELECT_SOURCE: + av7110->audiostate.stream_source=(audio_stream_source_t) arg; + break; + + case AUDIO_SET_MUTE: + { + audcom(av7110, arg ? 1 : 2); + av7110->audiostate.mute_state=(int) arg; + break; + } + + case AUDIO_SET_AV_SYNC: + av7110->audiostate.AV_sync_state=(int) arg; + audcom(av7110, arg ? 0x0f : 0x0e); + break; + + case AUDIO_SET_BYPASS_MODE: + ret=-EINVAL; + break; + + case AUDIO_CHANNEL_SELECT: + av7110->audiostate.channel_select=(audio_channel_select_t) arg; + + switch(av7110->audiostate.channel_select) { + case AUDIO_STEREO: + audcom(av7110, 0x80); + break; + + case AUDIO_MONO_LEFT: + audcom(av7110, 0x100); + break; + + case AUDIO_MONO_RIGHT: + audcom(av7110, 0x200); + break; + + default: + ret=-EINVAL; + break; + } + break; + + case AUDIO_GET_STATUS: + memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); + break; + + case AUDIO_GET_CAPABILITIES: + *(int *)parg=AUDIO_CAP_LPCM| + AUDIO_CAP_MP1| + AUDIO_CAP_MP2; + break; + + case AUDIO_CLEAR_BUFFER: + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + av7110_ipack_reset(&av7110->ipack[0]); + if (av7110->playing==RP_AV) + outcom(av7110, COMTYPE_REC_PLAY, + __Play, 2, AV_PES, 0); + break; + case AUDIO_SET_ID: + + break; + case AUDIO_SET_MIXER: + { + struct audio_mixer *amix=(struct audio_mixer *)parg; + + SetVolume(av7110, amix->volume_left, amix->volume_right); + break; + } + case AUDIO_SET_STREAMTYPE: + break; + default: + ret=-ENOIOCTLCMD; + break; + } + return ret; +} + + +static int dvb_video_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + int err; + + DEB_EE(("av7110: %p\n",av7110)); + + if ((err=dvb_generic_open(inode, file))<0) + return err; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + av7110->video_blank=1; + av7110->audiostate.AV_sync_state=1; + av7110->videostate.stream_source=VIDEO_SOURCE_DEMUX; + + /* empty event queue */ + av7110->video_events.eventr = av7110->video_events.eventw = 0; + } + + return 0; +} + +static int dvb_video_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + + DEB_EE(("av7110: %p\n",av7110)); + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + AV_Stop(av7110, RP_VIDEO); + } + + return dvb_generic_release(inode, file); +} + +static int dvb_audio_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + int err=dvb_generic_open(inode, file); + + DEB_EE(("av7110: %p\n",av7110)); + + if (err<0) + return err; + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + av7110->audiostate.stream_source=AUDIO_SOURCE_DEMUX; + return 0; +} + +static int dvb_audio_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev=(struct dvb_device *) file->private_data; + struct av7110 *av7110=(struct av7110 *) dvbdev->priv; + + DEB_EE(("av7110: %p\n",av7110)); + + AV_Stop(av7110, RP_AUDIO); + return dvb_generic_release(inode, file); +} + + + +/****************************************************************************** + * driver registration + ******************************************************************************/ + +static struct file_operations dvb_video_fops = { + .owner = THIS_MODULE, + .write = dvb_video_write, + .ioctl = dvb_generic_ioctl, + .open = dvb_video_open, + .release = dvb_video_release, + .poll = dvb_video_poll, +}; + +static struct dvb_device dvbdev_video = { + .priv = 0, + .users = 6, + .readers = 5, /* arbitrary */ + .writers = 1, + .fops = &dvb_video_fops, + .kernel_ioctl = dvb_video_ioctl, +}; + +static struct file_operations dvb_audio_fops = { + .owner = THIS_MODULE, + .write = dvb_audio_write, + .ioctl = dvb_generic_ioctl, + .open = dvb_audio_open, + .release = dvb_audio_release, + .poll = dvb_audio_poll, +}; + +static struct dvb_device dvbdev_audio = { + .priv = 0, + .users = 1, + .writers = 1, + .fops = &dvb_audio_fops, + .kernel_ioctl = dvb_audio_ioctl, +}; + + +int av7110_av_register(struct av7110 *av7110) +{ + av7110->audiostate.AV_sync_state=0; + av7110->audiostate.mute_state=0; + av7110->audiostate.play_state=AUDIO_STOPPED; + av7110->audiostate.stream_source=AUDIO_SOURCE_DEMUX; + av7110->audiostate.channel_select=AUDIO_STEREO; + av7110->audiostate.bypass_mode=0; + + av7110->videostate.video_blank=0; + av7110->videostate.play_state=VIDEO_STOPPED; + av7110->videostate.stream_source=VIDEO_SOURCE_DEMUX; + av7110->videostate.video_format=VIDEO_FORMAT_4_3; + av7110->videostate.display_format=VIDEO_CENTER_CUT_OUT; + av7110->display_ar=VIDEO_FORMAT_4_3; + + init_waitqueue_head(&av7110->video_events.wait_queue); + spin_lock_init(&av7110->video_events.lock); + av7110->video_events.eventw = av7110->video_events.eventr = 0; + av7110->video_events.overflow = 0; + memset(&av7110->video_size, 0, sizeof (video_size_t)); + + dvb_register_device(av7110->dvb_adapter, &av7110->video_dev, + &dvbdev_video, av7110, DVB_DEVICE_VIDEO); + + dvb_register_device(av7110->dvb_adapter, &av7110->audio_dev, + &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); + + return 0; +} + + +void av7110_av_unregister(struct av7110 *av7110) +{ + dvb_unregister_device(av7110->audio_dev); + dvb_unregister_device(av7110->video_dev); +} + + +int av7110_av_init(struct av7110 *av7110) +{ + av7110->vidmode=VIDEO_MODE_PAL; + + av7110_ipack_init(&av7110->ipack[0], IPACKS, play_audio_cb); + av7110->ipack[0].data=(void *) av7110; + av7110_ipack_init(&av7110->ipack[1], IPACKS, play_video_cb); + av7110->ipack[1].data=(void *) av7110; + + dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); + dvb_ringbuffer_init(&av7110->aout, av7110->iobuf+AVOUTLEN, AOUTLEN); + + av7110->kbuf[0]=(u8 *)(av7110->iobuf+AVOUTLEN+AOUTLEN+BMPLEN); + av7110->kbuf[1]=av7110->kbuf[0]+2*IPACKS; + + return 0; +} + +int av7110_av_exit(struct av7110 *av7110) +{ + av7110_ipack_free(&av7110->ipack[0]); + av7110_ipack_free(&av7110->ipack[1]); + return 0; +} |