#include "common.h"
  #include <plat/dma.h>
  #include <plat/gpmc.h>
 -#include <plat/onenand.h>
 -#include <plat/gpmc-smc91x.h>
+ #include <plat/omap-pm.h>
 +#include "gpmc-smc91x.h"
  
 -#include <mach/board-rx51.h>
 +#include "board-rx51.h"
  
  #include <sound/tlv320aic3x.h>
  #include <sound/tpa6130a2-plat.h>
 
--- /dev/null
 -      flush_work_sync(&priv->set_multicast_list_wq);
 -      flush_work_sync(&priv->restart_net_feed_wq);
+ /*
+  * dvb_net.c
+  *
+  * Copyright (C) 2001 Convergence integrated media GmbH
+  *                    Ralph Metzler <ralph@convergence.de>
+  * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+  *
+  * ULE Decapsulation code:
+  * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH.
+  *                      and Department of Scientific Computing
+  *                          Paris Lodron University of Salzburg.
+  *                          Hilmar Linder <hlinder@cosy.sbg.ac.at>
+  *                      and Wolfram Stering <wstering@cosy.sbg.ac.at>
+  *
+  * ULE Decaps according to RFC 4326.
+  *
+  * 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
+  */
+ 
+ /*
+  * ULE ChangeLog:
+  * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt
+  *
+  * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt:
+  *                       ULE Extension header handling.
+  *                     Bugreports by Moritz Vieth and Hanno Tersteegen,
+  *                       Fraunhofer Institute for Open Communication Systems
+  *                       Competence Center for Advanced Satellite Communications.
+  *                     Bugfixes and robustness improvements.
+  *                     Filtering on dest MAC addresses, if present (D-Bit = 0)
+  *                     ULE_DEBUG compile-time option.
+  * Apr 2006: cp v3:    Bugfixes and compliency with RFC 4326 (ULE) by
+  *                       Christian Praehauser <cpraehaus@cosy.sbg.ac.at>,
+  *                       Paris Lodron University of Salzburg.
+  */
+ 
+ /*
+  * FIXME / TODO (dvb_net.c):
+  *
+  * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero.
+  *
+  */
+ 
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/dvb/net.h>
+ #include <linux/uio.h>
+ #include <asm/uaccess.h>
+ #include <linux/crc32.h>
+ #include <linux/mutex.h>
+ #include <linux/sched.h>
+ 
+ #include "dvb_demux.h"
+ #include "dvb_net.h"
+ 
+ static int dvb_net_debug;
+ module_param(dvb_net_debug, int, 0444);
+ MODULE_PARM_DESC(dvb_net_debug, "enable debug messages");
+ 
+ #define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0)
+ 
+ 
+ static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt )
+ {
+       unsigned int j;
+       for (j = 0; j < cnt; j++)
+               c = crc32_be( c, iov[j].iov_base, iov[j].iov_len );
+       return c;
+ }
+ 
+ 
+ #define DVB_NET_MULTICAST_MAX 10
+ 
+ #undef ULE_DEBUG
+ 
+ #ifdef ULE_DEBUG
+ 
+ #define MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"
+ #define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5]
+ 
+ #define isprint(c)    ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
+ 
+ static void hexdump( const unsigned char *buf, unsigned short len )
+ {
+       char str[80], octet[10];
+       int ofs, i, l;
+ 
+       for (ofs = 0; ofs < len; ofs += 16) {
+               sprintf( str, "%03d: ", ofs );
+ 
+               for (i = 0; i < 16; i++) {
+                       if ((i + ofs) < len)
+                               sprintf( octet, "%02x ", buf[ofs + i] );
+                       else
+                               strcpy( octet, "   " );
+ 
+                       strcat( str, octet );
+               }
+               strcat( str, "  " );
+               l = strlen( str );
+ 
+               for (i = 0; (i < 16) && ((i + ofs) < len); i++)
+                       str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
+ 
+               str[l] = '\0';
+               printk( KERN_WARNING "%s\n", str );
+       }
+ }
+ 
+ #endif
+ 
+ struct dvb_net_priv {
+       int in_use;
+       u16 pid;
+       struct net_device *net;
+       struct dvb_net *host;
+       struct dmx_demux *demux;
+       struct dmx_section_feed *secfeed;
+       struct dmx_section_filter *secfilter;
+       struct dmx_ts_feed *tsfeed;
+       int multi_num;
+       struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX];
+       unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
+       int rx_mode;
+ #define RX_MODE_UNI 0
+ #define RX_MODE_MULTI 1
+ #define RX_MODE_ALL_MULTI 2
+ #define RX_MODE_PROMISC 3
+       struct work_struct set_multicast_list_wq;
+       struct work_struct restart_net_feed_wq;
+       unsigned char feedtype;                 /* Either FEED_TYPE_ or FEED_TYPE_ULE */
+       int need_pusi;                          /* Set to 1, if synchronization on PUSI required. */
+       unsigned char tscc;                     /* TS continuity counter after sync on PUSI. */
+       struct sk_buff *ule_skb;                /* ULE SNDU decodes into this buffer. */
+       unsigned char *ule_next_hdr;            /* Pointer into skb to next ULE extension header. */
+       unsigned short ule_sndu_len;            /* ULE SNDU length in bytes, w/o D-Bit. */
+       unsigned short ule_sndu_type;           /* ULE SNDU type field, complete. */
+       unsigned char ule_sndu_type_1;          /* ULE SNDU type field, if split across 2 TS cells. */
+       unsigned char ule_dbit;                 /* Whether the DestMAC address present
+                                                * or not (bit is set). */
+       unsigned char ule_bridged;              /* Whether the ULE_BRIDGED extension header was found. */
+       int ule_sndu_remain;                    /* Nr. of bytes still required for current ULE SNDU. */
+       unsigned long ts_count;                 /* Current ts cell counter. */
+       struct mutex mutex;
+ };
+ 
+ 
+ /**
+  *    Determine the packet's protocol ID. The rule here is that we
+  *    assume 802.3 if the type field is short enough to be a length.
+  *    This is normal practice and works for any 'now in use' protocol.
+  *
+  *  stolen from eth.c out of the linux kernel, hacked for dvb-device
+  *  by Michael Holzt <kju@debian.org>
+  */
+ static __be16 dvb_net_eth_type_trans(struct sk_buff *skb,
+                                     struct net_device *dev)
+ {
+       struct ethhdr *eth;
+       unsigned char *rawp;
+ 
+       skb_reset_mac_header(skb);
+       skb_pull(skb,dev->hard_header_len);
+       eth = eth_hdr(skb);
+ 
+       if (*eth->h_dest & 1) {
+               if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+                       skb->pkt_type=PACKET_BROADCAST;
+               else
+                       skb->pkt_type=PACKET_MULTICAST;
+       }
+ 
+       if (ntohs(eth->h_proto) >= 1536)
+               return eth->h_proto;
+ 
+       rawp = skb->data;
+ 
+       /**
+        *      This is a magic hack to spot IPX packets. Older Novell breaks
+        *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
+        *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+        *      won't work for fault tolerant netware but does for the rest.
+        */
+       if (*(unsigned short *)rawp == 0xFFFF)
+               return htons(ETH_P_802_3);
+ 
+       /**
+        *      Real 802.2 LLC
+        */
+       return htons(ETH_P_802_2);
+ }
+ 
+ #define TS_SZ 188
+ #define TS_SYNC       0x47
+ #define TS_TEI        0x80
+ #define TS_SC 0xC0
+ #define TS_PUSI       0x40
+ #define TS_AF_A       0x20
+ #define TS_AF_D       0x10
+ 
+ /* ULE Extension Header handlers. */
+ 
+ #define ULE_TEST      0
+ #define ULE_BRIDGED   1
+ 
+ #define ULE_OPTEXTHDR_PADDING 0
+ 
+ static int ule_test_sndu( struct dvb_net_priv *p )
+ {
+       return -1;
+ }
+ 
+ static int ule_bridged_sndu( struct dvb_net_priv *p )
+ {
+       struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr;
+       if(ntohs(hdr->h_proto) < 1536) {
+               int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data);
+               /* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */
+               if(framelen != ntohs(hdr->h_proto)) {
+                       return -1;
+               }
+       }
+       /* Note:
+        * From RFC4326:
+        *  "A bridged SNDU is a Mandatory Extension Header of Type 1.
+        *   It must be the final (or only) extension header specified in the header chain of a SNDU."
+        * The 'ule_bridged' flag will cause the extension header processing loop to terminate.
+        */
+       p->ule_bridged = 1;
+       return 0;
+ }
+ 
+ static int ule_exthdr_padding(struct dvb_net_priv *p)
+ {
+       return 0;
+ }
+ 
+ /** Handle ULE extension headers.
+  *  Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding.
+  *  Returns: >= 0: nr. of bytes consumed by next extension header
+  *         -1:   Mandatory extension header that is not recognized or TEST SNDU; discard.
+  */
+ static int handle_one_ule_extension( struct dvb_net_priv *p )
+ {
+       /* Table of mandatory extension header handlers.  The header type is the index. */
+       static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) =
+               { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL,  };
+ 
+       /* Table of optional extension header handlers.  The header type is the index. */
+       static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) =
+               { [0] = ule_exthdr_padding, [1] = NULL, };
+ 
+       int ext_len = 0;
+       unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8;
+       unsigned char htype = p->ule_sndu_type & 0x00FF;
+ 
+       /* Discriminate mandatory and optional extension headers. */
+       if (hlen == 0) {
+               /* Mandatory extension header */
+               if (ule_mandatory_ext_handlers[htype]) {
+                       ext_len = ule_mandatory_ext_handlers[htype]( p );
+                       if(ext_len >= 0) {
+                               p->ule_next_hdr += ext_len;
+                               if (!p->ule_bridged) {
+                                       p->ule_sndu_type = ntohs(*(__be16 *)p->ule_next_hdr);
+                                       p->ule_next_hdr += 2;
+                               } else {
+                                       p->ule_sndu_type = ntohs(*(__be16 *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)));
+                                       /* This assures the extension handling loop will terminate. */
+                               }
+                       }
+                       // else: extension handler failed or SNDU should be discarded
+               } else
+                       ext_len = -1;   /* SNDU has to be discarded. */
+       } else {
+               /* Optional extension header.  Calculate the length. */
+               ext_len = hlen << 1;
+               /* Process the optional extension header according to its type. */
+               if (ule_optional_ext_handlers[htype])
+                       (void)ule_optional_ext_handlers[htype]( p );
+               p->ule_next_hdr += ext_len;
+               p->ule_sndu_type = ntohs( *(__be16 *)(p->ule_next_hdr-2) );
+               /*
+                * note: the length of the next header type is included in the
+                * length of THIS optional extension header
+                */
+       }
+ 
+       return ext_len;
+ }
+ 
+ static int handle_ule_extensions( struct dvb_net_priv *p )
+ {
+       int total_ext_len = 0, l;
+ 
+       p->ule_next_hdr = p->ule_skb->data;
+       do {
+               l = handle_one_ule_extension( p );
+               if (l < 0)
+                       return l;       /* Stop extension header processing and discard SNDU. */
+               total_ext_len += l;
+ #ifdef ULE_DEBUG
+               dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, "
+                       "l=%i, total_ext_len=%i\n", p->ule_next_hdr,
+                       (int) p->ule_sndu_type, l, total_ext_len);
+ #endif
+ 
+       } while (p->ule_sndu_type < 1536);
+ 
+       return total_ext_len;
+ }
+ 
+ 
+ /** Prepare for a new ULE SNDU: reset the decoder state. */
+ static inline void reset_ule( struct dvb_net_priv *p )
+ {
+       p->ule_skb = NULL;
+       p->ule_next_hdr = NULL;
+       p->ule_sndu_len = 0;
+       p->ule_sndu_type = 0;
+       p->ule_sndu_type_1 = 0;
+       p->ule_sndu_remain = 0;
+       p->ule_dbit = 0xFF;
+       p->ule_bridged = 0;
+ }
+ 
+ /**
+  * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of
+  * TS cells of a single PID.
+  */
+ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+       unsigned long skipped = 0L;
+       const u8 *ts, *ts_end, *from_where = NULL;
+       u8 ts_remain = 0, how_much = 0, new_ts = 1;
+       struct ethhdr *ethh = NULL;
+       bool error = false;
+ 
+ #ifdef ULE_DEBUG
+       /* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */
+       static unsigned char ule_hist[100*TS_SZ];
+       static unsigned char *ule_where = ule_hist, ule_dump;
+ #endif
+ 
+       /* For all TS cells in current buffer.
+        * Appearently, we are called for every single TS cell.
+        */
+       for (ts = buf, ts_end = buf + buf_len; ts < ts_end; /* no default incr. */ ) {
+ 
+               if (new_ts) {
+                       /* We are about to process a new TS cell. */
+ 
+ #ifdef ULE_DEBUG
+                       if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist;
+                       memcpy( ule_where, ts, TS_SZ );
+                       if (ule_dump) {
+                               hexdump( ule_where, TS_SZ );
+                               ule_dump = 0;
+                       }
+                       ule_where += TS_SZ;
+ #endif
+ 
+                       /* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */
+                       if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) {
+                               printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
+                                      priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6);
+ 
+                               /* Drop partly decoded SNDU, reset state, resync on PUSI. */
+                               if (priv->ule_skb) {
+                                       dev_kfree_skb( priv->ule_skb );
+                                       /* Prepare for next SNDU. */
+                                       dev->stats.rx_errors++;
+                                       dev->stats.rx_frame_errors++;
+                               }
+                               reset_ule(priv);
+                               priv->need_pusi = 1;
+ 
+                               /* Continue with next TS cell. */
+                               ts += TS_SZ;
+                               priv->ts_count++;
+                               continue;
+                       }
+ 
+                       ts_remain = 184;
+                       from_where = ts + 4;
+               }
+               /* Synchronize on PUSI, if required. */
+               if (priv->need_pusi) {
+                       if (ts[1] & TS_PUSI) {
+                               /* Find beginning of first ULE SNDU in current TS cell. */
+                               /* Synchronize continuity counter. */
+                               priv->tscc = ts[3] & 0x0F;
+                               /* There is a pointer field here. */
+                               if (ts[4] > ts_remain) {
+                                       printk(KERN_ERR "%lu: Invalid ULE packet "
+                                              "(pointer field %d)\n", priv->ts_count, ts[4]);
+                                       ts += TS_SZ;
+                                       priv->ts_count++;
+                                       continue;
+                               }
+                               /* Skip to destination of pointer field. */
+                               from_where = &ts[5] + ts[4];
+                               ts_remain -= 1 + ts[4];
+                               skipped = 0;
+                       } else {
+                               skipped++;
+                               ts += TS_SZ;
+                               priv->ts_count++;
+                               continue;
+                       }
+               }
+ 
+               if (new_ts) {
+                       /* Check continuity counter. */
+                       if ((ts[3] & 0x0F) == priv->tscc)
+                               priv->tscc = (priv->tscc + 1) & 0x0F;
+                       else {
+                               /* TS discontinuity handling: */
+                               printk(KERN_WARNING "%lu: TS discontinuity: got %#x, "
+                                      "expected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc);
+                               /* Drop partly decoded SNDU, reset state, resync on PUSI. */
+                               if (priv->ule_skb) {
+                                       dev_kfree_skb( priv->ule_skb );
+                                       /* Prepare for next SNDU. */
+                                       // reset_ule(priv);  moved to below.
+                                       dev->stats.rx_errors++;
+                                       dev->stats.rx_frame_errors++;
+                               }
+                               reset_ule(priv);
+                               /* skip to next PUSI. */
+                               priv->need_pusi = 1;
+                               continue;
+                       }
+                       /* If we still have an incomplete payload, but PUSI is
+                        * set; some TS cells are missing.
+                        * This is only possible here, if we missed exactly 16 TS
+                        * cells (continuity counter wrap). */
+                       if (ts[1] & TS_PUSI) {
+                               if (! priv->need_pusi) {
+                                       if (!(*from_where < (ts_remain-1)) || *from_where != priv->ule_sndu_remain) {
+                                               /* Pointer field is invalid.  Drop this TS cell and any started ULE SNDU. */
+                                               printk(KERN_WARNING "%lu: Invalid pointer "
+                                                      "field: %u.\n", priv->ts_count, *from_where);
+ 
+                                               /* Drop partly decoded SNDU, reset state, resync on PUSI. */
+                                               if (priv->ule_skb) {
+                                                       error = true;
+                                                       dev_kfree_skb(priv->ule_skb);
+                                               }
+ 
+                                               if (error || priv->ule_sndu_remain) {
+                                                       dev->stats.rx_errors++;
+                                                       dev->stats.rx_frame_errors++;
+                                                       error = false;
+                                               }
+ 
+                                               reset_ule(priv);
+                                               priv->need_pusi = 1;
+                                               continue;
+                                       }
+                                       /* Skip pointer field (we're processing a
+                                        * packed payload). */
+                                       from_where += 1;
+                                       ts_remain -= 1;
+                               } else
+                                       priv->need_pusi = 0;
+ 
+                               if (priv->ule_sndu_remain > 183) {
+                                       /* Current SNDU lacks more data than there could be available in the
+                                        * current TS cell. */
+                                       dev->stats.rx_errors++;
+                                       dev->stats.rx_length_errors++;
+                                       printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but "
+                                              "got PUSI (pf %d, ts_remain %d).  Flushing incomplete payload.\n",
+                                              priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain);
+                                       dev_kfree_skb(priv->ule_skb);
+                                       /* Prepare for next SNDU. */
+                                       reset_ule(priv);
+                                       /* Resync: go to where pointer field points to: start of next ULE SNDU. */
+                                       from_where += ts[4];
+                                       ts_remain -= ts[4];
+                               }
+                       }
+               }
+ 
+               /* Check if new payload needs to be started. */
+               if (priv->ule_skb == NULL) {
+                       /* Start a new payload with skb.
+                        * Find ULE header.  It is only guaranteed that the
+                        * length field (2 bytes) is contained in the current
+                        * TS.
+                        * Check ts_remain has to be >= 2 here. */
+                       if (ts_remain < 2) {
+                               printk(KERN_WARNING "Invalid payload packing: only %d "
+                                      "bytes left in TS.  Resyncing.\n", ts_remain);
+                               priv->ule_sndu_len = 0;
+                               priv->need_pusi = 1;
+                               ts += TS_SZ;
+                               continue;
+                       }
+ 
+                       if (! priv->ule_sndu_len) {
+                               /* Got at least two bytes, thus extrace the SNDU length. */
+                               priv->ule_sndu_len = from_where[0] << 8 | from_where[1];
+                               if (priv->ule_sndu_len & 0x8000) {
+                                       /* D-Bit is set: no dest mac present. */
+                                       priv->ule_sndu_len &= 0x7FFF;
+                                       priv->ule_dbit = 1;
+                               } else
+                                       priv->ule_dbit = 0;
+ 
+                               if (priv->ule_sndu_len < 5) {
+                                       printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
+                                              "Resyncing.\n", priv->ts_count, priv->ule_sndu_len);
+                                       dev->stats.rx_errors++;
+                                       dev->stats.rx_length_errors++;
+                                       priv->ule_sndu_len = 0;
+                                       priv->need_pusi = 1;
+                                       new_ts = 1;
+                                       ts += TS_SZ;
+                                       priv->ts_count++;
+                                       continue;
+                               }
+                               ts_remain -= 2; /* consume the 2 bytes SNDU length. */
+                               from_where += 2;
+                       }
+ 
+                       priv->ule_sndu_remain = priv->ule_sndu_len + 2;
+                       /*
+                        * State of current TS:
+                        *   ts_remain (remaining bytes in the current TS cell)
+                        *   0  ule_type is not available now, we need the next TS cell
+                        *   1  the first byte of the ule_type is present
+                        * >=2  full ULE header present, maybe some payload data as well.
+                        */
+                       switch (ts_remain) {
+                               case 1:
+                                       priv->ule_sndu_remain--;
+                                       priv->ule_sndu_type = from_where[0] << 8;
+                                       priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */
+                                       ts_remain -= 1; from_where += 1;
+                                       /* Continue w/ next TS. */
+                               case 0:
+                                       new_ts = 1;
+                                       ts += TS_SZ;
+                                       priv->ts_count++;
+                                       continue;
+ 
+                               default: /* complete ULE header is present in current TS. */
+                                       /* Extract ULE type field. */
+                                       if (priv->ule_sndu_type_1) {
+                                               priv->ule_sndu_type_1 = 0;
+                                               priv->ule_sndu_type |= from_where[0];
+                                               from_where += 1; /* points to payload start. */
+                                               ts_remain -= 1;
+                                       } else {
+                                               /* Complete type is present in new TS. */
+                                               priv->ule_sndu_type = from_where[0] << 8 | from_where[1];
+                                               from_where += 2; /* points to payload start. */
+                                               ts_remain -= 2;
+                                       }
+                                       break;
+                       }
+ 
+                       /* Allocate the skb (decoder target buffer) with the correct size, as follows:
+                        * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */
+                       priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN );
+                       if (priv->ule_skb == NULL) {
+                               printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+                                      dev->name);
+                               dev->stats.rx_dropped++;
+                               return;
+                       }
+ 
+                       /* This includes the CRC32 _and_ dest mac, if !dbit. */
+                       priv->ule_sndu_remain = priv->ule_sndu_len;
+                       priv->ule_skb->dev = dev;
+                       /* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */
+                       skb_reserve( priv->ule_skb, ETH_HLEN + ETH_ALEN );
+               }
+ 
+               /* Copy data into our current skb. */
+               how_much = min(priv->ule_sndu_remain, (int)ts_remain);
+               memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much);
+               priv->ule_sndu_remain -= how_much;
+               ts_remain -= how_much;
+               from_where += how_much;
+ 
+               /* Check for complete payload. */
+               if (priv->ule_sndu_remain <= 0) {
+                       /* Check CRC32, we've got it in our skb already. */
+                       __be16 ulen = htons(priv->ule_sndu_len);
+                       __be16 utype = htons(priv->ule_sndu_type);
+                       const u8 *tail;
+                       struct kvec iov[3] = {
+                               { &ulen, sizeof ulen },
+                               { &utype, sizeof utype },
+                               { priv->ule_skb->data, priv->ule_skb->len - 4 }
+                       };
+                       u32 ule_crc = ~0L, expected_crc;
+                       if (priv->ule_dbit) {
+                               /* Set D-bit for CRC32 verification,
+                                * if it was set originally. */
+                               ulen |= htons(0x8000);
+                       }
+ 
+                       ule_crc = iov_crc32(ule_crc, iov, 3);
+                       tail = skb_tail_pointer(priv->ule_skb);
+                       expected_crc = *(tail - 4) << 24 |
+                                      *(tail - 3) << 16 |
+                                      *(tail - 2) << 8 |
+                                      *(tail - 1);
+                       if (ule_crc != expected_crc) {
+                               printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
+                                      priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0);
+ 
+ #ifdef ULE_DEBUG
+                               hexdump( iov[0].iov_base, iov[0].iov_len );
+                               hexdump( iov[1].iov_base, iov[1].iov_len );
+                               hexdump( iov[2].iov_base, iov[2].iov_len );
+ 
+                               if (ule_where == ule_hist) {
+                                       hexdump( &ule_hist[98*TS_SZ], TS_SZ );
+                                       hexdump( &ule_hist[99*TS_SZ], TS_SZ );
+                               } else if (ule_where == &ule_hist[TS_SZ]) {
+                                       hexdump( &ule_hist[99*TS_SZ], TS_SZ );
+                                       hexdump( ule_hist, TS_SZ );
+                               } else {
+                                       hexdump( ule_where - TS_SZ - TS_SZ, TS_SZ );
+                                       hexdump( ule_where - TS_SZ, TS_SZ );
+                               }
+                               ule_dump = 1;
+ #endif
+ 
+                               dev->stats.rx_errors++;
+                               dev->stats.rx_crc_errors++;
+                               dev_kfree_skb(priv->ule_skb);
+                       } else {
+                               /* CRC32 verified OK. */
+                               u8 dest_addr[ETH_ALEN];
+                               static const u8 bc_addr[ETH_ALEN] =
+                                       { [ 0 ... ETH_ALEN-1] = 0xff };
+ 
+                               /* CRC32 was OK. Remove it from skb. */
+                               priv->ule_skb->tail -= 4;
+                               priv->ule_skb->len -= 4;
+ 
+                               if (!priv->ule_dbit) {
+                                       /*
+                                        * The destination MAC address is the
+                                        * next data in the skb.  It comes
+                                        * before any extension headers.
+                                        *
+                                        * Check if the payload of this SNDU
+                                        * should be passed up the stack.
+                                        */
+                                       register int drop = 0;
+                                       if (priv->rx_mode != RX_MODE_PROMISC) {
+                                               if (priv->ule_skb->data[0] & 0x01) {
+                                                       /* multicast or broadcast */
+                                                       if (memcmp(priv->ule_skb->data, bc_addr, ETH_ALEN)) {
+                                                               /* multicast */
+                                                               if (priv->rx_mode == RX_MODE_MULTI) {
+                                                                       int i;
+                                                                       for(i = 0; i < priv->multi_num && memcmp(priv->ule_skb->data, priv->multi_macs[i], ETH_ALEN); i++)
+                                                                               ;
+                                                                       if (i == priv->multi_num)
+                                                                               drop = 1;
+                                                               } else if (priv->rx_mode != RX_MODE_ALL_MULTI)
+                                                                       drop = 1; /* no broadcast; */
+                                                               /* else: all multicast mode: accept all multicast packets */
+                                                       }
+                                                       /* else: broadcast */
+                                               }
+                                               else if (memcmp(priv->ule_skb->data, dev->dev_addr, ETH_ALEN))
+                                                       drop = 1;
+                                               /* else: destination address matches the MAC address of our receiver device */
+                                       }
+                                       /* else: promiscuous mode; pass everything up the stack */
+ 
+                                       if (drop) {
+ #ifdef ULE_DEBUG
+                                               dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n",
+                                                       MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr));
+ #endif
+                                               dev_kfree_skb(priv->ule_skb);
+                                               goto sndu_done;
+                                       }
+                                       else
+                                       {
+                                               skb_copy_from_linear_data(priv->ule_skb,
+                                                             dest_addr,
+                                                             ETH_ALEN);
+                                               skb_pull(priv->ule_skb, ETH_ALEN);
+                                       }
+                               }
+ 
+                               /* Handle ULE Extension Headers. */
+                               if (priv->ule_sndu_type < 1536) {
+                                       /* There is an extension header.  Handle it accordingly. */
+                                       int l = handle_ule_extensions(priv);
+                                       if (l < 0) {
+                                               /* Mandatory extension header unknown or TEST SNDU.  Drop it. */
+                                               // printk( KERN_WARNING "Dropping SNDU, extension headers.\n" );
+                                               dev_kfree_skb(priv->ule_skb);
+                                               goto sndu_done;
+                                       }
+                                       skb_pull(priv->ule_skb, l);
+                               }
+ 
+                               /*
+                                * Construct/assure correct ethernet header.
+                                * Note: in bridged mode (priv->ule_bridged !=
+                                * 0) we already have the (original) ethernet
+                                * header at the start of the payload (after
+                                * optional dest. address and any extension
+                                * headers).
+                                */
+ 
+                               if (!priv->ule_bridged) {
+                                       skb_push(priv->ule_skb, ETH_HLEN);
+                                       ethh = (struct ethhdr *)priv->ule_skb->data;
+                                       if (!priv->ule_dbit) {
+                                                /* dest_addr buffer is only valid if priv->ule_dbit == 0 */
+                                               memcpy(ethh->h_dest, dest_addr, ETH_ALEN);
+                                               memset(ethh->h_source, 0, ETH_ALEN);
+                                       }
+                                       else /* zeroize source and dest */
+                                               memset( ethh, 0, ETH_ALEN*2 );
+ 
+                                       ethh->h_proto = htons(priv->ule_sndu_type);
+                               }
+                               /* else:  skb is in correct state; nothing to do. */
+                               priv->ule_bridged = 0;
+ 
+                               /* Stuff into kernel's protocol stack. */
+                               priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev);
+                               /* If D-bit is set (i.e. destination MAC address not present),
+                                * receive the packet anyhow. */
+                               /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST)
+                                       priv->ule_skb->pkt_type = PACKET_HOST; */
+                               dev->stats.rx_packets++;
+                               dev->stats.rx_bytes += priv->ule_skb->len;
+                               netif_rx(priv->ule_skb);
+                       }
+                       sndu_done:
+                       /* Prepare for next SNDU. */
+                       reset_ule(priv);
+               }
+ 
+               /* More data in current TS (look at the bytes following the CRC32)? */
+               if (ts_remain >= 2 && *((unsigned short *)from_where) != 0xFFFF) {
+                       /* Next ULE SNDU starts right there. */
+                       new_ts = 0;
+                       priv->ule_skb = NULL;
+                       priv->ule_sndu_type_1 = 0;
+                       priv->ule_sndu_len = 0;
+                       // printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n",
+                       //      *(from_where + 0), *(from_where + 1),
+                       //      *(from_where + 2), *(from_where + 3));
+                       // printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0);
+                       // hexdump(ts, 188);
+               } else {
+                       new_ts = 1;
+                       ts += TS_SZ;
+                       priv->ts_count++;
+                       if (priv->ule_skb == NULL) {
+                               priv->need_pusi = 1;
+                               priv->ule_sndu_type_1 = 0;
+                               priv->ule_sndu_len = 0;
+                       }
+               }
+       }       /* for all available TS cells */
+ }
+ 
+ static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
+                              const u8 *buffer2, size_t buffer2_len,
+                              struct dmx_ts_feed *feed, enum dmx_success success)
+ {
+       struct net_device *dev = feed->priv;
+ 
+       if (buffer2)
+               printk(KERN_WARNING "buffer2 not NULL: %p.\n", buffer2);
+       if (buffer1_len > 32768)
+               printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len);
+       /* printk("TS callback: %u bytes, %u TS cells @ %p.\n",
+                 buffer1_len, buffer1_len / TS_SZ, buffer1); */
+       dvb_net_ule(dev, buffer1, buffer1_len);
+       return 0;
+ }
+ 
+ 
+ static void dvb_net_sec(struct net_device *dev,
+                       const u8 *pkt, int pkt_len)
+ {
+       u8 *eth;
+       struct sk_buff *skb;
+       struct net_device_stats *stats = &dev->stats;
+       int snap = 0;
+ 
+       /* note: pkt_len includes a 32bit checksum */
+       if (pkt_len < 16) {
+               printk("%s: IP/MPE packet length = %d too small.\n",
+                       dev->name, pkt_len);
+               stats->rx_errors++;
+               stats->rx_length_errors++;
+               return;
+       }
+ /* it seems some ISPs manage to screw up here, so we have to
+  * relax the error checks... */
+ #if 0
+       if ((pkt[5] & 0xfd) != 0xc1) {
+               /* drop scrambled or broken packets */
+ #else
+       if ((pkt[5] & 0x3c) != 0x00) {
+               /* drop scrambled */
+ #endif
+               stats->rx_errors++;
+               stats->rx_crc_errors++;
+               return;
+       }
+       if (pkt[5] & 0x02) {
+               /* handle LLC/SNAP, see rfc-1042 */
+               if (pkt_len < 24 || memcmp(&pkt[12], "\xaa\xaa\x03\0\0\0", 6)) {
+                       stats->rx_dropped++;
+                       return;
+               }
+               snap = 8;
+       }
+       if (pkt[7]) {
+               /* FIXME: assemble datagram from multiple sections */
+               stats->rx_errors++;
+               stats->rx_frame_errors++;
+               return;
+       }
+ 
+       /* we have 14 byte ethernet header (ip header follows);
+        * 12 byte MPE header; 4 byte checksum; + 2 byte alignment, 8 byte LLC/SNAP
+        */
+       if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2 - snap))) {
+               //printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+               stats->rx_dropped++;
+               return;
+       }
+       skb_reserve(skb, 2);    /* longword align L3 header */
+       skb->dev = dev;
+ 
+       /* copy L3 payload */
+       eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14 - snap);
+       memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap);
+ 
+       /* create ethernet header: */
+       eth[0]=pkt[0x0b];
+       eth[1]=pkt[0x0a];
+       eth[2]=pkt[0x09];
+       eth[3]=pkt[0x08];
+       eth[4]=pkt[0x04];
+       eth[5]=pkt[0x03];
+ 
+       eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0;
+ 
+       if (snap) {
+               eth[12] = pkt[18];
+               eth[13] = pkt[19];
+       } else {
+               /* protocol numbers are from rfc-1700 or
+                * http://www.iana.org/assignments/ethernet-numbers
+                */
+               if (pkt[12] >> 4 == 6) { /* version field from IP header */
+                       eth[12] = 0x86; /* IPv6 */
+                       eth[13] = 0xdd;
+               } else {
+                       eth[12] = 0x08; /* IPv4 */
+                       eth[13] = 0x00;
+               }
+       }
+ 
+       skb->protocol = dvb_net_eth_type_trans(skb, dev);
+ 
+       stats->rx_packets++;
+       stats->rx_bytes+=skb->len;
+       netif_rx(skb);
+ }
+ 
+ static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
+                const u8 *buffer2, size_t buffer2_len,
+                struct dmx_section_filter *filter,
+                enum dmx_success success)
+ {
+       struct net_device *dev = filter->priv;
+ 
+       /**
+        * we rely on the DVB API definition where exactly one complete
+        * section is delivered in buffer1
+        */
+       dvb_net_sec (dev, buffer1, buffer1_len);
+       return 0;
+ }
+ 
+ static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+ {
+       dev_kfree_skb(skb);
+       return NETDEV_TX_OK;
+ }
+ 
+ static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00};
+ static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
+ static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ 
+ static int dvb_net_filter_sec_set(struct net_device *dev,
+                  struct dmx_section_filter **secfilter,
+                  u8 *mac, u8 *mac_mask)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+       int ret;
+ 
+       *secfilter=NULL;
+       ret = priv->secfeed->allocate_filter(priv->secfeed, secfilter);
+       if (ret<0) {
+               printk("%s: could not get filter\n", dev->name);
+               return ret;
+       }
+ 
+       (*secfilter)->priv=(void *) dev;
+ 
+       memset((*secfilter)->filter_value, 0x00, DMX_MAX_FILTER_SIZE);
+       memset((*secfilter)->filter_mask,  0x00, DMX_MAX_FILTER_SIZE);
+       memset((*secfilter)->filter_mode,  0xff, DMX_MAX_FILTER_SIZE);
+ 
+       (*secfilter)->filter_value[0]=0x3e;
+       (*secfilter)->filter_value[3]=mac[5];
+       (*secfilter)->filter_value[4]=mac[4];
+       (*secfilter)->filter_value[8]=mac[3];
+       (*secfilter)->filter_value[9]=mac[2];
+       (*secfilter)->filter_value[10]=mac[1];
+       (*secfilter)->filter_value[11]=mac[0];
+ 
+       (*secfilter)->filter_mask[0] = 0xff;
+       (*secfilter)->filter_mask[3] = mac_mask[5];
+       (*secfilter)->filter_mask[4] = mac_mask[4];
+       (*secfilter)->filter_mask[8] = mac_mask[3];
+       (*secfilter)->filter_mask[9] = mac_mask[2];
+       (*secfilter)->filter_mask[10] = mac_mask[1];
+       (*secfilter)->filter_mask[11]=mac_mask[0];
+ 
+       dprintk("%s: filter mac=%pM\n", dev->name, mac);
+       dprintk("%s: filter mask=%pM\n", dev->name, mac_mask);
+ 
+       return 0;
+ }
+ 
+ static int dvb_net_feed_start(struct net_device *dev)
+ {
+       int ret = 0, i;
+       struct dvb_net_priv *priv = netdev_priv(dev);
+       struct dmx_demux *demux = priv->demux;
+       unsigned char *mac = (unsigned char *) dev->dev_addr;
+ 
+       dprintk("%s: rx_mode %i\n", __func__, priv->rx_mode);
+       mutex_lock(&priv->mutex);
+       if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
+               printk("%s: BUG %d\n", __func__, __LINE__);
+ 
+       priv->secfeed=NULL;
+       priv->secfilter=NULL;
+       priv->tsfeed = NULL;
+ 
+       if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+               dprintk("%s: alloc secfeed\n", __func__);
+               ret=demux->allocate_section_feed(demux, &priv->secfeed,
+                                        dvb_net_sec_callback);
+               if (ret<0) {
+                       printk("%s: could not allocate section feed\n", dev->name);
+                       goto error;
+               }
+ 
+               ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 1);
+ 
+               if (ret<0) {
+                       printk("%s: could not set section feed\n", dev->name);
+                       priv->demux->release_section_feed(priv->demux, priv->secfeed);
+                       priv->secfeed=NULL;
+                       goto error;
+               }
+ 
+               if (priv->rx_mode != RX_MODE_PROMISC) {
+                       dprintk("%s: set secfilter\n", __func__);
+                       dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal);
+               }
+ 
+               switch (priv->rx_mode) {
+               case RX_MODE_MULTI:
+                       for (i = 0; i < priv->multi_num; i++) {
+                               dprintk("%s: set multi_secfilter[%d]\n", __func__, i);
+                               dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i],
+                                                      priv->multi_macs[i], mask_normal);
+                       }
+                       break;
+               case RX_MODE_ALL_MULTI:
+                       priv->multi_num=1;
+                       dprintk("%s: set multi_secfilter[0]\n", __func__);
+                       dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0],
+                                              mac_allmulti, mask_allmulti);
+                       break;
+               case RX_MODE_PROMISC:
+                       priv->multi_num=0;
+                       dprintk("%s: set secfilter\n", __func__);
+                       dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
+                       break;
+               }
+ 
+               dprintk("%s: start filtering\n", __func__);
+               priv->secfeed->start_filtering(priv->secfeed);
+       } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+               struct timespec timeout = { 0, 10000000 }; // 10 msec
+ 
+               /* we have payloads encapsulated in TS */
+               dprintk("%s: alloc tsfeed\n", __func__);
+               ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
+               if (ret < 0) {
+                       printk("%s: could not allocate ts feed\n", dev->name);
+                       goto error;
+               }
+ 
+               /* Set netdevice pointer for ts decaps callback. */
+               priv->tsfeed->priv = (void *)dev;
+               ret = priv->tsfeed->set(priv->tsfeed,
+                                       priv->pid, /* pid */
+                                       TS_PACKET, /* type */
+                                       DMX_TS_PES_OTHER, /* pes type */
+                                       32768,     /* circular buffer size */
+                                       timeout    /* timeout */
+                                       );
+ 
+               if (ret < 0) {
+                       printk("%s: could not set ts feed\n", dev->name);
+                       priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+                       priv->tsfeed = NULL;
+                       goto error;
+               }
+ 
+               dprintk("%s: start filtering\n", __func__);
+               priv->tsfeed->start_filtering(priv->tsfeed);
+       } else
+               ret = -EINVAL;
+ 
+ error:
+       mutex_unlock(&priv->mutex);
+       return ret;
+ }
+ 
+ static int dvb_net_feed_stop(struct net_device *dev)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+       int i, ret = 0;
+ 
+       dprintk("%s\n", __func__);
+       mutex_lock(&priv->mutex);
+       if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+               if (priv->secfeed) {
+                       if (priv->secfeed->is_filtering) {
+                               dprintk("%s: stop secfeed\n", __func__);
+                               priv->secfeed->stop_filtering(priv->secfeed);
+                       }
+ 
+                       if (priv->secfilter) {
+                               dprintk("%s: release secfilter\n", __func__);
+                               priv->secfeed->release_filter(priv->secfeed,
+                                                             priv->secfilter);
+                               priv->secfilter=NULL;
+                       }
+ 
+                       for (i=0; i<priv->multi_num; i++) {
+                               if (priv->multi_secfilter[i]) {
+                                       dprintk("%s: release multi_filter[%d]\n",
+                                               __func__, i);
+                                       priv->secfeed->release_filter(priv->secfeed,
+                                                                     priv->multi_secfilter[i]);
+                                       priv->multi_secfilter[i] = NULL;
+                               }
+                       }
+ 
+                       priv->demux->release_section_feed(priv->demux, priv->secfeed);
+                       priv->secfeed = NULL;
+               } else
+                       printk("%s: no feed to stop\n", dev->name);
+       } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+               if (priv->tsfeed) {
+                       if (priv->tsfeed->is_filtering) {
+                               dprintk("%s: stop tsfeed\n", __func__);
+                               priv->tsfeed->stop_filtering(priv->tsfeed);
+                       }
+                       priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+                       priv->tsfeed = NULL;
+               }
+               else
+                       printk("%s: no ts feed to stop\n", dev->name);
+       } else
+               ret = -EINVAL;
+       mutex_unlock(&priv->mutex);
+       return ret;
+ }
+ 
+ 
+ static int dvb_set_mc_filter(struct net_device *dev, unsigned char *addr)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+ 
+       if (priv->multi_num == DVB_NET_MULTICAST_MAX)
+               return -ENOMEM;
+ 
+       memcpy(priv->multi_macs[priv->multi_num], addr, ETH_ALEN);
+ 
+       priv->multi_num++;
+       return 0;
+ }
+ 
+ 
+ static void wq_set_multicast_list (struct work_struct *work)
+ {
+       struct dvb_net_priv *priv =
+               container_of(work, struct dvb_net_priv, set_multicast_list_wq);
+       struct net_device *dev = priv->net;
+ 
+       dvb_net_feed_stop(dev);
+       priv->rx_mode = RX_MODE_UNI;
+       netif_addr_lock_bh(dev);
+ 
+       if (dev->flags & IFF_PROMISC) {
+               dprintk("%s: promiscuous mode\n", dev->name);
+               priv->rx_mode = RX_MODE_PROMISC;
+       } else if ((dev->flags & IFF_ALLMULTI)) {
+               dprintk("%s: allmulti mode\n", dev->name);
+               priv->rx_mode = RX_MODE_ALL_MULTI;
+       } else if (!netdev_mc_empty(dev)) {
+               struct netdev_hw_addr *ha;
+ 
+               dprintk("%s: set_mc_list, %d entries\n",
+                       dev->name, netdev_mc_count(dev));
+ 
+               priv->rx_mode = RX_MODE_MULTI;
+               priv->multi_num = 0;
+ 
+               netdev_for_each_mc_addr(ha, dev)
+                       dvb_set_mc_filter(dev, ha->addr);
+       }
+ 
+       netif_addr_unlock_bh(dev);
+       dvb_net_feed_start(dev);
+ }
+ 
+ 
+ static void dvb_net_set_multicast_list (struct net_device *dev)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+       schedule_work(&priv->set_multicast_list_wq);
+ }
+ 
+ 
+ static void wq_restart_net_feed (struct work_struct *work)
+ {
+       struct dvb_net_priv *priv =
+               container_of(work, struct dvb_net_priv, restart_net_feed_wq);
+       struct net_device *dev = priv->net;
+ 
+       if (netif_running(dev)) {
+               dvb_net_feed_stop(dev);
+               dvb_net_feed_start(dev);
+       }
+ }
+ 
+ 
+ static int dvb_net_set_mac (struct net_device *dev, void *p)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+       struct sockaddr *addr=p;
+ 
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ 
+       if (netif_running(dev))
+               schedule_work(&priv->restart_net_feed_wq);
+ 
+       return 0;
+ }
+ 
+ 
+ static int dvb_net_open(struct net_device *dev)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+ 
+       priv->in_use++;
+       dvb_net_feed_start(dev);
+       return 0;
+ }
+ 
+ 
+ static int dvb_net_stop(struct net_device *dev)
+ {
+       struct dvb_net_priv *priv = netdev_priv(dev);
+ 
+       priv->in_use--;
+       return dvb_net_feed_stop(dev);
+ }
+ 
+ static const struct header_ops dvb_header_ops = {
+       .create         = eth_header,
+       .parse          = eth_header_parse,
+       .rebuild        = eth_rebuild_header,
+ };
+ 
+ 
+ static const struct net_device_ops dvb_netdev_ops = {
+       .ndo_open               = dvb_net_open,
+       .ndo_stop               = dvb_net_stop,
+       .ndo_start_xmit         = dvb_net_tx,
+       .ndo_set_rx_mode        = dvb_net_set_multicast_list,
+       .ndo_set_mac_address    = dvb_net_set_mac,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+ };
+ 
+ static void dvb_net_setup(struct net_device *dev)
+ {
+       ether_setup(dev);
+ 
+       dev->header_ops         = &dvb_header_ops;
+       dev->netdev_ops         = &dvb_netdev_ops;
+       dev->mtu                = 4096;
+ 
+       dev->flags |= IFF_NOARP;
+ }
+ 
+ static int get_if(struct dvb_net *dvbnet)
+ {
+       int i;
+ 
+       for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+               if (!dvbnet->state[i])
+                       break;
+ 
+       if (i == DVB_NET_DEVICES_MAX)
+               return -1;
+ 
+       dvbnet->state[i]=1;
+       return i;
+ }
+ 
+ static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
+ {
+       struct net_device *net;
+       struct dvb_net_priv *priv;
+       int result;
+       int if_num;
+ 
+       if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE)
+               return -EINVAL;
+       if ((if_num = get_if(dvbnet)) < 0)
+               return -EINVAL;
+ 
+       net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", dvb_net_setup);
+       if (!net)
+               return -ENOMEM;
+ 
+       if (dvbnet->dvbdev->id)
+               snprintf(net->name, IFNAMSIZ, "dvb%d%u%d",
+                        dvbnet->dvbdev->adapter->num, dvbnet->dvbdev->id, if_num);
+       else
+               /* compatibility fix to keep dvb0_0 format */
+               snprintf(net->name, IFNAMSIZ, "dvb%d_%d",
+                        dvbnet->dvbdev->adapter->num, if_num);
+ 
+       net->addr_len = 6;
+       memcpy(net->dev_addr, dvbnet->dvbdev->adapter->proposed_mac, 6);
+ 
+       dvbnet->device[if_num] = net;
+ 
+       priv = netdev_priv(net);
+       priv->net = net;
+       priv->demux = dvbnet->demux;
+       priv->pid = pid;
+       priv->rx_mode = RX_MODE_UNI;
+       priv->need_pusi = 1;
+       priv->tscc = 0;
+       priv->feedtype = feedtype;
+       reset_ule(priv);
+ 
+       INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list);
+       INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed);
+       mutex_init(&priv->mutex);
+ 
+       net->base_addr = pid;
+ 
+       if ((result = register_netdev(net)) < 0) {
+               dvbnet->device[if_num] = NULL;
+               free_netdev(net);
+               return result;
+       }
+       printk("dvb_net: created network interface %s\n", net->name);
+ 
+       return if_num;
+ }
+ 
+ static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num)
+ {
+       struct net_device *net = dvbnet->device[num];
+       struct dvb_net_priv *priv;
+ 
+       if (!dvbnet->state[num])
+               return -EINVAL;
+       priv = netdev_priv(net);
+       if (priv->in_use)
+               return -EBUSY;
+ 
+       dvb_net_stop(net);
++      flush_work(&priv->set_multicast_list_wq);
++      flush_work(&priv->restart_net_feed_wq);
+       printk("dvb_net: removed network interface %s\n", net->name);
+       unregister_netdev(net);
+       dvbnet->state[num]=0;
+       dvbnet->device[num] = NULL;
+       free_netdev(net);
+ 
+       return 0;
+ }
+ 
+ static int dvb_net_do_ioctl(struct file *file,
+                 unsigned int cmd, void *parg)
+ {
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_net *dvbnet = dvbdev->priv;
+ 
+       if (((file->f_flags&O_ACCMODE)==O_RDONLY))
+               return -EPERM;
+ 
+       switch (cmd) {
+       case NET_ADD_IF:
+       {
+               struct dvb_net_if *dvbnetif = parg;
+               int result;
+ 
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+ 
+               if (!try_module_get(dvbdev->adapter->module))
+                       return -EPERM;
+ 
+               result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype);
+               if (result<0) {
+                       module_put(dvbdev->adapter->module);
+                       return result;
+               }
+               dvbnetif->if_num=result;
+               break;
+       }
+       case NET_GET_IF:
+       {
+               struct net_device *netdev;
+               struct dvb_net_priv *priv_data;
+               struct dvb_net_if *dvbnetif = parg;
+ 
+               if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+                   !dvbnet->state[dvbnetif->if_num])
+                       return -EINVAL;
+ 
+               netdev = dvbnet->device[dvbnetif->if_num];
+ 
+               priv_data = netdev_priv(netdev);
+               dvbnetif->pid=priv_data->pid;
+               dvbnetif->feedtype=priv_data->feedtype;
+               break;
+       }
+       case NET_REMOVE_IF:
+       {
+               int ret;
+ 
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               if ((unsigned long) parg >= DVB_NET_DEVICES_MAX)
+                       return -EINVAL;
+               ret = dvb_net_remove_if(dvbnet, (unsigned long) parg);
+               if (!ret)
+                       module_put(dvbdev->adapter->module);
+               return ret;
+       }
+ 
+       /* binary compatibility cruft */
+       case __NET_ADD_IF_OLD:
+       {
+               struct __dvb_net_if_old *dvbnetif = parg;
+               int result;
+ 
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+ 
+               if (!try_module_get(dvbdev->adapter->module))
+                       return -EPERM;
+ 
+               result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE);
+               if (result<0) {
+                       module_put(dvbdev->adapter->module);
+                       return result;
+               }
+               dvbnetif->if_num=result;
+               break;
+       }
+       case __NET_GET_IF_OLD:
+       {
+               struct net_device *netdev;
+               struct dvb_net_priv *priv_data;
+               struct __dvb_net_if_old *dvbnetif = parg;
+ 
+               if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+                   !dvbnet->state[dvbnetif->if_num])
+                       return -EINVAL;
+ 
+               netdev = dvbnet->device[dvbnetif->if_num];
+ 
+               priv_data = netdev_priv(netdev);
+               dvbnetif->pid=priv_data->pid;
+               break;
+       }
+       default:
+               return -ENOTTY;
+       }
+       return 0;
+ }
+ 
+ static long dvb_net_ioctl(struct file *file,
+             unsigned int cmd, unsigned long arg)
+ {
+       return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
+ }
+ 
+ static int dvb_net_close(struct inode *inode, struct file *file)
+ {
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_net *dvbnet = dvbdev->priv;
+ 
+       dvb_generic_release(inode, file);
+ 
+       if(dvbdev->users == 1 && dvbnet->exit == 1) {
+               fops_put(file->f_op);
+               file->f_op = NULL;
+               wake_up(&dvbdev->wait_queue);
+       }
+       return 0;
+ }
+ 
+ 
+ static const struct file_operations dvb_net_fops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = dvb_net_ioctl,
+       .open = dvb_generic_open,
+       .release = dvb_net_close,
+       .llseek = noop_llseek,
+ };
+ 
+ static struct dvb_device dvbdev_net = {
+       .priv = NULL,
+       .users = 1,
+       .writers = 1,
+       .fops = &dvb_net_fops,
+ };
+ 
+ 
+ void dvb_net_release (struct dvb_net *dvbnet)
+ {
+       int i;
+ 
+       dvbnet->exit = 1;
+       if (dvbnet->dvbdev->users < 1)
+               wait_event(dvbnet->dvbdev->wait_queue,
+                               dvbnet->dvbdev->users==1);
+ 
+       dvb_unregister_device(dvbnet->dvbdev);
+ 
+       for (i=0; i<DVB_NET_DEVICES_MAX; i++) {
+               if (!dvbnet->state[i])
+                       continue;
+               dvb_net_remove_if(dvbnet, i);
+       }
+ }
+ EXPORT_SYMBOL(dvb_net_release);
+ 
+ 
+ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
+                 struct dmx_demux *dmx)
+ {
+       int i;
+ 
+       dvbnet->demux = dmx;
+ 
+       for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+               dvbnet->state[i] = 0;
+ 
+       return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net,
+                            dvbnet, DVB_DEVICE_NET);
+ }
+ EXPORT_SYMBOL(dvb_net_init);
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+ 
+     bttv - Bt848 frame grabber driver
+ 
+     Copyright (C) 1996,97,98 Ralph  Metzler <rjkm@thp.uni-koeln.de>
+                          & Marcus Metzler <mocm@thp.uni-koeln.de>
+     (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+ 
+     some v4l2 code lines are taken from Justin's bttv2 driver which is
+     (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+ 
+     V4L1 removal from:
+     (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+ 
+     Fixes to be fully V4L2 compliant by
+     (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ 
+     Cropping and overscan support
+     Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+     Sponsored by OPQ Systems AB
+ 
+     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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ 
+ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
+ #include <linux/kdev_t.h>
+ #include "bttvp.h"
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+ #include <media/tvaudio.h>
+ #include <media/msp3400.h>
+ 
+ #include <linux/dma-mapping.h>
+ 
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+ 
+ #include <media/saa6588.h>
+ 
+ #define BTTV_VERSION "0.9.19"
+ 
+ unsigned int bttv_num;                        /* number of Bt848s in use */
+ struct bttv *bttvs[BTTV_MAX];
+ 
+ unsigned int bttv_debug;
+ unsigned int bttv_verbose = 1;
+ unsigned int bttv_gpio;
+ 
+ /* config variables */
+ #ifdef __BIG_ENDIAN
+ static unsigned int bigendian=1;
+ #else
+ static unsigned int bigendian;
+ #endif
+ static unsigned int radio[BTTV_MAX];
+ static unsigned int irq_debug;
+ static unsigned int gbuffers = 8;
+ static unsigned int gbufsize = 0x208000;
+ static unsigned int reset_crop = 1;
+ 
+ static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+ static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+ static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+ static int debug_latency;
+ static int disable_ir;
+ 
+ static unsigned int fdsr;
+ 
+ /* options */
+ static unsigned int combfilter;
+ static unsigned int lumafilter;
+ static unsigned int automute    = 1;
+ static unsigned int chroma_agc;
+ static unsigned int adc_crush   = 1;
+ static unsigned int whitecrush_upper = 0xCF;
+ static unsigned int whitecrush_lower = 0x7F;
+ static unsigned int vcr_hack;
+ static unsigned int irq_iswitch;
+ static unsigned int uv_ratio    = 50;
+ static unsigned int full_luma_range;
+ static unsigned int coring;
+ 
+ /* API features (turn on/off stuff for testing) */
+ static unsigned int v4l2        = 1;
+ 
+ /* insmod args */
+ module_param(bttv_verbose,      int, 0644);
+ module_param(bttv_gpio,         int, 0644);
+ module_param(bttv_debug,        int, 0644);
+ module_param(irq_debug,         int, 0644);
+ module_param(debug_latency,     int, 0644);
+ module_param(disable_ir,        int, 0444);
+ 
+ module_param(fdsr,              int, 0444);
+ module_param(gbuffers,          int, 0444);
+ module_param(gbufsize,          int, 0444);
+ module_param(reset_crop,        int, 0444);
+ 
+ module_param(v4l2,              int, 0644);
+ module_param(bigendian,         int, 0644);
+ module_param(irq_iswitch,       int, 0644);
+ module_param(combfilter,        int, 0444);
+ module_param(lumafilter,        int, 0444);
+ module_param(automute,          int, 0444);
+ module_param(chroma_agc,        int, 0444);
+ module_param(adc_crush,         int, 0444);
+ module_param(whitecrush_upper,  int, 0444);
+ module_param(whitecrush_lower,  int, 0444);
+ module_param(vcr_hack,          int, 0444);
+ module_param(uv_ratio,          int, 0444);
+ module_param(full_luma_range,   int, 0444);
+ module_param(coring,            int, 0444);
+ 
+ module_param_array(radio,       int, NULL, 0444);
+ module_param_array(video_nr,    int, NULL, 0444);
+ module_param_array(radio_nr,    int, NULL, 0444);
+ module_param_array(vbi_nr,      int, NULL, 0444);
+ 
+ MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
+ MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
+ MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+ MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
+ MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+ MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+ MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+ MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
+ MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+ MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+                "is 1 (yes) for compatibility with older applications");
+ MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+ MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+ MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+ MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+ MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+ MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+ MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
+ MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
+ MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
+ MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+ MODULE_PARM_DESC(video_nr, "video device numbers");
+ MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+ MODULE_PARM_DESC(radio_nr, "radio device numbers");
+ 
+ MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
+ MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(BTTV_VERSION);
+ 
+ /* ----------------------------------------------------------------------- */
+ /* sysfs                                                                   */
+ 
+ static ssize_t show_card(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+ {
+       struct video_device *vfd = container_of(cd, struct video_device, dev);
+       struct bttv *btv = video_get_drvdata(vfd);
+       return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+ }
+ static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+ 
+ /* ----------------------------------------------------------------------- */
+ /* dvb auto-load setup                                                     */
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       request_module("dvb-bt8xx");
+ }
+ 
+ static void request_modules(struct bttv *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_modules(struct bttv *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ 
+ 
+ /* ----------------------------------------------------------------------- */
+ /* static data                                                             */
+ 
+ /* special timing tables from conexant... */
+ static u8 SRAM_Table[][60] =
+ {
+       /* PAL digital input over GPIO[7:0] */
+       {
+               45, // 45 bytes following
+               0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
+               0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
+               0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
+               0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
+               0x37,0x00,0xAF,0x21,0x00
+       },
+       /* NTSC digital input over GPIO[7:0] */
+       {
+               51, // 51 bytes following
+               0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
+               0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
+               0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
+               0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
+               0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
+               0x00,
+       },
+       // TGB_NTSC392 // quartzsight
+       // This table has been modified to be used for Fusion Rev D
+       {
+               0x2A, // size of table = 42
+               0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
+               0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
+               0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
+               0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
+               0x20, 0x00
+       }
+ };
+ 
+ /* minhdelayx1        first video pixel we can capture on a line and
+    hdelayx1   start of active video, both relative to rising edge of
+               /HRESET pulse (0H) in 1 / fCLKx1.
+    swidth     width of active video and
+    totalwidth total line width, both in 1 / fCLKx1.
+    sqwidth    total line width in square pixels.
+    vdelay     start of active video in 2 * field lines relative to
+               trailing edge of /VRESET pulse (VDELAY register).
+    sheight    height of active video in 2 * field lines.
+    videostart0        ITU-R frame line number of the line corresponding
+               to vdelay in the first field. */
+ #define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth,    \
+               vdelay, sheight, videostart0)                            \
+       .cropcap.bounds.left = minhdelayx1,                              \
+       /* * 2 because vertically we count field lines times two, */     \
+       /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */           \
+       .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+       /* 4 is a safety margin at the end of the line. */               \
+       .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4,        \
+       .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY,      \
+       .cropcap.defrect.left = hdelayx1,                                \
+       .cropcap.defrect.top = (videostart0) * 2,                        \
+       .cropcap.defrect.width = swidth,                                 \
+       .cropcap.defrect.height = sheight,                               \
+       .cropcap.pixelaspect.numerator = totalwidth,                     \
+       .cropcap.pixelaspect.denominator = sqwidth,
+ 
+ const struct bttv_tvnorm bttv_tvnorms[] = {
+       /* PAL-BDGHI */
+       /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+       /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+       {
+               .v4l2_id        = V4L2_STD_PAL,
+               .name           = "PAL",
+               .Fsc            = 35468950,
+               .swidth         = 924,
+               .sheight        = 576,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0x72,
+               .iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+               .scaledtwidth   = 1135,
+               .hdelayx1       = 186,
+               .hactivex1      = 924,
+               .vdelay         = 0x20,
+               .vbipack        = 255, /* min (2048 / 4, 0x1ff) & 0xff */
+               .sram           = 0,
+               /* ITU-R frame line number of the first VBI line
+                  we can capture, of the first and second field.
+                  The last line is determined by cropcap.bounds. */
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* Should be (768 * 1135 + 944 / 2) / 944.
+                          cropcap.defrect is used for image width
+                          checks, so we keep the old value 924. */
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+               /* bt878 (and bt848?) can capture another
+                  line below active video. */
+               .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+       },{
+               .v4l2_id        = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+               .name           = "NTSC",
+               .Fsc            = 28636363,
+               .swidth         = 768,
+               .sheight        = 480,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+               .scaledtwidth   = 910,
+               .hdelayx1       = 128,
+               .hactivex1      = 910,
+               .vdelay         = 0x1a,
+               .vbipack        = 144, /* min (1600 / 4, 0x1ff) & 0xff */
+               .sram           = 1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 128,
+                       /* Should be (640 * 910 + 780 / 2) / 780? */
+                       /* swidth */ 768,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_SECAM,
+               .name           = "SECAM",
+               .Fsc            = 35468950,
+               .swidth         = 924,
+               .sheight        = 576,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0xb0,
+               .iform          = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+               .scaledtwidth   = 1135,
+               .hdelayx1       = 186,
+               .hactivex1      = 922,
+               .vdelay         = 0x20,
+               .vbipack        = 255,
+               .sram           = 0, /* like PAL, correct? */
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_PAL_Nc,
+               .name           = "PAL-Nc",
+               .Fsc            = 28636363,
+               .swidth         = 640,
+               .sheight        = 576,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+               .scaledtwidth   = 780,
+               .hdelayx1       = 130,
+               .hactivex1      = 734,
+               .vdelay         = 0x1a,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 130,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_PAL_M,
+               .name           = "PAL-M",
+               .Fsc            = 28636363,
+               .swidth         = 640,
+               .sheight        = 480,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+               .scaledtwidth   = 780,
+               .hdelayx1       = 135,
+               .hactivex1      = 754,
+               .vdelay         = 0x1a,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 135,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_PAL_N,
+               .name           = "PAL-N",
+               .Fsc            = 35468950,
+               .swidth         = 768,
+               .sheight        = 576,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0x72,
+               .iform          = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+               .scaledtwidth   = 944,
+               .hdelayx1       = 186,
+               .hactivex1      = 922,
+               .vdelay         = 0x20,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 7, 320 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ (768 * 1135 + 944 / 2) / 944,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x20,
+                       /* sheight */ 576,
+                       /* videostart0 */ 23)
+       },{
+               .v4l2_id        = V4L2_STD_NTSC_M_JP,
+               .name           = "NTSC-JP",
+               .Fsc            = 28636363,
+               .swidth         = 640,
+               .sheight        = 480,
+               .totalwidth     = 910,
+               .adelay         = 0x68,
+               .bdelay         = 0x5d,
+               .iform          = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+               .scaledtwidth   = 780,
+               .hdelayx1       = 135,
+               .hactivex1      = 754,
+               .vdelay         = 0x16,
+               .vbipack        = 144,
+               .sram           = -1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 135,
+                       /* swidth */ (640 * 910 + 780 / 2) / 780,
+                       /* totalwidth */ 910,
+                       /* sqwidth */ 780,
+                       /* vdelay */ 0x16,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       },{
+               /* that one hopefully works with the strange timing
+                * which video recorders produce when playing a NTSC
+                * tape on a PAL TV ... */
+               .v4l2_id        = V4L2_STD_PAL_60,
+               .name           = "PAL-60",
+               .Fsc            = 35468950,
+               .swidth         = 924,
+               .sheight        = 480,
+               .totalwidth     = 1135,
+               .adelay         = 0x7f,
+               .bdelay         = 0x72,
+               .iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+               .scaledtwidth   = 1135,
+               .hdelayx1       = 186,
+               .hactivex1      = 924,
+               .vdelay         = 0x1a,
+               .vbipack        = 255,
+               .vtotal         = 524,
+               .sram           = -1,
+               .vbistart       = { 10, 273 },
+               CROPCAP(/* minhdelayx1 */ 68,
+                       /* hdelayx1 */ 186,
+                       /* swidth */ 924,
+                       /* totalwidth */ 1135,
+                       /* sqwidth */ 944,
+                       /* vdelay */ 0x1a,
+                       /* sheight */ 480,
+                       /* videostart0 */ 23)
+       }
+ };
+ static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
+ 
+ /* ----------------------------------------------------------------------- */
+ /* bttv format list
+    packed pixel formats must come first */
+ static const struct bttv_format formats[] = {
+       {
+               .name     = "8 bpp, gray",
+               .fourcc   = V4L2_PIX_FMT_GREY,
+               .btformat = BT848_COLOR_FMT_Y8,
+               .depth    = 8,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "8 bpp, dithered color",
+               .fourcc   = V4L2_PIX_FMT_HI240,
+               .btformat = BT848_COLOR_FMT_RGB8,
+               .depth    = 8,
+               .flags    = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+       },{
+               .name     = "15 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_RGB555,
+               .btformat = BT848_COLOR_FMT_RGB15,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "15 bpp RGB, be",
+               .fourcc   = V4L2_PIX_FMT_RGB555X,
+               .btformat = BT848_COLOR_FMT_RGB15,
+               .btswap   = 0x03, /* byteswap */
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "16 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_RGB565,
+               .btformat = BT848_COLOR_FMT_RGB16,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "16 bpp RGB, be",
+               .fourcc   = V4L2_PIX_FMT_RGB565X,
+               .btformat = BT848_COLOR_FMT_RGB16,
+               .btswap   = 0x03, /* byteswap */
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "24 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_BGR24,
+               .btformat = BT848_COLOR_FMT_RGB24,
+               .depth    = 24,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "32 bpp RGB, le",
+               .fourcc   = V4L2_PIX_FMT_BGR32,
+               .btformat = BT848_COLOR_FMT_RGB32,
+               .depth    = 32,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "32 bpp RGB, be",
+               .fourcc   = V4L2_PIX_FMT_RGB32,
+               .btformat = BT848_COLOR_FMT_RGB32,
+               .btswap   = 0x0f, /* byte+word swap */
+               .depth    = 32,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "4:2:2, packed, YUYV",
+               .fourcc   = V4L2_PIX_FMT_YUYV,
+               .btformat = BT848_COLOR_FMT_YUY2,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "4:2:2, packed, UYVY",
+               .fourcc   = V4L2_PIX_FMT_UYVY,
+               .btformat = BT848_COLOR_FMT_YUY2,
+               .btswap   = 0x03, /* byteswap */
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PACKED,
+       },{
+               .name     = "4:2:2, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV422P,
+               .btformat = BT848_COLOR_FMT_YCrCb422,
+               .depth    = 16,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 1,
+               .vshift   = 0,
+       },{
+               .name     = "4:2:0, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV420,
+               .btformat = BT848_COLOR_FMT_YCrCb422,
+               .depth    = 12,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 1,
+               .vshift   = 1,
+       },{
+               .name     = "4:2:0, planar, Y-Cr-Cb",
+               .fourcc   = V4L2_PIX_FMT_YVU420,
+               .btformat = BT848_COLOR_FMT_YCrCb422,
+               .depth    = 12,
+               .flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+               .hshift   = 1,
+               .vshift   = 1,
+       },{
+               .name     = "4:1:1, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV411P,
+               .btformat = BT848_COLOR_FMT_YCrCb411,
+               .depth    = 12,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 2,
+               .vshift   = 0,
+       },{
+               .name     = "4:1:0, planar, Y-Cb-Cr",
+               .fourcc   = V4L2_PIX_FMT_YUV410,
+               .btformat = BT848_COLOR_FMT_YCrCb411,
+               .depth    = 9,
+               .flags    = FORMAT_FLAGS_PLANAR,
+               .hshift   = 2,
+               .vshift   = 2,
+       },{
+               .name     = "4:1:0, planar, Y-Cr-Cb",
+               .fourcc   = V4L2_PIX_FMT_YVU410,
+               .btformat = BT848_COLOR_FMT_YCrCb411,
+               .depth    = 9,
+               .flags    = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+               .hshift   = 2,
+               .vshift   = 2,
+       },{
+               .name     = "raw scanlines",
+               .fourcc   = -1,
+               .btformat = BT848_COLOR_FMT_RAW,
+               .depth    = 8,
+               .flags    = FORMAT_FLAGS_RAW,
+       }
+ };
+ static const unsigned int FORMATS = ARRAY_SIZE(formats);
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+ #define V4L2_CID_PRIVATE_CHROMA_AGC  (V4L2_CID_PRIVATE_BASE + 0)
+ #define V4L2_CID_PRIVATE_COMBFILTER  (V4L2_CID_PRIVATE_BASE + 1)
+ #define V4L2_CID_PRIVATE_AUTOMUTE    (V4L2_CID_PRIVATE_BASE + 2)
+ #define V4L2_CID_PRIVATE_LUMAFILTER  (V4L2_CID_PRIVATE_BASE + 3)
+ #define V4L2_CID_PRIVATE_AGC_CRUSH   (V4L2_CID_PRIVATE_BASE + 4)
+ #define V4L2_CID_PRIVATE_VCR_HACK    (V4L2_CID_PRIVATE_BASE + 5)
+ #define V4L2_CID_PRIVATE_WHITECRUSH_UPPER   (V4L2_CID_PRIVATE_BASE + 6)
+ #define V4L2_CID_PRIVATE_WHITECRUSH_LOWER   (V4L2_CID_PRIVATE_BASE + 7)
+ #define V4L2_CID_PRIVATE_UV_RATIO    (V4L2_CID_PRIVATE_BASE + 8)
+ #define V4L2_CID_PRIVATE_FULL_LUMA_RANGE    (V4L2_CID_PRIVATE_BASE + 9)
+ #define V4L2_CID_PRIVATE_CORING      (V4L2_CID_PRIVATE_BASE + 10)
+ #define V4L2_CID_PRIVATE_LASTP1      (V4L2_CID_PRIVATE_BASE + 11)
+ 
+ static const struct v4l2_queryctrl no_ctl = {
+       .name  = "42",
+       .flags = V4L2_CTRL_FLAG_DISABLED,
+ };
+ static const struct v4l2_queryctrl bttv_ctls[] = {
+       /* --- video --- */
+       {
+               .id            = V4L2_CID_BRIGHTNESS,
+               .name          = "Brightness",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 256,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_CONTRAST,
+               .name          = "Contrast",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 128,
+               .default_value = 27648,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_SATURATION,
+               .name          = "Saturation",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 128,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_HUE,
+               .name          = "Hue",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 256,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },
+       /* --- audio --- */
+       {
+               .id            = V4L2_CID_AUDIO_MUTE,
+               .name          = "Mute",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_AUDIO_VOLUME,
+               .name          = "Volume",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 65535,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_BALANCE,
+               .name          = "Balance",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_BASS,
+               .name          = "Bass",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_AUDIO_TREBLE,
+               .name          = "Treble",
+               .minimum       = 0,
+               .maximum       = 65535,
+               .step          = 65535/100,
+               .default_value = 32768,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },
+       /* --- private --- */
+       {
+               .id            = V4L2_CID_PRIVATE_CHROMA_AGC,
+               .name          = "chroma agc",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_COMBFILTER,
+               .name          = "combfilter",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_AUTOMUTE,
+               .name          = "automute",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_LUMAFILTER,
+               .name          = "luma decimation filter",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_AGC_CRUSH,
+               .name          = "agc crush",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_VCR_HACK,
+               .name          = "vcr hack",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+               .name          = "whitecrush upper",
+               .minimum       = 0,
+               .maximum       = 255,
+               .step          = 1,
+               .default_value = 0xCF,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+               .name          = "whitecrush lower",
+               .minimum       = 0,
+               .maximum       = 255,
+               .step          = 1,
+               .default_value = 0x7F,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_PRIVATE_UV_RATIO,
+               .name          = "uv ratio",
+               .minimum       = 0,
+               .maximum       = 100,
+               .step          = 1,
+               .default_value = 50,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       },{
+               .id            = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
+               .name          = "full luma range",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       },{
+               .id            = V4L2_CID_PRIVATE_CORING,
+               .name          = "coring",
+               .minimum       = 0,
+               .maximum       = 3,
+               .step          = 1,
+               .default_value = 0,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
+       }
+ 
+ 
+ 
+ };
+ 
+ static const struct v4l2_queryctrl *ctrl_by_id(int id)
+ {
+       int i;
+ 
+       for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+               if (bttv_ctls[i].id == id)
+                       return bttv_ctls+i;
+ 
+       return NULL;
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ /* resource management                                                     */
+ 
+ /*
+    RESOURCE_    allocated by                freed by
+ 
+    VIDEO_READ   bttv_read 1)                bttv_read 2)
+ 
+    VIDEO_STREAM VIDIOC_STREAMON             VIDIOC_STREAMOFF
+                VIDIOC_QBUF 1)              bttv_release
+                VIDIOCMCAPTURE 1)
+ 
+    OVERLAY     VIDIOCCAPTURE on            VIDIOCCAPTURE off
+                VIDIOC_OVERLAY on           VIDIOC_OVERLAY off
+                3)                          bttv_release
+ 
+    VBI                 VIDIOC_STREAMON             VIDIOC_STREAMOFF
+                VIDIOC_QBUF 1)              bttv_release
+                bttv_read, bttv_poll 1) 4)
+ 
+    1) The resource must be allocated when we enter buffer prepare functions
+       and remain allocated while buffers are in the DMA queue.
+    2) This is a single frame read.
+    3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+       RESOURCE_OVERLAY is allocated.
+    4) This is a continuous read, implies VIDIOC_STREAMON.
+ 
+    Note this driver permits video input and standard changes regardless if
+    resources are allocated.
+ */
+ 
+ #define VBI_RESOURCES (RESOURCE_VBI)
+ #define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+                        RESOURCE_VIDEO_STREAM | \
+                        RESOURCE_OVERLAY)
+ 
+ static
+ int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
+ {
+       int xbits; /* mutual exclusive resources */
+ 
+       if (fh->resources & bit)
+               /* have it already allocated */
+               return 1;
+ 
+       xbits = bit;
+       if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+               xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+ 
+       /* is it free? */
+       if (btv->resources & xbits) {
+               /* no, someone else uses it */
+               goto fail;
+       }
+ 
+       if ((bit & VIDEO_RESOURCES)
+           && 0 == (btv->resources & VIDEO_RESOURCES)) {
+               /* Do crop - use current, don't - use default parameters. */
+               __s32 top = btv->crop[!!fh->do_crop].rect.top;
+ 
+               if (btv->vbi_end > top)
+                       goto fail;
+ 
+               /* We cannot capture the same line as video and VBI data.
+                  Claim scan lines crop[].rect.top to bottom. */
+               btv->crop_start = top;
+       } else if (bit & VBI_RESOURCES) {
+               __s32 end = fh->vbi_fmt.end;
+ 
+               if (end > btv->crop_start)
+                       goto fail;
+ 
+               /* Claim scan lines above fh->vbi_fmt.end. */
+               btv->vbi_end = end;
+       }
+ 
+       /* it's free, grab it */
+       fh->resources  |= bit;
+       btv->resources |= bit;
+       return 1;
+ 
+  fail:
+       return 0;
+ }
+ 
+ static
+ int check_btres(struct bttv_fh *fh, int bit)
+ {
+       return (fh->resources & bit);
+ }
+ 
+ static
+ int locked_btres(struct bttv *btv, int bit)
+ {
+       return (btv->resources & bit);
+ }
+ 
+ /* Call with btv->lock down. */
+ static void
+ disclaim_vbi_lines(struct bttv *btv)
+ {
+       btv->vbi_end = 0;
+ }
+ 
+ /* Call with btv->lock down. */
+ static void
+ disclaim_video_lines(struct bttv *btv)
+ {
+       const struct bttv_tvnorm *tvnorm;
+       u8 crop;
+ 
+       tvnorm = &bttv_tvnorms[btv->tvnorm];
+       btv->crop_start = tvnorm->cropcap.bounds.top
+               + tvnorm->cropcap.bounds.height;
+ 
+       /* VBI capturing ends at VDELAY, start of video capturing, no
+          matter how many lines the VBI RISC program expects. When video
+          capturing is off, it shall no longer "preempt" VBI capturing,
+          so we set VDELAY to maximum. */
+       crop = btread(BT848_E_CROP) | 0xc0;
+       btwrite(crop, BT848_E_CROP);
+       btwrite(0xfe, BT848_E_VDELAY_LO);
+       btwrite(crop, BT848_O_CROP);
+       btwrite(0xfe, BT848_O_VDELAY_LO);
+ }
+ 
+ static
+ void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
+ {
+       if ((fh->resources & bits) != bits) {
+               /* trying to free resources not allocated by us ... */
+               pr_err("BUG! (btres)\n");
+       }
+       fh->resources  &= ~bits;
+       btv->resources &= ~bits;
+ 
+       bits = btv->resources;
+ 
+       if (0 == (bits & VIDEO_RESOURCES))
+               disclaim_video_lines(btv);
+ 
+       if (0 == (bits & VBI_RESOURCES))
+               disclaim_vbi_lines(btv);
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ /* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC          */
+ 
+ /* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
+    PLL_X = Reference pre-divider (0=1, 1=2)
+    PLL_C = Post divider (0=6, 1=4)
+    PLL_I = Integer input
+    PLL_F = Fractional input
+ 
+    F_input = 28.636363 MHz:
+    PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+ */
+ 
+ static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
+ {
+       unsigned char fl, fh, fi;
+ 
+       /* prevent overflows */
+       fin/=4;
+       fout/=4;
+ 
+       fout*=12;
+       fi=fout/fin;
+ 
+       fout=(fout%fin)*256;
+       fh=fout/fin;
+ 
+       fout=(fout%fin)*256;
+       fl=fout/fin;
+ 
+       btwrite(fl, BT848_PLL_F_LO);
+       btwrite(fh, BT848_PLL_F_HI);
+       btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
+ }
+ 
+ static void set_pll(struct bttv *btv)
+ {
+       int i;
+ 
+       if (!btv->pll.pll_crystal)
+               return;
+ 
+       if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+               dprintk("%d: PLL: no change required\n", btv->c.nr);
+               return;
+       }
+ 
+       if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+               /* no PLL needed */
+               if (btv->pll.pll_current == 0)
+                       return;
+               if (bttv_verbose)
+                       pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+                               btv->c.nr, btv->pll.pll_ifreq);
+               btwrite(0x00,BT848_TGCTRL);
+               btwrite(0x00,BT848_PLL_XCI);
+               btv->pll.pll_current = 0;
+               return;
+       }
+ 
+       if (bttv_verbose)
+               pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+                       btv->c.nr,
+                       btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+       set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ 
+       for (i=0; i<10; i++) {
+               /*  Let other people run while the PLL stabilizes */
+               msleep(10);
+ 
+               if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+                       btwrite(0,BT848_DSTATUS);
+               } else {
+                       btwrite(0x08,BT848_TGCTRL);
+                       btv->pll.pll_current = btv->pll.pll_ofreq;
+                       if (bttv_verbose)
+                               pr_info("PLL set ok\n");
+                       return;
+               }
+       }
+       btv->pll.pll_current = -1;
+       if (bttv_verbose)
+               pr_info("Setting PLL failed\n");
+       return;
+ }
+ 
+ /* used to switch between the bt848's analog/digital video capture modes */
+ static void bt848A_set_timing(struct bttv *btv)
+ {
+       int i, len;
+       int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+       int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
+ 
+       if (btv->input == btv->dig) {
+               dprintk("%d: load digital timing table (table_idx=%d)\n",
+                       btv->c.nr,table_idx);
+ 
+               /* timing change...reset timing generator address */
+               btwrite(0x00, BT848_TGCTRL);
+               btwrite(0x02, BT848_TGCTRL);
+               btwrite(0x00, BT848_TGCTRL);
+ 
+               len=SRAM_Table[table_idx][0];
+               for(i = 1; i <= len; i++)
+                       btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+               btv->pll.pll_ofreq = 27000000;
+ 
+               set_pll(btv);
+               btwrite(0x11, BT848_TGCTRL);
+               btwrite(0x41, BT848_DVSIF);
+       } else {
+               btv->pll.pll_ofreq = fsc;
+               set_pll(btv);
+               btwrite(0x0, BT848_DVSIF);
+       }
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+ static void bt848_bright(struct bttv *btv, int bright)
+ {
+       int value;
+ 
+       // printk("set bright: %d\n", bright); // DEBUG
+       btv->bright = bright;
+ 
+       /* We want -128 to 127 we get 0-65535 */
+       value = (bright >> 8) - 128;
+       btwrite(value & 0xff, BT848_BRIGHT);
+ }
+ 
+ static void bt848_hue(struct bttv *btv, int hue)
+ {
+       int value;
+ 
+       btv->hue = hue;
+ 
+       /* -128 to 127 */
+       value = (hue >> 8) - 128;
+       btwrite(value & 0xff, BT848_HUE);
+ }
+ 
+ static void bt848_contrast(struct bttv *btv, int cont)
+ {
+       int value,hibit;
+ 
+       btv->contrast = cont;
+ 
+       /* 0-511 */
+       value = (cont  >> 7);
+       hibit = (value >> 6) & 4;
+       btwrite(value & 0xff, BT848_CONTRAST_LO);
+       btaor(hibit, ~4, BT848_E_CONTROL);
+       btaor(hibit, ~4, BT848_O_CONTROL);
+ }
+ 
+ static void bt848_sat(struct bttv *btv, int color)
+ {
+       int val_u,val_v,hibits;
+ 
+       btv->saturation = color;
+ 
+       /* 0-511 for the color */
+       val_u   = ((color * btv->opt_uv_ratio) / 50) >> 7;
+       val_v   = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
+       hibits  = (val_u >> 7) & 2;
+       hibits |= (val_v >> 8) & 1;
+       btwrite(val_u & 0xff, BT848_SAT_U_LO);
+       btwrite(val_v & 0xff, BT848_SAT_V_LO);
+       btaor(hibits, ~3, BT848_E_CONTROL);
+       btaor(hibits, ~3, BT848_O_CONTROL);
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+ static int
+ video_mux(struct bttv *btv, unsigned int input)
+ {
+       int mux,mask2;
+ 
+       if (input >= bttv_tvcards[btv->c.type].video_inputs)
+               return -EINVAL;
+ 
+       /* needed by RemoteVideo MX */
+       mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+       if (mask2)
+               gpio_inout(mask2,mask2);
+ 
+       if (input == btv->svhs)  {
+               btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+               btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+       } else {
+               btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+               btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+       }
+       mux = bttv_muxsel(btv, input);
+       btaor(mux<<5, ~(3<<5), BT848_IFORM);
+       dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
+ 
+       /* card specific hook */
+       if(bttv_tvcards[btv->c.type].muxsel_hook)
+               bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+       return 0;
+ }
+ 
+ static char *audio_modes[] = {
+       "audio: tuner", "audio: radio", "audio: extern",
+       "audio: intern", "audio: mute"
+ };
+ 
+ static int
+ audio_mux(struct bttv *btv, int input, int mute)
+ {
+       int gpio_val, signal;
+       struct v4l2_control ctrl;
+ 
+       gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+                  bttv_tvcards[btv->c.type].gpiomask);
+       signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+ 
+       btv->mute = mute;
+       btv->audio = input;
+ 
+       /* automute */
+       mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
+ 
+       if (mute)
+               gpio_val = bttv_tvcards[btv->c.type].gpiomute;
+       else
+               gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+ 
+       switch (btv->c.type) {
+       case BTTV_BOARD_VOODOOTV_FM:
+       case BTTV_BOARD_VOODOOTV_200:
+               gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+               break;
+ 
+       default:
+               gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+       }
+ 
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+       if (in_interrupt())
+               return 0;
+ 
+       ctrl.id = V4L2_CID_AUDIO_MUTE;
+       ctrl.value = btv->mute;
+       bttv_call_all(btv, core, s_ctrl, &ctrl);
+       if (btv->sd_msp34xx) {
+               u32 in;
+ 
+               /* Note: the inputs tuner/radio/extern/intern are translated
+                  to msp routings. This assumes common behavior for all msp3400
+                  based TV cards. When this assumption fails, then the
+                  specific MSP routing must be added to the card table.
+                  For now this is sufficient. */
+               switch (input) {
+               case TVAUDIO_INPUT_RADIO:
+                       /* Some boards need the msp do to the radio demod */
+                       if (btv->radio_uses_msp_demodulator) {
+                               in = MSP_INPUT_DEFAULT;
+                               break;
+                       }
+                       in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+                       break;
+               case TVAUDIO_INPUT_EXTERN:
+                       in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+                       break;
+               case TVAUDIO_INPUT_INTERN:
+                       /* Yes, this is the same input as for RADIO. I doubt
+                          if this is ever used. The only board with an INTERN
+                          input is the BTTV_BOARD_AVERMEDIA98. I wonder how
+                          that was tested. My guess is that the whole INTERN
+                          input does not work. */
+                       in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+                                   MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+                       break;
+               case TVAUDIO_INPUT_TUNER:
+               default:
+                       /* This is the only card that uses TUNER2, and afaik,
+                          is the only difference between the VOODOOTV_FM
+                          and VOODOOTV_200 */
+                       if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+                               in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+                                       MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+                       else
+                               in = MSP_INPUT_DEFAULT;
+                       break;
+               }
+               v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+                              in, MSP_OUTPUT_DEFAULT, 0);
+       }
+       if (btv->sd_tvaudio) {
+               v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+                               input, 0, 0);
+       }
+       return 0;
+ }
+ 
+ static inline int
+ audio_mute(struct bttv *btv, int mute)
+ {
+       return audio_mux(btv, btv->audio, mute);
+ }
+ 
+ static inline int
+ audio_input(struct bttv *btv, int input)
+ {
+       return audio_mux(btv, input, btv->mute);
+ }
+ 
+ static void
+ bttv_crop_calc_limits(struct bttv_crop *c)
+ {
+       /* Scale factor min. 1:1, max. 16:1. Min. image size
+          48 x 32. Scaled width must be a multiple of 4. */
+ 
+       if (1) {
+               /* For bug compatibility with VIDIOCGCAP and image
+                  size checks in earlier driver versions. */
+               c->min_scaled_width = 48;
+               c->min_scaled_height = 32;
+       } else {
+               c->min_scaled_width =
+                       (max(48, c->rect.width >> 4) + 3) & ~3;
+               c->min_scaled_height =
+                       max(32, c->rect.height >> 4);
+       }
+ 
+       c->max_scaled_width  = c->rect.width & ~3;
+       c->max_scaled_height = c->rect.height;
+ }
+ 
+ static void
+ bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+ {
+       c->rect = bttv_tvnorms[norm].cropcap.defrect;
+       bttv_crop_calc_limits(c);
+ }
+ 
+ /* Call with btv->lock down. */
+ static int
+ set_tvnorm(struct bttv *btv, unsigned int norm)
+ {
+       const struct bttv_tvnorm *tvnorm;
+       v4l2_std_id id;
+ 
+       BUG_ON(norm >= BTTV_TVNORMS);
+       BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+ 
+       tvnorm = &bttv_tvnorms[norm];
+ 
+       if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+                   sizeof (tvnorm->cropcap))) {
+               bttv_crop_reset(&btv->crop[0], norm);
+               btv->crop[1] = btv->crop[0]; /* current = default */
+ 
+               if (0 == (btv->resources & VIDEO_RESOURCES)) {
+                       btv->crop_start = tvnorm->cropcap.bounds.top
+                               + tvnorm->cropcap.bounds.height;
+               }
+       }
+ 
+       btv->tvnorm = norm;
+ 
+       btwrite(tvnorm->adelay, BT848_ADELAY);
+       btwrite(tvnorm->bdelay, BT848_BDELAY);
+       btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+             BT848_IFORM);
+       btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+       btwrite(1, BT848_VBI_PACK_DEL);
+       bt848A_set_timing(btv);
+ 
+       switch (btv->c.type) {
+       case BTTV_BOARD_VOODOOTV_FM:
+       case BTTV_BOARD_VOODOOTV_200:
+               bttv_tda9880_setnorm(btv, gpio_read());
+               break;
+       }
+       id = tvnorm->v4l2_id;
+       bttv_call_all(btv, core, s_std, id);
+ 
+       return 0;
+ }
+ 
+ /* Call with btv->lock down. */
+ static void
+ set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+ {
+       unsigned long flags;
+ 
+       btv->input = input;
+       if (irq_iswitch) {
+               spin_lock_irqsave(&btv->s_lock,flags);
+               if (btv->curr.frame_irq) {
+                       /* active capture -> delayed input switch */
+                       btv->new_input = input;
+               } else {
+                       video_mux(btv,input);
+               }
+               spin_unlock_irqrestore(&btv->s_lock,flags);
+       } else {
+               video_mux(btv,input);
+       }
+       audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+                        TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+       set_tvnorm(btv, norm);
+ }
+ 
+ static void init_irqreg(struct bttv *btv)
+ {
+       /* clear status */
+       btwrite(0xfffffUL, BT848_INT_STAT);
+ 
+       if (bttv_tvcards[btv->c.type].no_video) {
+               /* i2c only */
+               btwrite(BT848_INT_I2CDONE,
+                       BT848_INT_MASK);
+       } else {
+               /* full video */
+               btwrite((btv->triton1)  |
+                       (btv->gpioirq ? BT848_INT_GPINT : 0) |
+                       BT848_INT_SCERR |
+                       (fdsr ? BT848_INT_FDSR : 0) |
+                       BT848_INT_RISCI | BT848_INT_OCERR |
+                       BT848_INT_FMTCHG|BT848_INT_HLOCK|
+                       BT848_INT_I2CDONE,
+                       BT848_INT_MASK);
+       }
+ }
+ 
+ static void init_bt848(struct bttv *btv)
+ {
+       int val;
+ 
+       if (bttv_tvcards[btv->c.type].no_video) {
+               /* very basic init only */
+               init_irqreg(btv);
+               return;
+       }
+ 
+       btwrite(0x00, BT848_CAP_CTL);
+       btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+       btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+ 
+       /* set planar and packed mode trigger points and         */
+       /* set rising edge of inverted GPINTR pin as irq trigger */
+       btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+               BT848_GPIO_DMA_CTL_PLTP1_16|
+               BT848_GPIO_DMA_CTL_PLTP23_16|
+               BT848_GPIO_DMA_CTL_GPINTC|
+               BT848_GPIO_DMA_CTL_GPINTI,
+               BT848_GPIO_DMA_CTL);
+ 
+       val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+       btwrite(val, BT848_E_SCLOOP);
+       btwrite(val, BT848_O_SCLOOP);
+ 
+       btwrite(0x20, BT848_E_VSCALE_HI);
+       btwrite(0x20, BT848_O_VSCALE_HI);
+       btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+               BT848_ADC);
+ 
+       btwrite(whitecrush_upper, BT848_WC_UP);
+       btwrite(whitecrush_lower, BT848_WC_DOWN);
+ 
+       if (btv->opt_lumafilter) {
+               btwrite(0, BT848_E_CONTROL);
+               btwrite(0, BT848_O_CONTROL);
+       } else {
+               btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+               btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+       }
+ 
+       bt848_bright(btv,   btv->bright);
+       bt848_hue(btv,      btv->hue);
+       bt848_contrast(btv, btv->contrast);
+       bt848_sat(btv,      btv->saturation);
+ 
+       /* interrupt */
+       init_irqreg(btv);
+ }
+ 
+ static void bttv_reinit_bt848(struct bttv *btv)
+ {
+       unsigned long flags;
+ 
+       if (bttv_verbose)
+               pr_info("%d: reset, reinitialize\n", btv->c.nr);
+       spin_lock_irqsave(&btv->s_lock,flags);
+       btv->errors=0;
+       bttv_set_dma(btv,0);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+ 
+       init_bt848(btv);
+       btv->pll.pll_current = -1;
+       set_input(btv, btv->input, btv->tvnorm);
+ }
+ 
+ static int bttv_g_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *c)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               c->value = btv->bright;
+               break;
+       case V4L2_CID_HUE:
+               c->value = btv->hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               c->value = btv->contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               c->value = btv->saturation;
+               break;
+ 
+       case V4L2_CID_AUDIO_MUTE:
+       case V4L2_CID_AUDIO_VOLUME:
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+               bttv_call_all(btv, core, g_ctrl, c);
+               break;
+ 
+       case V4L2_CID_PRIVATE_CHROMA_AGC:
+               c->value = btv->opt_chroma_agc;
+               break;
+       case V4L2_CID_PRIVATE_COMBFILTER:
+               c->value = btv->opt_combfilter;
+               break;
+       case V4L2_CID_PRIVATE_LUMAFILTER:
+               c->value = btv->opt_lumafilter;
+               break;
+       case V4L2_CID_PRIVATE_AUTOMUTE:
+               c->value = btv->opt_automute;
+               break;
+       case V4L2_CID_PRIVATE_AGC_CRUSH:
+               c->value = btv->opt_adc_crush;
+               break;
+       case V4L2_CID_PRIVATE_VCR_HACK:
+               c->value = btv->opt_vcr_hack;
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+               c->value = btv->opt_whitecrush_upper;
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+               c->value = btv->opt_whitecrush_lower;
+               break;
+       case V4L2_CID_PRIVATE_UV_RATIO:
+               c->value = btv->opt_uv_ratio;
+               break;
+       case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+               c->value = btv->opt_full_luma_range;
+               break;
+       case V4L2_CID_PRIVATE_CORING:
+               c->value = btv->opt_coring;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+ }
+ 
+ static int bttv_s_ctrl(struct file *file, void *f,
+                                       struct v4l2_control *c)
+ {
+       int err;
+       int val;
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (0 != err)
+               return err;
+ 
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               bt848_bright(btv, c->value);
+               break;
+       case V4L2_CID_HUE:
+               bt848_hue(btv, c->value);
+               break;
+       case V4L2_CID_CONTRAST:
+               bt848_contrast(btv, c->value);
+               break;
+       case V4L2_CID_SATURATION:
+               bt848_sat(btv, c->value);
+               break;
+       case V4L2_CID_AUDIO_MUTE:
+               audio_mute(btv, c->value);
+               /* fall through */
+       case V4L2_CID_AUDIO_VOLUME:
+               if (btv->volume_gpio)
+                       btv->volume_gpio(btv, c->value);
+ 
+               bttv_call_all(btv, core, s_ctrl, c);
+               break;
+       case V4L2_CID_AUDIO_BALANCE:
+       case V4L2_CID_AUDIO_BASS:
+       case V4L2_CID_AUDIO_TREBLE:
+               bttv_call_all(btv, core, s_ctrl, c);
+               break;
+ 
+       case V4L2_CID_PRIVATE_CHROMA_AGC:
+               btv->opt_chroma_agc = c->value;
+               val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+               btwrite(val, BT848_E_SCLOOP);
+               btwrite(val, BT848_O_SCLOOP);
+               break;
+       case V4L2_CID_PRIVATE_COMBFILTER:
+               btv->opt_combfilter = c->value;
+               break;
+       case V4L2_CID_PRIVATE_LUMAFILTER:
+               btv->opt_lumafilter = c->value;
+               if (btv->opt_lumafilter) {
+                       btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+                       btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               } else {
+                       btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+                       btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               }
+               break;
+       case V4L2_CID_PRIVATE_AUTOMUTE:
+               btv->opt_automute = c->value;
+               break;
+       case V4L2_CID_PRIVATE_AGC_CRUSH:
+               btv->opt_adc_crush = c->value;
+               btwrite(BT848_ADC_RESERVED |
+                               (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+                               BT848_ADC);
+               break;
+       case V4L2_CID_PRIVATE_VCR_HACK:
+               btv->opt_vcr_hack = c->value;
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+               btv->opt_whitecrush_upper = c->value;
+               btwrite(c->value, BT848_WC_UP);
+               break;
+       case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+               btv->opt_whitecrush_lower = c->value;
+               btwrite(c->value, BT848_WC_DOWN);
+               break;
+       case V4L2_CID_PRIVATE_UV_RATIO:
+               btv->opt_uv_ratio = c->value;
+               bt848_sat(btv, btv->saturation);
+               break;
+       case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+               btv->opt_full_luma_range = c->value;
+               btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
+               break;
+       case V4L2_CID_PRIVATE_CORING:
+               btv->opt_coring = c->value;
+               btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+ void bttv_gpio_tracking(struct bttv *btv, char *comment)
+ {
+       unsigned int outbits, data;
+       outbits = btread(BT848_GPIO_OUT_EN);
+       data    = btread(BT848_GPIO_DATA);
+       pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+                btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
+ }
+ 
+ static void bttv_field_count(struct bttv *btv)
+ {
+       int need_count = 0;
+ 
+       if (btv->users)
+               need_count++;
+ 
+       if (need_count) {
+               /* start field counter */
+               btor(BT848_INT_VSYNC,BT848_INT_MASK);
+       } else {
+               /* stop field counter */
+               btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+               btv->field_count = 0;
+       }
+ }
+ 
+ static const struct bttv_format*
+ format_by_fourcc(int fourcc)
+ {
+       unsigned int i;
+ 
+       for (i = 0; i < FORMATS; i++) {
+               if (-1 == formats[i].fourcc)
+                       continue;
+               if (formats[i].fourcc == fourcc)
+                       return formats+i;
+       }
+       return NULL;
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ /* misc helpers                                                            */
+ 
+ static int
+ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+                   struct bttv_buffer *new)
+ {
+       struct bttv_buffer *old;
+       unsigned long flags;
+       int retval = 0;
+ 
+       dprintk("switch_overlay: enter [new=%p]\n", new);
+       if (new)
+               new->vb.state = VIDEOBUF_DONE;
+       spin_lock_irqsave(&btv->s_lock,flags);
+       old = btv->screen;
+       btv->screen = new;
+       btv->loop_irq |= 1;
+       bttv_set_dma(btv, 0x03);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       if (NULL != old) {
+               dprintk("switch_overlay: old=%p state is %d\n",
+                       old, old->vb.state);
+               bttv_dma_free(&fh->cap,btv, old);
+               kfree(old);
+       }
+       if (NULL == new)
+               free_btres_lock(btv,fh,RESOURCE_OVERLAY);
+       dprintk("switch_overlay: done\n");
+       return retval;
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ /* video4linux (1) interface                                               */
+ 
+ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
+                              struct bttv_buffer *buf,
+                              const struct bttv_format *fmt,
+                              unsigned int width, unsigned int height,
+                              enum v4l2_field field)
+ {
+       struct bttv_fh *fh = q->priv_data;
+       int redo_dma_risc = 0;
+       struct bttv_crop c;
+       int norm;
+       int rc;
+ 
+       /* check settings */
+       if (NULL == fmt)
+               return -EINVAL;
+       if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+               width  = RAW_BPL;
+               height = RAW_LINES*2;
+               if (width*height > buf->vb.bsize)
+                       return -EINVAL;
+               buf->vb.size = buf->vb.bsize;
+ 
+               /* Make sure tvnorm and vbi_end remain consistent
+                  until we're done. */
+ 
+               norm = btv->tvnorm;
+ 
+               /* In this mode capturing always starts at defrect.top
+                  (default VDELAY), ignoring cropping parameters. */
+               if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+                       return -EINVAL;
+               }
+ 
+               c.rect = bttv_tvnorms[norm].cropcap.defrect;
+       } else {
+               norm = btv->tvnorm;
+               c = btv->crop[!!fh->do_crop];
+ 
+               if (width < c.min_scaled_width ||
+                   width > c.max_scaled_width ||
+                   height < c.min_scaled_height)
+                       return -EINVAL;
+ 
+               switch (field) {
+               case V4L2_FIELD_TOP:
+               case V4L2_FIELD_BOTTOM:
+               case V4L2_FIELD_ALTERNATE:
+                       /* btv->crop counts frame lines. Max. scale
+                          factor is 16:1 for frames, 8:1 for fields. */
+                       if (height * 2 > c.max_scaled_height)
+                               return -EINVAL;
+                       break;
+ 
+               default:
+                       if (height > c.max_scaled_height)
+                               return -EINVAL;
+                       break;
+               }
+ 
+               buf->vb.size = (width * height * fmt->depth) >> 3;
+               if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+                       return -EINVAL;
+       }
+ 
+       /* alloc + fill struct bttv_buffer (if changed) */
+       if (buf->vb.width != width || buf->vb.height != height ||
+           buf->vb.field != field ||
+           buf->tvnorm != norm || buf->fmt != fmt ||
+           buf->crop.top != c.rect.top ||
+           buf->crop.left != c.rect.left ||
+           buf->crop.width != c.rect.width ||
+           buf->crop.height != c.rect.height) {
+               buf->vb.width  = width;
+               buf->vb.height = height;
+               buf->vb.field  = field;
+               buf->tvnorm    = norm;
+               buf->fmt       = fmt;
+               buf->crop      = c.rect;
+               redo_dma_risc = 1;
+       }
+ 
+       /* alloc risc memory */
+       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+               redo_dma_risc = 1;
+               if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
+                       goto fail;
+       }
+ 
+       if (redo_dma_risc)
+               if (0 != (rc = bttv_buffer_risc(btv,buf)))
+                       goto fail;
+ 
+       buf->vb.state = VIDEOBUF_PREPARED;
+       return 0;
+ 
+  fail:
+       bttv_dma_free(q,btv,buf);
+       return rc;
+ }
+ 
+ static int
+ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+ {
+       struct bttv_fh *fh = q->priv_data;
+ 
+       *size = fh->fmt->depth*fh->width*fh->height >> 3;
+       if (0 == *count)
+               *count = gbuffers;
+       if (*size * *count > gbuffers * gbufsize)
+               *count = (gbuffers * gbufsize) / *size;
+       return 0;
+ }
+ 
+ static int
+ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+              enum v4l2_field field)
+ {
+       struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       struct bttv_fh *fh = q->priv_data;
+ 
+       return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
+                                  fh->width, fh->height, field);
+ }
+ 
+ static void
+ buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+ {
+       struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       struct bttv_fh *fh = q->priv_data;
+       struct bttv    *btv = fh->btv;
+ 
+       buf->vb.state = VIDEOBUF_QUEUED;
+       list_add_tail(&buf->vb.queue,&btv->capture);
+       if (!btv->curr.frame_irq) {
+               btv->loop_irq |= 1;
+               bttv_set_dma(btv, 0x03);
+       }
+ }
+ 
+ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+ {
+       struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+       struct bttv_fh *fh = q->priv_data;
+ 
+       bttv_dma_free(q,fh->btv,buf);
+ }
+ 
+ static struct videobuf_queue_ops bttv_video_qops = {
+       .buf_setup    = buffer_setup,
+       .buf_prepare  = buffer_prepare,
+       .buf_queue    = buffer_queue,
+       .buf_release  = buffer_release,
+ };
+ 
+ static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       unsigned int i;
+       int err;
+ 
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (err)
+               goto err;
+ 
+       for (i = 0; i < BTTV_TVNORMS; i++)
+               if (*id & bttv_tvnorms[i].v4l2_id)
+                       break;
+       if (i == BTTV_TVNORMS) {
+               err = -EINVAL;
+               goto err;
+       }
+ 
+       set_tvnorm(btv, i);
+ 
+ err:
+ 
+       return err;
+ }
+ 
+ static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+               *id = V4L2_STD_625_50;
+       else
+               *id = V4L2_STD_525_60;
+       return 0;
+ }
+ 
+ static int bttv_enum_input(struct file *file, void *priv,
+                                       struct v4l2_input *i)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int rc = 0;
+ 
+       if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+               rc = -EINVAL;
+               goto err;
+       }
+ 
+       i->type     = V4L2_INPUT_TYPE_CAMERA;
+       i->audioset = 1;
+ 
+       if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+               sprintf(i->name, "Television");
+               i->type  = V4L2_INPUT_TYPE_TUNER;
+               i->tuner = 0;
+       } else if (i->index == btv->svhs) {
+               sprintf(i->name, "S-Video");
+       } else {
+               sprintf(i->name, "Composite%d", i->index);
+       }
+ 
+       if (i->index == btv->input) {
+               __u32 dstatus = btread(BT848_DSTATUS);
+               if (0 == (dstatus & BT848_DSTATUS_PRES))
+                       i->status |= V4L2_IN_ST_NO_SIGNAL;
+               if (0 == (dstatus & BT848_DSTATUS_HLOC))
+                       i->status |= V4L2_IN_ST_NO_H_LOCK;
+       }
+ 
+       i->std = BTTV_NORMS;
+ 
+ err:
+ 
+       return rc;
+ }
+ 
+ static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       *i = btv->input;
+ 
+       return 0;
+ }
+ 
+ static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+ 
+       int err;
+ 
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (unlikely(err))
+               goto err;
+ 
+       if (i > bttv_tvcards[btv->c.type].video_inputs) {
+               err = -EINVAL;
+               goto err;
+       }
+ 
+       set_input(btv, i, btv->tvnorm);
+ 
+ err:
+       return 0;
+ }
+ 
+ static int bttv_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
+ 
+       if (unlikely(0 != t->index))
+               return -EINVAL;
+ 
+       if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+               err = -EINVAL;
+               goto err;
+       }
+ 
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (unlikely(err))
+               goto err;
+ 
+       bttv_call_all(btv, tuner, s_tuner, t);
+ 
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 1);
+ 
+ err:
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+ 
+       f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+       f->frequency = btv->freq;
+ 
+       return 0;
+ }
+ 
+ static int bttv_s_frequency(struct file *file, void *priv,
+                                       struct v4l2_frequency *f)
+ {
+       struct bttv_fh *fh  = priv;
+       struct bttv *btv = fh->btv;
+       int err;
+ 
+       if (unlikely(f->tuner != 0))
+               return -EINVAL;
+ 
+       err = v4l2_prio_check(&btv->prio, fh->prio);
+       if (unlikely(err))
+               goto err;
+ 
+       if (unlikely(f->type != (btv->radio_user
+               ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+               err = -EINVAL;
+               goto err;
+       }
+       btv->freq = f->frequency;
+       bttv_call_all(btv, tuner, s_frequency, f);
+       if (btv->has_matchbox && btv->radio_user)
+               tea5757_set_freq(btv, btv->freq);
+ err:
+ 
+       return 0;
+ }
+ 
+ static int bttv_log_status(struct file *file, void *f)
+ {
+       struct bttv_fh *fh  = f;
+       struct bttv *btv = fh->btv;
+ 
+       bttv_call_all(btv, core, log_status);
+       return 0;
+ }
+ 
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+ static int bttv_g_register(struct file *file, void *f,
+                                       struct v4l2_dbg_register *reg)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+ 
+       if (!v4l2_chip_match_host(®->match))
+               return -EINVAL;
+ 
+       /* bt848 has a 12-bit register space */
+       reg->reg &= 0xfff;
+       reg->val = btread(reg->reg);
+       reg->size = 1;
+ 
+       return 0;
+ }
+ 
+ static int bttv_s_register(struct file *file, void *f,
+                                       struct v4l2_dbg_register *reg)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+ 
+       if (!v4l2_chip_match_host(®->match))
+               return -EINVAL;
+ 
+       /* bt848 has a 12-bit register space */
+       reg->reg &= 0xfff;
+       btwrite(reg->val, reg->reg);
+ 
+       return 0;
+ }
+ #endif
+ 
+ /* Given cropping boundaries b and the scaled width and height of a
+    single field or frame, which must not exceed hardware limits, this
+    function adjusts the cropping parameters c. */
+ static void
+ bttv_crop_adjust      (struct bttv_crop *             c,
+                        const struct v4l2_rect *       b,
+                        __s32                          width,
+                        __s32                          height,
+                        enum v4l2_field                field)
+ {
+       __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+       __s32 max_left;
+       __s32 max_top;
+ 
+       if (width < c->min_scaled_width) {
+               /* Max. hor. scale factor 16:1. */
+               c->rect.width = width * 16;
+       } else if (width > c->max_scaled_width) {
+               /* Min. hor. scale factor 1:1. */
+               c->rect.width = width;
+ 
+               max_left = b->left + b->width - width;
+               max_left = min(max_left, (__s32) MAX_HDELAY);
+               if (c->rect.left > max_left)
+                       c->rect.left = max_left;
+       }
+ 
+       if (height < c->min_scaled_height) {
+               /* Max. vert. scale factor 16:1, single fields 8:1. */
+               c->rect.height = height * 16;
+       } else if (frame_height > c->max_scaled_height) {
+               /* Min. vert. scale factor 1:1.
+                  Top and height count field lines times two. */
+               c->rect.height = (frame_height + 1) & ~1;
+ 
+               max_top = b->top + b->height - c->rect.height;
+               if (c->rect.top > max_top)
+                       c->rect.top = max_top;
+       }
+ 
+       bttv_crop_calc_limits(c);
+ }
+ 
+ /* Returns an error if scaling to a frame or single field with the given
+    width and height is not possible with the current cropping parameters
+    and width aligned according to width_mask. If adjust_size is TRUE the
+    function may adjust the width and/or height instead, rounding width
+    to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+    also adjust the current cropping parameters to get closer to the
+    desired image size. */
+ static int
+ limit_scaled_size_lock       (struct bttv_fh *               fh,
+                        __s32 *                        width,
+                        __s32 *                        height,
+                        enum v4l2_field                field,
+                        unsigned int                   width_mask,
+                        unsigned int                   width_bias,
+                        int                            adjust_size,
+                        int                            adjust_crop)
+ {
+       struct bttv *btv = fh->btv;
+       const struct v4l2_rect *b;
+       struct bttv_crop *c;
+       __s32 min_width;
+       __s32 min_height;
+       __s32 max_width;
+       __s32 max_height;
+       int rc;
+ 
+       BUG_ON((int) width_mask >= 0 ||
+              width_bias >= (unsigned int) -width_mask);
+ 
+       /* Make sure tvnorm, vbi_end and the current cropping parameters
+          remain consistent until we're done. */
+ 
+       b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+ 
+       /* Do crop - use current, don't - use default parameters. */
+       c = &btv->crop[!!fh->do_crop];
+ 
+       if (fh->do_crop
+           && adjust_size
+           && adjust_crop
+           && !locked_btres(btv, VIDEO_RESOURCES)) {
+               min_width = 48;
+               min_height = 32;
+ 
+               /* We cannot scale up. When the scaled image is larger
+                  than crop.rect we adjust the crop.rect as required
+                  by the V4L2 spec, hence cropcap.bounds are our limit. */
+               max_width = min(b->width, (__s32) MAX_HACTIVE);
+               max_height = b->height;
+ 
+               /* We cannot capture the same line as video and VBI data.
+                  Note btv->vbi_end is really a minimum, see
+                  bttv_vbi_try_fmt(). */
+               if (btv->vbi_end > b->top) {
+                       max_height -= btv->vbi_end - b->top;
+                       rc = -EBUSY;
+                       if (min_height > max_height)
+                               goto fail;
+               }
+       } else {
+               rc = -EBUSY;
+               if (btv->vbi_end > c->rect.top)
+                       goto fail;
+ 
+               min_width  = c->min_scaled_width;
+               min_height = c->min_scaled_height;
+               max_width  = c->max_scaled_width;
+               max_height = c->max_scaled_height;
+ 
+               adjust_crop = 0;
+       }
+ 
+       min_width = (min_width - width_mask - 1) & width_mask;
+       max_width = max_width & width_mask;
+ 
+       /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+       min_height = min_height;
+       /* Min. scale factor is 1:1. */
+       max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+ 
+       if (adjust_size) {
+               *width = clamp(*width, min_width, max_width);
+               *height = clamp(*height, min_height, max_height);
+ 
+               /* Round after clamping to avoid overflow. */
+               *width = (*width + width_bias) & width_mask;
+ 
+               if (adjust_crop) {
+                       bttv_crop_adjust(c, b, *width, *height, field);
+ 
+                       if (btv->vbi_end > c->rect.top) {
+                               /* Move the crop window out of the way. */
+                               c->rect.top = btv->vbi_end;
+                       }
+               }
+       } else {
+               rc = -EINVAL;
+               if (*width  < min_width ||
+                   *height < min_height ||
+                   *width  > max_width ||
+                   *height > max_height ||
+                   0 != (*width & ~width_mask))
+                       goto fail;
+       }
+ 
+       rc = 0; /* success */
+ 
+  fail:
+ 
+       return rc;
+ }
+ 
+ /* Returns an error if the given overlay window dimensions are not
+    possible with the current cropping parameters. If adjust_size is
+    TRUE the function may adjust the window width and/or height
+    instead, however it always rounds the horizontal position and
+    width as btcx_align() does. If adjust_crop is TRUE the function
+    may also adjust the current cropping parameters to get closer
+    to the desired window size. */
+ static int
+ verify_window_lock            (struct bttv_fh *               fh,
+                        struct v4l2_window *           win,
+                        int                            adjust_size,
+                        int                            adjust_crop)
+ {
+       enum v4l2_field field;
+       unsigned int width_mask;
+       int rc;
+ 
+       if (win->w.width  < 48 || win->w.height < 32)
+               return -EINVAL;
+       if (win->clipcount > 2048)
+               return -EINVAL;
+ 
+       field = win->field;
+ 
+       if (V4L2_FIELD_ANY == field) {
+               __s32 height2;
+ 
+               height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (win->w.height > height2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_TOP;
+       }
+       switch (field) {
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_INTERLACED:
+               break;
+       default:
+               return -EINVAL;
+       }
+ 
+       /* 4-byte alignment. */
+       if (NULL == fh->ovfmt)
+               return -EINVAL;
+       width_mask = ~0;
+       switch (fh->ovfmt->depth) {
+       case 8:
+       case 24:
+               width_mask = ~3;
+               break;
+       case 16:
+               width_mask = ~1;
+               break;
+       case 32:
+               break;
+       default:
+               BUG();
+       }
+ 
+       win->w.width -= win->w.left & ~width_mask;
+       win->w.left = (win->w.left - width_mask - 1) & width_mask;
+ 
+       rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
+                              field, width_mask,
+                              /* width_bias: round down */ 0,
+                              adjust_size, adjust_crop);
+       if (0 != rc)
+               return rc;
+ 
+       win->field = field;
+       return 0;
+ }
+ 
+ static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
+                       struct v4l2_window *win, int fixup)
+ {
+       struct v4l2_clip *clips = NULL;
+       int n,size,retval = 0;
+ 
+       if (NULL == fh->ovfmt)
+               return -EINVAL;
+       if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+       retval = verify_window_lock(fh, win,
+                              /* adjust_size */ fixup,
+                              /* adjust_crop */ fixup);
+       if (0 != retval)
+               return retval;
+ 
+       /* copy clips  --  luckily v4l1 + v4l2 are binary
+          compatible here ...*/
+       n = win->clipcount;
+       size = sizeof(*clips)*(n+4);
+       clips = kmalloc(size,GFP_KERNEL);
+       if (NULL == clips)
+               return -ENOMEM;
+       if (n > 0) {
+               if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+                       kfree(clips);
+                       return -EFAULT;
+               }
+       }
+ 
+       /* clip against screen */
+       if (NULL != btv->fbuf.base)
+               n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+                                     &win->w, clips, n);
+       btcx_sort_clips(clips,n);
+ 
+       /* 4-byte alignments */
+       switch (fh->ovfmt->depth) {
+       case 8:
+       case 24:
+               btcx_align(&win->w, clips, n, 3);
+               break;
+       case 16:
+               btcx_align(&win->w, clips, n, 1);
+               break;
+       case 32:
+               /* no alignment fixups needed */
+               break;
+       default:
+               BUG();
+       }
+ 
+       kfree(fh->ov.clips);
+       fh->ov.clips    = clips;
+       fh->ov.nclips   = n;
+ 
+       fh->ov.w        = win->w;
+       fh->ov.field    = win->field;
+       fh->ov.setup_ok = 1;
+ 
+       btv->init.ov.w.width   = win->w.width;
+       btv->init.ov.w.height  = win->w.height;
+       btv->init.ov.field     = win->field;
+ 
+       /* update overlay if needed */
+       retval = 0;
+       if (check_btres(fh, RESOURCE_OVERLAY)) {
+               struct bttv_buffer *new;
+ 
+               new = videobuf_sg_alloc(sizeof(*new));
+               new->crop = btv->crop[!!fh->do_crop].rect;
+               bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+               retval = bttv_switch_overlay(btv,fh,new);
+       }
+       return retval;
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ 
+ static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
+ {
+       struct videobuf_queue* q = NULL;
+ 
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               q = &fh->cap;
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               q = &fh->vbi;
+               break;
+       default:
+               BUG();
+       }
+       return q;
+ }
+ 
+ static int bttv_resource(struct bttv_fh *fh)
+ {
+       int res = 0;
+ 
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               res = RESOURCE_VIDEO_STREAM;
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               res = RESOURCE_VBI;
+               break;
+       default:
+               BUG();
+       }
+       return res;
+ }
+ 
+ static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+ {
+       struct videobuf_queue *q = bttv_queue(fh);
+       int res = bttv_resource(fh);
+ 
+       if (check_btres(fh,res))
+               return -EBUSY;
+       if (videobuf_queue_is_busy(q))
+               return -EBUSY;
+       fh->type = type;
+       return 0;
+ }
+ 
+ static void
+ pix_format_set_size     (struct v4l2_pix_format *       f,
+                        const struct bttv_format *     fmt,
+                        unsigned int                   width,
+                        unsigned int                   height)
+ {
+       f->width = width;
+       f->height = height;
+ 
+       if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+               f->bytesperline = width; /* Y plane */
+               f->sizeimage = (width * height * fmt->depth) >> 3;
+       } else {
+               f->bytesperline = (width * fmt->depth) >> 3;
+               f->sizeimage = height * f->bytesperline;
+       }
+ }
+ 
+ static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+ {
+       struct bttv_fh *fh  = priv;
+ 
+       pix_format_set_size(&f->fmt.pix, fh->fmt,
+                               fh->width, fh->height);
+       f->fmt.pix.field        = fh->cap.field;
+       f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
+                                       struct v4l2_format *f)
+ {
+       struct bttv_fh *fh  = priv;
+ 
+       f->fmt.win.w     = fh->ov.w;
+       f->fmt.win.field = fh->ov.field;
+ 
+       return 0;
+ }
+ 
+ static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+ {
+       const struct bttv_format *fmt;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       enum v4l2_field field;
+       __s32 width, height;
+       int rc;
+ 
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+ 
+       field = f->fmt.pix.field;
+ 
+       if (V4L2_FIELD_ANY == field) {
+               __s32 height2;
+ 
+               height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+               field = (f->fmt.pix.height > height2)
+                       ? V4L2_FIELD_INTERLACED
+                       : V4L2_FIELD_BOTTOM;
+       }
+ 
+       if (V4L2_FIELD_SEQ_BT == field)
+               field = V4L2_FIELD_SEQ_TB;
+ 
+       switch (field) {
+       case V4L2_FIELD_TOP:
+       case V4L2_FIELD_BOTTOM:
+       case V4L2_FIELD_ALTERNATE:
+       case V4L2_FIELD_INTERLACED:
+               break;
+       case V4L2_FIELD_SEQ_TB:
+               if (fmt->flags & FORMAT_FLAGS_PLANAR)
+                       return -EINVAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+ 
+       width = f->fmt.pix.width;
+       height = f->fmt.pix.height;
+ 
+       rc = limit_scaled_size_lock(fh, &width, &height, field,
+                              /* width_mask: 4 pixels */ ~3,
+                              /* width_bias: nearest */ 2,
+                              /* adjust_size */ 1,
+                              /* adjust_crop */ 0);
+       if (0 != rc)
+               return rc;
+ 
+       /* update data for the application */
+       f->fmt.pix.field = field;
+       pix_format_set_size(&f->fmt.pix, fmt, width, height);
+ 
+       return 0;
+ }
+ 
+ static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+ {
+       struct bttv_fh *fh = priv;
+ 
+       return verify_window_lock(fh, &f->fmt.win,
+                       /* adjust_size */ 1,
+                       /* adjust_crop */ 0);
+ }
+ 
+ static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       int retval;
+       const struct bttv_format *fmt;
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       __s32 width, height;
+       enum v4l2_field field;
+ 
+       retval = bttv_switch_type(fh, f->type);
+       if (0 != retval)
+               return retval;
+ 
+       retval = bttv_try_fmt_vid_cap(file, priv, f);
+       if (0 != retval)
+               return retval;
+ 
+       width = f->fmt.pix.width;
+       height = f->fmt.pix.height;
+       field = f->fmt.pix.field;
+ 
+       retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
+                              /* width_mask: 4 pixels */ ~3,
+                              /* width_bias: nearest */ 2,
+                              /* adjust_size */ 1,
+                              /* adjust_crop */ 1);
+       if (0 != retval)
+               return retval;
+ 
+       f->fmt.pix.field = field;
+ 
+       fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ 
+       /* update our state informations */
+       fh->fmt              = fmt;
+       fh->cap.field        = f->fmt.pix.field;
+       fh->cap.last         = V4L2_FIELD_NONE;
+       fh->width            = f->fmt.pix.width;
+       fh->height           = f->fmt.pix.height;
+       btv->init.fmt        = fmt;
+       btv->init.width      = f->fmt.pix.width;
+       btv->init.height     = f->fmt.pix.height;
+ 
+       return 0;
+ }
+ 
+ static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       if (no_overlay > 0) {
+               pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+               return -EINVAL;
+       }
+ 
+       return setup_window_lock(fh, btv, &f->fmt.win, 1);
+ }
+ 
+ static int bttv_querycap(struct file *file, void  *priv,
+                               struct v4l2_capability *cap)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       if (0 == v4l2)
+               return -EINVAL;
+ 
+       strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+       strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+       snprintf(cap->bus_info, sizeof(cap->bus_info),
+                "PCI:%s", pci_name(btv->c.pci));
+       cap->capabilities =
+               V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_VBI_CAPTURE |
+               V4L2_CAP_READWRITE |
+               V4L2_CAP_STREAMING;
+       if (no_overlay <= 0)
+               cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+ 
+       /*
+        * No need to lock here: those vars are initialized during board
+        * probe and remains untouched during the rest of the driver lifecycle
+        */
+       if (btv->has_saa6588)
+               cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+       if (btv->tuner_type != TUNER_ABSENT)
+               cap->capabilities |= V4L2_CAP_TUNER;
+       return 0;
+ }
+ 
+ static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+ {
+       int index = -1, i;
+ 
+       for (i = 0; i < FORMATS; i++) {
+               if (formats[i].fourcc != -1)
+                       index++;
+               if ((unsigned int)index == f->index)
+                       break;
+       }
+       if (FORMATS == i)
+               return -EINVAL;
+ 
+       f->pixelformat = formats[i].fourcc;
+       strlcpy(f->description, formats[i].name, sizeof(f->description));
+ 
+       return i;
+ }
+ 
+ static int bttv_enum_fmt_vid_cap(struct file *file, void  *priv,
+                               struct v4l2_fmtdesc *f)
+ {
+       int rc = bttv_enum_fmt_cap_ovr(f);
+ 
+       if (rc < 0)
+               return rc;
+ 
+       return 0;
+ }
+ 
+ static int bttv_enum_fmt_vid_overlay(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+ {
+       int rc;
+ 
+       if (no_overlay > 0) {
+               pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+               return -EINVAL;
+       }
+ 
+       rc = bttv_enum_fmt_cap_ovr(f);
+ 
+       if (rc < 0)
+               return rc;
+ 
+       if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_fbuf(struct file *file, void *f,
+                               struct v4l2_framebuffer *fb)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       *fb = btv->fbuf;
+       fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+       if (fh->ovfmt)
+               fb->fmt.pixelformat  = fh->ovfmt->fourcc;
+       return 0;
+ }
+ 
+ static int bttv_overlay(struct file *file, void *f, unsigned int on)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       struct bttv_buffer *new;
+       int retval = 0;
+ 
+       if (on) {
+               /* verify args */
+               if (unlikely(!btv->fbuf.base)) {
+                       return -EINVAL;
+               }
+               if (unlikely(!fh->ov.setup_ok)) {
+                       dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
+                       retval = -EINVAL;
+               }
+               if (retval)
+                       return retval;
+       }
+ 
+       if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
+               return -EBUSY;
+ 
+       if (on) {
+               fh->ov.tvnorm = btv->tvnorm;
+               new = videobuf_sg_alloc(sizeof(*new));
+               new->crop = btv->crop[!!fh->do_crop].rect;
+               bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+       } else {
+               new = NULL;
+       }
+ 
+       /* switch over */
+       retval = bttv_switch_overlay(btv, fh, new);
+       return retval;
+ }
+ 
+ static int bttv_s_fbuf(struct file *file, void *f,
+                               const struct v4l2_framebuffer *fb)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       const struct bttv_format *fmt;
+       int retval;
+ 
+       if (!capable(CAP_SYS_ADMIN) &&
+               !capable(CAP_SYS_RAWIO))
+               return -EPERM;
+ 
+       /* check args */
+       fmt = format_by_fourcc(fb->fmt.pixelformat);
+       if (NULL == fmt)
+               return -EINVAL;
+       if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+               return -EINVAL;
+ 
+       retval = -EINVAL;
+       if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+               __s32 width = fb->fmt.width;
+               __s32 height = fb->fmt.height;
+ 
+               retval = limit_scaled_size_lock(fh, &width, &height,
+                                          V4L2_FIELD_INTERLACED,
+                                          /* width_mask */ ~3,
+                                          /* width_bias */ 2,
+                                          /* adjust_size */ 0,
+                                          /* adjust_crop */ 0);
+               if (0 != retval)
+                       return retval;
+       }
+ 
+       /* ok, accept it */
+       btv->fbuf.base       = fb->base;
+       btv->fbuf.fmt.width  = fb->fmt.width;
+       btv->fbuf.fmt.height = fb->fmt.height;
+       if (0 != fb->fmt.bytesperline)
+               btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+       else
+               btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+ 
+       retval = 0;
+       fh->ovfmt = fmt;
+       btv->init.ovfmt = fmt;
+       if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+               fh->ov.w.left   = 0;
+               fh->ov.w.top    = 0;
+               fh->ov.w.width  = fb->fmt.width;
+               fh->ov.w.height = fb->fmt.height;
+               btv->init.ov.w.width  = fb->fmt.width;
+               btv->init.ov.w.height = fb->fmt.height;
+                       kfree(fh->ov.clips);
+               fh->ov.clips = NULL;
+               fh->ov.nclips = 0;
+ 
+               if (check_btres(fh, RESOURCE_OVERLAY)) {
+                       struct bttv_buffer *new;
+ 
+                       new = videobuf_sg_alloc(sizeof(*new));
+                       new->crop = btv->crop[!!fh->do_crop].rect;
+                       bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+                       retval = bttv_switch_overlay(btv, fh, new);
+               }
+       }
+       return retval;
+ }
+ 
+ static int bttv_reqbufs(struct file *file, void *priv,
+                               struct v4l2_requestbuffers *p)
+ {
+       struct bttv_fh *fh = priv;
+       return videobuf_reqbufs(bttv_queue(fh), p);
+ }
+ 
+ static int bttv_querybuf(struct file *file, void *priv,
+                               struct v4l2_buffer *b)
+ {
+       struct bttv_fh *fh = priv;
+       return videobuf_querybuf(bttv_queue(fh), b);
+ }
+ 
+ static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int res = bttv_resource(fh);
+ 
+       if (!check_alloc_btres_lock(btv, fh, res))
+               return -EBUSY;
+ 
+       return videobuf_qbuf(bttv_queue(fh), b);
+ }
+ 
+ static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ {
+       struct bttv_fh *fh = priv;
+       return videobuf_dqbuf(bttv_queue(fh), b,
+                       file->f_flags & O_NONBLOCK);
+ }
+ 
+ static int bttv_streamon(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int res = bttv_resource(fh);
+ 
+       if (!check_alloc_btres_lock(btv, fh, res))
+               return -EBUSY;
+       return videobuf_streamon(bttv_queue(fh));
+ }
+ 
+ 
+ static int bttv_streamoff(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       int retval;
+       int res = bttv_resource(fh);
+ 
+ 
+       retval = videobuf_streamoff(bttv_queue(fh));
+       if (retval < 0)
+               return retval;
+       free_btres_lock(btv, fh, res);
+       return 0;
+ }
+ 
+ static int bttv_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+       const struct v4l2_queryctrl *ctrl;
+ 
+       if ((c->id <  V4L2_CID_BASE ||
+            c->id >= V4L2_CID_LASTP1) &&
+           (c->id <  V4L2_CID_PRIVATE_BASE ||
+            c->id >= V4L2_CID_PRIVATE_LASTP1))
+               return -EINVAL;
+ 
+       if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+               *c = no_ctl;
+       else {
+               ctrl = ctrl_by_id(c->id);
+ 
+               *c = (NULL != ctrl) ? *ctrl : no_ctl;
+       }
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_parm(struct file *file, void *f,
+                               struct v4l2_streamparm *parm)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+                                   &parm->parm.capture.timeperframe);
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_tuner(struct file *file, void *priv,
+                               struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+ 
+       t->rxsubchans = V4L2_TUNER_SUB_MONO;
+       bttv_call_all(btv, tuner, g_tuner, t);
+       strcpy(t->name, "Television");
+       t->capability = V4L2_TUNER_CAP_NORM;
+       t->type       = V4L2_TUNER_ANALOG_TV;
+       if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+               t->signal = 0xffff;
+ 
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 0);
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       *p = v4l2_prio_max(&btv->prio);
+ 
+       return 0;
+ }
+ 
+ static int bttv_s_priority(struct file *file, void *f,
+                                       enum v4l2_priority prio)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       int     rc;
+ 
+       rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+ 
+       return rc;
+ }
+ 
+ static int bttv_cropcap(struct file *file, void *priv,
+                               struct v4l2_cropcap *cap)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
+ 
+       *cap = bttv_tvnorms[btv->tvnorm].cropcap;
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+ 
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
+ 
+       /* No fh->do_crop = 1; because btv->crop[1] may be
+          inconsistent with fh->width or fh->height and apps
+          do not expect a change here. */
+ 
+       crop->c = btv->crop[!!fh->do_crop].rect;
+ 
+       return 0;
+ }
+ 
+ static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
+ {
+       struct bttv_fh *fh = f;
+       struct bttv *btv = fh->btv;
+       const struct v4l2_rect *b;
+       int retval;
+       struct bttv_crop c;
+       __s32 b_left;
+       __s32 b_top;
+       __s32 b_right;
+       __s32 b_bottom;
+ 
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+           crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+               return -EINVAL;
+ 
+       /* Make sure tvnorm, vbi_end and the current cropping
+          parameters remain consistent until we're done. Note
+          read() may change vbi_end in check_alloc_btres_lock(). */
+       retval = v4l2_prio_check(&btv->prio, fh->prio);
+       if (0 != retval) {
+               return retval;
+       }
+ 
+       retval = -EBUSY;
+ 
+       if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+               return retval;
+       }
+ 
+       b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+ 
+       b_left = b->left;
+       b_right = b_left + b->width;
+       b_bottom = b->top + b->height;
+ 
+       b_top = max(b->top, btv->vbi_end);
+       if (b_top + 32 >= b_bottom) {
+               return retval;
+       }
+ 
+       /* Min. scaled size 48 x 32. */
+       c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48);
+       c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+ 
+       c.rect.width = clamp_t(s32, crop->c.width,
+                            48, b_right - c.rect.left);
+ 
+       c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32);
+       /* Top and height must be a multiple of two. */
+       c.rect.top = (c.rect.top + 1) & ~1;
+ 
+       c.rect.height = clamp_t(s32, crop->c.height,
+                             32, b_bottom - c.rect.top);
+       c.rect.height = (c.rect.height + 1) & ~1;
+ 
+       bttv_crop_calc_limits(&c);
+ 
+       btv->crop[1] = c;
+ 
+       fh->do_crop = 1;
+ 
+       if (fh->width < c.min_scaled_width) {
+               fh->width = c.min_scaled_width;
+               btv->init.width = c.min_scaled_width;
+       } else if (fh->width > c.max_scaled_width) {
+               fh->width = c.max_scaled_width;
+               btv->init.width = c.max_scaled_width;
+       }
+ 
+       if (fh->height < c.min_scaled_height) {
+               fh->height = c.min_scaled_height;
+               btv->init.height = c.min_scaled_height;
+       } else if (fh->height > c.max_scaled_height) {
+               fh->height = c.max_scaled_height;
+               btv->init.height = c.max_scaled_height;
+       }
+ 
+       return 0;
+ }
+ 
+ static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+ 
+       strcpy(a->name, "audio");
+       return 0;
+ }
+ 
+ static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ static ssize_t bttv_read(struct file *file, char __user *data,
+                        size_t count, loff_t *ppos)
+ {
+       struct bttv_fh *fh = file->private_data;
+       int retval = 0;
+ 
+       if (fh->btv->errors)
+               bttv_reinit_bt848(fh->btv);
+       dprintk("%d: read count=%d type=%s\n",
+               fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
+ 
+       switch (fh->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+                       /* VIDEO_READ in use by another fh,
+                          or VIDEO_STREAM by any fh. */
+                       return -EBUSY;
+               }
+               retval = videobuf_read_one(&fh->cap, data, count, ppos,
+                                          file->f_flags & O_NONBLOCK);
+               free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
+               break;
+       case V4L2_BUF_TYPE_VBI_CAPTURE:
+               if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+                       return -EBUSY;
+               retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
+                                             file->f_flags & O_NONBLOCK);
+               break;
+       default:
+               BUG();
+       }
+       return retval;
+ }
+ 
+ static unsigned int bttv_poll(struct file *file, poll_table *wait)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv_buffer *buf;
+       enum v4l2_field field;
+       unsigned int rc = POLLERR;
+ 
+       if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+               if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+                       return POLLERR;
+               return videobuf_poll_stream(file, &fh->vbi, wait);
+       }
+ 
+       if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+               /* streaming capture */
+               if (list_empty(&fh->cap.stream))
+                       goto err;
+               buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+       } else {
+               /* read() capture */
+               if (NULL == fh->cap.read_buf) {
+                       /* need to capture a new frame */
+                       if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+                               goto err;
+                       fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+                       if (NULL == fh->cap.read_buf)
+                               goto err;
+                       fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+                       field = videobuf_next_field(&fh->cap);
+                       if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
+                               kfree (fh->cap.read_buf);
+                               fh->cap.read_buf = NULL;
+                               goto err;
+                       }
+                       fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+                       fh->cap.read_off = 0;
+               }
+               buf = (struct bttv_buffer*)fh->cap.read_buf;
+       }
+ 
+       poll_wait(file, &buf->vb.done, wait);
+       if (buf->vb.state == VIDEOBUF_DONE ||
+           buf->vb.state == VIDEOBUF_ERROR)
+               rc =  POLLIN|POLLRDNORM;
+       else
+               rc = 0;
+ err:
+       return rc;
+ }
+ 
+ static int bttv_open(struct file *file)
+ {
+       struct video_device *vdev = video_devdata(file);
+       struct bttv *btv = video_drvdata(file);
+       struct bttv_fh *fh;
+       enum v4l2_buf_type type = 0;
+ 
+       dprintk("open dev=%s\n", video_device_node_name(vdev));
+ 
+       if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       } else if (vdev->vfl_type == VFL_TYPE_VBI) {
+               type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       } else {
+               WARN_ON(1);
+               return -ENODEV;
+       }
+ 
+       dprintk("%d: open called (type=%s)\n",
+               btv->c.nr, v4l2_type_names[type]);
+ 
+       /* allocate per filehandle data */
+       fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+       if (unlikely(!fh))
+               return -ENOMEM;
+       file->private_data = fh;
+ 
+       *fh = btv->init;
+ 
+       fh->type = type;
+       fh->ov.setup_ok = 0;
+ 
+       v4l2_prio_open(&btv->prio, &fh->prio);
+ 
+       videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+                           &btv->c.pci->dev, &btv->s_lock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_INTERLACED,
+                           sizeof(struct bttv_buffer),
+                           fh, &btv->lock);
+       videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+                           &btv->c.pci->dev, &btv->s_lock,
+                           V4L2_BUF_TYPE_VBI_CAPTURE,
+                           V4L2_FIELD_SEQ_TB,
+                           sizeof(struct bttv_buffer),
+                           fh, &btv->lock);
+       set_tvnorm(btv,btv->tvnorm);
+       set_input(btv, btv->input, btv->tvnorm);
+ 
+       btv->users++;
+ 
+       /* The V4L2 spec requires one global set of cropping parameters
+          which only change on request. These are stored in btv->crop[1].
+          However for compatibility with V4L apps and cropping unaware
+          V4L2 apps we now reset the cropping parameters as seen through
+          this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+          will use btv->crop[0], the default cropping parameters for the
+          current video standard, and VIDIOC_S_FMT will not implicitely
+          change the cropping parameters until VIDIOC_S_CROP has been
+          called. */
+       fh->do_crop = !reset_crop; /* module parameter */
+ 
+       /* Likewise there should be one global set of VBI capture
+          parameters, but for compatibility with V4L apps and earlier
+          driver versions each fh has its own parameters. */
+       bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+ 
+       bttv_field_count(btv);
+       return 0;
+ }
+ 
+ static int bttv_release(struct file *file)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+ 
+       /* turn off overlay */
+       if (check_btres(fh, RESOURCE_OVERLAY))
+               bttv_switch_overlay(btv,fh,NULL);
+ 
+       /* stop video capture */
+       if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
+               videobuf_streamoff(&fh->cap);
+               free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
+       }
+       if (fh->cap.read_buf) {
+               buffer_release(&fh->cap,fh->cap.read_buf);
+               kfree(fh->cap.read_buf);
+       }
+       if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+               free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
+       }
+ 
+       /* stop vbi capture */
+       if (check_btres(fh, RESOURCE_VBI)) {
+               videobuf_stop(&fh->vbi);
+               free_btres_lock(btv,fh,RESOURCE_VBI);
+       }
+ 
+       /* free stuff */
+ 
+       videobuf_mmap_free(&fh->cap);
+       videobuf_mmap_free(&fh->vbi);
+       v4l2_prio_close(&btv->prio, fh->prio);
+       file->private_data = NULL;
+       kfree(fh);
+ 
+       btv->users--;
+       bttv_field_count(btv);
+ 
+       if (!btv->users)
+               audio_mute(btv, 1);
+ 
+       return 0;
+ }
+ 
+ static int
+ bttv_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+       struct bttv_fh *fh = file->private_data;
+ 
+       dprintk("%d: mmap type=%s 0x%lx+%ld\n",
+               fh->btv->c.nr, v4l2_type_names[fh->type],
+               vma->vm_start, vma->vm_end - vma->vm_start);
+       return videobuf_mmap_mapper(bttv_queue(fh),vma);
+ }
+ 
+ static const struct v4l2_file_operations bttv_fops =
+ {
+       .owner            = THIS_MODULE,
+       .open             = bttv_open,
+       .release          = bttv_release,
+       .unlocked_ioctl   = video_ioctl2,
+       .read             = bttv_read,
+       .mmap             = bttv_mmap,
+       .poll             = bttv_poll,
+ };
+ 
+ static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+       .vidioc_querycap                = bttv_querycap,
+       .vidioc_enum_fmt_vid_cap        = bttv_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = bttv_g_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap         = bttv_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = bttv_s_fmt_vid_cap,
+       .vidioc_enum_fmt_vid_overlay    = bttv_enum_fmt_vid_overlay,
+       .vidioc_g_fmt_vid_overlay       = bttv_g_fmt_vid_overlay,
+       .vidioc_try_fmt_vid_overlay     = bttv_try_fmt_vid_overlay,
+       .vidioc_s_fmt_vid_overlay       = bttv_s_fmt_vid_overlay,
+       .vidioc_g_fmt_vbi_cap           = bttv_g_fmt_vbi_cap,
+       .vidioc_try_fmt_vbi_cap         = bttv_try_fmt_vbi_cap,
+       .vidioc_s_fmt_vbi_cap           = bttv_s_fmt_vbi_cap,
+       .vidioc_g_audio                 = bttv_g_audio,
+       .vidioc_s_audio                 = bttv_s_audio,
+       .vidioc_cropcap                 = bttv_cropcap,
+       .vidioc_reqbufs                 = bttv_reqbufs,
+       .vidioc_querybuf                = bttv_querybuf,
+       .vidioc_qbuf                    = bttv_qbuf,
+       .vidioc_dqbuf                   = bttv_dqbuf,
+       .vidioc_s_std                   = bttv_s_std,
+       .vidioc_enum_input              = bttv_enum_input,
+       .vidioc_g_input                 = bttv_g_input,
+       .vidioc_s_input                 = bttv_s_input,
+       .vidioc_queryctrl               = bttv_queryctrl,
+       .vidioc_g_ctrl                  = bttv_g_ctrl,
+       .vidioc_s_ctrl                  = bttv_s_ctrl,
+       .vidioc_streamon                = bttv_streamon,
+       .vidioc_streamoff               = bttv_streamoff,
+       .vidioc_g_tuner                 = bttv_g_tuner,
+       .vidioc_s_tuner                 = bttv_s_tuner,
+       .vidioc_g_crop                  = bttv_g_crop,
+       .vidioc_s_crop                  = bttv_s_crop,
+       .vidioc_g_fbuf                  = bttv_g_fbuf,
+       .vidioc_s_fbuf                  = bttv_s_fbuf,
+       .vidioc_overlay                 = bttv_overlay,
+       .vidioc_g_priority              = bttv_g_priority,
+       .vidioc_s_priority              = bttv_s_priority,
+       .vidioc_g_parm                  = bttv_g_parm,
+       .vidioc_g_frequency             = bttv_g_frequency,
+       .vidioc_s_frequency             = bttv_s_frequency,
+       .vidioc_log_status              = bttv_log_status,
+       .vidioc_querystd                = bttv_querystd,
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register              = bttv_g_register,
+       .vidioc_s_register              = bttv_s_register,
+ #endif
+ };
+ 
+ static struct video_device bttv_video_template = {
+       .fops         = &bttv_fops,
+       .ioctl_ops    = &bttv_ioctl_ops,
+       .tvnorms      = BTTV_NORMS,
+       .current_norm = V4L2_STD_PAL,
+ };
+ 
+ /* ----------------------------------------------------------------------- */
+ /* radio interface                                                         */
+ 
+ static int radio_open(struct file *file)
+ {
+       struct video_device *vdev = video_devdata(file);
+       struct bttv *btv = video_drvdata(file);
+       struct bttv_fh *fh;
+ 
+       dprintk("open dev=%s\n", video_device_node_name(vdev));
+ 
+       dprintk("%d: open called (radio)\n", btv->c.nr);
+ 
+       /* allocate per filehandle data */
+       fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+       if (unlikely(!fh))
+               return -ENOMEM;
+       file->private_data = fh;
+       *fh = btv->init;
+ 
+       v4l2_prio_open(&btv->prio, &fh->prio);
+ 
+       btv->radio_user++;
+ 
+       bttv_call_all(btv, tuner, s_radio);
+       audio_input(btv,TVAUDIO_INPUT_RADIO);
+ 
+       return 0;
+ }
+ 
+ static int radio_release(struct file *file)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       struct saa6588_command cmd;
+ 
+       v4l2_prio_close(&btv->prio, fh->prio);
+       file->private_data = NULL;
+       kfree(fh);
+ 
+       btv->radio_user--;
+ 
+       bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+ 
+       return 0;
+ }
+ 
+ static int radio_querycap(struct file *file, void *priv,
+                                       struct v4l2_capability *cap)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       strcpy(cap->driver, "bttv");
+       strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+       sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+       cap->capabilities = V4L2_CAP_TUNER;
+ 
+       return 0;
+ }
+ 
+ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       if (btv->tuner_type == TUNER_ABSENT)
+               return -EINVAL;
+       if (0 != t->index)
+               return -EINVAL;
+       strcpy(t->name, "Radio");
+       t->type = V4L2_TUNER_RADIO;
+ 
+       bttv_call_all(btv, tuner, g_tuner, t);
+ 
+       if (btv->audio_mode_gpio)
+               btv->audio_mode_gpio(btv, t, 0);
+ 
+       return 0;
+ }
+ 
+ static int radio_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *i)
+ {
+       if (i->index != 0)
+               return -EINVAL;
+ 
+       strcpy(i->name, "Radio");
+       i->type = V4L2_INPUT_TYPE_TUNER;
+ 
+       return 0;
+ }
+ 
+ static int radio_g_audio(struct file *file, void *priv,
+                                       struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+ 
+       strcpy(a->name, "Radio");
+ 
+       return 0;
+ }
+ 
+ static int radio_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+ {
+       struct bttv_fh *fh = priv;
+       struct bttv *btv = fh->btv;
+ 
+       if (0 != t->index)
+               return -EINVAL;
+ 
+       bttv_call_all(btv, tuner, s_tuner, t);
+       return 0;
+ }
+ 
+ static int radio_s_audio(struct file *file, void *priv,
+                                       const struct v4l2_audio *a)
+ {
+       if (unlikely(a->index))
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+       if (unlikely(i))
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+ {
+       return 0;
+ }
+ 
+ static int radio_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+ {
+       const struct v4l2_queryctrl *ctrl;
+ 
+       if (c->id <  V4L2_CID_BASE ||
+                       c->id >= V4L2_CID_LASTP1)
+               return -EINVAL;
+ 
+       if (c->id == V4L2_CID_AUDIO_MUTE) {
+               ctrl = ctrl_by_id(c->id);
+               *c = *ctrl;
+       } else
+               *c = no_ctl;
+ 
+       return 0;
+ }
+ 
+ static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+       *i = 0;
+       return 0;
+ }
+ 
+ static ssize_t radio_read(struct file *file, char __user *data,
+                        size_t count, loff_t *ppos)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       struct saa6588_command cmd;
+       cmd.block_count = count/3;
+       cmd.buffer = data;
+       cmd.instance = file;
+       cmd.result = -ENODEV;
+ 
+       bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
+ 
+       return cmd.result;
+ }
+ 
+ static unsigned int radio_poll(struct file *file, poll_table *wait)
+ {
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       struct saa6588_command cmd;
+       cmd.instance = file;
+       cmd.event_list = wait;
+       cmd.result = -ENODEV;
+       bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
+ 
+       return cmd.result;
+ }
+ 
+ static const struct v4l2_file_operations radio_fops =
+ {
+       .owner    = THIS_MODULE,
+       .open     = radio_open,
+       .read     = radio_read,
+       .release  = radio_release,
+       .unlocked_ioctl = video_ioctl2,
+       .poll     = radio_poll,
+ };
+ 
+ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+       .vidioc_querycap        = radio_querycap,
+       .vidioc_g_tuner         = radio_g_tuner,
+       .vidioc_enum_input      = radio_enum_input,
+       .vidioc_g_audio         = radio_g_audio,
+       .vidioc_s_tuner         = radio_s_tuner,
+       .vidioc_s_audio         = radio_s_audio,
+       .vidioc_s_input         = radio_s_input,
+       .vidioc_s_std           = radio_s_std,
+       .vidioc_queryctrl       = radio_queryctrl,
+       .vidioc_g_input         = radio_g_input,
+       .vidioc_g_ctrl          = bttv_g_ctrl,
+       .vidioc_s_ctrl          = bttv_s_ctrl,
+       .vidioc_g_frequency     = bttv_g_frequency,
+       .vidioc_s_frequency     = bttv_s_frequency,
+ };
+ 
+ static struct video_device radio_template = {
+       .fops      = &radio_fops,
+       .ioctl_ops = &radio_ioctl_ops,
+ };
+ 
+ /* ----------------------------------------------------------------------- */
+ /* some debug code                                                         */
+ 
+ static int bttv_risc_decode(u32 risc)
+ {
+       static char *instr[16] = {
+               [ BT848_RISC_WRITE     >> 28 ] = "write",
+               [ BT848_RISC_SKIP      >> 28 ] = "skip",
+               [ BT848_RISC_WRITEC    >> 28 ] = "writec",
+               [ BT848_RISC_JUMP      >> 28 ] = "jump",
+               [ BT848_RISC_SYNC      >> 28 ] = "sync",
+               [ BT848_RISC_WRITE123  >> 28 ] = "write123",
+               [ BT848_RISC_SKIP123   >> 28 ] = "skip123",
+               [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
+       };
+       static int incr[16] = {
+               [ BT848_RISC_WRITE     >> 28 ] = 2,
+               [ BT848_RISC_JUMP      >> 28 ] = 2,
+               [ BT848_RISC_SYNC      >> 28 ] = 2,
+               [ BT848_RISC_WRITE123  >> 28 ] = 5,
+               [ BT848_RISC_SKIP123   >> 28 ] = 2,
+               [ BT848_RISC_WRITE1S23 >> 28 ] = 3,
+       };
+       static char *bits[] = {
+               "be0",  "be1",  "be2",  "be3/resync",
+               "set0", "set1", "set2", "set3",
+               "clr0", "clr1", "clr2", "clr3",
+               "irq",  "res",  "eol",  "sol",
+       };
+       int i;
+ 
+       pr_cont("0x%08x [ %s", risc,
+              instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+       for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+               if (risc & (1 << (i + 12)))
+                       pr_cont(" %s", bits[i]);
+       pr_cont(" count=%d ]\n", risc & 0xfff);
+       return incr[risc >> 28] ? incr[risc >> 28] : 1;
+ }
+ 
+ static void bttv_risc_disasm(struct bttv *btv,
+                            struct btcx_riscmem *risc)
+ {
+       unsigned int i,j,n;
+ 
+       pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+               btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+       for (i = 0; i < (risc->size >> 2); i += n) {
+               pr_info("%s:   0x%lx: ",
+                       btv->c.v4l2_dev.name,
+                       (unsigned long)(risc->dma + (i<<2)));
+               n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+               for (j = 1; j < n; j++)
+                       pr_info("%s:   0x%lx: 0x%08x [ arg #%d ]\n",
+                               btv->c.v4l2_dev.name,
+                               (unsigned long)(risc->dma + ((i+j)<<2)),
+                               risc->cpu[i+j], j);
+               if (0 == risc->cpu[i])
+                       break;
+       }
+ }
+ 
+ static void bttv_print_riscaddr(struct bttv *btv)
+ {
+       pr_info("  main: %08llx\n", (unsigned long long)btv->main.dma);
+       pr_info("  vbi : o=%08llx e=%08llx\n",
+               btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+               btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+       pr_info("  cap : o=%08llx e=%08llx\n",
+               btv->curr.top
+               ? (unsigned long long)btv->curr.top->top.dma : 0,
+               btv->curr.bottom
+               ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+       pr_info("  scr : o=%08llx e=%08llx\n",
+               btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+               btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+       bttv_risc_disasm(btv, &btv->main);
+ }
+ 
+ /* ----------------------------------------------------------------------- */
+ /* irq handler                                                             */
+ 
+ static char *irq_name[] = {
+       "FMTCHG",  // format change detected (525 vs. 625)
+       "VSYNC",   // vertical sync (new field)
+       "HSYNC",   // horizontal sync
+       "OFLOW",   // chroma/luma AGC overflow
+       "HLOCK",   // horizontal lock changed
+       "VPRES",   // video presence changed
+       "6", "7",
+       "I2CDONE", // hw irc operation finished
+       "GPINT",   // gpio port triggered irq
+       "10",
+       "RISCI",   // risc instruction triggered irq
+       "FBUS",    // pixel data fifo dropped data (high pci bus latencies)
+       "FTRGT",   // pixel data fifo overrun
+       "FDSR",    // fifo data stream resyncronisation
+       "PPERR",   // parity error (data transfer)
+       "RIPERR",  // parity error (read risc instructions)
+       "PABORT",  // pci abort
+       "OCERR",   // risc instruction error
+       "SCERR",   // syncronisation error
+ };
+ 
+ static void bttv_print_irqbits(u32 print, u32 mark)
+ {
+       unsigned int i;
+ 
+       pr_cont("bits:");
+       for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+               if (print & (1 << i))
+                       pr_cont(" %s", irq_name[i]);
+               if (mark & (1 << i))
+                       pr_cont("*");
+       }
+ }
+ 
+ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+ {
+       pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+               btv->c.nr,
+               (unsigned long)btv->main.dma,
+               (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+               (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+               (unsigned long)rc);
+ 
+       if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+               pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+                         "Ok, then this is harmless, don't worry ;)\n",
+                         btv->c.nr);
+               return;
+       }
+       pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+                 btv->c.nr);
+       pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+                 btv->c.nr);
+       dump_stack();
+ }
+ 
+ static int
+ bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+ {
+       struct bttv_buffer *item;
+ 
+       memset(set,0,sizeof(*set));
+ 
+       /* capture request ? */
+       if (!list_empty(&btv->capture)) {
+               set->frame_irq = 1;
+               item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+               if (V4L2_FIELD_HAS_TOP(item->vb.field))
+                       set->top    = item;
+               if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+                       set->bottom = item;
+ 
+               /* capture request for other field ? */
+               if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+                   (item->vb.queue.next != &btv->capture)) {
+                       item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+                       /* Mike Isely <isely@pobox.com> - Only check
+                        * and set up the bottom field in the logic
+                        * below.  Don't ever do the top field.  This
+                        * of course means that if we set up the
+                        * bottom field in the above code that we'll
+                        * actually skip a field.  But that's OK.
+                        * Having processed only a single buffer this
+                        * time, then the next time around the first
+                        * available buffer should be for a top field.
+                        * That will then cause us here to set up a
+                        * top then a bottom field in the normal way.
+                        * The alternative to this understanding is
+                        * that we set up the second available buffer
+                        * as a top field, but that's out of order
+                        * since this driver always processes the top
+                        * field first - the effect will be the two
+                        * buffers being returned in the wrong order,
+                        * with the second buffer also being delayed
+                        * by one field time (owing to the fifo nature
+                        * of videobuf).  Worse still, we'll be stuck
+                        * doing fields out of order now every time
+                        * until something else causes a field to be
+                        * dropped.  By effectively forcing a field to
+                        * drop this way then we always get back into
+                        * sync within a single frame time.  (Out of
+                        * order fields can screw up deinterlacing
+                        * algorithms.) */
+                       if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+                               if (NULL == set->bottom &&
+                                   V4L2_FIELD_BOTTOM == item->vb.field) {
+                                       set->bottom = item;
+                               }
+                               if (NULL != set->top  &&  NULL != set->bottom)
+                                       set->top_irq = 2;
+                       }
+               }
+       }
+ 
+       /* screen overlay ? */
+       if (NULL != btv->screen) {
+               if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+                       if (NULL == set->top && NULL == set->bottom) {
+                               set->top    = btv->screen;
+                               set->bottom = btv->screen;
+                       }
+               } else {
+                       if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+                           NULL == set->top) {
+                               set->top = btv->screen;
+                       }
+                       if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+                           NULL == set->bottom) {
+                               set->bottom = btv->screen;
+                       }
+               }
+       }
+ 
+       dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+               btv->c.nr, set->top, set->bottom,
+               btv->screen, set->frame_irq, set->top_irq);
+       return 0;
+ }
+ 
+ static void
+ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+                     struct bttv_buffer_set *curr, unsigned int state)
+ {
+       struct timeval ts;
+ 
+       do_gettimeofday(&ts);
+ 
+       if (wakeup->top == wakeup->bottom) {
+               if (NULL != wakeup->top && curr->top != wakeup->top) {
+                       if (irq_debug > 1)
+                               pr_debug("%d: wakeup: both=%p\n",
+                                        btv->c.nr, wakeup->top);
+                       wakeup->top->vb.ts = ts;
+                       wakeup->top->vb.field_count = btv->field_count;
+                       wakeup->top->vb.state = state;
+                       wake_up(&wakeup->top->vb.done);
+               }
+       } else {
+               if (NULL != wakeup->top && curr->top != wakeup->top) {
+                       if (irq_debug > 1)
+                               pr_debug("%d: wakeup: top=%p\n",
+                                        btv->c.nr, wakeup->top);
+                       wakeup->top->vb.ts = ts;
+                       wakeup->top->vb.field_count = btv->field_count;
+                       wakeup->top->vb.state = state;
+                       wake_up(&wakeup->top->vb.done);
+               }
+               if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+                       if (irq_debug > 1)
+                               pr_debug("%d: wakeup: bottom=%p\n",
+                                        btv->c.nr, wakeup->bottom);
+                       wakeup->bottom->vb.ts = ts;
+                       wakeup->bottom->vb.field_count = btv->field_count;
+                       wakeup->bottom->vb.state = state;
+                       wake_up(&wakeup->bottom->vb.done);
+               }
+       }
+ }
+ 
+ static void
+ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+                   unsigned int state)
+ {
+       struct timeval ts;
+ 
+       if (NULL == wakeup)
+               return;
+ 
+       do_gettimeofday(&ts);
+       wakeup->vb.ts = ts;
+       wakeup->vb.field_count = btv->field_count;
+       wakeup->vb.state = state;
+       wake_up(&wakeup->vb.done);
+ }
+ 
+ static void bttv_irq_timeout(unsigned long data)
+ {
+       struct bttv *btv = (struct bttv *)data;
+       struct bttv_buffer_set old,new;
+       struct bttv_buffer *ovbi;
+       struct bttv_buffer *item;
+       unsigned long flags;
+ 
+       if (bttv_verbose) {
+               pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+                       btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+                       btread(BT848_RISC_COUNT));
+               bttv_print_irqbits(btread(BT848_INT_STAT),0);
+               pr_cont("\n");
+       }
+ 
+       spin_lock_irqsave(&btv->s_lock,flags);
+ 
+       /* deactivate stuff */
+       memset(&new,0,sizeof(new));
+       old  = btv->curr;
+       ovbi = btv->cvbi;
+       btv->curr = new;
+       btv->cvbi = NULL;
+       btv->loop_irq = 0;
+       bttv_buffer_activate_video(btv, &new);
+       bttv_buffer_activate_vbi(btv,   NULL);
+       bttv_set_dma(btv, 0);
+ 
+       /* wake up */
+       bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+       bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
+ 
+       /* cancel all outstanding capture / vbi requests */
+       while (!list_empty(&btv->capture)) {
+               item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+               list_del(&item->vb.queue);
+               item->vb.state = VIDEOBUF_ERROR;
+               wake_up(&item->vb.done);
+       }
+       while (!list_empty(&btv->vcapture)) {
+               item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+               list_del(&item->vb.queue);
+               item->vb.state = VIDEOBUF_ERROR;
+               wake_up(&item->vb.done);
+       }
+ 
+       btv->errors++;
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+ }
+ 
+ static void
+ bttv_irq_wakeup_top(struct bttv *btv)
+ {
+       struct bttv_buffer *wakeup = btv->curr.top;
+ 
+       if (NULL == wakeup)
+               return;
+ 
+       spin_lock(&btv->s_lock);
+       btv->curr.top_irq = 0;
+       btv->curr.top = NULL;
+       bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+ 
+       do_gettimeofday(&wakeup->vb.ts);
+       wakeup->vb.field_count = btv->field_count;
+       wakeup->vb.state = VIDEOBUF_DONE;
+       wake_up(&wakeup->vb.done);
+       spin_unlock(&btv->s_lock);
+ }
+ 
+ static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+ {
+       if (rc < risc->dma)
+               return 0;
+       if (rc > risc->dma + risc->size)
+               return 0;
+       return 1;
+ }
+ 
+ static void
+ bttv_irq_switch_video(struct bttv *btv)
+ {
+       struct bttv_buffer_set new;
+       struct bttv_buffer_set old;
+       dma_addr_t rc;
+ 
+       spin_lock(&btv->s_lock);
+ 
+       /* new buffer set */
+       bttv_irq_next_video(btv, &new);
+       rc = btread(BT848_RISC_COUNT);
+       if ((btv->curr.top    && is_active(&btv->curr.top->top,       rc)) ||
+           (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+               btv->framedrop++;
+               if (debug_latency)
+                       bttv_irq_debug_low_latency(btv, rc);
+               spin_unlock(&btv->s_lock);
+               return;
+       }
+ 
+       /* switch over */
+       old = btv->curr;
+       btv->curr = new;
+       btv->loop_irq &= ~1;
+       bttv_buffer_activate_video(btv, &new);
+       bttv_set_dma(btv, 0);
+ 
+       /* switch input */
+       if (UNSET != btv->new_input) {
+               video_mux(btv,btv->new_input);
+               btv->new_input = UNSET;
+       }
+ 
+       /* wake up finished buffers */
+       bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
+       spin_unlock(&btv->s_lock);
+ }
+ 
+ static void
+ bttv_irq_switch_vbi(struct bttv *btv)
+ {
+       struct bttv_buffer *new = NULL;
+       struct bttv_buffer *old;
+       u32 rc;
+ 
+       spin_lock(&btv->s_lock);
+ 
+       if (!list_empty(&btv->vcapture))
+               new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+       old = btv->cvbi;
+ 
+       rc = btread(BT848_RISC_COUNT);
+       if (NULL != old && (is_active(&old->top,    rc) ||
+                           is_active(&old->bottom, rc))) {
+               btv->framedrop++;
+               if (debug_latency)
+                       bttv_irq_debug_low_latency(btv, rc);
+               spin_unlock(&btv->s_lock);
+               return;
+       }
+ 
+       /* switch */
+       btv->cvbi = new;
+       btv->loop_irq &= ~4;
+       bttv_buffer_activate_vbi(btv, new);
+       bttv_set_dma(btv, 0);
+ 
+       bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
+       spin_unlock(&btv->s_lock);
+ }
+ 
+ static irqreturn_t bttv_irq(int irq, void *dev_id)
+ {
+       u32 stat,astat;
+       u32 dstat;
+       int count;
+       struct bttv *btv;
+       int handled = 0;
+ 
+       btv=(struct bttv *)dev_id;
+ 
+       count=0;
+       while (1) {
+               /* get/clear interrupt status bits */
+               stat=btread(BT848_INT_STAT);
+               astat=stat&btread(BT848_INT_MASK);
+               if (!astat)
+                       break;
+               handled = 1;
+               btwrite(stat,BT848_INT_STAT);
+ 
+               /* get device status bits */
+               dstat=btread(BT848_DSTATUS);
+ 
+               if (irq_debug) {
+                       pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+                                btv->c.nr, count, btv->field_count,
+                                stat>>28, btread(BT848_RISC_COUNT));
+                       bttv_print_irqbits(stat,astat);
+                       if (stat & BT848_INT_HLOCK)
+                               pr_cont("   HLOC => %s",
+                                       dstat & BT848_DSTATUS_HLOC
+                                       ? "yes" : "no");
+                       if (stat & BT848_INT_VPRES)
+                               pr_cont("   PRES => %s",
+                                       dstat & BT848_DSTATUS_PRES
+                                       ? "yes" : "no");
+                       if (stat & BT848_INT_FMTCHG)
+                               pr_cont("   NUML => %s",
+                                       dstat & BT848_DSTATUS_NUML
+                                       ? "625" : "525");
+                       pr_cont("\n");
+               }
+ 
+               if (astat&BT848_INT_VSYNC)
+                       btv->field_count++;
+ 
+               if ((astat & BT848_INT_GPINT) && btv->remote) {
+                       bttv_input_irq(btv);
+               }
+ 
+               if (astat & BT848_INT_I2CDONE) {
+                       btv->i2c_done = stat;
+                       wake_up(&btv->i2c_queue);
+               }
+ 
+               if ((astat & BT848_INT_RISCI)  &&  (stat & (4<<28)))
+                       bttv_irq_switch_vbi(btv);
+ 
+               if ((astat & BT848_INT_RISCI)  &&  (stat & (2<<28)))
+                       bttv_irq_wakeup_top(btv);
+ 
+               if ((astat & BT848_INT_RISCI)  &&  (stat & (1<<28)))
+                       bttv_irq_switch_video(btv);
+ 
+               if ((astat & BT848_INT_HLOCK)  &&  btv->opt_automute)
+                       audio_mute(btv, btv->mute);  /* trigger automute */
+ 
+               if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+                       pr_info("%d: %s%s @ %08x,",
+                               btv->c.nr,
+                               (astat & BT848_INT_SCERR) ? "SCERR" : "",
+                               (astat & BT848_INT_OCERR) ? "OCERR" : "",
+                               btread(BT848_RISC_COUNT));
+                       bttv_print_irqbits(stat,astat);
+                       pr_cont("\n");
+                       if (bttv_debug)
+                               bttv_print_riscaddr(btv);
+               }
+               if (fdsr && astat & BT848_INT_FDSR) {
+                       pr_info("%d: FDSR @ %08x\n",
+                               btv->c.nr, btread(BT848_RISC_COUNT));
+                       if (bttv_debug)
+                               bttv_print_riscaddr(btv);
+               }
+ 
+               count++;
+               if (count > 4) {
+ 
+                       if (count > 8 || !(astat & BT848_INT_GPINT)) {
+                               btwrite(0, BT848_INT_MASK);
+ 
+                               pr_err("%d: IRQ lockup, cleared int mask [",
+                                      btv->c.nr);
+                       } else {
+                               pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+                                      btv->c.nr);
+ 
+                               btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
+                                               BT848_INT_MASK);
+                       };
+ 
+                       bttv_print_irqbits(stat,astat);
+ 
+                       pr_cont("]\n");
+               }
+       }
+       btv->irq_total++;
+       if (handled)
+               btv->irq_me++;
+       return IRQ_RETVAL(handled);
+ }
+ 
+ 
+ /* ----------------------------------------------------------------------- */
+ /* initialitation                                                          */
+ 
+ static struct video_device *vdev_init(struct bttv *btv,
+                                     const struct video_device *template,
+                                     const char *type_name)
+ {
+       struct video_device *vfd;
+ 
+       vfd = video_device_alloc();
+       if (NULL == vfd)
+               return NULL;
+       *vfd = *template;
+       vfd->v4l2_dev = &btv->c.v4l2_dev;
+       vfd->release = video_device_release;
+       vfd->debug   = bttv_debug;
+       video_set_drvdata(vfd, btv);
+       snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+                btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+                type_name, bttv_tvcards[btv->c.type].name);
+       return vfd;
+ }
+ 
+ static void bttv_unregister_video(struct bttv *btv)
+ {
+       if (btv->video_dev) {
+               if (video_is_registered(btv->video_dev))
+                       video_unregister_device(btv->video_dev);
+               else
+                       video_device_release(btv->video_dev);
+               btv->video_dev = NULL;
+       }
+       if (btv->vbi_dev) {
+               if (video_is_registered(btv->vbi_dev))
+                       video_unregister_device(btv->vbi_dev);
+               else
+                       video_device_release(btv->vbi_dev);
+               btv->vbi_dev = NULL;
+       }
+       if (btv->radio_dev) {
+               if (video_is_registered(btv->radio_dev))
+                       video_unregister_device(btv->radio_dev);
+               else
+                       video_device_release(btv->radio_dev);
+               btv->radio_dev = NULL;
+       }
+ }
+ 
+ /* register video4linux devices */
+ static int __devinit bttv_register_video(struct bttv *btv)
+ {
+       if (no_overlay > 0)
+               pr_notice("Overlay support disabled\n");
+ 
+       /* video */
+       btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+ 
+       if (NULL == btv->video_dev)
+               goto err;
+       if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+                                 video_nr[btv->c.nr]) < 0)
+               goto err;
+       pr_info("%d: registered device %s\n",
+               btv->c.nr, video_device_node_name(btv->video_dev));
+       if (device_create_file(&btv->video_dev->dev,
+                                    &dev_attr_card)<0) {
+               pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
+               goto err;
+       }
+ 
+       /* vbi */
+       btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+ 
+       if (NULL == btv->vbi_dev)
+               goto err;
+       if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+                                 vbi_nr[btv->c.nr]) < 0)
+               goto err;
+       pr_info("%d: registered device %s\n",
+               btv->c.nr, video_device_node_name(btv->vbi_dev));
+ 
+       if (!btv->has_radio)
+               return 0;
+       /* radio */
+       btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+       if (NULL == btv->radio_dev)
+               goto err;
+       if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+                                 radio_nr[btv->c.nr]) < 0)
+               goto err;
+       pr_info("%d: registered device %s\n",
+               btv->c.nr, video_device_node_name(btv->radio_dev));
+ 
+       /* all done */
+       return 0;
+ 
+  err:
+       bttv_unregister_video(btv);
+       return -1;
+ }
+ 
+ 
+ /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+ /* response on cards with no firmware is not enabled by OF */
+ static void pci_set_command(struct pci_dev *dev)
+ {
+ #if defined(__powerpc__)
+       unsigned int cmd;
+ 
+       pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+       cmd = (cmd | PCI_COMMAND_MEMORY );
+       pci_write_config_dword(dev, PCI_COMMAND, cmd);
+ #endif
+ }
+ 
+ static int __devinit bttv_probe(struct pci_dev *dev,
+                               const struct pci_device_id *pci_id)
+ {
+       int result;
+       unsigned char lat;
+       struct bttv *btv;
+ 
+       if (bttv_num == BTTV_MAX)
+               return -ENOMEM;
+       pr_info("Bt8xx card found (%d)\n", bttv_num);
+       bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+       if (btv == NULL) {
+               pr_err("out of memory\n");
+               return -ENOMEM;
+       }
+       btv->c.nr  = bttv_num;
+       snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+                       "bttv%d", btv->c.nr);
+ 
+       /* initialize structs / fill in defaults */
+       mutex_init(&btv->lock);
+       spin_lock_init(&btv->s_lock);
+       spin_lock_init(&btv->gpio_lock);
+       init_waitqueue_head(&btv->i2c_queue);
+       INIT_LIST_HEAD(&btv->c.subs);
+       INIT_LIST_HEAD(&btv->capture);
+       INIT_LIST_HEAD(&btv->vcapture);
+       v4l2_prio_init(&btv->prio);
+ 
+       init_timer(&btv->timeout);
+       btv->timeout.function = bttv_irq_timeout;
+       btv->timeout.data     = (unsigned long)btv;
+ 
+       btv->i2c_rc = -1;
+       btv->tuner_type  = UNSET;
+       btv->new_input   = UNSET;
+       btv->has_radio=radio[btv->c.nr];
+ 
+       /* pci stuff (init, get irq/mmio, ... */
+       btv->c.pci = dev;
+       btv->id  = dev->device;
+       if (pci_enable_device(dev)) {
+               pr_warn("%d: Can't enable device\n", btv->c.nr);
+               return -EIO;
+       }
+       if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+               pr_warn("%d: No suitable DMA available\n", btv->c.nr);
+               return -EIO;
+       }
+       if (!request_mem_region(pci_resource_start(dev,0),
+                               pci_resource_len(dev,0),
+                               btv->c.v4l2_dev.name)) {
+               pr_warn("%d: can't request iomem (0x%llx)\n",
+                       btv->c.nr,
+                       (unsigned long long)pci_resource_start(dev, 0));
+               return -EBUSY;
+       }
+       pci_set_master(dev);
+       pci_set_command(dev);
+ 
+       result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+       if (result < 0) {
+               pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
+               goto fail0;
+       }
+ 
+       btv->revision = dev->revision;
+       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+       pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+               bttv_num, btv->id, btv->revision, pci_name(dev),
+               btv->c.pci->irq, lat,
+               (unsigned long long)pci_resource_start(dev, 0));
+       schedule();
+ 
+       btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+       if (NULL == btv->bt848_mmio) {
+               pr_err("%d: ioremap() failed\n", btv->c.nr);
+               result = -EIO;
+               goto fail1;
+       }
+ 
+       /* identify card */
+       bttv_idcard(btv);
+ 
+       /* disable irqs, register irq handler */
+       btwrite(0, BT848_INT_MASK);
+       result = request_irq(btv->c.pci->irq, bttv_irq,
+           IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+       if (result < 0) {
+               pr_err("%d: can't get IRQ %d\n",
+                      bttv_num, btv->c.pci->irq);
+               goto fail1;
+       }
+ 
+       if (0 != bttv_handle_chipset(btv)) {
+               result = -EIO;
+               goto fail2;
+       }
+ 
+       /* init options from insmod args */
+       btv->opt_combfilter = combfilter;
+       btv->opt_lumafilter = lumafilter;
+       btv->opt_automute   = automute;
+       btv->opt_chroma_agc = chroma_agc;
+       btv->opt_adc_crush  = adc_crush;
+       btv->opt_vcr_hack   = vcr_hack;
+       btv->opt_whitecrush_upper  = whitecrush_upper;
+       btv->opt_whitecrush_lower  = whitecrush_lower;
+       btv->opt_uv_ratio   = uv_ratio;
+       btv->opt_full_luma_range   = full_luma_range;
+       btv->opt_coring     = coring;
+ 
+       /* fill struct bttv with some useful defaults */
+       btv->init.btv         = btv;
+       btv->init.ov.w.width  = 320;
+       btv->init.ov.w.height = 240;
+       btv->init.fmt         = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+       btv->init.width       = 320;
+       btv->init.height      = 240;
+       btv->input = 0;
+ 
+       /* initialize hardware */
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv,"pre-init");
+ 
+       bttv_risc_init_main(btv);
+       init_bt848(btv);
+ 
+       /* gpio */
+       btwrite(0x00, BT848_GPIO_REG_INP);
+       btwrite(0x00, BT848_GPIO_OUT_EN);
+       if (bttv_verbose)
+               bttv_gpio_tracking(btv,"init");
+ 
+       /* needs to be done before i2c is registered */
+       bttv_init_card1(btv);
+ 
+       /* register i2c + gpio */
+       init_bttv_i2c(btv);
+ 
+       /* some card-specific stuff (needs working i2c) */
+       bttv_init_card2(btv);
+       bttv_init_tuner(btv);
+       init_irqreg(btv);
+ 
+       /* register video4linux + input */
+       if (!bttv_tvcards[btv->c.type].no_video) {
+               bttv_register_video(btv);
+               bt848_bright(btv,32768);
+               bt848_contrast(btv, 27648);
+               bt848_hue(btv,32768);
+               bt848_sat(btv,32768);
+               audio_mute(btv, 1);
+               set_input(btv, 0, btv->tvnorm);
+               bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+               btv->crop[1] = btv->crop[0]; /* current = default */
+               disclaim_vbi_lines(btv);
+               disclaim_video_lines(btv);
+       }
+ 
+       /* add subdevices and autoload dvb-bt8xx if needed */
+       if (bttv_tvcards[btv->c.type].has_dvb) {
+               bttv_sub_add_device(&btv->c, "dvb");
+               request_modules(btv);
+       }
+ 
+       if (!disable_ir) {
+               init_bttv_i2c_ir(btv);
+               bttv_input_init(btv);
+       }
+ 
+       /* everything is fine */
+       bttv_num++;
+       return 0;
+ 
+ fail2:
+       free_irq(btv->c.pci->irq,btv);
+ 
+ fail1:
+       v4l2_device_unregister(&btv->c.v4l2_dev);
+ 
+ fail0:
+       if (btv->bt848_mmio)
+               iounmap(btv->bt848_mmio);
+       release_mem_region(pci_resource_start(btv->c.pci,0),
+                          pci_resource_len(btv->c.pci,0));
+       return result;
+ }
+ 
+ static void __devexit bttv_remove(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
+ 
+       if (bttv_verbose)
+               pr_info("%d: unloading\n", btv->c.nr);
+ 
+       if (bttv_tvcards[btv->c.type].has_dvb)
+               flush_request_modules(btv);
+ 
+       /* shutdown everything (DMA+IRQs) */
+       btand(~15, BT848_GPIO_DMA_CTL);
+       btwrite(0, BT848_INT_MASK);
+       btwrite(~0x0, BT848_INT_STAT);
+       btwrite(0x0, BT848_GPIO_OUT_EN);
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv,"cleanup");
+ 
+       /* tell gpio modules we are leaving ... */
+       btv->shutdown=1;
+       bttv_input_fini(btv);
+       bttv_sub_del_devices(&btv->c);
+ 
+       /* unregister i2c_bus + input */
+       fini_bttv_i2c(btv);
+ 
+       /* unregister video4linux */
+       bttv_unregister_video(btv);
+ 
+       /* free allocated memory */
+       btcx_riscmem_free(btv->c.pci,&btv->main);
+ 
+       /* free ressources */
+       free_irq(btv->c.pci->irq,btv);
+       iounmap(btv->bt848_mmio);
+       release_mem_region(pci_resource_start(btv->c.pci,0),
+                          pci_resource_len(btv->c.pci,0));
+ 
+       v4l2_device_unregister(&btv->c.v4l2_dev);
+       bttvs[btv->c.nr] = NULL;
+       kfree(btv);
+ 
+       return;
+ }
+ 
+ #ifdef CONFIG_PM
+ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
+       struct bttv_buffer_set idle;
+       unsigned long flags;
+ 
+       dprintk("%d: suspend %d\n", btv->c.nr, state.event);
+ 
+       /* stop dma + irqs */
+       spin_lock_irqsave(&btv->s_lock,flags);
+       memset(&idle, 0, sizeof(idle));
+       btv->state.video = btv->curr;
+       btv->state.vbi   = btv->cvbi;
+       btv->state.loop_irq = btv->loop_irq;
+       btv->curr = idle;
+       btv->loop_irq = 0;
+       bttv_buffer_activate_video(btv, &idle);
+       bttv_buffer_activate_vbi(btv, NULL);
+       bttv_set_dma(btv, 0);
+       btwrite(0, BT848_INT_MASK);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+ 
+       /* save bt878 state */
+       btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+       btv->state.gpio_data   = gpio_read();
+ 
+       /* save pci state */
+       pci_save_state(pci_dev);
+       if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+               pci_disable_device(pci_dev);
+               btv->state.disabled = 1;
+       }
+       return 0;
+ }
+ 
+ static int bttv_resume(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct bttv *btv = to_bttv(v4l2_dev);
+       unsigned long flags;
+       int err;
+ 
+       dprintk("%d: resume\n", btv->c.nr);
+ 
+       /* restore pci state */
+       if (btv->state.disabled) {
+               err=pci_enable_device(pci_dev);
+               if (err) {
+                       pr_warn("%d: Can't enable device\n", btv->c.nr);
+                       return err;
+               }
+               btv->state.disabled = 0;
+       }
+       err=pci_set_power_state(pci_dev, PCI_D0);
+       if (err) {
+               pci_disable_device(pci_dev);
+               pr_warn("%d: Can't enable device\n", btv->c.nr);
+               btv->state.disabled = 1;
+               return err;
+       }
+ 
+       pci_restore_state(pci_dev);
+ 
+       /* restore bt878 state */
+       bttv_reinit_bt848(btv);
+       gpio_inout(0xffffff, btv->state.gpio_enable);
+       gpio_write(btv->state.gpio_data);
+ 
+       /* restart dma */
+       spin_lock_irqsave(&btv->s_lock,flags);
+       btv->curr = btv->state.video;
+       btv->cvbi = btv->state.vbi;
+       btv->loop_irq = btv->state.loop_irq;
+       bttv_buffer_activate_video(btv, &btv->curr);
+       bttv_buffer_activate_vbi(btv, btv->cvbi);
+       bttv_set_dma(btv, 0);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       return 0;
+ }
+ #endif
+ 
+ static struct pci_device_id bttv_pci_tbl[] = {
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
+       {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
+       {0,}
+ };
+ 
+ MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
+ 
+ static struct pci_driver bttv_pci_driver = {
+       .name     = "bttv",
+       .id_table = bttv_pci_tbl,
+       .probe    = bttv_probe,
+       .remove   = __devexit_p(bttv_remove),
+ #ifdef CONFIG_PM
+       .suspend  = bttv_suspend,
+       .resume   = bttv_resume,
+ #endif
+ };
+ 
+ static int __init bttv_init_module(void)
+ {
+       int ret;
+ 
+       bttv_num = 0;
+ 
+       pr_info("driver version %s loaded\n", BTTV_VERSION);
+       if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+               gbuffers = 2;
+       if (gbufsize > BTTV_MAX_FBUF)
+               gbufsize = BTTV_MAX_FBUF;
+       gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+       if (bttv_verbose)
+               pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+                       gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+ 
+       bttv_check_chipset();
+ 
+       ret = bus_register(&bttv_sub_bus_type);
+       if (ret < 0) {
+               pr_warn("bus_register error: %d\n", ret);
+               return ret;
+       }
+       ret = pci_register_driver(&bttv_pci_driver);
+       if (ret < 0)
+               bus_unregister(&bttv_sub_bus_type);
+ 
+       return ret;
+ }
+ 
+ static void __exit bttv_cleanup_module(void)
+ {
+       pci_unregister_driver(&bttv_pci_driver);
+       bus_unregister(&bttv_sub_bus_type);
+ }
+ 
+ module_init(bttv_init_module);
+ module_exit(bttv_cleanup_module);
+ 
+ /*
+  * Local variables:
+  * c-basic-offset: 8
+  * End:
+  */
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+  *  cx18 driver initialization and card probing
+  *
+  *  Derived from ivtv-driver.c
+  *
+  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+  *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
+  *
+  *  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
+  */
+ 
+ #include "cx18-driver.h"
+ #include "cx18-io.h"
+ #include "cx18-version.h"
+ #include "cx18-cards.h"
+ #include "cx18-i2c.h"
+ #include "cx18-irq.h"
+ #include "cx18-gpio.h"
+ #include "cx18-firmware.h"
+ #include "cx18-queue.h"
+ #include "cx18-streams.h"
+ #include "cx18-av-core.h"
+ #include "cx18-scb.h"
+ #include "cx18-mailbox.h"
+ #include "cx18-ioctl.h"
+ #include "cx18-controls.h"
+ #include "tuner-xc2028.h"
+ #include <linux/dma-mapping.h>
+ #include <media/tveeprom.h>
+ 
+ /* If you have already X v4l cards, then set this to X. This way
+    the device numbers stay matched. Example: you have a WinTV card
+    without radio and a Compro H900 with. Normally this would give a
+    video1 device together with a radio0 device for the Compro. By
+    setting this to 1 you ensure that radio0 is now also radio1. */
+ int cx18_first_minor;
+ 
+ /* Callback for registering extensions */
+ int (*cx18_ext_init)(struct cx18 *);
+ EXPORT_SYMBOL(cx18_ext_init);
+ 
+ /* add your revision and whatnot here */
+ static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+       {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0,}
+ };
+ 
+ MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+ 
+ static atomic_t cx18_instance = ATOMIC_INIT(0);
+ 
+ /* Parameter declarations */
+ static int cardtype[CX18_MAX_CARDS];
+ static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1 };
+ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1,
+                                    -1, -1, -1, -1, -1, -1, -1, -1 };
+ static unsigned cardtype_c = 1;
+ static unsigned tuner_c = 1;
+ static unsigned radio_c = 1;
+ static char pal[] = "--";
+ static char secam[] = "--";
+ static char ntsc[] = "-";
+ 
+ /* Buffers */
+ static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+ static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+ static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
+ static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+ static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+ static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+ 
+ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+ static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+ static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+ static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+ 
+ static int enc_ts_bufs = -1;
+ static int enc_mpg_bufs = -1;
+ static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
+ static int enc_yuv_bufs = -1;
+ static int enc_vbi_bufs = -1;
+ static int enc_pcm_bufs = -1;
+ 
+ 
+ static int cx18_pci_latency = 1;
+ 
+ static int mmio_ndelay;
+ static int retry_mmio = 1;
+ 
+ int cx18_debug;
+ 
+ module_param_array(tuner, int, &tuner_c, 0644);
+ module_param_array(radio, int, &radio_c, 0644);
+ module_param_array(cardtype, int, &cardtype_c, 0644);
+ module_param_string(pal, pal, sizeof(pal), 0644);
+ module_param_string(secam, secam, sizeof(secam), 0644);
+ module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+ module_param_named(debug, cx18_debug, int, 0644);
+ module_param(mmio_ndelay, int, 0644);
+ module_param(retry_mmio, int, 0644);
+ module_param(cx18_pci_latency, int, 0644);
+ module_param(cx18_first_minor, int, 0644);
+ 
+ module_param(enc_ts_buffers, int, 0644);
+ module_param(enc_mpg_buffers, int, 0644);
+ module_param(enc_idx_buffers, int, 0644);
+ module_param(enc_yuv_buffers, int, 0644);
+ module_param(enc_vbi_buffers, int, 0644);
+ module_param(enc_pcm_buffers, int, 0644);
+ 
+ module_param(enc_ts_bufsize, int, 0644);
+ module_param(enc_mpg_bufsize, int, 0644);
+ module_param(enc_idx_bufsize, int, 0644);
+ module_param(enc_yuv_bufsize, int, 0644);
+ module_param(enc_pcm_bufsize, int, 0644);
+ 
+ module_param(enc_ts_bufs, int, 0644);
+ module_param(enc_mpg_bufs, int, 0644);
+ module_param(enc_idx_bufs, int, 0644);
+ module_param(enc_yuv_bufs, int, 0644);
+ module_param(enc_vbi_bufs, int, 0644);
+ module_param(enc_pcm_bufs, int, 0644);
+ 
+ MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+                       "\t\t\tsee tuner.h for values");
+ MODULE_PARM_DESC(radio,
+                "Enable or disable the radio. Use only if autodetection\n"
+                "\t\t\tfails. 0 = disable, 1 = enable");
+ MODULE_PARM_DESC(cardtype,
+                "Only use this option if your card is not detected properly.\n"
+                "\t\tSpecify card type:\n"
+                "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
+                "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+                "\t\t\t 3 = Compro VideoMate H900\n"
+                "\t\t\t 4 = Yuan MPC718\n"
+                "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+                "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+                "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+                "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
+                "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
+                "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
+                "\t\t\t 0 = Autodetect (default)\n"
+                "\t\t\t-1 = Ignore this card\n\t\t");
+ MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+ MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+ MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+ MODULE_PARM_DESC(debug,
+                "Debug level (bitmask). Default: 0\n"
+                "\t\t\t  1/0x0001: warning\n"
+                "\t\t\t  2/0x0002: info\n"
+                "\t\t\t  4/0x0004: mailbox\n"
+                "\t\t\t  8/0x0008: dma\n"
+                "\t\t\t 16/0x0010: ioctl\n"
+                "\t\t\t 32/0x0020: file\n"
+                "\t\t\t 64/0x0040: i2c\n"
+                "\t\t\t128/0x0080: irq\n"
+                "\t\t\t256/0x0100: high volume\n");
+ MODULE_PARM_DESC(cx18_pci_latency,
+                "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+                "\t\t\tDefault: Yes");
+ MODULE_PARM_DESC(retry_mmio,
+                "(Deprecated) MMIO writes are now always checked and retried\n"
+                "\t\t\tEffectively: 1 [Yes]");
+ MODULE_PARM_DESC(mmio_ndelay,
+                "(Deprecated) MMIO accesses are now never purposely delayed\n"
+                "\t\t\tEffectively: 0 ns");
+ MODULE_PARM_DESC(enc_ts_buffers,
+                "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+ MODULE_PARM_DESC(enc_ts_bufsize,
+                "Size of an encoder TS buffer (kB)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+ MODULE_PARM_DESC(enc_ts_bufs,
+                "Number of encoder TS buffers\n"
+                "\t\t\tDefault is computed from other enc_ts_* parameters");
+ MODULE_PARM_DESC(enc_mpg_buffers,
+                "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+ MODULE_PARM_DESC(enc_mpg_bufsize,
+                "Size of an encoder MPG buffer (kB)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+ MODULE_PARM_DESC(enc_mpg_bufs,
+                "Number of encoder MPG buffers\n"
+                "\t\t\tDefault is computed from other enc_mpg_* parameters");
+ MODULE_PARM_DESC(enc_idx_buffers,
+                "(Deprecated) Encoder IDX buffer memory (MB)\n"
+                "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
+                "\t\t\tDefault: 1 [Enabled]");
+ MODULE_PARM_DESC(enc_idx_bufsize,
+                "Size of an encoder IDX buffer (kB)\n"
+                "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
+                "\t\t\t(multiples of size required for 64 index entries)\n"
+                "\t\t\tDefault: 2");
+ MODULE_PARM_DESC(enc_idx_bufs,
+                "Number of encoder IDX buffers\n"
+                "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
+ MODULE_PARM_DESC(enc_yuv_buffers,
+                "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+ MODULE_PARM_DESC(enc_yuv_bufsize,
+                "Size of an encoder YUV buffer (kB)\n"
+                "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+                "\t\t\t(multiples of size required for 32 screen lines)\n"
+                "\t\t\tDefault: 102");
+ MODULE_PARM_DESC(enc_yuv_bufs,
+                "Number of encoder YUV buffers\n"
+                "\t\t\tDefault is computed from other enc_yuv_* parameters");
+ MODULE_PARM_DESC(enc_vbi_buffers,
+                "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+ MODULE_PARM_DESC(enc_vbi_bufs,
+                "Number of encoder VBI buffers\n"
+                "\t\t\tDefault is computed from enc_vbi_buffers");
+ MODULE_PARM_DESC(enc_pcm_buffers,
+                "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+ MODULE_PARM_DESC(enc_pcm_bufsize,
+                "Size of an encoder PCM buffer (kB)\n"
+                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+ MODULE_PARM_DESC(enc_pcm_bufs,
+                "Number of encoder PCM buffers\n"
+                "\t\t\tDefault is computed from other enc_pcm_* parameters");
+ 
+ MODULE_PARM_DESC(cx18_first_minor,
+                "Set device node number assigned to first card");
+ 
+ MODULE_AUTHOR("Hans Verkuil");
+ MODULE_DESCRIPTION("CX23418 driver");
+ MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+ MODULE_LICENSE("GPL");
+ 
+ MODULE_VERSION(CX18_VERSION);
+ 
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
+ 
+       /* Make sure cx18-alsa module is loaded */
+       request_module("cx18-alsa");
+ 
+       /* Initialize cx18-alsa for this instance of the cx18 device */
+       if (cx18_ext_init != NULL)
+               cx18_ext_init(dev);
+ }
+ 
+ static void request_modules(struct cx18 *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_modules(struct cx18 *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ 
+ /* Generic utility functions */
+ int cx18_msleep_timeout(unsigned int msecs, int intr)
+ {
+       long int timeout = msecs_to_jiffies(msecs);
+       int sig;
+ 
+       do {
+               set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+               timeout = schedule_timeout(timeout);
+               sig = intr ? signal_pending(current) : 0;
+       } while (!sig && timeout);
+       return sig;
+ }
+ 
+ /* Release ioremapped memory */
+ static void cx18_iounmap(struct cx18 *cx)
+ {
+       if (cx == NULL)
+               return;
+ 
+       /* Release io memory */
+       if (cx->enc_mem != NULL) {
+               CX18_DEBUG_INFO("releasing enc_mem\n");
+               iounmap(cx->enc_mem);
+               cx->enc_mem = NULL;
+       }
+ }
+ 
+ static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
+ {
+       int i;
+ 
+       CX18_INFO("eeprom dump:\n");
+       for (i = 0; i < len; i++) {
+               if (0 == (i % 16))
+                       CX18_INFO("eeprom %02x:", i);
+               printk(KERN_CONT " %02x", eedata[i]);
+               if (15 == (i % 16))
+                       printk(KERN_CONT "\n");
+       }
+ }
+ 
+ /* Hauppauge card? get values from tveeprom */
+ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+ {
+       struct i2c_client c;
+       u8 eedata[256];
+ 
+       memset(&c, 0, sizeof(c));
+       strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+       c.adapter = &cx->i2c_adap[0];
+       c.addr = 0xA0 >> 1;
+ 
+       memset(tv, 0, sizeof(*tv));
+       if (tveeprom_read(&c, eedata, sizeof(eedata)))
+               return;
+ 
+       switch (cx->card->type) {
+       case CX18_CARD_HVR_1600_ESMT:
+       case CX18_CARD_HVR_1600_SAMSUNG:
+       case CX18_CARD_HVR_1600_S5H1411:
+               tveeprom_hauppauge_analog(&c, tv, eedata);
+               break;
+       case CX18_CARD_YUAN_MPC718:
+       case CX18_CARD_GOTVIEW_PCI_DVD3:
+               tv->model = 0x718;
+               cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+               CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
+                         eedata[2], eedata[1], eedata[4], eedata[3]);
+               break;
+       default:
+               tv->model = 0xffffffff;
+               cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+               break;
+       }
+ }
+ 
+ static void cx18_process_eeprom(struct cx18 *cx)
+ {
+       struct tveeprom tv;
+ 
+       cx18_read_eeprom(cx, &tv);
+ 
+       /* Many thanks to Steven Toth from Hauppauge for providing the
+          model numbers */
+       /* Note: the Samsung memory models cannot be reliably determined
+          from the model number. Use the cardtype module option if you
+          have one of these preproduction models. */
+       switch (tv.model) {
+       case 74301: /* Retail models */
+       case 74321:
+       case 74351: /* OEM models */
+       case 74361:
+               /* Digital side is s5h1411/tda18271 */
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
+               break;
+       case 74021: /* Retail models */
+       case 74031:
+       case 74041:
+       case 74141:
+       case 74541: /* OEM models */
+       case 74551:
+       case 74591:
+       case 74651:
+       case 74691:
+       case 74751:
+       case 74891:
+               /* Digital side is s5h1409/mxl5005s */
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+               break;
+       case 0x718:
+               return;
+       case 0xffffffff:
+               CX18_INFO("Unknown EEPROM encoding\n");
+               return;
+       case 0:
+               CX18_ERR("Invalid EEPROM\n");
+               return;
+       default:
+               CX18_ERR("Unknown model %d, defaulting to original HVR-1600 "
+                        "(cardtype=1)\n", tv.model);
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+               break;
+       }
+ 
+       cx->v4l2_cap = cx->card->v4l2_capabilities;
+       cx->card_name = cx->card->name;
+       cx->card_i2c = cx->card->i2c;
+ 
+       CX18_INFO("Autodetected %s\n", cx->card_name);
+ 
+       if (tv.tuner_type == TUNER_ABSENT)
+               CX18_ERR("tveeprom cannot autodetect tuner!\n");
+ 
+       if (cx->options.tuner == -1)
+               cx->options.tuner = tv.tuner_type;
+       if (cx->options.radio == -1)
+               cx->options.radio = (tv.has_radio != 0);
+ 
+       if (cx->std != 0)
+               /* user specified tuner standard */
+               return;
+ 
+       /* autodetect tuner standard */
+ #define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B  | V4L2_STD_GH | \
+                                  V4L2_STD_MN | \
+                                  V4L2_STD_PAL_I | \
+                                  V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
+                                  V4L2_STD_DK)
+       if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
+                                       == TVEEPROM_TUNER_FORMAT_ALL) {
+               CX18_DEBUG_INFO("Worldwide tuner detected\n");
+               cx->std = V4L2_STD_ALL;
+       } else if (tv.tuner_formats & V4L2_STD_PAL) {
+               CX18_DEBUG_INFO("PAL tuner detected\n");
+               cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+       } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+               CX18_DEBUG_INFO("NTSC tuner detected\n");
+               cx->std |= V4L2_STD_NTSC_M;
+       } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+               CX18_DEBUG_INFO("SECAM tuner detected\n");
+               cx->std |= V4L2_STD_SECAM_L;
+       } else {
+               CX18_INFO("No tuner detected, default to NTSC-M\n");
+               cx->std |= V4L2_STD_NTSC_M;
+       }
+ }
+ 
+ static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+ {
+       switch (pal[0]) {
+       case '6':
+               return V4L2_STD_PAL_60;
+       case 'b':
+       case 'B':
+       case 'g':
+       case 'G':
+               return V4L2_STD_PAL_BG;
+       case 'h':
+       case 'H':
+               return V4L2_STD_PAL_H;
+       case 'n':
+       case 'N':
+               if (pal[1] == 'c' || pal[1] == 'C')
+                       return V4L2_STD_PAL_Nc;
+               return V4L2_STD_PAL_N;
+       case 'i':
+       case 'I':
+               return V4L2_STD_PAL_I;
+       case 'd':
+       case 'D':
+       case 'k':
+       case 'K':
+               return V4L2_STD_PAL_DK;
+       case 'M':
+       case 'm':
+               return V4L2_STD_PAL_M;
+       case '-':
+               break;
+       default:
+               CX18_WARN("pal= argument not recognised\n");
+               return 0;
+       }
+ 
+       switch (secam[0]) {
+       case 'b':
+       case 'B':
+       case 'g':
+       case 'G':
+       case 'h':
+       case 'H':
+               return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+       case 'd':
+       case 'D':
+       case 'k':
+       case 'K':
+               return V4L2_STD_SECAM_DK;
+       case 'l':
+       case 'L':
+               if (secam[1] == 'C' || secam[1] == 'c')
+                       return V4L2_STD_SECAM_LC;
+               return V4L2_STD_SECAM_L;
+       case '-':
+               break;
+       default:
+               CX18_WARN("secam= argument not recognised\n");
+               return 0;
+       }
+ 
+       switch (ntsc[0]) {
+       case 'm':
+       case 'M':
+               return V4L2_STD_NTSC_M;
+       case 'j':
+       case 'J':
+               return V4L2_STD_NTSC_M_JP;
+       case 'k':
+       case 'K':
+               return V4L2_STD_NTSC_M_KR;
+       case '-':
+               break;
+       default:
+               CX18_WARN("ntsc= argument not recognised\n");
+               return 0;
+       }
+ 
+       /* no match found */
+       return 0;
+ }
+ 
+ static void cx18_process_options(struct cx18 *cx)
+ {
+       int i, j;
+ 
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+       cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+ 
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+       cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+ 
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+       cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+ 
+       /* Ensure stream_buffers & stream_buf_size are valid */
+       for (i = 0; i < CX18_MAX_STREAMS; i++) {
+               if (cx->stream_buffers[i] == 0 ||     /* User said 0 buffers */
+                   cx->options.megabytes[i] <= 0 ||  /* User said 0 MB total */
+                   cx->stream_buf_size[i] <= 0) {    /* User said buf size 0 */
+                       cx->options.megabytes[i] = 0;
+                       cx->stream_buffers[i] = 0;
+                       cx->stream_buf_size[i] = 0;
+                       continue;
+               }
+               /*
+                * YUV is a special case where the stream_buf_size needs to be
+                * an integral multiple of 33.75 kB (storage for 32 screens
+                * lines to maintain alignment in case of lost buffers).
+                *
+                * IDX is a special case where the stream_buf_size should be
+                * an integral multiple of 1.5 kB (storage for 64 index entries
+                * to maintain alignment in case of lost buffers).
+                *
+                */
+               if (i == CX18_ENC_STREAM_TYPE_YUV) {
+                       cx->stream_buf_size[i] *= 1024;
+                       cx->stream_buf_size[i] -=
+                          (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+ 
+                       if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+                               cx->stream_buf_size[i] =
+                                               CX18_UNIT_ENC_YUV_BUFSIZE;
+               } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
+                       cx->stream_buf_size[i] *= 1024;
+                       cx->stream_buf_size[i] -=
+                          (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
+ 
+                       if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
+                               cx->stream_buf_size[i] =
+                                               CX18_UNIT_ENC_IDX_BUFSIZE;
+               }
+               /*
+                * YUV and IDX are special cases where the stream_buf_size is
+                * now in bytes.
+                * VBI is a special case where the stream_buf_size is fixed
+                * and already in bytes
+                */
+               if (i == CX18_ENC_STREAM_TYPE_VBI ||
+                   i == CX18_ENC_STREAM_TYPE_YUV ||
+                   i == CX18_ENC_STREAM_TYPE_IDX) {
+                       if (cx->stream_buffers[i] < 0) {
+                               cx->stream_buffers[i] =
+                                       cx->options.megabytes[i] * 1024 * 1024
+                                       / cx->stream_buf_size[i];
+                       } else {
+                               /* N.B. This might round down to 0 */
+                               cx->options.megabytes[i] =
+                                       cx->stream_buffers[i]
+                                       * cx->stream_buf_size[i]/(1024 * 1024);
+                       }
+               } else {
+                       /* All other streams have stream_buf_size in kB here */
+                       if (cx->stream_buffers[i] < 0) {
+                               cx->stream_buffers[i] =
+                                               cx->options.megabytes[i] * 1024
+                                               / cx->stream_buf_size[i];
+                       } else {
+                               /* N.B. This might round down to 0 */
+                               cx->options.megabytes[i] =
+                                               cx->stream_buffers[i]
+                                               * cx->stream_buf_size[i] / 1024;
+                       }
+                       /* convert from kB to bytes */
+                       cx->stream_buf_size[i] *= 1024;
+               }
+               CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+                               "%d bytes\n", i, cx->options.megabytes[i],
+                               cx->stream_buffers[i], cx->stream_buf_size[i]);
+       }
+ 
+       cx->options.cardtype = cardtype[cx->instance];
+       cx->options.tuner = tuner[cx->instance];
+       cx->options.radio = radio[cx->instance];
+ 
+       cx->std = cx18_parse_std(cx);
+       if (cx->options.cardtype == -1) {
+               CX18_INFO("Ignore card\n");
+               return;
+       }
+       cx->card = cx18_get_card(cx->options.cardtype - 1);
+       if (cx->card)
+               CX18_INFO("User specified %s card\n", cx->card->name);
+       else if (cx->options.cardtype != 0)
+               CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+       if (cx->card == NULL) {
+               if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+                       cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+                       CX18_INFO("Autodetected Hauppauge card\n");
+               }
+       }
+       if (cx->card == NULL) {
+               for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+                       if (cx->card->pci_list == NULL)
+                               continue;
+                       for (j = 0; cx->card->pci_list[j].device; j++) {
+                               if (cx->pci_dev->device !=
+                                   cx->card->pci_list[j].device)
+                                       continue;
+                               if (cx->pci_dev->subsystem_vendor !=
+                                   cx->card->pci_list[j].subsystem_vendor)
+                                       continue;
+                               if (cx->pci_dev->subsystem_device !=
+                                   cx->card->pci_list[j].subsystem_device)
+                                       continue;
+                               CX18_INFO("Autodetected %s card\n", cx->card->name);
+                               goto done;
+                       }
+               }
+       }
+ done:
+ 
+       if (cx->card == NULL) {
+               cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+               CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+                        cx->pci_dev->vendor, cx->pci_dev->device);
+               CX18_ERR("              subsystem vendor/device: [%04x:%04x]\n",
+                        cx->pci_dev->subsystem_vendor,
+                        cx->pci_dev->subsystem_device);
+               CX18_ERR("Defaulting to %s card\n", cx->card->name);
+               CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+               CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+               CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+       }
+       cx->v4l2_cap = cx->card->v4l2_capabilities;
+       cx->card_name = cx->card->name;
+       cx->card_i2c = cx->card->i2c;
+ }
+ 
+ static int __devinit cx18_create_in_workq(struct cx18 *cx)
+ {
+       snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
+                cx->v4l2_dev.name);
+       cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0);
+       if (cx->in_work_queue == NULL) {
+               CX18_ERR("Unable to create incoming mailbox handler thread\n");
+               return -ENOMEM;
+       }
+       return 0;
+ }
+ 
+ static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+               cx->in_work_order[i].cx = cx;
+               cx->in_work_order[i].str = cx->epu_debug_str;
+               INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
+       }
+ }
+ 
+ /* Precondition: the cx18 structure has been memset to 0. Only
+    the dev and instance fields have been filled in.
+    No assumptions on the card type may be made here (see cx18_init_struct2
+    for that).
+  */
+ static int __devinit cx18_init_struct1(struct cx18 *cx)
+ {
+       int ret;
+ 
+       cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+ 
+       mutex_init(&cx->serialize_lock);
+       mutex_init(&cx->gpio_lock);
+       mutex_init(&cx->epu2apu_mb_lock);
+       mutex_init(&cx->epu2cpu_mb_lock);
+ 
+       ret = cx18_create_in_workq(cx);
+       if (ret)
+               return ret;
+ 
+       cx18_init_in_work_orders(cx);
+ 
+       /* start counting open_id at 1 */
+       cx->open_id = 1;
+ 
+       /* Initial settings */
+       cx->cxhdl.port = CX2341X_PORT_MEMORY;
+       cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+       cx->cxhdl.ops = &cx18_cxhdl_ops;
+       cx->cxhdl.func = cx18_api_func;
+       cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+       ret = cx2341x_handler_init(&cx->cxhdl, 50);
+       if (ret)
+               return ret;
+       cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
+ 
+       cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
+       cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
+       cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
+               (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
+               (cx->cxhdl.video_median_filter_type->cur.val << 2);
+ 
+       init_waitqueue_head(&cx->cap_w);
+       init_waitqueue_head(&cx->mb_apu_waitq);
+       init_waitqueue_head(&cx->mb_cpu_waitq);
+       init_waitqueue_head(&cx->dma_waitq);
+ 
+       /* VBI */
+       cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+       cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+ 
+       /* IVTV style VBI insertion into MPEG streams */
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+       list_add(&cx->vbi.sliced_mpeg_buf.list,
+                &cx->vbi.sliced_mpeg_mdl.buf_list);
+       return 0;
+ }
+ 
+ /* Second initialization part. Here the card type has been
+    autodetected. */
+ static void __devinit cx18_init_struct2(struct cx18 *cx)
+ {
+       int i;
+ 
+       for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+               if (cx->card->video_inputs[i].video_type == 0)
+                       break;
+       cx->nof_inputs = i;
+       for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+               if (cx->card->audio_inputs[i].audio_type == 0)
+                       break;
+       cx->nof_audio_inputs = i;
+ 
+       /* Find tuner input */
+       for (i = 0; i < cx->nof_inputs; i++) {
+               if (cx->card->video_inputs[i].video_type ==
+                               CX18_CARD_INPUT_VID_TUNER)
+                       break;
+       }
+       if (i == cx->nof_inputs)
+               i = 0;
+       cx->active_input = i;
+       cx->audio_input = cx->card->video_inputs[i].audio_index;
+ }
+ 
+ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
+                         const struct pci_device_id *pci_id)
+ {
+       u16 cmd;
+       unsigned char pci_latency;
+ 
+       CX18_DEBUG_INFO("Enabling pci device\n");
+ 
+       if (pci_enable_device(pci_dev)) {
+               CX18_ERR("Can't enable device %d!\n", cx->instance);
+               return -EIO;
+       }
+       if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
+               CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
+               return -EIO;
+       }
+       if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+               CX18_ERR("Cannot request encoder memory region, card %d\n",
+                        cx->instance);
+               return -EIO;
+       }
+ 
+       /* Enable bus mastering and memory mapped IO for the CX23418 */
+       pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
+       cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+       pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
+ 
+       cx->card_rev = pci_dev->revision;
+       pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+ 
+       if (pci_latency < 64 && cx18_pci_latency) {
+               CX18_INFO("Unreasonably low latency timer, "
+                              "setting to 64 (was %d)\n", pci_latency);
+               pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
+               pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+       }
+ 
+       CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+                  "irq: %d, latency: %d, memory: 0x%llx\n",
+                  cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
+                  PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
+                  cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
+ 
+       return 0;
+ }
+ 
+ static void cx18_init_subdevs(struct cx18 *cx)
+ {
+       u32 hw = cx->card->hw_all;
+       u32 device;
+       int i;
+ 
+       for (i = 0, device = 1; i < 32; i++, device <<= 1) {
+ 
+               if (!(device & hw))
+                       continue;
+ 
+               switch (device) {
+               case CX18_HW_DVB:
+               case CX18_HW_TVEEPROM:
+                       /* These subordinate devices do not use probing */
+                       cx->hw_flags |= device;
+                       break;
+               case CX18_HW_418_AV:
+                       /* The A/V decoder gets probed earlier to set PLLs */
+                       /* Just note that the card uses it (i.e. has analog) */
+                       cx->hw_flags |= device;
+                       break;
+               case CX18_HW_GPIO_RESET_CTRL:
+                       /*
+                        * The Reset Controller gets probed and added to
+                        * hw_flags earlier for i2c adapter/bus initialization
+                        */
+                       break;
+               case CX18_HW_GPIO_MUX:
+                       if (cx18_gpio_register(cx, device) == 0)
+                               cx->hw_flags |= device;
+                       break;
+               default:
+                       if (cx18_i2c_register(cx, i) == 0)
+                               cx->hw_flags |= device;
+                       break;
+               }
+       }
+ 
+       if (cx->hw_flags & CX18_HW_418_AV)
+               cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+ 
+       if (cx->card->hw_muxer != 0)
+               cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
+ }
+ 
+ static int __devinit cx18_probe(struct pci_dev *pci_dev,
+                               const struct pci_device_id *pci_id)
+ {
+       int retval = 0;
+       int i;
+       u32 devtype;
+       struct cx18 *cx;
+ 
+       /* FIXME - module parameter arrays constrain max instances */
+       i = atomic_inc_return(&cx18_instance) - 1;
+       if (i >= CX18_MAX_CARDS) {
+               printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
+                      "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
+               return -ENOMEM;
+       }
+ 
+       cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+       if (cx == NULL) {
+               printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
+                      i);
+               return -ENOMEM;
+       }
+       cx->pci_dev = pci_dev;
+       cx->instance = i;
+ 
+       retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
+       if (retval) {
+               printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
+                      "\n", cx->instance);
+               kfree(cx);
+               return retval;
+       }
+       snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
+                cx->instance);
+       CX18_INFO("Initializing card %d\n", cx->instance);
+ 
+       cx18_process_options(cx);
+       if (cx->options.cardtype == -1) {
+               retval = -ENODEV;
+               goto err;
+       }
+ 
+       retval = cx18_init_struct1(cx);
+       if (retval)
+               goto err;
+ 
+       CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr);
+ 
+       /* PCI Device Setup */
+       retval = cx18_setup_pci(cx, pci_dev, pci_id);
+       if (retval != 0)
+               goto free_workqueues;
+ 
+       /* map io memory */
+       CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+                  (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+       cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+                                      CX18_MEM_SIZE);
+       if (!cx->enc_mem) {
+               CX18_ERR("ioremap failed. Can't get a window into CX23418 "
+                        "memory and register space\n");
+               CX18_ERR("Each capture card with a CX23418 needs 64 MB of "
+                        "vmalloc address space for the window\n");
+               CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+               CX18_ERR("Use the vmalloc= kernel command line option to set "
+                        "VmallocTotal to a larger value\n");
+               retval = -ENOMEM;
+               goto free_mem;
+       }
+       cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+       devtype = cx18_read_reg(cx, 0xC72028);
+       switch (devtype & 0xff000000) {
+       case 0xff000000:
+               CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+               break;
+       case 0x01000000:
+               CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+               break;
+       default:
+               CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+               break;
+       }
+ 
+       cx18_init_power(cx, 1);
+       cx18_init_memory(cx);
+ 
+       cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
+       cx18_init_scb(cx);
+ 
+       cx18_gpio_init(cx);
+ 
+       /* Initialize integrated A/V decoder early to set PLLs, just in case */
+       retval = cx18_av_probe(cx);
+       if (retval) {
+               CX18_ERR("Could not register A/V decoder subdevice\n");
+               goto free_map;
+       }
+ 
+       /* Initialize GPIO Reset Controller to do chip resets during i2c init */
+       if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+               if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+                       CX18_WARN("Could not register GPIO reset controller"
+                                 "subdevice; proceeding anyway.\n");
+               else
+                       cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+       }
+ 
+       /* active i2c  */
+       CX18_DEBUG_INFO("activating i2c...\n");
+       retval = init_cx18_i2c(cx);
+       if (retval) {
+               CX18_ERR("Could not initialize i2c\n");
+               goto free_map;
+       }
+ 
+       if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+               /* Based on the model number the cardtype may be changed.
+                  The PCI IDs are not always reliable. */
+               const struct cx18_card *orig_card = cx->card;
+               cx18_process_eeprom(cx);
+ 
+               if (cx->card != orig_card) {
+                       /* Changed the cardtype; re-reset the I2C chips */
+                       cx18_gpio_init(cx);
+                       cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+                                       core, reset, (u32) CX18_GPIO_RESET_I2C);
+               }
+       }
+       if (cx->card->comment)
+               CX18_INFO("%s", cx->card->comment);
+       if (cx->card->v4l2_capabilities == 0) {
+               retval = -ENODEV;
+               goto free_i2c;
+       }
+       cx18_init_memory(cx);
+       cx18_init_scb(cx);
+ 
+       /* Register IRQ */
+       retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
+                            IRQF_SHARED | IRQF_DISABLED,
+                            cx->v4l2_dev.name, (void *)cx);
+       if (retval) {
+               CX18_ERR("Failed to register irq %d\n", retval);
+               goto free_i2c;
+       }
+ 
+       if (cx->std == 0)
+               cx->std = V4L2_STD_NTSC_M;
+ 
+       if (cx->options.tuner == -1) {
+               for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+                       if ((cx->std & cx->card->tuners[i].std) == 0)
+                               continue;
+                       cx->options.tuner = cx->card->tuners[i].tuner;
+                       break;
+               }
+       }
+       /* if no tuner was found, then pick the first tuner in the card list */
+       if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+               cx->std = cx->card->tuners[0].std;
+               if (cx->std & V4L2_STD_PAL)
+                       cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+               else if (cx->std & V4L2_STD_NTSC)
+                       cx->std = V4L2_STD_NTSC_M;
+               else if (cx->std & V4L2_STD_SECAM)
+                       cx->std = V4L2_STD_SECAM_L;
+               cx->options.tuner = cx->card->tuners[0].tuner;
+       }
+       if (cx->options.radio == -1)
+               cx->options.radio = (cx->card->radio_input.audio_type != 0);
+ 
+       /* The card is now fully identified, continue with card-specific
+          initialization. */
+       cx18_init_struct2(cx);
+ 
+       cx18_init_subdevs(cx);
+ 
+       if (cx->std & V4L2_STD_525_60)
+               cx->is_60hz = 1;
+       else
+               cx->is_50hz = 1;
+ 
+       cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
+ 
+       if (cx->options.radio > 0)
+               cx->v4l2_cap |= V4L2_CAP_RADIO;
+ 
+       if (cx->options.tuner > -1) {
+               struct tuner_setup setup;
+ 
+               setup.addr = ADDR_UNSET;
+               setup.type = cx->options.tuner;
+               setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
+               if (cx->options.radio > 0)
+                       setup.mode_mask |= T_RADIO;
+               setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+                       cx18_reset_tuner_gpio : NULL;
+               cx18_call_all(cx, tuner, s_type_addr, &setup);
+               if (setup.type == TUNER_XC2028) {
+                       static struct xc2028_ctrl ctrl = {
+                               .fname = XC2028_DEFAULT_FIRMWARE,
+                               .max_len = 64,
+                       };
+                       struct v4l2_priv_tun_config cfg = {
+                               .tuner = cx->options.tuner,
+                               .priv = &ctrl,
+                       };
+                       cx18_call_all(cx, tuner, s_config, &cfg);
+               }
+       }
+ 
+       /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+          are not. */
+       cx->tuner_std = cx->std;
+       if (cx->std == V4L2_STD_ALL)
+               cx->std = V4L2_STD_NTSC_M;
+ 
+       retval = cx18_streams_setup(cx);
+       if (retval) {
+               CX18_ERR("Error %d setting up streams\n", retval);
+               goto free_irq;
+       }
+       retval = cx18_streams_register(cx);
+       if (retval) {
+               CX18_ERR("Error %d registering devices\n", retval);
+               goto free_streams;
+       }
+ 
+       CX18_INFO("Initialized card: %s\n", cx->card_name);
+ 
+       /* Load cx18 submodules (cx18-alsa) */
+       request_modules(cx);
+       return 0;
+ 
+ free_streams:
+       cx18_streams_cleanup(cx, 1);
+ free_irq:
+       free_irq(cx->pci_dev->irq, (void *)cx);
+ free_i2c:
+       exit_cx18_i2c(cx);
+ free_map:
+       cx18_iounmap(cx);
+ free_mem:
+       release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+ free_workqueues:
+       destroy_workqueue(cx->in_work_queue);
+ err:
+       if (retval == 0)
+               retval = -ENODEV;
+       CX18_ERR("Error %d on initialization\n", retval);
+ 
+       v4l2_device_unregister(&cx->v4l2_dev);
+       kfree(cx);
+       return retval;
+ }
+ 
+ int cx18_init_on_first_open(struct cx18 *cx)
+ {
+       int video_input;
+       int fw_retry_count = 3;
+       struct v4l2_frequency vf;
+       struct cx18_open_id fh;
+       v4l2_std_id std;
+ 
+       fh.cx = cx;
+ 
+       if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+               return -ENXIO;
+ 
+       if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+               return 0;
+ 
+       while (--fw_retry_count > 0) {
+               /* load firmware */
+               if (cx18_firmware_init(cx) == 0)
+                       break;
+               if (fw_retry_count > 1)
+                       CX18_WARN("Retry loading firmware\n");
+       }
+ 
+       if (fw_retry_count == 0) {
+               set_bit(CX18_F_I_FAILED, &cx->i_flags);
+               return -ENXIO;
+       }
+       set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+ 
+       /*
+        * Init the firmware twice to work around a silicon bug
+        * with the digital TS.
+        *
+        * The second firmware load requires us to normalize the APU state,
+        * or the audio for the first analog capture will be badly incorrect.
+        *
+        * I can't seem to call APU_RESETAI and have it succeed without the
+        * APU capturing audio, so we start and stop it here to do the reset
+        */
+ 
+       /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+       cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+       cx18_vapi(cx, CX18_APU_RESETAI, 0);
+       cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+ 
+       fw_retry_count = 3;
+       while (--fw_retry_count > 0) {
+               /* load firmware */
+               if (cx18_firmware_init(cx) == 0)
+                       break;
+               if (fw_retry_count > 1)
+                       CX18_WARN("Retry loading firmware\n");
+       }
+ 
+       if (fw_retry_count == 0) {
+               set_bit(CX18_F_I_FAILED, &cx->i_flags);
+               return -ENXIO;
+       }
+ 
+       /*
+        * The second firmware load requires us to normalize the APU state,
+        * or the audio for the first analog capture will be badly incorrect.
+        *
+        * I can't seem to call APU_RESETAI and have it succeed without the
+        * APU capturing audio, so we start and stop it here to do the reset
+        */
+ 
+       /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+       cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+       cx18_vapi(cx, CX18_APU_RESETAI, 0);
+       cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+ 
+       /* Init the A/V decoder, if it hasn't been already */
+       v4l2_subdev_call(cx->sd_av, core, load_fw);
+ 
+       vf.tuner = 0;
+       vf.type = V4L2_TUNER_ANALOG_TV;
+       vf.frequency = 6400; /* the tuner 'baseline' frequency */
+ 
+       /* Set initial frequency. For PAL/SECAM broadcasts no
+          'default' channel exists AFAIK. */
+       if (cx->std == V4L2_STD_NTSC_M_JP)
+               vf.frequency = 1460;    /* ch. 1 91250*16/1000 */
+       else if (cx->std & V4L2_STD_NTSC_M)
+               vf.frequency = 1076;    /* ch. 4 67250*16/1000 */
+ 
+       video_input = cx->active_input;
+       cx->active_input++;     /* Force update of input */
+       cx18_s_input(NULL, &fh, video_input);
+ 
+       /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+          in one place. */
+       cx->std++;              /* Force full standard initialization */
+       std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
+       cx18_s_std(NULL, &fh, &std);
+       cx18_s_frequency(NULL, &fh, &vf);
+       return 0;
+ }
+ 
+ static void cx18_cancel_in_work_orders(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
+               cancel_work_sync(&cx->in_work_order[i].work);
+ }
+ 
+ static void cx18_cancel_out_work_orders(struct cx18 *cx)
+ {
+       int i;
+       for (i = 0; i < CX18_MAX_STREAMS; i++)
+               if (&cx->streams[i].video_dev != NULL)
+                       cancel_work_sync(&cx->streams[i].out_work_order);
+ }
+ 
+ static void cx18_remove(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct cx18 *cx = to_cx18(v4l2_dev);
+       int i;
+ 
+       CX18_DEBUG_INFO("Removing Card\n");
+ 
+       flush_request_modules(cx);
+ 
+       /* Stop all captures */
+       CX18_DEBUG_INFO("Stopping all streams\n");
+       if (atomic_read(&cx->tot_capturing) > 0)
+               cx18_stop_all_captures(cx);
+ 
+       /* Stop interrupts that cause incoming work to be queued */
+       cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ 
+       /* Incoming work can cause outgoing work, so clean up incoming first */
+       cx18_cancel_in_work_orders(cx);
+       cx18_cancel_out_work_orders(cx);
+ 
+       /* Stop ack interrupts that may have been needed for work to finish */
+       cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+ 
+       cx18_halt_firmware(cx);
+ 
+       destroy_workqueue(cx->in_work_queue);
+ 
+       cx18_streams_cleanup(cx, 1);
+ 
+       exit_cx18_i2c(cx);
+ 
+       free_irq(cx->pci_dev->irq, (void *)cx);
+ 
+       cx18_iounmap(cx);
+ 
+       release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+ 
+       pci_disable_device(cx->pci_dev);
+ 
+       if (cx->vbi.sliced_mpeg_data[0] != NULL)
+               for (i = 0; i < CX18_VBI_FRAMES; i++)
+                       kfree(cx->vbi.sliced_mpeg_data[i]);
+ 
+       v4l2_ctrl_handler_free(&cx->av_state.hdl);
+ 
+       CX18_INFO("Removed %s\n", cx->card_name);
+ 
+       v4l2_device_unregister(v4l2_dev);
+       kfree(cx);
+ }
+ 
+ 
+ /* define a pci_driver for card detection */
+ static struct pci_driver cx18_pci_driver = {
+       .name =     "cx18",
+       .id_table = cx18_pci_tbl,
+       .probe =    cx18_probe,
+       .remove =   cx18_remove,
+ };
+ 
+ static int __init module_start(void)
+ {
+       printk(KERN_INFO "cx18:  Start initialization, version %s\n",
+              CX18_VERSION);
+ 
+       /* Validate parameters */
+       if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+               printk(KERN_ERR "cx18:  Exiting, cx18_first_minor must be between 0 and %d\n",
+                    CX18_MAX_CARDS - 1);
+               return -1;
+       }
+ 
+       if (cx18_debug < 0 || cx18_debug > 511) {
+               cx18_debug = 0;
+               printk(KERN_INFO "cx18:   Debug value must be >= 0 and <= 511!\n");
+       }
+ 
+       if (pci_register_driver(&cx18_pci_driver)) {
+               printk(KERN_ERR "cx18:   Error detecting PCI card\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "cx18:  End initialization\n");
+       return 0;
+ }
+ 
+ static void __exit module_cleanup(void)
+ {
+       pci_unregister_driver(&cx18_pci_driver);
+ }
+ 
+ module_init(module_start);
+ module_exit(module_cleanup);
+ MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
 
--- /dev/null
 -      flush_work_sync(&dev->cx25840_work);
 -      flush_work_sync(&dev->ir_rx_work);
 -      flush_work_sync(&dev->ir_tx_work);
+ /*
+  *  Driver for the Conexant CX23885/7/8 PCIe bridge
+  *
+  *  Infrared remote control input device
+  *
+  *  Most of this file is
+  *
+  *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
+  *
+  *  However, the cx23885_input_{init,fini} functions contained herein are
+  *  derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+  *
+  *  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+  *  Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+  *                   Markus Rechberger <mrechberger@gmail.com>
+  *                   Mauro Carvalho Chehab <mchehab@infradead.org>
+  *                   Sascha Sommer <saschasommer@freenet.de>
+  *  Copyright (C) 2004, 2005 Chris Pascoe
+  *  Copyright (C) 2003, 2004 Gerd Knorr
+  *  Copyright (C) 2003 Pavel Machek
+  *
+  *  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., 51 Franklin Street, Fifth Floor, Boston, MA
+  *  02110-1301, USA.
+  */
+ 
+ #include <linux/slab.h>
+ #include <media/rc-core.h>
+ #include <media/v4l2-subdev.h>
+ 
+ #include "cx23885.h"
+ 
+ #define MODULE_NAME "cx23885"
+ 
+ static void cx23885_input_process_measurements(struct cx23885_dev *dev,
+                                              bool overrun)
+ {
+       struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
+ 
+       ssize_t num;
+       int count, i;
+       bool handle = false;
+       struct ir_raw_event ir_core_event[64];
+ 
+       do {
+               num = 0;
+               v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+                                sizeof(ir_core_event), &num);
+ 
+               count = num / sizeof(struct ir_raw_event);
+ 
+               for (i = 0; i < count; i++) {
+                       ir_raw_event_store(kernel_ir->rc,
+                                          &ir_core_event[i]);
+                       handle = true;
+               }
+       } while (num != 0);
+ 
+       if (overrun)
+               ir_raw_event_reset(kernel_ir->rc);
+       else if (handle)
+               ir_raw_event_handle(kernel_ir->rc);
+ }
+ 
+ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+ {
+       struct v4l2_subdev_ir_parameters params;
+       int overrun, data_available;
+ 
+       if (dev->sd_ir == NULL || events == 0)
+               return;
+ 
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1270:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+       case CX23885_BOARD_TEVII_S470:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /*
+                * The only boards we handle right now.  However other boards
+                * using the CX2388x integrated IR controller should be similar
+                */
+               break;
+       default:
+               return;
+       }
+ 
+       overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+                           V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+ 
+       data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+                                  V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+ 
+       if (overrun) {
+               /* If there was a FIFO overrun, stop the device */
+               v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms);
+               params.enable = false;
+               /* Mitigate race with cx23885_input_ir_stop() */
+               params.shutdown = atomic_read(&dev->ir_input_stopping);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
+       }
+ 
+       if (data_available)
+               cx23885_input_process_measurements(dev, overrun);
+ 
+       if (overrun) {
+               /* If there was a FIFO overrun, clear & restart the device */
+               params.enable = true;
+               /* Mitigate race with cx23885_input_ir_stop() */
+               params.shutdown = atomic_read(&dev->ir_input_stopping);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
+       }
+ }
+ 
+ static int cx23885_input_ir_start(struct cx23885_dev *dev)
+ {
+       struct v4l2_subdev_ir_parameters params;
+ 
+       if (dev->sd_ir == NULL)
+               return -ENODEV;
+ 
+       atomic_set(&dev->ir_input_stopping, 0);
+ 
+       v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms);
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1270:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /*
+                * The IR controller on this board only returns pulse widths.
+                * Any other mode setting will fail to set up the device.
+               */
+               params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+               params.enable = true;
+               params.interrupt_enable = true;
+               params.shutdown = false;
+ 
+               /* Setup for baseband compatible with both RC-5 and RC-6A */
+               params.modulation = false;
+               /* RC-5:  2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+               /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+               params.max_pulse_width = 3333333; /* ns */
+               /* RC-5:    666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+               /* RC-6A:   333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+               params.noise_filter_min_width = 333333; /* ns */
+               /*
+                * This board has inverted receive sense:
+                * mark is received as low logic level;
+                * falling edges are detected as rising edges; etc.
+                */
+               params.invert_level = true;
+               break;
+       case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+       case CX23885_BOARD_TEVII_S470:
+               /*
+                * The IR controller on this board only returns pulse widths.
+                * Any other mode setting will fail to set up the device.
+                */
+               params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+               params.enable = true;
+               params.interrupt_enable = true;
+               params.shutdown = false;
+ 
+               /* Setup for a standard NEC protocol */
+               params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+               params.carrier_range_lower = 33000; /* Hz */
+               params.carrier_range_upper = 43000; /* Hz */
+               params.duty_cycle = 33; /* percent, 33 percent for NEC */
+ 
+               /*
+                * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+                * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+                */
+               params.max_pulse_width = 12378022; /* ns */
+ 
+               /*
+                * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+                * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+                */
+               params.noise_filter_min_width = 351648; /* ns */
+ 
+               params.modulation = false;
+               params.invert_level = true;
+               break;
+       }
+       v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
+       return 0;
+ }
+ 
+ static int cx23885_input_ir_open(struct rc_dev *rc)
+ {
+       struct cx23885_kernel_ir *kernel_ir = rc->priv;
+ 
+       if (kernel_ir->cx == NULL)
+               return -ENODEV;
+ 
+       return cx23885_input_ir_start(kernel_ir->cx);
+ }
+ 
+ static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+ {
+       struct v4l2_subdev_ir_parameters params;
+ 
+       if (dev->sd_ir == NULL)
+               return;
+ 
+       /*
+        * Stop the sd_ir subdevice from generating notifications and
+        * scheduling work.
+        * It is shutdown this way in order to mitigate a race with
+        * cx23885_input_rx_work_handler() in the overrun case, which could
+        * re-enable the subdevice.
+        */
+       atomic_set(&dev->ir_input_stopping, 1);
+       v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms);
+       while (params.shutdown == false) {
+               params.enable = false;
+               params.interrupt_enable = false;
+               params.shutdown = true;
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms);
+       }
++      flush_work(&dev->cx25840_work);
++      flush_work(&dev->ir_rx_work);
++      flush_work(&dev->ir_tx_work);
+ }
+ 
+ static void cx23885_input_ir_close(struct rc_dev *rc)
+ {
+       struct cx23885_kernel_ir *kernel_ir = rc->priv;
+ 
+       if (kernel_ir->cx != NULL)
+               cx23885_input_ir_stop(kernel_ir->cx);
+ }
+ 
+ int cx23885_input_init(struct cx23885_dev *dev)
+ {
+       struct cx23885_kernel_ir *kernel_ir;
+       struct rc_dev *rc;
+       char *rc_map;
+       enum rc_driver_type driver_type;
+       unsigned long allowed_protos;
+ 
+       int ret;
+ 
+       /*
+        * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+        * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+        */
+       if (dev->sd_ir == NULL)
+               return -ENODEV;
+ 
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1270:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+       case CX23885_BOARD_HAUPPAUGE_HVR1250:
+               /* Integrated CX2388[58] IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_TYPE_ALL;
+               /* The grey Hauppauge RC-5 remote */
+               rc_map = RC_MAP_HAUPPAUGE;
+               break;
+       case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+               /* Integrated CX23885 IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_TYPE_NEC;
+               /* The grey Terratec remote with orange buttons */
+               rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS;
+               break;
+       case CX23885_BOARD_TEVII_S470:
+               /* Integrated CX23885 IR controller */
+               driver_type = RC_DRIVER_IR_RAW;
+               allowed_protos = RC_TYPE_ALL;
+               /* A guess at the remote */
+               rc_map = RC_MAP_TEVII_NEC;
+               break;
+       default:
+               return -ENODEV;
+       }
+ 
+       /* cx23885 board instance kernel IR state */
+       kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
+       if (kernel_ir == NULL)
+               return -ENOMEM;
+ 
+       kernel_ir->cx = dev;
+       kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
+                                   cx23885_boards[dev->board].name);
+       kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
+                                   pci_name(dev->pci));
+ 
+       /* input device */
+       rc = rc_allocate_device();
+       if (!rc) {
+               ret = -ENOMEM;
+               goto err_out_free;
+       }
+ 
+       kernel_ir->rc = rc;
+       rc->input_name = kernel_ir->name;
+       rc->input_phys = kernel_ir->phys;
+       rc->input_id.bustype = BUS_PCI;
+       rc->input_id.version = 1;
+       if (dev->pci->subsystem_vendor) {
+               rc->input_id.vendor  = dev->pci->subsystem_vendor;
+               rc->input_id.product = dev->pci->subsystem_device;
+       } else {
+               rc->input_id.vendor  = dev->pci->vendor;
+               rc->input_id.product = dev->pci->device;
+       }
+       rc->dev.parent = &dev->pci->dev;
+       rc->driver_type = driver_type;
+       rc->allowed_protos = allowed_protos;
+       rc->priv = kernel_ir;
+       rc->open = cx23885_input_ir_open;
+       rc->close = cx23885_input_ir_close;
+       rc->map_name = rc_map;
+       rc->driver_name = MODULE_NAME;
+ 
+       /* Go */
+       dev->kernel_ir = kernel_ir;
+       ret = rc_register_device(rc);
+       if (ret)
+               goto err_out_stop;
+ 
+       return 0;
+ 
+ err_out_stop:
+       cx23885_input_ir_stop(dev);
+       dev->kernel_ir = NULL;
+       rc_free_device(rc);
+ err_out_free:
+       kfree(kernel_ir->phys);
+       kfree(kernel_ir->name);
+       kfree(kernel_ir);
+       return ret;
+ }
+ 
+ void cx23885_input_fini(struct cx23885_dev *dev)
+ {
+       /* Always stop the IR hardware from generating interrupts */
+       cx23885_input_ir_stop(dev);
+ 
+       if (dev->kernel_ir == NULL)
+               return;
+       rc_unregister_device(dev->kernel_ir->rc);
+       kfree(dev->kernel_ir->phys);
+       kfree(dev->kernel_ir->name);
+       kfree(dev->kernel_ir);
+       dev->kernel_ir = NULL;
+ }
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+  *
+  *  Support for the mpeg transport stream transfers
+  *  PCI function #2 of the cx2388x.
+  *
+  *    (c) 2004 Jelle Foks <jelle@foks.us>
+  *    (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+  *    (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+  *
+  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ 
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/interrupt.h>
+ #include <asm/delay.h>
+ 
+ #include "cx88.h"
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
+ MODULE_AUTHOR("Jelle Foks <jelle@foks.us>");
+ MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(CX88_VERSION);
+ 
+ static unsigned int debug;
+ module_param(debug,int,0644);
+ MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
+ 
+ #define dprintk(level,fmt, arg...)    if (debug >= level) \
+       printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
+ 
+ #define mpeg_dbg(level,fmt, arg...)   if (debug >= level) \
+       printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+ 
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk);
+ 
+       if (dev->core->board.mpeg & CX88_MPEG_DVB)
+               request_module("cx88-dvb");
+       if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD)
+               request_module("cx88-blackbird");
+ }
+ 
+ static void request_modules(struct cx8802_dev *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_modules(struct cx8802_dev *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ 
+ 
+ static LIST_HEAD(cx8802_devlist);
+ static DEFINE_MUTEX(cx8802_mutex);
+ /* ------------------------------------------------------------------ */
+ 
+ static int cx8802_start_dma(struct cx8802_dev    *dev,
+                           struct cx88_dmaqueue *q,
+                           struct cx88_buffer   *buf)
+ {
+       struct cx88_core *core = dev->core;
+ 
+       dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",
+               buf->vb.width, buf->vb.height, buf->vb.field);
+ 
+       /* setup fifo + format */
+       cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
+                               dev->ts_packet_size, buf->risc.dma);
+ 
+       /* write TS length to chip */
+       cx_write(MO_TS_LNGTH, buf->vb.width);
+ 
+       /* FIXME: this needs a review.
+        * also: move to cx88-blackbird + cx88-dvb source files? */
+ 
+       dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
+ 
+       if ( (core->active_type_id == CX88_MPEG_DVB) &&
+               (core->board.mpeg & CX88_MPEG_DVB) ) {
+ 
+               dprintk( 1, "cx8802_start_dma doing .dvb\n");
+               /* negedge driven & software reset */
+               cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
+               udelay(100);
+               cx_write(MO_PINMUX_IO, 0x00);
+               cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
+               switch (core->boardnr) {
+               case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
+               case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
+               case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+               case CX88_BOARD_PCHDTV_HD5500:
+                       cx_write(TS_SOP_STAT, 1<<13);
+                       break;
+               case CX88_BOARD_SAMSUNG_SMT_7020:
+                       cx_write(TS_SOP_STAT, 0x00);
+                       break;
+               case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+               case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+                       cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
+                       udelay(100);
+                       break;
+               case CX88_BOARD_HAUPPAUGE_HVR1300:
+                       /* Enable MPEG parallel IO and video signal pins */
+                       cx_write(MO_PINMUX_IO, 0x88);
+                       cx_write(TS_SOP_STAT, 0);
+                       cx_write(TS_VALERR_CNTRL, 0);
+                       break;
+               case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+                       /* Enable MPEG parallel IO and video signal pins */
+                       cx_write(MO_PINMUX_IO, 0x88);
+                       cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4));
+                       dev->ts_gen_cntrl = 5;
+                       cx_write(TS_SOP_STAT, 0);
+                       cx_write(TS_VALERR_CNTRL, 0);
+                       udelay(100);
+                       break;
+               default:
+                       cx_write(TS_SOP_STAT, 0x00);
+                       break;
+               }
+               cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
+               udelay(100);
+       } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
+               (core->board.mpeg & CX88_MPEG_BLACKBIRD) ) {
+               dprintk( 1, "cx8802_start_dma doing .blackbird\n");
+               cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
+ 
+               cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
+               udelay(100);
+ 
+               cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
+               cx_write(TS_VALERR_CNTRL, 0x2000);
+ 
+               cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
+               udelay(100);
+       } else {
+               printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__,
+                       core->board.mpeg );
+               return -EINVAL;
+       }
+ 
+       /* reset counter */
+       cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
+       q->count = 1;
+ 
+       /* enable irqs */
+       dprintk( 1, "setting the interrupt mask\n" );
+       cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT);
+       cx_set(MO_TS_INTMSK,  0x1f0011);
+ 
+       /* start dma */
+       cx_set(MO_DEV_CNTRL2, (1<<5));
+       cx_set(MO_TS_DMACNTRL, 0x11);
+       return 0;
+ }
+ 
+ static int cx8802_stop_dma(struct cx8802_dev *dev)
+ {
+       struct cx88_core *core = dev->core;
+       dprintk( 1, "cx8802_stop_dma\n" );
+ 
+       /* stop dma */
+       cx_clear(MO_TS_DMACNTRL, 0x11);
+ 
+       /* disable irqs */
+       cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT);
+       cx_clear(MO_TS_INTMSK, 0x1f0011);
+ 
+       /* Reset the controller */
+       cx_write(TS_GEN_CNTRL, 0xcd);
+       return 0;
+ }
+ 
+ static int cx8802_restart_queue(struct cx8802_dev    *dev,
+                               struct cx88_dmaqueue *q)
+ {
+       struct cx88_buffer *buf;
+ 
+       dprintk( 1, "cx8802_restart_queue\n" );
+       if (list_empty(&q->active))
+       {
+               struct cx88_buffer *prev;
+               prev = NULL;
+ 
+               dprintk(1, "cx8802_restart_queue: queue is empty\n" );
+ 
+               for (;;) {
+                       if (list_empty(&q->queued))
+                               return 0;
+                       buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
+                       if (NULL == prev) {
+                               list_del(&buf->vb.queue);
+                               list_add_tail(&buf->vb.queue,&q->active);
+                               cx8802_start_dma(dev, q, buf);
+                               buf->vb.state = VIDEOBUF_ACTIVE;
+                               buf->count    = q->count++;
+                               mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+                               dprintk(1,"[%p/%d] restart_queue - first active\n",
+                                       buf,buf->vb.i);
+ 
+                       } else if (prev->vb.width  == buf->vb.width  &&
+                                  prev->vb.height == buf->vb.height &&
+                                  prev->fmt       == buf->fmt) {
+                               list_del(&buf->vb.queue);
+                               list_add_tail(&buf->vb.queue,&q->active);
+                               buf->vb.state = VIDEOBUF_ACTIVE;
+                               buf->count    = q->count++;
+                               prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+                               dprintk(1,"[%p/%d] restart_queue - move to active\n",
+                                       buf,buf->vb.i);
+                       } else {
+                               return 0;
+                       }
+                       prev = buf;
+               }
+               return 0;
+       }
+ 
+       buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+       dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+               buf, buf->vb.i);
+       cx8802_start_dma(dev, q, buf);
+       list_for_each_entry(buf, &q->active, vb.queue)
+               buf->count = q->count++;
+       mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+       return 0;
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
+                       struct cx88_buffer *buf, enum v4l2_field field)
+ {
+       int size = dev->ts_packet_size * dev->ts_packet_count;
+       struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+       int rc;
+ 
+       dprintk(1, "%s: %p\n", __func__, buf);
+       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+               return -EINVAL;
+ 
+       if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+               buf->vb.width  = dev->ts_packet_size;
+               buf->vb.height = dev->ts_packet_count;
+               buf->vb.size   = size;
+               buf->vb.field  = field /*V4L2_FIELD_TOP*/;
+ 
+               if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+                       goto fail;
+               cx88_risc_databuffer(dev->pci, &buf->risc,
+                                    dma->sglist,
+                                    buf->vb.width, buf->vb.height, 0);
+       }
+       buf->vb.state = VIDEOBUF_PREPARED;
+       return 0;
+ 
+  fail:
+       cx88_free_buffer(q,buf);
+       return rc;
+ }
+ 
+ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
+ {
+       struct cx88_buffer    *prev;
+       struct cx88_dmaqueue  *cx88q = &dev->mpegq;
+ 
+       dprintk( 1, "cx8802_buf_queue\n" );
+       /* add jump to stopper */
+       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+       buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
+ 
+       if (list_empty(&cx88q->active)) {
+               dprintk( 1, "queue is empty - first active\n" );
+               list_add_tail(&buf->vb.queue,&cx88q->active);
+               cx8802_start_dma(dev, cx88q, buf);
+               buf->vb.state = VIDEOBUF_ACTIVE;
+               buf->count    = cx88q->count++;
+               mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
+               dprintk(1,"[%p/%d] %s - first active\n",
+                       buf, buf->vb.i, __func__);
+ 
+       } else {
+               dprintk( 1, "queue is not empty - append to active\n" );
+               prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
+               list_add_tail(&buf->vb.queue,&cx88q->active);
+               buf->vb.state = VIDEOBUF_ACTIVE;
+               buf->count    = cx88q->count++;
+               prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+               dprintk( 1, "[%p/%d] %s - append to active\n",
+                       buf, buf->vb.i, __func__);
+       }
+ }
+ 
+ /* ----------------------------------------------------------- */
+ 
+ static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)
+ {
+       struct cx88_dmaqueue *q = &dev->mpegq;
+       struct cx88_buffer *buf;
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&dev->slock,flags);
+       while (!list_empty(&q->active)) {
+               buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+               list_del(&buf->vb.queue);
+               buf->vb.state = VIDEOBUF_ERROR;
+               wake_up(&buf->vb.done);
+               dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
+                       buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
+       }
+       if (restart)
+       {
+               dprintk(1, "restarting queue\n" );
+               cx8802_restart_queue(dev,q);
+       }
+       spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ 
+ void cx8802_cancel_buffers(struct cx8802_dev *dev)
+ {
+       struct cx88_dmaqueue *q = &dev->mpegq;
+ 
+       dprintk( 1, "cx8802_cancel_buffers" );
+       del_timer_sync(&q->timeout);
+       cx8802_stop_dma(dev);
+       do_cancel_buffers(dev,"cancel",0);
+ }
+ 
+ static void cx8802_timeout(unsigned long data)
+ {
+       struct cx8802_dev *dev = (struct cx8802_dev*)data;
+ 
+       dprintk(1, "%s\n",__func__);
+ 
+       if (debug)
+               cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+       cx8802_stop_dma(dev);
+       do_cancel_buffers(dev,"timeout",1);
+ }
+ 
+ static const char * cx88_mpeg_irqs[32] = {
+       "ts_risci1", NULL, NULL, NULL,
+       "ts_risci2", NULL, NULL, NULL,
+       "ts_oflow",  NULL, NULL, NULL,
+       "ts_sync",   NULL, NULL, NULL,
+       "opc_err", "par_err", "rip_err", "pci_abort",
+       "ts_err?",
+ };
+ 
+ static void cx8802_mpeg_irq(struct cx8802_dev *dev)
+ {
+       struct cx88_core *core = dev->core;
+       u32 status, mask, count;
+ 
+       dprintk( 1, "cx8802_mpeg_irq\n" );
+       status = cx_read(MO_TS_INTSTAT);
+       mask   = cx_read(MO_TS_INTMSK);
+       if (0 == (status & mask))
+               return;
+ 
+       cx_write(MO_TS_INTSTAT, status);
+ 
+       if (debug || (status & mask & ~0xff))
+               cx88_print_irqbits(core->name, "irq mpeg ",
+                                  cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs),
+                                  status, mask);
+ 
+       /* risc op code error */
+       if (status & (1 << 16)) {
+               printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
+               cx_clear(MO_TS_DMACNTRL, 0x11);
+               cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+       }
+ 
+       /* risc1 y */
+       if (status & 0x01) {
+               dprintk( 1, "wake up\n" );
+               spin_lock(&dev->slock);
+               count = cx_read(MO_TS_GPCNT);
+               cx88_wakeup(dev->core, &dev->mpegq, count);
+               spin_unlock(&dev->slock);
+       }
+ 
+       /* risc2 y */
+       if (status & 0x10) {
+               spin_lock(&dev->slock);
+               cx8802_restart_queue(dev,&dev->mpegq);
+               spin_unlock(&dev->slock);
+       }
+ 
+       /* other general errors */
+       if (status & 0x1f0100) {
+               dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );
+               spin_lock(&dev->slock);
+               cx8802_stop_dma(dev);
+               cx8802_restart_queue(dev,&dev->mpegq);
+               spin_unlock(&dev->slock);
+       }
+ }
+ 
+ #define MAX_IRQ_LOOP 10
+ 
+ static irqreturn_t cx8802_irq(int irq, void *dev_id)
+ {
+       struct cx8802_dev *dev = dev_id;
+       struct cx88_core *core = dev->core;
+       u32 status;
+       int loop, handled = 0;
+ 
+       for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
+               status = cx_read(MO_PCI_INTSTAT) &
+                       (core->pci_irqmask | PCI_INT_TSINT);
+               if (0 == status)
+                       goto out;
+               dprintk( 1, "cx8802_irq\n" );
+               dprintk( 1, "    loop: %d/%d\n", loop, MAX_IRQ_LOOP );
+               dprintk( 1, "    status: %d\n", status );
+               handled = 1;
+               cx_write(MO_PCI_INTSTAT, status);
+ 
+               if (status & core->pci_irqmask)
+                       cx88_core_irq(core,status);
+               if (status & PCI_INT_TSINT)
+                       cx8802_mpeg_irq(dev);
+       };
+       if (MAX_IRQ_LOOP == loop) {
+               dprintk( 0, "clearing mask\n" );
+               printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+                      core->name);
+               cx_write(MO_PCI_INTMSK,0);
+       }
+ 
+  out:
+       return IRQ_RETVAL(handled);
+ }
+ 
+ static int cx8802_init_common(struct cx8802_dev *dev)
+ {
+       struct cx88_core *core = dev->core;
+       int err;
+ 
+       /* pci init */
+       if (pci_enable_device(dev->pci))
+               return -EIO;
+       pci_set_master(dev->pci);
+       if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) {
+               printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
+               return -EIO;
+       }
+ 
+       dev->pci_rev = dev->pci->revision;
+       pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER,  &dev->pci_lat);
+       printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
+              "latency: %d, mmio: 0x%llx\n", dev->core->name,
+              pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
+              dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));
+ 
+       /* initialize driver struct */
+       spin_lock_init(&dev->slock);
+ 
+       /* init dma queue */
+       INIT_LIST_HEAD(&dev->mpegq.active);
+       INIT_LIST_HEAD(&dev->mpegq.queued);
+       dev->mpegq.timeout.function = cx8802_timeout;
+       dev->mpegq.timeout.data     = (unsigned long)dev;
+       init_timer(&dev->mpegq.timeout);
+       cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
+                         MO_TS_DMACNTRL,0x11,0x00);
+ 
+       /* get irq */
+       err = request_irq(dev->pci->irq, cx8802_irq,
+                         IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev);
+       if (err < 0) {
+               printk(KERN_ERR "%s: can't get IRQ %d\n",
+                      dev->core->name, dev->pci->irq);
+               return err;
+       }
+       cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+ 
+       /* everything worked */
+       pci_set_drvdata(dev->pci,dev);
+       return 0;
+ }
+ 
+ static void cx8802_fini_common(struct cx8802_dev *dev)
+ {
+       dprintk( 2, "cx8802_fini_common\n" );
+       cx8802_stop_dma(dev);
+       pci_disable_device(dev->pci);
+ 
+       /* unregister stuff */
+       free_irq(dev->pci->irq, dev);
+       pci_set_drvdata(dev->pci, NULL);
+ 
+       /* free memory */
+       btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
+ }
+ 
+ /* ----------------------------------------------------------- */
+ 
+ static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state)
+ {
+       struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+       struct cx88_core *core = dev->core;
+ 
+       /* stop mpeg dma */
+       spin_lock(&dev->slock);
+       if (!list_empty(&dev->mpegq.active)) {
+               dprintk( 2, "suspend\n" );
+               printk("%s: suspend mpeg\n", core->name);
+               cx8802_stop_dma(dev);
+               del_timer(&dev->mpegq.timeout);
+       }
+       spin_unlock(&dev->slock);
+ 
+       /* FIXME -- shutdown device */
+       cx88_shutdown(dev->core);
+ 
+       pci_save_state(pci_dev);
+       if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+               pci_disable_device(pci_dev);
+               dev->state.disabled = 1;
+       }
+       return 0;
+ }
+ 
+ static int cx8802_resume_common(struct pci_dev *pci_dev)
+ {
+       struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+       struct cx88_core *core = dev->core;
+       int err;
+ 
+       if (dev->state.disabled) {
+               err=pci_enable_device(pci_dev);
+               if (err) {
+                       printk(KERN_ERR "%s: can't enable device\n",
+                                              dev->core->name);
+                       return err;
+               }
+               dev->state.disabled = 0;
+       }
+       err=pci_set_power_state(pci_dev, PCI_D0);
+       if (err) {
+               printk(KERN_ERR "%s: can't enable device\n",
+                                              dev->core->name);
+               pci_disable_device(pci_dev);
+               dev->state.disabled = 1;
+ 
+               return err;
+       }
+       pci_restore_state(pci_dev);
+ 
+       /* FIXME: re-initialize hardware */
+       cx88_reset(dev->core);
+ 
+       /* restart video+vbi capture */
+       spin_lock(&dev->slock);
+       if (!list_empty(&dev->mpegq.active)) {
+               printk("%s: resume mpeg\n", core->name);
+               cx8802_restart_queue(dev,&dev->mpegq);
+       }
+       spin_unlock(&dev->slock);
+ 
+       return 0;
+ }
+ 
+ struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
+ {
+       struct cx8802_driver *d;
+ 
+       list_for_each_entry(d, &dev->drvlist, drvlist)
+               if (d->type_id == btype)
+                       return d;
+ 
+       return NULL;
+ }
+ 
+ /* Driver asked for hardware access. */
+ static int cx8802_request_acquire(struct cx8802_driver *drv)
+ {
+       struct cx88_core *core = drv->core;
+       unsigned int    i;
+ 
+       /* Fail a request for hardware if the device is busy. */
+       if (core->active_type_id != CX88_BOARD_NONE &&
+           core->active_type_id != drv->type_id)
+               return -EBUSY;
+ 
+       if (drv->type_id == CX88_MPEG_DVB) {
+               /* When switching to DVB, always set the input to the tuner */
+               core->last_analog_input = core->input;
+               core->input = 0;
+               for (i = 0;
+                    i < (sizeof(core->board.input) / sizeof(struct cx88_input));
+                    i++) {
+                       if (core->board.input[i].type == CX88_VMUX_DVB) {
+                               core->input = i;
+                               break;
+                       }
+               }
+       }
+ 
+       if (drv->advise_acquire)
+       {
+               core->active_ref++;
+               if (core->active_type_id == CX88_BOARD_NONE) {
+                       core->active_type_id = drv->type_id;
+                       drv->advise_acquire(drv);
+               }
+ 
+               mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
+       }
+ 
+       return 0;
+ }
+ 
+ /* Driver asked to release hardware. */
+ static int cx8802_request_release(struct cx8802_driver *drv)
+ {
+       struct cx88_core *core = drv->core;
+ 
+       if (drv->advise_release && --core->active_ref == 0)
+       {
+               if (drv->type_id == CX88_MPEG_DVB) {
+                       /* If the DVB driver is releasing, reset the input
+                          state to the last configured analog input */
+                       core->input = core->last_analog_input;
+               }
+ 
+               drv->advise_release(drv);
+               core->active_type_id = CX88_BOARD_NONE;
+               mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO));
+       }
+ 
+       return 0;
+ }
+ 
+ static int cx8802_check_driver(struct cx8802_driver *drv)
+ {
+       if (drv == NULL)
+               return -ENODEV;
+ 
+       if ((drv->type_id != CX88_MPEG_DVB) &&
+               (drv->type_id != CX88_MPEG_BLACKBIRD))
+               return -EINVAL;
+ 
+       if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
+               (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
+               return -EINVAL;
+ 
+       if ((drv->probe == NULL) ||
+               (drv->remove == NULL) ||
+               (drv->advise_acquire == NULL) ||
+               (drv->advise_release == NULL))
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ int cx8802_register_driver(struct cx8802_driver *drv)
+ {
+       struct cx8802_dev *dev;
+       struct cx8802_driver *driver;
+       int err, i = 0;
+ 
+       printk(KERN_INFO
+              "cx88/2: registering cx8802 driver, type: %s access: %s\n",
+              drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+              drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+ 
+       if ((err = cx8802_check_driver(drv)) != 0) {
+               printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n");
+               return err;
+       }
+ 
+       mutex_lock(&cx8802_mutex);
+ 
+       list_for_each_entry(dev, &cx8802_devlist, devlist) {
+               printk(KERN_INFO
+                      "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
+                      dev->core->name, dev->pci->subsystem_vendor,
+                      dev->pci->subsystem_device, dev->core->board.name,
+                      dev->core->boardnr);
+ 
+               /* Bring up a new struct for each driver instance */
+               driver = kzalloc(sizeof(*drv),GFP_KERNEL);
+               if (driver == NULL) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+ 
+               /* Snapshot of the driver registration data */
+               drv->core = dev->core;
+               drv->suspend = cx8802_suspend_common;
+               drv->resume = cx8802_resume_common;
+               drv->request_acquire = cx8802_request_acquire;
+               drv->request_release = cx8802_request_release;
+               memcpy(driver, drv, sizeof(*driver));
+ 
+               mutex_lock(&drv->core->lock);
+               err = drv->probe(driver);
+               if (err == 0) {
+                       i++;
+                       list_add_tail(&driver->drvlist, &dev->drvlist);
+               } else {
+                       printk(KERN_ERR
+                              "%s/2: cx8802 probe failed, err = %d\n",
+                              dev->core->name, err);
+               }
+               mutex_unlock(&drv->core->lock);
+       }
+ 
+       err = i ? 0 : -ENODEV;
+ out:
+       mutex_unlock(&cx8802_mutex);
+       return err;
+ }
+ 
+ int cx8802_unregister_driver(struct cx8802_driver *drv)
+ {
+       struct cx8802_dev *dev;
+       struct cx8802_driver *d, *dtmp;
+       int err = 0;
+ 
+       printk(KERN_INFO
+              "cx88/2: unregistering cx8802 driver, type: %s access: %s\n",
+              drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+              drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+ 
+       mutex_lock(&cx8802_mutex);
+ 
+       list_for_each_entry(dev, &cx8802_devlist, devlist) {
+               printk(KERN_INFO
+                      "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n",
+                      dev->core->name, dev->pci->subsystem_vendor,
+                      dev->pci->subsystem_device, dev->core->board.name,
+                      dev->core->boardnr);
+ 
+               mutex_lock(&dev->core->lock);
+ 
+               list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) {
+                       /* only unregister the correct driver type */
+                       if (d->type_id != drv->type_id)
+                               continue;
+ 
+                       err = d->remove(d);
+                       if (err == 0) {
+                               list_del(&d->drvlist);
+                               kfree(d);
+                       } else
+                               printk(KERN_ERR "%s/2: cx8802 driver remove "
+                                      "failed (%d)\n", dev->core->name, err);
+               }
+ 
+               mutex_unlock(&dev->core->lock);
+       }
+ 
+       mutex_unlock(&cx8802_mutex);
+ 
+       return err;
+ }
+ 
+ /* ----------------------------------------------------------- */
+ static int __devinit cx8802_probe(struct pci_dev *pci_dev,
+                              const struct pci_device_id *pci_id)
+ {
+       struct cx8802_dev *dev;
+       struct cx88_core  *core;
+       int err;
+ 
+       /* general setup */
+       core = cx88_core_get(pci_dev);
+       if (NULL == core)
+               return -EINVAL;
+ 
+       printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
+ 
+       err = -ENODEV;
+       if (!core->board.mpeg)
+               goto fail_core;
+ 
+       err = -ENOMEM;
+       dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+       if (NULL == dev)
+               goto fail_core;
+       dev->pci = pci_dev;
+       dev->core = core;
+ 
+       /* Maintain a reference so cx88-video can query the 8802 device. */
+       core->dvbdev = dev;
+ 
+       err = cx8802_init_common(dev);
+       if (err != 0)
+               goto fail_free;
+ 
+       INIT_LIST_HEAD(&dev->drvlist);
+       mutex_lock(&cx8802_mutex);
+       list_add_tail(&dev->devlist,&cx8802_devlist);
+       mutex_unlock(&cx8802_mutex);
+ 
+       /* now autoload cx88-dvb or cx88-blackbird */
+       request_modules(dev);
+       return 0;
+ 
+  fail_free:
+       kfree(dev);
+  fail_core:
+       core->dvbdev = NULL;
+       cx88_core_put(core,pci_dev);
+       return err;
+ }
+ 
+ static void __devexit cx8802_remove(struct pci_dev *pci_dev)
+ {
+       struct cx8802_dev *dev;
+ 
+       dev = pci_get_drvdata(pci_dev);
+ 
+       dprintk( 1, "%s\n", __func__);
+ 
+       flush_request_modules(dev);
+ 
+       mutex_lock(&dev->core->lock);
+ 
+       if (!list_empty(&dev->drvlist)) {
+               struct cx8802_driver *drv, *tmp;
+               int err;
+ 
+               printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver "
+                      "while cx8802 sub-drivers still loaded?!\n",
+                      dev->core->name);
+ 
+               list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) {
+                       err = drv->remove(drv);
+                       if (err == 0) {
+                               list_del(&drv->drvlist);
+                       } else
+                               printk(KERN_ERR "%s/2: cx8802 driver remove "
+                                      "failed (%d)\n", dev->core->name, err);
+                       kfree(drv);
+               }
+       }
+ 
+       mutex_unlock(&dev->core->lock);
+ 
+       /* Destroy any 8802 reference. */
+       dev->core->dvbdev = NULL;
+ 
+       /* common */
+       cx8802_fini_common(dev);
+       cx88_core_put(dev->core,dev->pci);
+       kfree(dev);
+ }
+ 
+ static const struct pci_device_id cx8802_pci_tbl[] = {
+       {
+               .vendor       = 0x14f1,
+               .device       = 0x8802,
+               .subvendor    = PCI_ANY_ID,
+               .subdevice    = PCI_ANY_ID,
+       },{
+               /* --- end of list --- */
+       }
+ };
+ MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+ 
+ static struct pci_driver cx8802_pci_driver = {
+       .name     = "cx88-mpeg driver manager",
+       .id_table = cx8802_pci_tbl,
+       .probe    = cx8802_probe,
+       .remove   = __devexit_p(cx8802_remove),
+ };
+ 
+ static int __init cx8802_init(void)
+ {
+       printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n",
+              CX88_VERSION);
+       return pci_register_driver(&cx8802_pci_driver);
+ }
+ 
+ static void __exit cx8802_fini(void)
+ {
+       pci_unregister_driver(&cx8802_pci_driver);
+ }
+ 
+ module_init(cx8802_init);
+ module_exit(cx8802_fini);
+ EXPORT_SYMBOL(cx8802_buf_prepare);
+ EXPORT_SYMBOL(cx8802_buf_queue);
+ EXPORT_SYMBOL(cx8802_cancel_buffers);
+ 
+ EXPORT_SYMBOL(cx8802_register_driver);
+ EXPORT_SYMBOL(cx8802_unregister_driver);
+ EXPORT_SYMBOL(cx8802_get_driver);
+ /* ----------------------------------------------------------- */
+ /*
+  * Local variables:
+  * c-basic-offset: 8
+  * End:
+  * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+  */
 
--- /dev/null
 -      flush_work_sync(&ca->hif_evm_work);
+ /*
+       Mantis PCI bridge driver
+ 
+       Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+ 
+       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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ 
+ #include <linux/kernel.h>
+ 
+ #include <linux/signal.h>
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
+ #include <asm/io.h>
+ 
+ #include "dmxdev.h"
+ #include "dvbdev.h"
+ #include "dvb_demux.h"
+ #include "dvb_frontend.h"
+ #include "dvb_net.h"
+ 
+ #include "mantis_common.h"
+ #include "mantis_link.h"
+ #include "mantis_hif.h"
+ #include "mantis_reg.h"
+ 
+ static void mantis_hifevm_work(struct work_struct *work)
+ {
+       struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work);
+       struct mantis_pci *mantis = ca->ca_priv;
+ 
+       u32 gpif_stat;
+ 
+       gpif_stat = mmread(MANTIS_GPIF_STATUS);
+ 
+       if (gpif_stat & MANTIS_GPIF_DETSTAT) {
+               if (gpif_stat & MANTIS_CARD_PLUGIN) {
+                       dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Plugin", mantis->num);
+                       mmwrite(0xdada0000, MANTIS_CARD_RESET);
+                       mantis_event_cam_plugin(ca);
+                       dvb_ca_en50221_camchange_irq(&ca->en50221,
+                                                    0,
+                                                    DVB_CA_EN50221_CAMCHANGE_INSERTED);
+               }
+       } else {
+               if (gpif_stat & MANTIS_CARD_PLUGOUT) {
+                       dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Unplug", mantis->num);
+                       mmwrite(0xdada0000, MANTIS_CARD_RESET);
+                       mantis_event_cam_unplug(ca);
+                       dvb_ca_en50221_camchange_irq(&ca->en50221,
+                                                    0,
+                                                    DVB_CA_EN50221_CAMCHANGE_REMOVED);
+               }
+       }
+ 
+       if (mantis->gpif_status & MANTIS_GPIF_EXTIRQ)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Ext IRQ", mantis->num);
+ 
+       if (mantis->gpif_status & MANTIS_SBUF_WSTO)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Timeout", mantis->num);
+ 
+       if (mantis->gpif_status & MANTIS_GPIF_OTHERR)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Alignment Error", mantis->num);
+ 
+       if (gpif_stat & MANTIS_SBUF_OVFLW)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Overflow", mantis->num);
+ 
+       if (gpif_stat & MANTIS_GPIF_BRRDY)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Read Ready", mantis->num);
+ 
+       if (gpif_stat & MANTIS_GPIF_INTSTAT)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): GPIF IRQ", mantis->num);
+ 
+       if (gpif_stat & MANTIS_SBUF_EMPTY)
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Empty", mantis->num);
+ 
+       if (gpif_stat & MANTIS_SBUF_OPDONE) {
+               dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer operation complete", mantis->num);
+               ca->sbuf_status = MANTIS_SBUF_DATA_AVAIL;
+               ca->hif_event = MANTIS_SBUF_OPDONE;
+               wake_up(&ca->hif_opdone_wq);
+       }
+ }
+ 
+ int mantis_evmgr_init(struct mantis_ca *ca)
+ {
+       struct mantis_pci *mantis = ca->ca_priv;
+ 
+       dprintk(MANTIS_DEBUG, 1, "Initializing Mantis Host I/F Event manager");
+       INIT_WORK(&ca->hif_evm_work, mantis_hifevm_work);
+       mantis_pcmcia_init(ca);
+       schedule_work(&ca->hif_evm_work);
+       mantis_hif_init(ca);
+       return 0;
+ }
+ 
+ void mantis_evmgr_exit(struct mantis_ca *ca)
+ {
+       struct mantis_pci *mantis = ca->ca_priv;
+ 
+       dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting");
++      flush_work(&ca->hif_evm_work);
+       mantis_hif_exit(ca);
+       mantis_pcmcia_exit(ca);
+ }
 
--- /dev/null
 -      flush_work_sync(&mantis->uart_work);
+ /*
+       Mantis PCI bridge driver
+ 
+       Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+ 
+       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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ 
+ #include <linux/kernel.h>
+ #include <linux/spinlock.h>
+ #include <asm/io.h>
+ 
+ #include <linux/signal.h>
+ #include <linux/sched.h>
+ #include <linux/interrupt.h>
+ 
+ #include "dmxdev.h"
+ #include "dvbdev.h"
+ #include "dvb_demux.h"
+ #include "dvb_frontend.h"
+ #include "dvb_net.h"
+ 
+ #include "mantis_common.h"
+ #include "mantis_reg.h"
+ #include "mantis_uart.h"
+ 
+ struct mantis_uart_params {
+       enum mantis_baud        baud_rate;
+       enum mantis_parity      parity;
+ };
+ 
+ static struct {
+       char string[7];
+ } rates[5] = {
+       { "9600" },
+       { "19200" },
+       { "38400" },
+       { "57600" },
+       { "115200" }
+ };
+ 
+ static struct {
+       char string[5];
+ } parity[3] = {
+       { "NONE" },
+       { "ODD" },
+       { "EVEN" }
+ };
+ 
+ #define UART_MAX_BUF                  16
+ 
+ int mantis_uart_read(struct mantis_pci *mantis, u8 *data)
+ {
+       struct mantis_hwconfig *config = mantis->hwconfig;
+       u32 stat = 0, i;
+ 
+       /* get data */
+       for (i = 0; i < (config->bytes + 1); i++) {
+ 
+               stat = mmread(MANTIS_UART_STAT);
+ 
+               if (stat & MANTIS_UART_RXFIFO_FULL) {
+                       dprintk(MANTIS_ERROR, 1, "RX Fifo FULL");
+               }
+               data[i] = mmread(MANTIS_UART_RXD) & 0x3f;
+ 
+               dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f);
+ 
+               if (data[i] & (1 << 7)) {
+                       dprintk(MANTIS_ERROR, 1, "UART framing error");
+                       return -EINVAL;
+               }
+               if (data[i] & (1 << 6)) {
+                       dprintk(MANTIS_ERROR, 1, "UART parity error");
+                       return -EINVAL;
+               }
+       }
+ 
+       return 0;
+ }
+ 
+ static void mantis_uart_work(struct work_struct *work)
+ {
+       struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work);
+       struct mantis_hwconfig *config = mantis->hwconfig;
+       u8 buf[16];
+       int i;
+ 
+       mantis_uart_read(mantis, buf);
+ 
+       for (i = 0; i < (config->bytes + 1); i++)
+               dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]);
+ 
+       dprintk(MANTIS_DEBUG, 0, "\n");
+ }
+ 
+ static int mantis_uart_setup(struct mantis_pci *mantis,
+                            struct mantis_uart_params *params)
+ {
+       u32 reg;
+ 
+       mmwrite((mmread(MANTIS_UART_CTL) | (params->parity & 0x3)), MANTIS_UART_CTL);
+ 
+       reg = mmread(MANTIS_UART_BAUD);
+ 
+       switch (params->baud_rate) {
+       case MANTIS_BAUD_9600:
+               reg |= 0xd8;
+               break;
+       case MANTIS_BAUD_19200:
+               reg |= 0x6c;
+               break;
+       case MANTIS_BAUD_38400:
+               reg |= 0x36;
+               break;
+       case MANTIS_BAUD_57600:
+               reg |= 0x23;
+               break;
+       case MANTIS_BAUD_115200:
+               reg |= 0x11;
+               break;
+       default:
+               return -EINVAL;
+       }
+ 
+       mmwrite(reg, MANTIS_UART_BAUD);
+ 
+       return 0;
+ }
+ 
+ int mantis_uart_init(struct mantis_pci *mantis)
+ {
+       struct mantis_hwconfig *config = mantis->hwconfig;
+       struct mantis_uart_params params;
+ 
+       /* default parity: */
+       params.baud_rate = config->baud_rate;
+       params.parity = config->parity;
+       dprintk(MANTIS_INFO, 1, "Initializing UART @ %sbps parity:%s",
+               rates[params.baud_rate].string,
+               parity[params.parity].string);
+ 
+       init_waitqueue_head(&mantis->uart_wq);
+       spin_lock_init(&mantis->uart_lock);
+ 
+       INIT_WORK(&mantis->uart_work, mantis_uart_work);
+ 
+       /* disable interrupt */
+       mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL);
+ 
+       mantis_uart_setup(mantis, ¶ms);
+ 
+       /* default 1 byte */
+       mmwrite((mmread(MANTIS_UART_BAUD) | (config->bytes << 8)), MANTIS_UART_BAUD);
+ 
+       /* flush buffer */
+       mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL);
+ 
+       /* enable interrupt */
+       mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK);
+       mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL);
+ 
+       schedule_work(&mantis->uart_work);
+       dprintk(MANTIS_DEBUG, 1, "UART successfully initialized");
+ 
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(mantis_uart_init);
+ 
+ void mantis_uart_exit(struct mantis_pci *mantis)
+ {
+       /* disable interrupt */
+       mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL);
++      flush_work(&mantis->uart_work);
+ }
+ EXPORT_SYMBOL_GPL(mantis_uart_exit);
 
--- /dev/null
 -static struct pci_error_handlers ngene_errors = {
+ /*
+  * ngene-cards.c: nGene PCIe bridge driver - card specific info
+  *
+  * Copyright (C) 2005-2007 Micronas
+  *
+  * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+  *                         Modifications for new nGene firmware,
+  *                         support for EEPROM-copying,
+  *                         support for new dual DVB-S2 card prototype
+  *
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 only, as published by the Free Software Foundation.
+  *
+  *
+  * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+  * 02110-1301, USA
+  * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+  */
+ 
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/pci_ids.h>
+ 
+ #include "ngene.h"
+ 
+ /* demods/tuners */
+ #include "stv6110x.h"
+ #include "stv090x.h"
+ #include "lnbh24.h"
+ #include "lgdt330x.h"
+ #include "mt2131.h"
+ #include "tda18271c2dd.h"
+ #include "drxk.h"
+ #include "drxd.h"
+ #include "dvb-pll.h"
+ 
+ 
+ /****************************************************************************/
+ /* Demod/tuner attachment ***************************************************/
+ /****************************************************************************/
+ 
+ static int tuner_attach_stv6110(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct stv090x_config *feconf = (struct stv090x_config *)
+               chan->dev->card_info->fe_config[chan->number];
+       struct stv6110x_config *tunerconf = (struct stv6110x_config *)
+               chan->dev->card_info->tuner_config[chan->number];
+       struct stv6110x_devctl *ctl;
+ 
+       /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+       if (chan->number < 2)
+               i2c = &chan->dev->channel[0].i2c_adapter;
+       else
+               i2c = &chan->dev->channel[1].i2c_adapter;
+ 
+       ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
+       if (ctl == NULL) {
+               printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
+               return -ENODEV;
+       }
+ 
+       feconf->tuner_init          = ctl->tuner_init;
+       feconf->tuner_sleep         = ctl->tuner_sleep;
+       feconf->tuner_set_mode      = ctl->tuner_set_mode;
+       feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+       feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+       feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+       feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+       feconf->tuner_set_bbgain    = ctl->tuner_set_bbgain;
+       feconf->tuner_get_bbgain    = ctl->tuner_get_bbgain;
+       feconf->tuner_set_refclk    = ctl->tuner_set_refclk;
+       feconf->tuner_get_status    = ctl->tuner_get_status;
+ 
+       return 0;
+ }
+ 
+ 
+ static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+ {
+       struct ngene_channel *chan = fe->sec_priv;
+       int status;
+ 
+       if (enable) {
+               down(&chan->dev->pll_mutex);
+               status = chan->gate_ctrl(fe, 1);
+       } else {
+               status = chan->gate_ctrl(fe, 0);
+               up(&chan->dev->pll_mutex);
+       }
+       return status;
+ }
+ 
+ static int tuner_attach_tda18271(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct dvb_frontend *fe;
+ 
+       i2c = &chan->dev->channel[0].i2c_adapter;
+       if (chan->fe->ops.i2c_gate_ctrl)
+               chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+       fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
+       if (chan->fe->ops.i2c_gate_ctrl)
+               chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+       if (!fe) {
+               printk(KERN_ERR "No TDA18271 found!\n");
+               return -ENODEV;
+       }
+ 
+       return 0;
+ }
+ 
+ static int tuner_attach_probe(struct ngene_channel *chan)
+ {
+       if (chan->demod_type == 0)
+               return tuner_attach_stv6110(chan);
+       if (chan->demod_type == 1)
+               return tuner_attach_tda18271(chan);
+       return -EINVAL;
+ }
+ 
+ static int demod_attach_stv0900(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct stv090x_config *feconf = (struct stv090x_config *)
+               chan->dev->card_info->fe_config[chan->number];
+ 
+       /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+       /* Note: Both adapters share the same i2c bus, but the demod     */
+       /*       driver requires that each demod has its own i2c adapter */
+       if (chan->number < 2)
+               i2c = &chan->dev->channel[0].i2c_adapter;
+       else
+               i2c = &chan->dev->channel[1].i2c_adapter;
+ 
+       chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
+                       (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
+                                               : STV090x_DEMODULATOR_1);
+       if (chan->fe == NULL) {
+               printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
+               return -ENODEV;
+       }
+ 
+       /* store channel info */
+       if (feconf->tuner_i2c_lock)
+               chan->fe->analog_demod_priv = chan;
+ 
+       if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
+                       0, chan->dev->card_info->lnb[chan->number])) {
+               printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+               dvb_frontend_detach(chan->fe);
+               chan->fe = NULL;
+               return -ENODEV;
+       }
+ 
+       return 0;
+ }
+ 
+ static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
+ {
+       struct ngene_channel *chan = fe->analog_demod_priv;
+ 
+       if (lock)
+               down(&chan->dev->pll_mutex);
+       else
+               up(&chan->dev->pll_mutex);
+ }
+ 
+ static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+ {
+       struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
+                                  .buf  = val,  .len   = 1 } };
+       return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+ }
+ 
+ static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+                         u16 reg, u8 *val)
+ {
+       u8 msg[2] = {reg>>8, reg&0xff};
+       struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+                                  .buf  = msg, .len   = 2},
+                                 {.addr = adr, .flags = I2C_M_RD,
+                                  .buf  = val, .len   = 1} };
+       return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+ }
+ 
+ static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+ {
+       u8 val;
+       if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+               return 0;
+       return 1;
+ }
+ 
+ static int port_has_drxk(struct i2c_adapter *i2c, int port)
+ {
+       u8 val;
+ 
+       if (i2c_read(i2c, 0x29+port, &val) < 0)
+               return 0;
+       return 1;
+ }
+ 
+ static int demod_attach_drxk(struct ngene_channel *chan,
+                            struct i2c_adapter *i2c)
+ {
+       struct drxk_config config;
+ 
+       memset(&config, 0, sizeof(config));
+       config.microcode_name = "drxk_a3.mc";
+       config.qam_demod_parameter_count = 4;
+       config.adr = 0x29 + (chan->number ^ 2);
+ 
+       chan->fe = dvb_attach(drxk_attach, &config, i2c);
+       if (!chan->fe) {
+               printk(KERN_ERR "No DRXK found!\n");
+               return -ENODEV;
+       }
+       chan->fe->sec_priv = chan;
+       chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+       chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+       return 0;
+ }
+ 
+ static int cineS2_probe(struct ngene_channel *chan)
+ {
+       struct i2c_adapter *i2c;
+       struct stv090x_config *fe_conf;
+       u8 buf[3];
+       struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
+       int rc;
+ 
+       /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+       if (chan->number < 2)
+               i2c = &chan->dev->channel[0].i2c_adapter;
+       else
+               i2c = &chan->dev->channel[1].i2c_adapter;
+ 
+       if (port_has_stv0900(i2c, chan->number)) {
+               chan->demod_type = 0;
+               fe_conf = chan->dev->card_info->fe_config[chan->number];
+               /* demod found, attach it */
+               rc = demod_attach_stv0900(chan);
+               if (rc < 0 || chan->number < 2)
+                       return rc;
+ 
+               /* demod #2: reprogram outputs DPN1 & DPN2 */
+               i2c_msg.addr = fe_conf->address;
+               i2c_msg.len = 3;
+               buf[0] = 0xf1;
+               switch (chan->number) {
+               case 2:
+                       buf[1] = 0x5c;
+                       buf[2] = 0xc2;
+                       break;
+               case 3:
+                       buf[1] = 0x61;
+                       buf[2] = 0xcc;
+                       break;
+               default:
+                       return -ENODEV;
+               }
+               rc = i2c_transfer(i2c, &i2c_msg, 1);
+               if (rc != 1) {
+                       printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+                       return -EIO;
+               }
+       } else if (port_has_drxk(i2c, chan->number^2)) {
+               chan->demod_type = 1;
+               demod_attach_drxk(chan, i2c);
+       } else {
+               printk(KERN_ERR "No demod found on chan %d\n", chan->number);
+               return -ENODEV;
+       }
+       return 0;
+ }
+ 
+ 
+ static struct lgdt330x_config aver_m780 = {
+       .demod_address = 0xb2 >> 1,
+       .demod_chip    = LGDT3303,
+       .serial_mpeg   = 0x00, /* PARALLEL */
+       .clock_polarity_flip = 1,
+ };
+ 
+ static struct mt2131_config m780_tunerconfig = {
+       0xc0 >> 1
+ };
+ 
+ /* A single func to attach the demo and tuner, rather than
+  * use two sep funcs like the current design mandates.
+  */
+ static int demod_attach_lg330x(struct ngene_channel *chan)
+ {
+       chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
+       if (chan->fe == NULL) {
+               printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n");
+               return -ENODEV;
+       }
+ 
+       dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter,
+                  &m780_tunerconfig, 0);
+ 
+       return (chan->fe) ? 0 : -ENODEV;
+ }
+ 
+ static int demod_attach_drxd(struct ngene_channel *chan)
+ {
+       struct drxd_config *feconf;
+ 
+       feconf = chan->dev->card_info->fe_config[chan->number];
+ 
+       chan->fe = dvb_attach(drxd_attach, feconf, chan,
+                       &chan->i2c_adapter, &chan->dev->pci_dev->dev);
+       if (!chan->fe) {
+               pr_err("No DRXD found!\n");
+               return -ENODEV;
+       }
+ 
+       if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
+                       &chan->i2c_adapter,
+                       feconf->pll_type)) {
+               pr_err("No pll(%d) found!\n", feconf->pll_type);
+               return -ENODEV;
+       }
+       return 0;
+ }
+ 
+ /****************************************************************************/
+ /* EEPROM TAGS **************************************************************/
+ /****************************************************************************/
+ 
+ #define MICNG_EE_START      0x0100
+ #define MICNG_EE_END        0x0FF0
+ 
+ #define MICNG_EETAG_END0    0x0000
+ #define MICNG_EETAG_END1    0xFFFF
+ 
+ /* 0x0001 - 0x000F reserved for housekeeping */
+ /* 0xFFFF - 0xFFFE reserved for housekeeping */
+ 
+ /* Micronas assigned tags
+    EEProm tags for hardware support */
+ 
+ #define MICNG_EETAG_DRXD1_OSCDEVIATION  0x1000  /* 2 Bytes data */
+ #define MICNG_EETAG_DRXD2_OSCDEVIATION  0x1001  /* 2 Bytes data */
+ 
+ #define MICNG_EETAG_MT2060_1_1STIF      0x1100  /* 2 Bytes data */
+ #define MICNG_EETAG_MT2060_2_1STIF      0x1101  /* 2 Bytes data */
+ 
+ /* Tag range for OEMs */
+ 
+ #define MICNG_EETAG_OEM_FIRST  0xC000
+ #define MICNG_EETAG_OEM_LAST   0xFFEF
+ 
+ static int i2c_write_eeprom(struct i2c_adapter *adapter,
+                           u8 adr, u16 reg, u8 data)
+ {
+       u8 m[3] = {(reg >> 8), (reg & 0xff), data};
+       struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
+                             .len = sizeof(m)};
+ 
+       if (i2c_transfer(adapter, &msg, 1) != 1) {
+               pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
+               return -EIO;
+       }
+       return 0;
+ }
+ 
+ static int i2c_read_eeprom(struct i2c_adapter *adapter,
+                          u8 adr, u16 reg, u8 *data, int len)
+ {
+       u8 msg[2] = {(reg >> 8), (reg & 0xff)};
+       struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+                                  .buf = msg, .len = 2 },
+                                 {.addr = adr, .flags = I2C_M_RD,
+                                  .buf = data, .len = len} };
+ 
+       if (i2c_transfer(adapter, msgs, 2) != 2) {
+               pr_err(DEVICE_NAME ": Error reading EEPROM\n");
+               return -EIO;
+       }
+       return 0;
+ }
+ 
+ static int ReadEEProm(struct i2c_adapter *adapter,
+                     u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
+ {
+       int status = 0;
+       u16 Addr = MICNG_EE_START, Length, tag = 0;
+       u8  EETag[3];
+ 
+       while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+               if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+                       return -1;
+               tag = (EETag[0] << 8) | EETag[1];
+               if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+                       return -1;
+               if (tag == Tag)
+                       break;
+               Addr += sizeof(u16) + 1 + EETag[2];
+       }
+       if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+               pr_err(DEVICE_NAME
+                      ": Reached EOEE @ Tag = %04x Length = %3d\n",
+                      tag, EETag[2]);
+               return -1;
+       }
+       Length = EETag[2];
+       if (Length > MaxLen)
+               Length = (u16) MaxLen;
+       if (Length > 0) {
+               Addr += sizeof(u16) + 1;
+               status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
+               if (!status) {
+                       *pLength = EETag[2];
+                       if (Length < EETag[2])
+                               ; /*status=STATUS_BUFFER_OVERFLOW; */
+               }
+       }
+       return status;
+ }
+ 
+ static int WriteEEProm(struct i2c_adapter *adapter,
+                      u16 Tag, u32 Length, u8 *data)
+ {
+       int status = 0;
+       u16 Addr = MICNG_EE_START;
+       u8 EETag[3];
+       u16 tag = 0;
+       int retry, i;
+ 
+       while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+               if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+                       return -1;
+               tag = (EETag[0] << 8) | EETag[1];
+               if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+                       return -1;
+               if (tag == Tag)
+                       break;
+               Addr += sizeof(u16) + 1 + EETag[2];
+       }
+       if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+               pr_err(DEVICE_NAME
+                      ": Reached EOEE @ Tag = %04x Length = %3d\n",
+                      tag, EETag[2]);
+               return -1;
+       }
+ 
+       if (Length > EETag[2])
+               return -EINVAL;
+       /* Note: We write the data one byte at a time to avoid
+          issues with page sizes. (which are different for
+          each manufacture and eeprom size)
+        */
+       Addr += sizeof(u16) + 1;
+       for (i = 0; i < Length; i++, Addr++) {
+               status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]);
+ 
+               if (status)
+                       break;
+ 
+               /* Poll for finishing write cycle */
+               retry = 10;
+               while (retry) {
+                       u8 Tmp;
+ 
+                       msleep(50);
+                       status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1);
+                       if (status)
+                               break;
+                       if (Tmp != data[i])
+                               pr_err(DEVICE_NAME
+                                      "eeprom write error\n");
+                       retry -= 1;
+               }
+               if (status) {
+                       pr_err(DEVICE_NAME
+                              ": Timeout polling eeprom\n");
+                       break;
+               }
+       }
+       return status;
+ }
+ 
+ static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data)
+ {
+       int stat;
+       u8 buf[2];
+       u32 len = 0;
+ 
+       stat = ReadEEProm(adapter, tag, 2, buf, &len);
+       if (stat)
+               return stat;
+       if (len != 2)
+               return -EINVAL;
+ 
+       *data = (buf[0] << 8) | buf[1];
+       return 0;
+ }
+ 
+ static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
+ {
+       int stat;
+       u8 buf[2];
+ 
+       buf[0] = data >> 8;
+       buf[1] = data & 0xff;
+       stat = WriteEEProm(adapter, tag, 2, buf);
+       if (stat)
+               return stat;
+       return 0;
+ }
+ 
+ static s16 osc_deviation(void *priv, s16 deviation, int flag)
+ {
+       struct ngene_channel *chan = priv;
+       struct i2c_adapter *adap = &chan->i2c_adapter;
+       u16 data = 0;
+ 
+       if (flag) {
+               data = (u16) deviation;
+               pr_info(DEVICE_NAME ": write deviation %d\n",
+                      deviation);
+               eeprom_write_ushort(adap, 0x1000 + chan->number, data);
+       } else {
+               if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
+                       data = 0;
+               pr_info(DEVICE_NAME ": read deviation %d\n",
+                      (s16) data);
+       }
+ 
+       return (s16) data;
+ }
+ 
+ /****************************************************************************/
+ /* Switch control (I2C gates, etc.) *****************************************/
+ /****************************************************************************/
+ 
+ 
+ static struct stv090x_config fe_cineS2 = {
+       .device         = STV0900,
+       .demod_mode     = STV090x_DUAL,
+       .clk_mode       = STV090x_CLK_EXT,
+ 
+       .xtal           = 27000000,
+       .address        = 0x68,
+ 
+       .ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+       .ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+ 
+       .repeater_level = STV090x_RPTLEVEL_16,
+ 
+       .adc1_range     = STV090x_ADC_1Vpp,
+       .adc2_range     = STV090x_ADC_1Vpp,
+ 
+       .diseqc_envelope_mode = true,
+ 
+       .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+ };
+ 
+ static struct stv090x_config fe_cineS2_2 = {
+       .device         = STV0900,
+       .demod_mode     = STV090x_DUAL,
+       .clk_mode       = STV090x_CLK_EXT,
+ 
+       .xtal           = 27000000,
+       .address        = 0x69,
+ 
+       .ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+       .ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
+ 
+       .repeater_level = STV090x_RPTLEVEL_16,
+ 
+       .adc1_range     = STV090x_ADC_1Vpp,
+       .adc2_range     = STV090x_ADC_1Vpp,
+ 
+       .diseqc_envelope_mode = true,
+ 
+       .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+ };
+ 
+ static struct stv6110x_config tuner_cineS2_0 = {
+       .addr   = 0x60,
+       .refclk = 27000000,
+       .clk_div = 1,
+ };
+ 
+ static struct stv6110x_config tuner_cineS2_1 = {
+       .addr   = 0x63,
+       .refclk = 27000000,
+       .clk_div = 1,
+ };
+ 
+ static struct ngene_info ngene_info_cineS2 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Linux4Media cineS2 DVB-S2 Twin Tuner",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110},
+       .fe_config      = {&fe_cineS2, &fe_cineS2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0b, 0x08},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ 
+ static struct ngene_info ngene_info_satixS2 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Mystique SaTiX-S2 Dual",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110},
+       .fe_config      = {&fe_cineS2, &fe_cineS2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0b, 0x08},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ 
+ static struct ngene_info ngene_info_satixS2v2 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Mystique SaTiX-S2 Dual (v2)",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+                          NGENE_IO_TSOUT},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+       .fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0a, 0x08, 0x0b, 0x09},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ 
+ static struct ngene_info ngene_info_cineS2v5 = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+                          NGENE_IO_TSOUT},
+       .demod_attach   = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+       .tuner_attach   = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+       .fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0a, 0x08, 0x0b, 0x09},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ 
+ 
+ static struct ngene_info ngene_info_duoFlex = {
+       .type           = NGENE_SIDEWINDER,
+       .name           = "Digital Devices DuoFlex PCIe or miniPCIe",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+                          NGENE_IO_TSOUT},
+       .demod_attach   = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
+       .tuner_attach   = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
+       .fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+       .tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+       .lnb            = {0x0a, 0x08, 0x0b, 0x09},
+       .tsf            = {3, 3},
+       .fw_version     = 18,
+       .msi_supported  = true,
+ };
+ 
+ static struct ngene_info ngene_info_m780 = {
+       .type           = NGENE_APP,
+       .name           = "Aver M780 ATSC/QAM-B",
+ 
+       /* Channel 0 is analog, which is currently unsupported */
+       .io_type        = { NGENE_IO_NONE, NGENE_IO_TSIN },
+       .demod_attach   = { NULL, demod_attach_lg330x },
+ 
+       /* Ensure these are NULL else the frame will call them (as funcs) */
+       .tuner_attach   = { 0, 0, 0, 0 },
+       .fe_config      = { NULL, &aver_m780 },
+       .avf            = { 0 },
+ 
+       /* A custom electrical interface config for the demod to bridge */
+       .tsf            = { 4, 4 },
+       .fw_version     = 15,
+ };
+ 
+ static struct drxd_config fe_terratec_dvbt_0 = {
+       .index          = 0,
+       .demod_address  = 0x70,
+       .demod_revision = 0xa2,
+       .demoda_address = 0x00,
+       .pll_address    = 0x60,
+       .pll_type       = DVB_PLL_THOMSON_DTT7520X,
+       .clock          = 20000,
+       .osc_deviation  = osc_deviation,
+ };
+ 
+ static struct drxd_config fe_terratec_dvbt_1 = {
+       .index          = 1,
+       .demod_address  = 0x71,
+       .demod_revision = 0xa2,
+       .demoda_address = 0x00,
+       .pll_address    = 0x60,
+       .pll_type       = DVB_PLL_THOMSON_DTT7520X,
+       .clock          = 20000,
+       .osc_deviation  = osc_deviation,
+ };
+ 
+ static struct ngene_info ngene_info_terratec = {
+       .type           = NGENE_TERRATEC,
+       .name           = "Terratec Integra/Cinergy2400i Dual DVB-T",
+       .io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+       .demod_attach   = {demod_attach_drxd, demod_attach_drxd},
+       .fe_config      = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1},
+       .i2c_access     = 1,
+ };
+ 
+ /****************************************************************************/
+ 
+ 
+ 
+ /****************************************************************************/
+ /* PCI Subsystem ID *********************************************************/
+ /****************************************************************************/
+ 
+ #define NGENE_ID(_subvend, _subdev, _driverdata) { \
+       .vendor = NGENE_VID, .device = NGENE_PID, \
+       .subvendor = _subvend, .subdevice = _subdev, \
+       .driver_data = (unsigned long) &_driverdata }
+ 
+ /****************************************************************************/
+ 
+ static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
+       NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
+       NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
+       NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
+       NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
+       NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
+       NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
+       NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
+       NGENE_ID(0x1461, 0x062e, ngene_info_m780),
+       NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
+       {0}
+ };
+ MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
+ 
+ /****************************************************************************/
+ /* Init/Exit ****************************************************************/
+ /****************************************************************************/
+ 
+ static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
+                                            enum pci_channel_state state)
+ {
+       printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+       if (state == pci_channel_io_frozen)
+               return PCI_ERS_RESULT_NEED_RESET;
+       return PCI_ERS_RESULT_CAN_RECOVER;
+ }
+ 
+ static pci_ers_result_t ngene_link_reset(struct pci_dev *dev)
+ {
+       printk(KERN_INFO DEVICE_NAME ": link reset\n");
+       return 0;
+ }
+ 
+ static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
+ {
+       printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+       return 0;
+ }
+ 
+ static void ngene_resume(struct pci_dev *dev)
+ {
+       printk(KERN_INFO DEVICE_NAME ": resume\n");
+ }
+ 
++static const struct pci_error_handlers ngene_errors = {
+       .error_detected = ngene_error_detected,
+       .link_reset = ngene_link_reset,
+       .slot_reset = ngene_slot_reset,
+       .resume = ngene_resume,
+ };
+ 
+ static struct pci_driver ngene_pci_driver = {
+       .name        = "ngene",
+       .id_table    = ngene_id_tbl,
+       .probe       = ngene_probe,
+       .remove      = __devexit_p(ngene_remove),
+       .err_handler = &ngene_errors,
+       .shutdown    = ngene_shutdown,
+ };
+ 
+ static __init int module_init_ngene(void)
+ {
+       printk(KERN_INFO
+              "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+       return pci_register_driver(&ngene_pci_driver);
+ }
+ 
+ static __exit void module_exit_ngene(void)
+ {
+       pci_unregister_driver(&ngene_pci_driver);
+ }
+ 
+ module_init(module_init_ngene);
+ module_exit(module_exit_ngene);
+ 
+ MODULE_DESCRIPTION("nGene");
+ MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
+ MODULE_LICENSE("GPL");
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+  *
+  * device driver for philips saa7134 based TV cards
+  * driver core
+  *
+  * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+  *
+  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/kmod.h>
+ #include <linux/sound.h>
+ #include <linux/interrupt.h>
+ #include <linux/delay.h>
+ #include <linux/mutex.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/pm.h>
+ 
+ #include "saa7134-reg.h"
+ #include "saa7134.h"
+ 
+ MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
+ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(SAA7134_VERSION);
+ 
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ static unsigned int irq_debug;
+ module_param(irq_debug, int, 0644);
+ MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
+ 
+ static unsigned int core_debug;
+ module_param(core_debug, int, 0644);
+ MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+ 
+ static unsigned int gpio_tracking;
+ module_param(gpio_tracking, int, 0644);
+ MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
+ 
+ static unsigned int alsa = 1;
+ module_param(alsa, int, 0644);
+ MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]");
+ 
+ static unsigned int latency = UNSET;
+ module_param(latency, int, 0444);
+ MODULE_PARM_DESC(latency,"pci latency timer");
+ 
+ int saa7134_no_overlay=-1;
+ module_param_named(no_overlay, saa7134_no_overlay, int, 0444);
+ MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+               " [some VIA/SIS chipsets are known to have problem with overlay]");
+ 
+ static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+ static unsigned int vbi_nr[]   = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+ static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+ static unsigned int tuner[]    = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+ static unsigned int card[]     = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+ 
+ 
+ module_param_array(video_nr, int, NULL, 0444);
+ module_param_array(vbi_nr,   int, NULL, 0444);
+ module_param_array(radio_nr, int, NULL, 0444);
+ module_param_array(tuner,    int, NULL, 0444);
+ module_param_array(card,     int, NULL, 0444);
+ 
+ MODULE_PARM_DESC(video_nr, "video device number");
+ MODULE_PARM_DESC(vbi_nr,   "vbi device number");
+ MODULE_PARM_DESC(radio_nr, "radio device number");
+ MODULE_PARM_DESC(tuner,    "tuner type");
+ MODULE_PARM_DESC(card,     "card type");
+ 
+ DEFINE_MUTEX(saa7134_devlist_lock);
+ EXPORT_SYMBOL(saa7134_devlist_lock);
+ LIST_HEAD(saa7134_devlist);
+ EXPORT_SYMBOL(saa7134_devlist);
+ static LIST_HEAD(mops_list);
+ static unsigned int saa7134_devcount;
+ 
+ int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
+ int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
+ 
+ #define dprintk(fmt, arg...)  if (core_debug) \
+       printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg)
+ 
+ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
+ {
+       unsigned long mode,status;
+ 
+       if (!gpio_tracking)
+               return;
+       /* rising SAA7134_GPIO_GPRESCAN reads the status */
+       saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0);
+       saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN);
+       mode   = saa_readl(SAA7134_GPIO_GPMODE0   >> 2) & 0xfffffff;
+       status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff;
+       printk(KERN_DEBUG
+              "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n",
+              dev->name, mode, (~mode) & status, mode & status, msg);
+ }
+ 
+ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value)
+ {
+       u32 index, bitval;
+ 
+       index = 1 << bit_no;
+       switch (value) {
+       case 0: /* static value */
+       case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value);
+               /* turn sync mode off if necessary */
+               if (index & 0x00c00000)
+                       saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00);
+               if (value)
+                       bitval = index;
+               else
+                       bitval = 0;
+               saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index);
+               saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval);
+               break;
+       case 3: /* tristate */
+               dprintk("setting GPIO%d to tristate\n", bit_no);
+               saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0);
+               break;
+       }
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ 
+ /* ----------------------------------------------------------- */
+ /* delayed request_module                                      */
+ 
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ 
+ static void request_module_async(struct work_struct *work){
+       struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk);
+       if (card_is_empress(dev))
+               request_module("saa7134-empress");
+       if (card_is_dvb(dev))
+               request_module("saa7134-dvb");
+       if (alsa) {
+               if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130)
+                       request_module("saa7134-alsa");
+       }
+ }
+ 
+ static void request_submodules(struct saa7134_dev *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_submodules(struct saa7134_dev *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ 
+ #else
+ #define request_submodules(dev)
+ #define flush_request_submodules(dev)
+ #endif /* CONFIG_MODULES */
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ /* nr of (saa7134-)pages for the given buffer size */
+ static int saa7134_buffer_pages(int size)
+ {
+       size  = PAGE_ALIGN(size);
+       size += PAGE_SIZE; /* for non-page-aligned buffers */
+       size /= 4096;
+       return size;
+ }
+ 
+ /* calc max # of buffers from size (must not exceed the 4MB virtual
+  * address space per DMA channel) */
+ int saa7134_buffer_count(unsigned int size, unsigned int count)
+ {
+       unsigned int maxcount;
+ 
+       maxcount = 1024 / saa7134_buffer_pages(size);
+       if (count > maxcount)
+               count = maxcount;
+       return count;
+ }
+ 
+ int saa7134_buffer_startpage(struct saa7134_buf *buf)
+ {
+       return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i;
+ }
+ 
+ unsigned long saa7134_buffer_base(struct saa7134_buf *buf)
+ {
+       unsigned long base;
+       struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ 
+       base  = saa7134_buffer_startpage(buf) * 4096;
+       base += dma->sglist[0].offset;
+       return base;
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt)
+ {
+       __le32       *cpu;
+       dma_addr_t   dma_addr = 0;
+ 
+       cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr);
+       if (NULL == cpu)
+               return -ENOMEM;
+       pt->size = SAA7134_PGTABLE_SIZE;
+       pt->cpu  = cpu;
+       pt->dma  = dma_addr;
+       return 0;
+ }
+ 
+ int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+                         struct scatterlist *list, unsigned int length,
+                         unsigned int startpage)
+ {
+       __le32        *ptr;
+       unsigned int  i,p;
+ 
+       BUG_ON(NULL == pt || NULL == pt->cpu);
+ 
+       ptr = pt->cpu + startpage;
+       for (i = 0; i < length; i++, list++)
+               for (p = 0; p * 4096 < list->length; p++, ptr++)
+                       *ptr = cpu_to_le32(sg_dma_address(list) - list->offset);
+       return 0;
+ }
+ 
+ void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt)
+ {
+       if (NULL == pt->cpu)
+               return;
+       pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+       pt->cpu = NULL;
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf)
+ {
+       struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+       BUG_ON(in_interrupt());
+ 
+       videobuf_waiton(q, &buf->vb, 0, 0);
+       videobuf_dma_unmap(q->dev, dma);
+       videobuf_dma_free(dma);
+       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ int saa7134_buffer_queue(struct saa7134_dev *dev,
+                        struct saa7134_dmaqueue *q,
+                        struct saa7134_buf *buf)
+ {
+       struct saa7134_buf *next = NULL;
+ 
+       assert_spin_locked(&dev->slock);
+       dprintk("buffer_queue %p\n",buf);
+       if (NULL == q->curr) {
+               if (!q->need_two) {
+                       q->curr = buf;
+                       buf->activate(dev,buf,NULL);
+               } else if (list_empty(&q->queue)) {
+                       list_add_tail(&buf->vb.queue,&q->queue);
+                       buf->vb.state = VIDEOBUF_QUEUED;
+               } else {
+                       next = list_entry(q->queue.next,struct saa7134_buf,
+                                         vb.queue);
+                       q->curr = buf;
+                       buf->activate(dev,buf,next);
+               }
+       } else {
+               list_add_tail(&buf->vb.queue,&q->queue);
+               buf->vb.state = VIDEOBUF_QUEUED;
+       }
+       return 0;
+ }
+ 
+ void saa7134_buffer_finish(struct saa7134_dev *dev,
+                          struct saa7134_dmaqueue *q,
+                          unsigned int state)
+ {
+       assert_spin_locked(&dev->slock);
+       dprintk("buffer_finish %p\n",q->curr);
+ 
+       /* finish current buffer */
+       q->curr->vb.state = state;
+       do_gettimeofday(&q->curr->vb.ts);
+       wake_up(&q->curr->vb.done);
+       q->curr = NULL;
+ }
+ 
+ void saa7134_buffer_next(struct saa7134_dev *dev,
+                        struct saa7134_dmaqueue *q)
+ {
+       struct saa7134_buf *buf,*next = NULL;
+ 
+       assert_spin_locked(&dev->slock);
+       BUG_ON(NULL != q->curr);
+ 
+       if (!list_empty(&q->queue)) {
+               /* activate next one from queue */
+               buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue);
+               dprintk("buffer_next %p [prev=%p/next=%p]\n",
+                       buf,q->queue.prev,q->queue.next);
+               list_del(&buf->vb.queue);
+               if (!list_empty(&q->queue))
+                       next = list_entry(q->queue.next,struct saa7134_buf,
+                                         vb.queue);
+               q->curr = buf;
+               buf->activate(dev,buf,next);
+               dprintk("buffer_next #2 prev=%p/next=%p\n",
+                       q->queue.prev,q->queue.next);
+       } else {
+               /* nothing to do -- just stop DMA */
+               dprintk("buffer_next %p\n",NULL);
+               saa7134_set_dmabits(dev);
+               del_timer(&q->timeout);
+ 
+               if (card_has_mpeg(dev))
+                       if (dev->ts_started)
+                               saa7134_ts_stop(dev);
+       }
+ }
+ 
+ void saa7134_buffer_timeout(unsigned long data)
+ {
+       struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data;
+       struct saa7134_dev *dev = q->dev;
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&dev->slock,flags);
+ 
+       /* try to reset the hardware (SWRST) */
+       saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+       saa_writeb(SAA7134_REGION_ENABLE, 0x80);
+       saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+ 
+       /* flag current buffer as failed,
+          try to start over with the next one. */
+       if (q->curr) {
+               dprintk("timeout on %p\n",q->curr);
+               saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR);
+       }
+       saa7134_buffer_next(dev,q);
+       spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ int saa7134_set_dmabits(struct saa7134_dev *dev)
+ {
+       u32 split, task=0, ctrl=0, irq=0;
+       enum v4l2_field cap = V4L2_FIELD_ANY;
+       enum v4l2_field ov  = V4L2_FIELD_ANY;
+ 
+       assert_spin_locked(&dev->slock);
+ 
+       if (dev->insuspend)
+               return 0;
+ 
+       /* video capture -- dma 0 + video task A */
+       if (dev->video_q.curr) {
+               task |= 0x01;
+               ctrl |= SAA7134_MAIN_CTRL_TE0;
+               irq  |= SAA7134_IRQ1_INTE_RA0_1 |
+                       SAA7134_IRQ1_INTE_RA0_0;
+               cap = dev->video_q.curr->vb.field;
+       }
+ 
+       /* video capture -- dma 1+2 (planar modes) */
+       if (dev->video_q.curr &&
+           dev->video_q.curr->fmt->planar) {
+               ctrl |= SAA7134_MAIN_CTRL_TE4 |
+                       SAA7134_MAIN_CTRL_TE5;
+       }
+ 
+       /* screen overlay -- dma 0 + video task B */
+       if (dev->ovenable) {
+               task |= 0x10;
+               ctrl |= SAA7134_MAIN_CTRL_TE1;
+               ov = dev->ovfield;
+       }
+ 
+       /* vbi capture -- dma 0 + vbi task A+B */
+       if (dev->vbi_q.curr) {
+               task |= 0x22;
+               ctrl |= SAA7134_MAIN_CTRL_TE2 |
+                       SAA7134_MAIN_CTRL_TE3;
+               irq  |= SAA7134_IRQ1_INTE_RA0_7 |
+                       SAA7134_IRQ1_INTE_RA0_6 |
+                       SAA7134_IRQ1_INTE_RA0_5 |
+                       SAA7134_IRQ1_INTE_RA0_4;
+       }
+ 
+       /* audio capture -- dma 3 */
+       if (dev->dmasound.dma_running) {
+               ctrl |= SAA7134_MAIN_CTRL_TE6;
+               irq  |= SAA7134_IRQ1_INTE_RA3_1 |
+                       SAA7134_IRQ1_INTE_RA3_0;
+       }
+ 
+       /* TS capture -- dma 5 */
+       if (dev->ts_q.curr) {
+               ctrl |= SAA7134_MAIN_CTRL_TE5;
+               irq  |= SAA7134_IRQ1_INTE_RA2_1 |
+                       SAA7134_IRQ1_INTE_RA2_0;
+       }
+ 
+       /* set task conditions + field handling */
+       if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) {
+               /* default config -- use full frames */
+               saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+               saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+               saa_writeb(SAA7134_FIELD_HANDLING(TASK_A),  0x02);
+               saa_writeb(SAA7134_FIELD_HANDLING(TASK_B),  0x02);
+               split = 0;
+       } else {
+               /* split fields between tasks */
+               if (V4L2_FIELD_TOP == cap) {
+                       /* odd A, even B, repeat */
+                       saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+                       saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e);
+               } else {
+                       /* odd B, even A, repeat */
+                       saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e);
+                       saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+               }
+               saa_writeb(SAA7134_FIELD_HANDLING(TASK_A),  0x01);
+               saa_writeb(SAA7134_FIELD_HANDLING(TASK_B),  0x01);
+               split = 1;
+       }
+ 
+       /* irqs */
+       saa_writeb(SAA7134_REGION_ENABLE, task);
+       saa_writel(SAA7134_IRQ1,          irq);
+       saa_andorl(SAA7134_MAIN_CTRL,
+                  SAA7134_MAIN_CTRL_TE0 |
+                  SAA7134_MAIN_CTRL_TE1 |
+                  SAA7134_MAIN_CTRL_TE2 |
+                  SAA7134_MAIN_CTRL_TE3 |
+                  SAA7134_MAIN_CTRL_TE4 |
+                  SAA7134_MAIN_CTRL_TE5 |
+                  SAA7134_MAIN_CTRL_TE6,
+                  ctrl);
+       dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n",
+               task, ctrl, irq, split ? "no" : "yes");
+ 
+       return 0;
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ /* IRQ handler + helpers                                              */
+ 
+ static char *irqbits[] = {
+       "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3",
+       "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC",
+       "TRIG_ERR", "CONF_ERR", "LOAD_ERR",
+       "GPIO16", "GPIO18", "GPIO22", "GPIO23"
+ };
+ #define IRQBITS ARRAY_SIZE(irqbits)
+ 
+ static void print_irqstatus(struct saa7134_dev *dev, int loop,
+                           unsigned long report, unsigned long status)
+ {
+       unsigned int i;
+ 
+       printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx",
+              dev->name,loop,jiffies,report,status);
+       for (i = 0; i < IRQBITS; i++) {
+               if (!(report & (1 << i)))
+                       continue;
+               printk(" %s",irqbits[i]);
+       }
+       if (report & SAA7134_IRQ_REPORT_DONE_RA0) {
+               printk(" | RA0=%s,%s,%s,%ld",
+                      (status & 0x40) ? "vbi"  : "video",
+                      (status & 0x20) ? "b"    : "a",
+                      (status & 0x10) ? "odd"  : "even",
+                      (status & 0x0f));
+       }
+       printk("\n");
+ }
+ 
+ static irqreturn_t saa7134_irq(int irq, void *dev_id)
+ {
+       struct saa7134_dev *dev = (struct saa7134_dev*) dev_id;
+       unsigned long report,status;
+       int loop, handled = 0;
+ 
+       if (dev->insuspend)
+               goto out;
+ 
+       for (loop = 0; loop < 10; loop++) {
+               report = saa_readl(SAA7134_IRQ_REPORT);
+               status = saa_readl(SAA7134_IRQ_STATUS);
+ 
+               /* If dmasound support is active and we get a sound report,
+                * mask out the report and let the saa7134-alsa module deal
+                * with it */
+               if ((report & SAA7134_IRQ_REPORT_DONE_RA3) &&
+                       (dev->dmasound.priv_data != NULL) )
+               {
+                       if (irq_debug > 1)
+                               printk(KERN_DEBUG "%s/irq: preserving DMA sound interrupt\n",
+                                      dev->name);
+                       report &= ~SAA7134_IRQ_REPORT_DONE_RA3;
+               }
+ 
+               if (0 == report) {
+                       if (irq_debug > 1)
+                               printk(KERN_DEBUG "%s/irq: no (more) work\n",
+                                      dev->name);
+                       goto out;
+               }
+ 
+               handled = 1;
+               saa_writel(SAA7134_IRQ_REPORT,report);
+               if (irq_debug)
+                       print_irqstatus(dev,loop,report,status);
+ 
+ 
+               if ((report & SAA7134_IRQ_REPORT_RDCAP) ||
+                       (report & SAA7134_IRQ_REPORT_INTL))
+                               saa7134_irq_video_signalchange(dev);
+ 
+ 
+               if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+                   (status & 0x60) == 0)
+                       saa7134_irq_video_done(dev,status);
+ 
+               if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+                   (status & 0x40) == 0x40)
+                       saa7134_irq_vbi_done(dev,status);
+ 
+               if ((report & SAA7134_IRQ_REPORT_DONE_RA2) &&
+                   card_has_mpeg(dev))
+                       saa7134_irq_ts_done(dev,status);
+ 
+               if (report & SAA7134_IRQ_REPORT_GPIO16) {
+                       switch (dev->has_remote) {
+                               case SAA7134_REMOTE_GPIO:
+                                       if (!dev->remote)
+                                               break;
+                                       if  (dev->remote->mask_keydown & 0x10000) {
+                                               saa7134_input_irq(dev);
+                                       }
+                                       break;
+ 
+                               case SAA7134_REMOTE_I2C:
+                                       break;                  /* FIXME: invoke I2C get_key() */
+ 
+                               default:                        /* GPIO16 not used by IR remote */
+                                       break;
+                       }
+               }
+ 
+               if (report & SAA7134_IRQ_REPORT_GPIO18) {
+                       switch (dev->has_remote) {
+                               case SAA7134_REMOTE_GPIO:
+                                       if (!dev->remote)
+                                               break;
+                                       if ((dev->remote->mask_keydown & 0x40000) ||
+                                           (dev->remote->mask_keyup & 0x40000)) {
+                                               saa7134_input_irq(dev);
+                                       }
+                                       break;
+ 
+                               case SAA7134_REMOTE_I2C:
+                                       break;                  /* FIXME: invoke I2C get_key() */
+ 
+                               default:                        /* GPIO18 not used by IR remote */
+                                       break;
+                       }
+               }
+       }
+ 
+       if (10 == loop) {
+               print_irqstatus(dev,loop,report,status);
+               if (report & SAA7134_IRQ_REPORT_PE) {
+                       /* disable all parity error */
+                       printk(KERN_WARNING "%s/irq: looping -- "
+                              "clearing PE (parity error!) enable bit\n",dev->name);
+                       saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE);
+               } else if (report & SAA7134_IRQ_REPORT_GPIO16) {
+                       /* disable gpio16 IRQ */
+                       printk(KERN_WARNING "%s/irq: looping -- "
+                              "clearing GPIO16 enable bit\n",dev->name);
+                       saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P);
+                       saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N);
+               } else if (report & SAA7134_IRQ_REPORT_GPIO18) {
+                       /* disable gpio18 IRQs */
+                       printk(KERN_WARNING "%s/irq: looping -- "
+                              "clearing GPIO18 enable bit\n",dev->name);
+                       saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P);
+                       saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N);
+               } else {
+                       /* disable all irqs */
+                       printk(KERN_WARNING "%s/irq: looping -- "
+                              "clearing all enable bits\n",dev->name);
+                       saa_writel(SAA7134_IRQ1,0);
+                       saa_writel(SAA7134_IRQ2,0);
+               }
+       }
+ 
+  out:
+       return IRQ_RETVAL(handled);
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ /* early init (no i2c, no irq) */
+ 
+ static int saa7134_hw_enable1(struct saa7134_dev *dev)
+ {
+       /* RAM FIFO config */
+       saa_writel(SAA7134_FIFO_SIZE, 0x08070503);
+       saa_writel(SAA7134_THRESHOULD, 0x02020202);
+ 
+       /* enable audio + video processing */
+       saa_writel(SAA7134_MAIN_CTRL,
+                       SAA7134_MAIN_CTRL_VPLLE |
+                       SAA7134_MAIN_CTRL_APLLE |
+                       SAA7134_MAIN_CTRL_EXOSC |
+                       SAA7134_MAIN_CTRL_EVFE1 |
+                       SAA7134_MAIN_CTRL_EVFE2 |
+                       SAA7134_MAIN_CTRL_ESFE  |
+                       SAA7134_MAIN_CTRL_EBDAC);
+ 
+       /*
+       * Initialize OSS _after_ enabling audio clock PLL and audio processing.
+       * OSS initialization writes to registers via the audio DSP; these
+       * writes will fail unless the audio clock has been started.  At worst,
+       * audio will not work.
+       */
+ 
+       /* enable peripheral devices */
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+ 
+       /* set vertical line numbering start (vbi needs this) */
+       saa_writeb(SAA7134_SOURCE_TIMING2, 0x20);
+ 
+       return 0;
+ }
+ 
+ static int saa7134_hwinit1(struct saa7134_dev *dev)
+ {
+       dprintk("hwinit1\n");
+ 
+       saa_writel(SAA7134_IRQ1, 0);
+       saa_writel(SAA7134_IRQ2, 0);
+ 
+       /* Clear any stale IRQ reports */
+       saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT));
+ 
+       mutex_init(&dev->lock);
+       spin_lock_init(&dev->slock);
+ 
+       saa7134_track_gpio(dev,"pre-init");
+       saa7134_video_init1(dev);
+       saa7134_vbi_init1(dev);
+       if (card_has_mpeg(dev))
+               saa7134_ts_init1(dev);
+       saa7134_input_init1(dev);
+ 
+       saa7134_hw_enable1(dev);
+ 
+       return 0;
+ }
+ 
+ /* late init (with i2c + irq) */
+ static int saa7134_hw_enable2(struct saa7134_dev *dev)
+ {
+ 
+       unsigned int irq2_mask;
+ 
+       /* enable IRQ's */
+       irq2_mask =
+               SAA7134_IRQ2_INTE_DEC3    |
+               SAA7134_IRQ2_INTE_DEC2    |
+               SAA7134_IRQ2_INTE_DEC1    |
+               SAA7134_IRQ2_INTE_DEC0    |
+               SAA7134_IRQ2_INTE_PE      |
+               SAA7134_IRQ2_INTE_AR;
+ 
+       if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) {
+               if (dev->remote->mask_keydown & 0x10000)
+                       irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N;
+               else {          /* Allow enabling both IRQ edge triggers */
+                       if (dev->remote->mask_keydown & 0x40000)
+                               irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P;
+                       if (dev->remote->mask_keyup & 0x40000)
+                               irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N;
+               }
+       }
+ 
+       if (dev->has_remote == SAA7134_REMOTE_I2C) {
+               request_module("ir-kbd-i2c");
+       }
+ 
+       saa_writel(SAA7134_IRQ1, 0);
+       saa_writel(SAA7134_IRQ2, irq2_mask);
+ 
+       return 0;
+ }
+ 
+ static int saa7134_hwinit2(struct saa7134_dev *dev)
+ {
+ 
+       dprintk("hwinit2\n");
+ 
+       saa7134_video_init2(dev);
+       saa7134_tvaudio_init2(dev);
+ 
+       saa7134_hw_enable2(dev);
+ 
+       return 0;
+ }
+ 
+ 
+ /* shutdown */
+ static int saa7134_hwfini(struct saa7134_dev *dev)
+ {
+       dprintk("hwfini\n");
+ 
+       if (card_has_mpeg(dev))
+               saa7134_ts_fini(dev);
+       saa7134_input_fini(dev);
+       saa7134_vbi_fini(dev);
+       saa7134_tvaudio_fini(dev);
+       return 0;
+ }
+ 
+ static void __devinit must_configure_manually(int has_eeprom)
+ {
+       unsigned int i,p;
+ 
+       if (!has_eeprom)
+               printk(KERN_WARNING
+                      "saa7134: <rant>\n"
+                      "saa7134:  Congratulations!  Your TV card vendor saved a few\n"
+                      "saa7134:  cents for a eeprom, thus your pci board has no\n"
+                      "saa7134:  subsystem ID and I can't identify it automatically\n"
+                      "saa7134: </rant>\n"
+                      "saa7134: I feel better now.  Ok, here are the good news:\n"
+                      "saa7134: You can use the card=<nr> insmod option to specify\n"
+                      "saa7134: which board do you have.  The list:\n");
+       else
+               printk(KERN_WARNING
+                      "saa7134: Board is currently unknown. You might try to use the card=<nr>\n"
+                      "saa7134: insmod option to specify which board do you have, but this is\n"
+                      "saa7134: somewhat risky, as might damage your card. It is better to ask\n"
+                      "saa7134: for support at linux-media@vger.kernel.org.\n"
+                      "saa7134: The supported cards are:\n");
+ 
+       for (i = 0; i < saa7134_bcount; i++) {
+               printk(KERN_WARNING "saa7134:   card=%d -> %-40.40s",
+                      i,saa7134_boards[i].name);
+               for (p = 0; saa7134_pci_tbl[p].driver_data; p++) {
+                       if (saa7134_pci_tbl[p].driver_data != i)
+                               continue;
+                       printk(" %04x:%04x",
+                              saa7134_pci_tbl[p].subvendor,
+                              saa7134_pci_tbl[p].subdevice);
+               }
+               printk("\n");
+       }
+ }
+ 
+ static struct video_device *vdev_init(struct saa7134_dev *dev,
+                                     struct video_device *template,
+                                     char *type)
+ {
+       struct video_device *vfd;
+ 
+       vfd = video_device_alloc();
+       if (NULL == vfd)
+               return NULL;
+       *vfd = *template;
+       vfd->v4l2_dev  = &dev->v4l2_dev;
+       vfd->release = video_device_release;
+       vfd->debug   = video_debug;
+       snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+                dev->name, type, saa7134_boards[dev->board].name);
+       video_set_drvdata(vfd, dev);
+       return vfd;
+ }
+ 
+ static void saa7134_unregister_video(struct saa7134_dev *dev)
+ {
+       if (dev->video_dev) {
+               if (video_is_registered(dev->video_dev))
+                       video_unregister_device(dev->video_dev);
+               else
+                       video_device_release(dev->video_dev);
+               dev->video_dev = NULL;
+       }
+       if (dev->vbi_dev) {
+               if (video_is_registered(dev->vbi_dev))
+                       video_unregister_device(dev->vbi_dev);
+               else
+                       video_device_release(dev->vbi_dev);
+               dev->vbi_dev = NULL;
+       }
+       if (dev->radio_dev) {
+               if (video_is_registered(dev->radio_dev))
+                       video_unregister_device(dev->radio_dev);
+               else
+                       video_device_release(dev->radio_dev);
+               dev->radio_dev = NULL;
+       }
+ }
+ 
+ static void mpeg_ops_attach(struct saa7134_mpeg_ops *ops,
+                           struct saa7134_dev *dev)
+ {
+       int err;
+ 
+       if (NULL != dev->mops)
+               return;
+       if (saa7134_boards[dev->board].mpeg != ops->type)
+               return;
+       err = ops->init(dev);
+       if (0 != err)
+               return;
+       dev->mops = ops;
+ }
+ 
+ static void mpeg_ops_detach(struct saa7134_mpeg_ops *ops,
+                           struct saa7134_dev *dev)
+ {
+       if (NULL == dev->mops)
+               return;
+       if (dev->mops != ops)
+               return;
+       dev->mops->fini(dev);
+       dev->mops = NULL;
+ }
+ 
+ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+                                    const struct pci_device_id *pci_id)
+ {
+       struct saa7134_dev *dev;
+       struct saa7134_mpeg_ops *mops;
+       int err;
+ 
+       if (saa7134_devcount == SAA7134_MAXBOARDS)
+               return -ENOMEM;
+ 
+       dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+       if (NULL == dev)
+               return -ENOMEM;
+ 
+       err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
+       if (err)
+               goto fail0;
+ 
+       /* pci init */
+       dev->pci = pci_dev;
+       if (pci_enable_device(pci_dev)) {
+               err = -EIO;
+               goto fail1;
+       }
+ 
+       dev->nr = saa7134_devcount;
+       sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr);
+ 
+       /* pci quirks */
+       if (pci_pci_problems) {
+               if (pci_pci_problems & PCIPCI_TRITON)
+                       printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name);
+               if (pci_pci_problems & PCIPCI_NATOMA)
+                       printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name);
+               if (pci_pci_problems & PCIPCI_VIAETBF)
+                       printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name);
+               if (pci_pci_problems & PCIPCI_VSFX)
+                       printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name);
+ #ifdef PCIPCI_ALIMAGIK
+               if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+                       printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+                              dev->name);
+                       latency = 0x0A;
+               }
+ #endif
+               if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) {
+                       printk(KERN_INFO "%s: quirk: this driver and your "
+                                       "chipset may not work together"
+                                       " in overlay mode.\n",dev->name);
+                       if (!saa7134_no_overlay) {
+                               printk(KERN_INFO "%s: quirk: overlay "
+                                               "mode will be disabled.\n",
+                                               dev->name);
+                               saa7134_no_overlay = 1;
+                       } else {
+                               printk(KERN_INFO "%s: quirk: overlay "
+                                               "mode will be forced. Use this"
+                                               " option at your own risk.\n",
+                                               dev->name);
+                       }
+               }
+       }
+       if (UNSET != latency) {
+               printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+                      dev->name,latency);
+               pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
+       }
+ 
+       /* print pci info */
+       dev->pci_rev = pci_dev->revision;
+       pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
+       printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
+              "latency: %d, mmio: 0x%llx\n", dev->name,
+              pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+              dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
+       pci_set_master(pci_dev);
+       if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) {
+               printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name);
+               err = -EIO;
+               goto fail1;
+       }
+ 
+       /* board config */
+       dev->board = pci_id->driver_data;
+       if (card[dev->nr] >= 0 &&
+           card[dev->nr] < saa7134_bcount)
+               dev->board = card[dev->nr];
+       if (SAA7134_BOARD_UNKNOWN == dev->board)
+               must_configure_manually(0);
+       else if (SAA7134_BOARD_NOAUTO == dev->board) {
+               must_configure_manually(1);
+               dev->board = SAA7134_BOARD_UNKNOWN;
+       }
+       dev->autodetected = card[dev->nr] != dev->board;
+       dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+       dev->tuner_addr = saa7134_boards[dev->board].tuner_addr;
+       dev->radio_type = saa7134_boards[dev->board].radio_type;
+       dev->radio_addr = saa7134_boards[dev->board].radio_addr;
+       dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+       if (UNSET != tuner[dev->nr])
+               dev->tuner_type = tuner[dev->nr];
+       printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+               dev->name,pci_dev->subsystem_vendor,
+               pci_dev->subsystem_device,saa7134_boards[dev->board].name,
+               dev->board, dev->autodetected ?
+               "autodetected" : "insmod option");
+ 
+       /* get mmio */
+       if (!request_mem_region(pci_resource_start(pci_dev,0),
+                               pci_resource_len(pci_dev,0),
+                               dev->name)) {
+               err = -EBUSY;
+               printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+                      dev->name,(unsigned long long)pci_resource_start(pci_dev,0));
+               goto fail1;
+       }
+       dev->lmmio = ioremap(pci_resource_start(pci_dev, 0),
+                            pci_resource_len(pci_dev, 0));
+       dev->bmmio = (__u8 __iomem *)dev->lmmio;
+       if (NULL == dev->lmmio) {
+               err = -EIO;
+               printk(KERN_ERR "%s: can't ioremap() MMIO memory\n",
+                      dev->name);
+               goto fail2;
+       }
+ 
+       /* initialize hardware #1 */
+       saa7134_board_init1(dev);
+       saa7134_hwinit1(dev);
+ 
+       /* get irq */
+       err = request_irq(pci_dev->irq, saa7134_irq,
+                         IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+       if (err < 0) {
+               printk(KERN_ERR "%s: can't get IRQ %d\n",
+                      dev->name,pci_dev->irq);
+               goto fail3;
+       }
+ 
+       /* wait a bit, register i2c bus */
+       msleep(100);
+       saa7134_i2c_register(dev);
+       saa7134_board_init2(dev);
+ 
+       saa7134_hwinit2(dev);
+ 
+       /* load i2c helpers */
+       if (card_is_empress(dev)) {
+               struct v4l2_subdev *sd =
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                               "saa6752hs",
+                               saa7134_boards[dev->board].empress_addr, NULL);
+ 
+               if (sd)
+                       sd->grp_id = GRP_EMPRESS;
+       }
+ 
+       if (saa7134_boards[dev->board].rds_addr) {
+               struct v4l2_subdev *sd;
+ 
+               sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap, "saa6588",
+                               0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr));
+               if (sd) {
+                       printk(KERN_INFO "%s: found RDS decoder\n", dev->name);
+                       dev->has_rds = 1;
+               }
+       }
+ 
+       v4l2_prio_init(&dev->prio);
+ 
+       mutex_lock(&saa7134_devlist_lock);
+       list_for_each_entry(mops, &mops_list, next)
+               mpeg_ops_attach(mops, dev);
+       list_add_tail(&dev->devlist, &saa7134_devlist);
+       mutex_unlock(&saa7134_devlist_lock);
+ 
+       /* check for signal */
+       saa7134_irq_video_signalchange(dev);
+ 
+       if (TUNER_ABSENT != dev->tuner_type)
+               saa_call_all(dev, core, s_power, 0);
+ 
+       /* register v4l devices */
+       if (saa7134_no_overlay > 0)
+               printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name);
+ 
+       dev->video_dev = vdev_init(dev,&saa7134_video_template,"video");
+       err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+                                   video_nr[dev->nr]);
+       if (err < 0) {
+               printk(KERN_INFO "%s: can't register video device\n",
+                      dev->name);
+               goto fail4;
+       }
+       printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+              dev->name, video_device_node_name(dev->video_dev));
+ 
+       dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi");
+ 
+       err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+                                   vbi_nr[dev->nr]);
+       if (err < 0)
+               goto fail4;
+       printk(KERN_INFO "%s: registered device %s\n",
+              dev->name, video_device_node_name(dev->vbi_dev));
+ 
+       if (card_has_radio(dev)) {
+               dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
+               err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+                                           radio_nr[dev->nr]);
+               if (err < 0)
+                       goto fail4;
+               printk(KERN_INFO "%s: registered device %s\n",
+                      dev->name, video_device_node_name(dev->radio_dev));
+       }
+ 
+       /* everything worked */
+       saa7134_devcount++;
+ 
+       if (saa7134_dmasound_init && !dev->dmasound.priv_data)
+               saa7134_dmasound_init(dev);
+ 
+       request_submodules(dev);
+       return 0;
+ 
+  fail4:
+       saa7134_unregister_video(dev);
+       saa7134_i2c_unregister(dev);
+       free_irq(pci_dev->irq, dev);
+  fail3:
+       saa7134_hwfini(dev);
+       iounmap(dev->lmmio);
+  fail2:
+       release_mem_region(pci_resource_start(pci_dev,0),
+                          pci_resource_len(pci_dev,0));
+  fail1:
+       v4l2_device_unregister(&dev->v4l2_dev);
+  fail0:
+       kfree(dev);
+       return err;
+ }
+ 
+ static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+       struct saa7134_mpeg_ops *mops;
+ 
+       flush_request_submodules(dev);
+ 
+       /* Release DMA sound modules if present */
+       if (saa7134_dmasound_exit && dev->dmasound.priv_data) {
+               saa7134_dmasound_exit(dev);
+       }
+ 
+       /* debugging ... */
+       if (irq_debug) {
+               u32 report = saa_readl(SAA7134_IRQ_REPORT);
+               u32 status = saa_readl(SAA7134_IRQ_STATUS);
+               print_irqstatus(dev,42,report,status);
+       }
+ 
+       /* disable peripheral devices */
+       saa_writeb(SAA7134_SPECIAL_MODE,0);
+ 
+       /* shutdown hardware */
+       saa_writel(SAA7134_IRQ1,0);
+       saa_writel(SAA7134_IRQ2,0);
+       saa_writel(SAA7134_MAIN_CTRL,0);
+ 
+       /* shutdown subsystems */
+       saa7134_hwfini(dev);
+ 
+       /* unregister */
+       mutex_lock(&saa7134_devlist_lock);
+       list_del(&dev->devlist);
+       list_for_each_entry(mops, &mops_list, next)
+               mpeg_ops_detach(mops, dev);
+       mutex_unlock(&saa7134_devlist_lock);
+       saa7134_devcount--;
+ 
+       saa7134_i2c_unregister(dev);
+       saa7134_unregister_video(dev);
+ 
+ 
+       /* the DMA sound modules should be unloaded before reaching
+          this, but just in case they are still present... */
+       if (dev->dmasound.priv_data != NULL) {
+               free_irq(pci_dev->irq, &dev->dmasound);
+               dev->dmasound.priv_data = NULL;
+       }
+ 
+ 
+       /* release resources */
+       free_irq(pci_dev->irq, dev);
+       iounmap(dev->lmmio);
+       release_mem_region(pci_resource_start(pci_dev,0),
+                          pci_resource_len(pci_dev,0));
+ 
+ 
+       v4l2_device_unregister(&dev->v4l2_dev);
+ 
+       /* free memory */
+       kfree(dev);
+ }
+ 
+ #ifdef CONFIG_PM
+ 
+ /* resends a current buffer in queue after resume */
+ static int saa7134_buffer_requeue(struct saa7134_dev *dev,
+                                 struct saa7134_dmaqueue *q)
+ {
+       struct saa7134_buf *buf, *next;
+ 
+       assert_spin_locked(&dev->slock);
+ 
+       buf  = q->curr;
+       next = buf;
+       dprintk("buffer_requeue\n");
+ 
+       if (!buf)
+               return 0;
+ 
+       dprintk("buffer_requeue : resending active buffers \n");
+ 
+       if (!list_empty(&q->queue))
+               next = list_entry(q->queue.next, struct saa7134_buf,
+                                         vb.queue);
+       buf->activate(dev, buf, next);
+ 
+       return 0;
+ }
+ 
+ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+ 
+       /* disable overlay - apps should enable it explicitly on resume*/
+       dev->ovenable = 0;
+ 
+       /* Disable interrupts, DMA, and rest of the chip*/
+       saa_writel(SAA7134_IRQ1, 0);
+       saa_writel(SAA7134_IRQ2, 0);
+       saa_writel(SAA7134_MAIN_CTRL, 0);
+ 
+       dev->insuspend = 1;
+       synchronize_irq(pci_dev->irq);
+ 
+       /* ACK interrupts once more, just in case,
+               since the IRQ handler won't ack them anymore*/
+ 
+       saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT));
+ 
+       /* Disable timeout timers - if we have active buffers, we will
+          fill them on resume*/
+ 
+       del_timer(&dev->video_q.timeout);
+       del_timer(&dev->vbi_q.timeout);
+       del_timer(&dev->ts_q.timeout);
+ 
+       if (dev->remote)
+               saa7134_ir_stop(dev);
+ 
+       pci_save_state(pci_dev);
+       pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+ 
+       return 0;
+ }
+ 
+ static int saa7134_resume(struct pci_dev *pci_dev)
+ {
+       struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+       struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+       unsigned long flags;
+ 
+       pci_set_power_state(pci_dev, PCI_D0);
+       pci_restore_state(pci_dev);
+ 
+       /* Do things that are done in saa7134_initdev ,
+               except of initializing memory structures.*/
+ 
+       saa7134_board_init1(dev);
+ 
+       /* saa7134_hwinit1 */
+       if (saa7134_boards[dev->board].video_out)
+               saa7134_videoport_init(dev);
+       if (card_has_mpeg(dev))
+               saa7134_ts_init_hw(dev);
+       if (dev->remote)
+               saa7134_ir_start(dev);
+       saa7134_hw_enable1(dev);
+ 
+       msleep(100);
+ 
+       saa7134_board_init2(dev);
+ 
+       /*saa7134_hwinit2*/
+       saa7134_set_tvnorm_hw(dev);
+       saa7134_tvaudio_setmute(dev);
+       saa7134_tvaudio_setvolume(dev, dev->ctl_volume);
+       saa7134_tvaudio_init(dev);
+       saa7134_enable_i2s(dev);
+       saa7134_hw_enable2(dev);
+ 
+       saa7134_irq_video_signalchange(dev);
+ 
+       /*resume unfinished buffer(s)*/
+       spin_lock_irqsave(&dev->slock, flags);
+       saa7134_buffer_requeue(dev, &dev->video_q);
+       saa7134_buffer_requeue(dev, &dev->vbi_q);
+       saa7134_buffer_requeue(dev, &dev->ts_q);
+ 
+       /* FIXME: Disable DMA audio sound - temporary till proper support
+                 is implemented*/
+ 
+       dev->dmasound.dma_running = 0;
+ 
+       /* start DMA now*/
+       dev->insuspend = 0;
+       smp_wmb();
+       saa7134_set_dmabits(dev);
+       spin_unlock_irqrestore(&dev->slock, flags);
+ 
+       return 0;
+ }
+ #endif
+ 
+ /* ----------------------------------------------------------- */
+ 
+ int saa7134_ts_register(struct saa7134_mpeg_ops *ops)
+ {
+       struct saa7134_dev *dev;
+ 
+       mutex_lock(&saa7134_devlist_lock);
+       list_for_each_entry(dev, &saa7134_devlist, devlist)
+               mpeg_ops_attach(ops, dev);
+       list_add_tail(&ops->next,&mops_list);
+       mutex_unlock(&saa7134_devlist_lock);
+       return 0;
+ }
+ 
+ void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops)
+ {
+       struct saa7134_dev *dev;
+ 
+       mutex_lock(&saa7134_devlist_lock);
+       list_del(&ops->next);
+       list_for_each_entry(dev, &saa7134_devlist, devlist)
+               mpeg_ops_detach(ops, dev);
+       mutex_unlock(&saa7134_devlist_lock);
+ }
+ 
+ EXPORT_SYMBOL(saa7134_ts_register);
+ EXPORT_SYMBOL(saa7134_ts_unregister);
+ 
+ /* ----------------------------------------------------------- */
+ 
+ static struct pci_driver saa7134_pci_driver = {
+       .name     = "saa7134",
+       .id_table = saa7134_pci_tbl,
+       .probe    = saa7134_initdev,
+       .remove   = __devexit_p(saa7134_finidev),
+ #ifdef CONFIG_PM
+       .suspend  = saa7134_suspend,
+       .resume   = saa7134_resume
+ #endif
+ };
+ 
+ static int __init saa7134_init(void)
+ {
+       INIT_LIST_HEAD(&saa7134_devlist);
+       printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n",
+              SAA7134_VERSION);
+       return pci_register_driver(&saa7134_pci_driver);
+ }
+ 
+ static void __exit saa7134_fini(void)
+ {
+       pci_unregister_driver(&saa7134_pci_driver);
+ }
+ 
+ module_init(saa7134_init);
+ module_exit(saa7134_fini);
+ 
+ /* ----------------------------------------------------------- */
+ 
+ EXPORT_SYMBOL(saa7134_set_gpio);
+ EXPORT_SYMBOL(saa7134_boards);
+ 
+ /* ----------------- for the DMA sound modules --------------- */
+ 
+ EXPORT_SYMBOL(saa7134_dmasound_init);
+ EXPORT_SYMBOL(saa7134_dmasound_exit);
+ EXPORT_SYMBOL(saa7134_pgtable_free);
+ EXPORT_SYMBOL(saa7134_pgtable_build);
+ EXPORT_SYMBOL(saa7134_pgtable_alloc);
+ EXPORT_SYMBOL(saa7134_set_dmabits);
+ 
+ /* ----------------------------------------------------------- */
+ /*
+  * Local variables:
+  * c-basic-offset: 8
+  * End:
+  */
 
--- /dev/null
 -      flush_work_sync(&dev->empress_workqueue);
+ /*
+  *
+  * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+  *
+  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/delay.h>
+ 
+ #include "saa7134-reg.h"
+ #include "saa7134.h"
+ 
+ #include <media/saa6752hs.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+ MODULE_LICENSE("GPL");
+ 
+ static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+ 
+ module_param_array(empress_nr, int, NULL, 0444);
+ MODULE_PARM_DESC(empress_nr,"ts device number");
+ 
+ static unsigned int debug;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug,"enable debug messages");
+ 
+ #define dprintk(fmt, arg...)  if (debug)                      \
+       printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg)
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ static void ts_reset_encoder(struct saa7134_dev* dev)
+ {
+       if (!dev->empress_started)
+               return;
+ 
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+       msleep(10);
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+       msleep(100);
+       dev->empress_started = 0;
+ }
+ 
+ static int ts_init_encoder(struct saa7134_dev* dev)
+ {
+       u32 leading_null_bytes = 0;
+ 
+       /* If more cards start to need this, then this
+          should probably be added to the card definitions. */
+       switch (dev->board) {
+       case SAA7134_BOARD_BEHOLD_M6:
+       case SAA7134_BOARD_BEHOLD_M63:
+       case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+               leading_null_bytes = 1;
+               break;
+       }
+       ts_reset_encoder(dev);
+       saa_call_all(dev, core, init, leading_null_bytes);
+       dev->empress_started = 1;
+       return 0;
+ }
+ 
+ /* ------------------------------------------------------------------ */
+ 
+ static int ts_open(struct file *file)
+ {
+       struct video_device *vdev = video_devdata(file);
+       struct saa7134_dev *dev = video_drvdata(file);
+       int err;
+ 
+       dprintk("open dev=%s\n", video_device_node_name(vdev));
+       err = -EBUSY;
+       if (!mutex_trylock(&dev->empress_tsq.vb_lock))
+               return err;
+       if (atomic_read(&dev->empress_users))
+               goto done;
+ 
+       /* Unmute audio */
+       saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+               saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6));
+ 
+       atomic_inc(&dev->empress_users);
+       file->private_data = dev;
+       err = 0;
+ 
+ done:
+       mutex_unlock(&dev->empress_tsq.vb_lock);
+       return err;
+ }
+ 
+ static int ts_release(struct file *file)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       videobuf_stop(&dev->empress_tsq);
+       videobuf_mmap_free(&dev->empress_tsq);
+ 
+       /* stop the encoder */
+       ts_reset_encoder(dev);
+ 
+       /* Mute audio */
+       saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
+               saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
+ 
+       atomic_dec(&dev->empress_users);
+ 
+       return 0;
+ }
+ 
+ static ssize_t
+ ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       if (!dev->empress_started)
+               ts_init_encoder(dev);
+ 
+       return videobuf_read_stream(&dev->empress_tsq,
+                                   data, count, ppos, 0,
+                                   file->f_flags & O_NONBLOCK);
+ }
+ 
+ static unsigned int
+ ts_poll(struct file *file, struct poll_table_struct *wait)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_poll_stream(file, &dev->empress_tsq, wait);
+ }
+ 
+ 
+ static int
+ ts_mmap(struct file *file, struct vm_area_struct * vma)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_mmap_mapper(&dev->empress_tsq, vma);
+ }
+ 
+ /*
+  * This function is _not_ called directly, but from
+  * video_generic_ioctl (and maybe others).  userspace
+  * copying is done already, arg is a kernel pointer.
+  */
+ 
+ static int empress_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *cap)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       strcpy(cap->driver, "saa7134");
+       strlcpy(cap->card, saa7134_boards[dev->board].name,
+               sizeof(cap->card));
+       sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+       cap->capabilities =
+               V4L2_CAP_VIDEO_CAPTURE |
+               V4L2_CAP_READWRITE |
+               V4L2_CAP_STREAMING;
+       return 0;
+ }
+ 
+ static int empress_enum_input(struct file *file, void *priv,
+                                       struct v4l2_input *i)
+ {
+       if (i->index != 0)
+               return -EINVAL;
+ 
+       i->type = V4L2_INPUT_TYPE_CAMERA;
+       strcpy(i->name, "CCIR656");
+ 
+       return 0;
+ }
+ 
+ static int empress_g_input(struct file *file, void *priv, unsigned int *i)
+ {
+       *i = 0;
+       return 0;
+ }
+ 
+ static int empress_s_input(struct file *file, void *priv, unsigned int i)
+ {
+       if (i != 0)
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ static int empress_enum_fmt_vid_cap(struct file *file, void  *priv,
+                                       struct v4l2_fmtdesc *f)
+ {
+       if (f->index != 0)
+               return -EINVAL;
+ 
+       strlcpy(f->description, "MPEG TS", sizeof(f->description));
+       f->pixelformat = V4L2_PIX_FMT_MPEG;
+ 
+       return 0;
+ }
+ 
+ static int empress_g_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       struct saa7134_dev *dev = file->private_data;
+       struct v4l2_mbus_framefmt mbus_fmt;
+ 
+       saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt);
+ 
+       v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+ 
+       return 0;
+ }
+ 
+ static int empress_s_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       struct saa7134_dev *dev = file->private_data;
+       struct v4l2_mbus_framefmt mbus_fmt;
+ 
+       v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+       saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+       v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+ 
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+ 
+       return 0;
+ }
+ 
+ static int empress_try_fmt_vid_cap(struct file *file, void *priv,
+                               struct v4l2_format *f)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+ 
+       return 0;
+ }
+ 
+ static int empress_reqbufs(struct file *file, void *priv,
+                                       struct v4l2_requestbuffers *p)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_reqbufs(&dev->empress_tsq, p);
+ }
+ 
+ static int empress_querybuf(struct file *file, void *priv,
+                                       struct v4l2_buffer *b)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_querybuf(&dev->empress_tsq, b);
+ }
+ 
+ static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_qbuf(&dev->empress_tsq, b);
+ }
+ 
+ static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_dqbuf(&dev->empress_tsq, b,
+                               file->f_flags & O_NONBLOCK);
+ }
+ 
+ static int empress_streamon(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_streamon(&dev->empress_tsq);
+ }
+ 
+ static int empress_streamoff(struct file *file, void *priv,
+                                       enum v4l2_buf_type type)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return videobuf_streamoff(&dev->empress_tsq);
+ }
+ 
+ static int empress_s_ext_ctrls(struct file *file, void *priv,
+                              struct v4l2_ext_controls *ctrls)
+ {
+       struct saa7134_dev *dev = file->private_data;
+       int err;
+ 
+       /* count == 0 is abused in saa6752hs.c, so that special
+               case is handled here explicitly. */
+       if (ctrls->count == 0)
+               return 0;
+ 
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+ 
+       err = saa_call_empress(dev, core, s_ext_ctrls, ctrls);
+       ts_init_encoder(dev);
+ 
+       return err;
+ }
+ 
+ static int empress_g_ext_ctrls(struct file *file, void *priv,
+                              struct v4l2_ext_controls *ctrls)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+       return saa_call_empress(dev, core, g_ext_ctrls, ctrls);
+ }
+ 
+ static int empress_g_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *c)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return saa7134_g_ctrl_internal(dev, NULL, c);
+ }
+ 
+ static int empress_s_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *c)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return saa7134_s_ctrl_internal(dev, NULL, c);
+ }
+ 
+ static int empress_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+ {
+       /* Must be sorted from low to high control ID! */
+       static const u32 user_ctrls[] = {
+               V4L2_CID_USER_CLASS,
+               V4L2_CID_BRIGHTNESS,
+               V4L2_CID_CONTRAST,
+               V4L2_CID_SATURATION,
+               V4L2_CID_HUE,
+               V4L2_CID_AUDIO_VOLUME,
+               V4L2_CID_AUDIO_MUTE,
+               V4L2_CID_HFLIP,
+               0
+       };
+ 
+       /* Must be sorted from low to high control ID! */
+       static const u32 mpeg_ctrls[] = {
+               V4L2_CID_MPEG_CLASS,
+               V4L2_CID_MPEG_STREAM_TYPE,
+               V4L2_CID_MPEG_STREAM_PID_PMT,
+               V4L2_CID_MPEG_STREAM_PID_AUDIO,
+               V4L2_CID_MPEG_STREAM_PID_VIDEO,
+               V4L2_CID_MPEG_STREAM_PID_PCR,
+               V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+               V4L2_CID_MPEG_AUDIO_ENCODING,
+               V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+               V4L2_CID_MPEG_VIDEO_ENCODING,
+               V4L2_CID_MPEG_VIDEO_ASPECT,
+               V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+               V4L2_CID_MPEG_VIDEO_BITRATE,
+               V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+               0
+       };
+       static const u32 *ctrl_classes[] = {
+               user_ctrls,
+               mpeg_ctrls,
+               NULL
+       };
+       struct saa7134_dev *dev = file->private_data;
+ 
+       c->id = v4l2_ctrl_next(ctrl_classes, c->id);
+       if (c->id == 0)
+               return -EINVAL;
+       if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS)
+               return v4l2_ctrl_query_fill(c, 0, 0, 0, 0);
+       if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+               return saa7134_queryctrl(file, priv, c);
+       return saa_call_empress(dev, core, queryctrl, c);
+ }
+ 
+ static int empress_querymenu(struct file *file, void *priv,
+                                       struct v4l2_querymenu *c)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+               return -EINVAL;
+       return saa_call_empress(dev, core, querymenu, c);
+ }
+ 
+ static int empress_g_chip_ident(struct file *file, void *fh,
+              struct v4l2_dbg_chip_ident *chip)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       chip->ident = V4L2_IDENT_NONE;
+       chip->revision = 0;
+       if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER &&
+           !strcmp(chip->match.name, "saa6752hs"))
+               return saa_call_empress(dev, core, g_chip_ident, chip);
+       if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR)
+               return saa_call_empress(dev, core, g_chip_ident, chip);
+       return -EINVAL;
+ }
+ 
+ static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       return saa7134_s_std_internal(dev, NULL, id);
+ }
+ 
+ static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id)
+ {
+       struct saa7134_dev *dev = file->private_data;
+ 
+       *id = dev->tvnorm->id;
+       return 0;
+ }
+ 
+ static const struct v4l2_file_operations ts_fops =
+ {
+       .owner    = THIS_MODULE,
+       .open     = ts_open,
+       .release  = ts_release,
+       .read     = ts_read,
+       .poll     = ts_poll,
+       .mmap     = ts_mmap,
+       .ioctl    = video_ioctl2,
+ };
+ 
+ static const struct v4l2_ioctl_ops ts_ioctl_ops = {
+       .vidioc_querycap                = empress_querycap,
+       .vidioc_enum_fmt_vid_cap        = empress_enum_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap         = empress_try_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap           = empress_s_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = empress_g_fmt_vid_cap,
+       .vidioc_reqbufs                 = empress_reqbufs,
+       .vidioc_querybuf                = empress_querybuf,
+       .vidioc_qbuf                    = empress_qbuf,
+       .vidioc_dqbuf                   = empress_dqbuf,
+       .vidioc_streamon                = empress_streamon,
+       .vidioc_streamoff               = empress_streamoff,
+       .vidioc_s_ext_ctrls             = empress_s_ext_ctrls,
+       .vidioc_g_ext_ctrls             = empress_g_ext_ctrls,
+       .vidioc_enum_input              = empress_enum_input,
+       .vidioc_g_input                 = empress_g_input,
+       .vidioc_s_input                 = empress_s_input,
+       .vidioc_queryctrl               = empress_queryctrl,
+       .vidioc_querymenu               = empress_querymenu,
+       .vidioc_g_ctrl                  = empress_g_ctrl,
+       .vidioc_s_ctrl                  = empress_s_ctrl,
+       .vidioc_g_chip_ident            = empress_g_chip_ident,
+       .vidioc_s_std                   = empress_s_std,
+       .vidioc_g_std                   = empress_g_std,
+ };
+ 
+ /* ----------------------------------------------------------- */
+ 
+ static struct video_device saa7134_empress_template = {
+       .name          = "saa7134-empress",
+       .fops          = &ts_fops,
+       .ioctl_ops     = &ts_ioctl_ops,
+ 
+       .tvnorms                        = SAA7134_NORMS,
+       .current_norm                   = V4L2_STD_PAL,
+ };
+ 
+ static void empress_signal_update(struct work_struct *work)
+ {
+       struct saa7134_dev* dev =
+               container_of(work, struct saa7134_dev, empress_workqueue);
+ 
+       if (dev->nosignal) {
+               dprintk("no video signal\n");
+       } else {
+               dprintk("video signal acquired\n");
+       }
+ }
+ 
+ static void empress_signal_change(struct saa7134_dev *dev)
+ {
+       schedule_work(&dev->empress_workqueue);
+ }
+ 
+ 
+ static int empress_init(struct saa7134_dev *dev)
+ {
+       int err;
+ 
+       dprintk("%s: %s\n",dev->name,__func__);
+       dev->empress_dev = video_device_alloc();
+       if (NULL == dev->empress_dev)
+               return -ENOMEM;
+       *(dev->empress_dev) = saa7134_empress_template;
+       dev->empress_dev->parent  = &dev->pci->dev;
+       dev->empress_dev->release = video_device_release;
+       snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
+                "%s empress (%s)", dev->name,
+                saa7134_boards[dev->board].name);
+ 
+       INIT_WORK(&dev->empress_workqueue, empress_signal_update);
+ 
+       video_set_drvdata(dev->empress_dev, dev);
+       err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
+                                   empress_nr[dev->nr]);
+       if (err < 0) {
+               printk(KERN_INFO "%s: can't register video device\n",
+                      dev->name);
+               video_device_release(dev->empress_dev);
+               dev->empress_dev = NULL;
+               return err;
+       }
+       printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+              dev->name, video_device_node_name(dev->empress_dev));
+ 
+       videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops,
+                           &dev->pci->dev, &dev->slock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_ALTERNATE,
+                           sizeof(struct saa7134_buf),
+                           dev, NULL);
+ 
+       empress_signal_update(&dev->empress_workqueue);
+       return 0;
+ }
+ 
+ static int empress_fini(struct saa7134_dev *dev)
+ {
+       dprintk("%s: %s\n",dev->name,__func__);
+ 
+       if (NULL == dev->empress_dev)
+               return 0;
++      flush_work(&dev->empress_workqueue);
+       video_unregister_device(dev->empress_dev);
+       dev->empress_dev = NULL;
+       return 0;
+ }
+ 
+ static struct saa7134_mpeg_ops empress_ops = {
+       .type          = SAA7134_MPEG_EMPRESS,
+       .init          = empress_init,
+       .fini          = empress_fini,
+       .signal_change = empress_signal_change,
+ };
+ 
+ static int __init empress_register(void)
+ {
+       return saa7134_ts_register(&empress_ops);
+ }
+ 
+ static void __exit empress_unregister(void)
+ {
+       saa7134_ts_unregister(&empress_ops);
+ }
+ 
+ module_init(empress_register);
+ module_exit(empress_unregister);
+ 
+ /* ----------------------------------------------------------- */
+ /*
+  * Local variables:
+  * c-basic-offset: 8
+  * End:
+  */
 
--- /dev/null
 -#include <mach/i2c.h>
+ /*
+  * Copyright (C) 2010 Texas Instruments Inc
+  *
+  * 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 version 2.
+  *
+  * 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
+  */
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/ctype.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+ #include <linux/videodev2.h>
+ #include <linux/slab.h>
+ 
+ #include <mach/hardware.h>
+ #include <mach/mux.h>
++#include <linux/platform_data/i2c-davinci.h>
+ 
+ #include <linux/io.h>
+ 
+ #include <media/davinci/vpbe_types.h>
+ #include <media/davinci/vpbe_venc.h>
+ #include <media/davinci/vpss.h>
+ #include <media/v4l2-device.h>
+ 
+ #include "vpbe_venc_regs.h"
+ 
+ #define MODULE_NAME   VPBE_VENC_SUBDEV_NAME
+ 
+ static int debug = 2;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug, "Debug level 0-2");
+ 
+ struct venc_state {
+       struct v4l2_subdev sd;
+       struct venc_callback *callback;
+       struct venc_platform_data *pdata;
+       struct device *pdev;
+       u32 output;
+       v4l2_std_id std;
+       spinlock_t lock;
+       void __iomem *venc_base;
+       void __iomem *vdaccfg_reg;
+ };
+ 
+ static inline struct venc_state *to_state(struct v4l2_subdev *sd)
+ {
+       return container_of(sd, struct venc_state, sd);
+ }
+ 
+ static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset)
+ {
+       struct venc_state *venc = to_state(sd);
+ 
+       return readl(venc->venc_base + offset);
+ }
+ 
+ static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val)
+ {
+       struct venc_state *venc = to_state(sd);
+ 
+       writel(val, (venc->venc_base + offset));
+ 
+       return val;
+ }
+ 
+ static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset,
+                                u32 val, u32 mask)
+ {
+       u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask);
+ 
+       venc_write(sd, offset, new_val);
+ 
+       return new_val;
+ }
+ 
+ static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val)
+ {
+       struct venc_state *venc = to_state(sd);
+ 
+       writel(val, venc->vdaccfg_reg);
+ 
+       val = readl(venc->vdaccfg_reg);
+ 
+       return val;
+ }
+ 
+ #define VDAC_COMPONENT        0x543
+ #define VDAC_S_VIDEO  0x210
+ /* This function sets the dac of the VPBE for various outputs
+  */
+ static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index)
+ {
+       switch (out_index) {
+       case 0:
+               v4l2_dbg(debug, 1, sd, "Setting output to Composite\n");
+               venc_write(sd, VENC_DACSEL, 0);
+               break;
+       case 1:
+               v4l2_dbg(debug, 1, sd, "Setting output to Component\n");
+               venc_write(sd, VENC_DACSEL, VDAC_COMPONENT);
+               break;
+       case 2:
+               v4l2_dbg(debug, 1, sd, "Setting output to S-video\n");
+               venc_write(sd, VENC_DACSEL, VDAC_S_VIDEO);
+               break;
+       default:
+               return -EINVAL;
+       }
+ 
+       return 0;
+ }
+ 
+ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable)
+ {
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+       v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n");
+ 
+       if (benable) {
+               venc_write(sd, VENC_VMOD, 0);
+               venc_write(sd, VENC_CVBS, 0);
+               venc_write(sd, VENC_LCDOUT, 0);
+               venc_write(sd, VENC_HSPLS, 0);
+               venc_write(sd, VENC_HSTART, 0);
+               venc_write(sd, VENC_HVALID, 0);
+               venc_write(sd, VENC_HINT, 0);
+               venc_write(sd, VENC_VSPLS, 0);
+               venc_write(sd, VENC_VSTART, 0);
+               venc_write(sd, VENC_VVALID, 0);
+               venc_write(sd, VENC_VINT, 0);
+               venc_write(sd, VENC_YCCCTL, 0);
+               venc_write(sd, VENC_DACSEL, 0);
+ 
+       } else {
+               venc_write(sd, VENC_VMOD, 0);
+               /* disable VCLK output pin enable */
+               venc_write(sd, VENC_VIDCTL, 0x141);
+ 
+               /* Disable output sync pins */
+               venc_write(sd, VENC_SYNCCTL, 0);
+ 
+               /* Disable DCLOCK */
+               venc_write(sd, VENC_DCLKCTL, 0);
+               venc_write(sd, VENC_DRGBX1, 0x0000057C);
+ 
+               /* Disable LCD output control (accepting default polarity) */
+               venc_write(sd, VENC_LCDOUT, 0);
+               if (pdata->venc_type != VPBE_VERSION_3)
+                       venc_write(sd, VENC_CMPNT, 0x100);
+               venc_write(sd, VENC_HSPLS, 0);
+               venc_write(sd, VENC_HINT, 0);
+               venc_write(sd, VENC_HSTART, 0);
+               venc_write(sd, VENC_HVALID, 0);
+ 
+               venc_write(sd, VENC_VSPLS, 0);
+               venc_write(sd, VENC_VINT, 0);
+               venc_write(sd, VENC_VSTART, 0);
+               venc_write(sd, VENC_VVALID, 0);
+ 
+               venc_write(sd, VENC_HSDLY, 0);
+               venc_write(sd, VENC_VSDLY, 0);
+ 
+               venc_write(sd, VENC_YCCCTL, 0);
+               venc_write(sd, VENC_VSTARTA, 0);
+ 
+               /* Set OSD clock and OSD Sync Adavance registers */
+               venc_write(sd, VENC_OSDCLK0, 1);
+               venc_write(sd, VENC_OSDCLK1, 2);
+       }
+ }
+ 
+ #define VDAC_CONFIG_SD_V3     0x0E21A6B6
+ #define VDAC_CONFIG_SD_V2     0x081141CF
+ /*
+  * setting NTSC mode
+  */
+ static int venc_set_ntsc(struct v4l2_subdev *sd)
+ {
+       u32 val;
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+ 
+       v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n");
+ 
+       /* Setup clock at VPSS & VENC for SD */
+       vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+       if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0)
+               return -EINVAL;
+ 
+       venc_enabledigitaloutput(sd, 0);
+ 
+       if (pdata->venc_type == VPBE_VERSION_3) {
+               venc_write(sd, VENC_CLKCTL, 0x01);
+               venc_write(sd, VENC_VIDCTL, 0);
+               val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
+       } else if (pdata->venc_type == VPBE_VERSION_2) {
+               venc_write(sd, VENC_CLKCTL, 0x01);
+               venc_write(sd, VENC_VIDCTL, 0);
+               vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
+       } else {
+               /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+               venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+               /* Set REC656 Mode */
+               venc_write(sd, VENC_YCCCTL, 0x1);
+               venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ);
+               venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS);
+       }
+ 
+       venc_write(sd, VENC_VMOD, 0);
+       venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+                       VENC_VMOD_VIE);
+       venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+       venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT),
+                       VENC_VMOD_TVTYP);
+       venc_write(sd, VENC_DACTST, 0x0);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+ 
+       return 0;
+ }
+ 
+ /*
+  * setting PAL mode
+  */
+ static int venc_set_pal(struct v4l2_subdev *sd)
+ {
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+ 
+       v4l2_dbg(debug, 2, sd, "venc_set_pal\n");
+ 
+       /* Setup clock at VPSS & VENC for SD */
+       vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1);
+       if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0)
+               return -EINVAL;
+ 
+       venc_enabledigitaloutput(sd, 0);
+ 
+       if (pdata->venc_type == VPBE_VERSION_3) {
+               venc_write(sd, VENC_CLKCTL, 0x1);
+               venc_write(sd, VENC_VIDCTL, 0);
+               vdaccfg_write(sd, VDAC_CONFIG_SD_V3);
+       } else if (pdata->venc_type == VPBE_VERSION_2) {
+               venc_write(sd, VENC_CLKCTL, 0x1);
+               venc_write(sd, VENC_VIDCTL, 0);
+               vdaccfg_write(sd, VDAC_CONFIG_SD_V2);
+       } else {
+               /* to set VENC CLK DIV to 1 - final clock is 54 MHz */
+               venc_modify(sd, VENC_VIDCTL, 0, 1 << 1);
+               /* Set REC656 Mode */
+               venc_write(sd, VENC_YCCCTL, 0x1);
+       }
+ 
+       venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT,
+                       VENC_SYNCCTL_OVD);
+       venc_write(sd, VENC_VMOD, 0);
+       venc_modify(sd, VENC_VMOD,
+                       (1 << VENC_VMOD_VIE_SHIFT),
+                       VENC_VMOD_VIE);
+       venc_modify(sd, VENC_VMOD,
+                       (0 << VENC_VMOD_VMD), VENC_VMOD_VMD);
+       venc_modify(sd, VENC_VMOD,
+                       (1 << VENC_VMOD_TVTYP_SHIFT),
+                       VENC_VMOD_TVTYP);
+       venc_write(sd, VENC_DACTST, 0x0);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+ 
+       return 0;
+ }
+ 
+ #define VDAC_CONFIG_HD_V2     0x081141EF
+ /*
+  * venc_set_480p59_94
+  *
+  * This function configures the video encoder to EDTV(525p) component setting.
+  */
+ static int venc_set_480p59_94(struct v4l2_subdev *sd)
+ {
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+ 
+       v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n");
+       if ((pdata->venc_type != VPBE_VERSION_1) &&
+           (pdata->venc_type != VPBE_VERSION_2))
+               return -EINVAL;
+ 
+       /* Setup clock at VPSS & VENC for SD */
+       if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0)
+               return -EINVAL;
+ 
+       venc_enabledigitaloutput(sd, 0);
+ 
+       if (pdata->venc_type == VPBE_VERSION_2)
+               vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+       venc_write(sd, VENC_OSDCLK0, 0);
+       venc_write(sd, VENC_OSDCLK1, 1);
+ 
+       if (pdata->venc_type == VPBE_VERSION_1) {
+               venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+                           VENC_VDPRO_DAFRQ);
+               venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+                           VENC_VDPRO_DAUPS);
+       }
+ 
+       venc_write(sd, VENC_VMOD, 0);
+       venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+                   VENC_VMOD_VIE);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+       venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT),
+                   VENC_VMOD_TVTYP);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+                   VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+ 
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+ 
+       return 0;
+ }
+ 
+ /*
+  * venc_set_625p
+  *
+  * This function configures the video encoder to HDTV(625p) component setting
+  */
+ static int venc_set_576p50(struct v4l2_subdev *sd)
+ {
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+ 
+       v4l2_dbg(debug, 2, sd, "venc_set_576p50\n");
+ 
+       if ((pdata->venc_type != VPBE_VERSION_1) &&
+         (pdata->venc_type != VPBE_VERSION_2))
+               return -EINVAL;
+       /* Setup clock at VPSS & VENC for SD */
+       if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0)
+               return -EINVAL;
+ 
+       venc_enabledigitaloutput(sd, 0);
+ 
+       if (pdata->venc_type == VPBE_VERSION_2)
+               vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+ 
+       venc_write(sd, VENC_OSDCLK0, 0);
+       venc_write(sd, VENC_OSDCLK1, 1);
+ 
+       if (pdata->venc_type == VPBE_VERSION_1) {
+               venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ,
+                           VENC_VDPRO_DAFRQ);
+               venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS,
+                           VENC_VDPRO_DAUPS);
+       }
+ 
+       venc_write(sd, VENC_VMOD, 0);
+       venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+                   VENC_VMOD_VIE);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+       venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT),
+                   VENC_VMOD_TVTYP);
+ 
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 <<
+                   VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+ 
+       return 0;
+ }
+ 
+ /*
+  * venc_set_720p60_internal - Setup 720p60 in venc for dm365 only
+  */
+ static int venc_set_720p60_internal(struct v4l2_subdev *sd)
+ {
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+ 
+       if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_720P60) < 0)
+               return -EINVAL;
+ 
+       venc_enabledigitaloutput(sd, 0);
+ 
+       venc_write(sd, VENC_OSDCLK0, 0);
+       venc_write(sd, VENC_OSDCLK1, 1);
+ 
+       venc_write(sd, VENC_VMOD, 0);
+       /* DM365 component HD mode */
+       venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+           VENC_VMOD_VIE);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+       venc_modify(sd, VENC_VMOD, (HDTV_720P << VENC_VMOD_TVTYP_SHIFT),
+                   VENC_VMOD_TVTYP);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+       venc_write(sd, VENC_XHINTVL, 0);
+       return 0;
+ }
+ 
+ /*
+  * venc_set_1080i30_internal - Setup 1080i30 in venc for dm365 only
+  */
+ static int venc_set_1080i30_internal(struct v4l2_subdev *sd)
+ {
+       struct venc_state *venc = to_state(sd);
+       struct venc_platform_data *pdata = venc->pdata;
+ 
+       if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_1080P30) < 0)
+               return -EINVAL;
+ 
+       venc_enabledigitaloutput(sd, 0);
+ 
+       venc_write(sd, VENC_OSDCLK0, 0);
+       venc_write(sd, VENC_OSDCLK1, 1);
+ 
+ 
+       venc_write(sd, VENC_VMOD, 0);
+       /* DM365 component HD mode */
+       venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT),
+                   VENC_VMOD_VIE);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD);
+       venc_modify(sd, VENC_VMOD, (HDTV_1080I << VENC_VMOD_TVTYP_SHIFT),
+                   VENC_VMOD_TVTYP);
+       venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC);
+       venc_write(sd, VENC_XHINTVL, 0);
+       return 0;
+ }
+ 
+ static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
+ {
+       v4l2_dbg(debug, 1, sd, "venc_s_std_output\n");
+ 
+       if (norm & V4L2_STD_525_60)
+               return venc_set_ntsc(sd);
+       else if (norm & V4L2_STD_625_50)
+               return venc_set_pal(sd);
+ 
+       return -EINVAL;
+ }
+ 
+ static int venc_s_dv_preset(struct v4l2_subdev *sd,
+                           struct v4l2_dv_preset *dv_preset)
+ {
+       struct venc_state *venc = to_state(sd);
+       int ret;
+ 
+       v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n");
+ 
+       if (dv_preset->preset == V4L2_DV_576P50)
+               return venc_set_576p50(sd);
+       else if (dv_preset->preset == V4L2_DV_480P59_94)
+               return venc_set_480p59_94(sd);
+       else if ((dv_preset->preset == V4L2_DV_720P60) &&
+                       (venc->pdata->venc_type == VPBE_VERSION_2)) {
+               /* TBD setup internal 720p mode here */
+               ret = venc_set_720p60_internal(sd);
+               /* for DM365 VPBE, there is DAC inside */
+               vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+               return ret;
+       } else if ((dv_preset->preset == V4L2_DV_1080I30) &&
+               (venc->pdata->venc_type == VPBE_VERSION_2)) {
+               /* TBD setup internal 1080i mode here */
+               ret = venc_set_1080i30_internal(sd);
+               /* for DM365 VPBE, there is DAC inside */
+               vdaccfg_write(sd, VDAC_CONFIG_HD_V2);
+               return ret;
+       }
+       return -EINVAL;
+ }
+ 
+ static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+                         u32 config)
+ {
+       struct venc_state *venc = to_state(sd);
+       int ret;
+ 
+       v4l2_dbg(debug, 1, sd, "venc_s_routing\n");
+ 
+       ret = venc_set_dac(sd, output);
+       if (!ret)
+               venc->output = output;
+ 
+       return ret;
+ }
+ 
+ static long venc_ioctl(struct v4l2_subdev *sd,
+                       unsigned int cmd,
+                       void *arg)
+ {
+       u32 val;
+ 
+       switch (cmd) {
+       case VENC_GET_FLD:
+               val = venc_read(sd, VENC_VSTAT);
+               *((int *)arg) = ((val & VENC_VSTAT_FIDST) ==
+               VENC_VSTAT_FIDST);
+               break;
+       default:
+               v4l2_err(sd, "Wrong IOCTL cmd\n");
+               break;
+       }
+ 
+       return 0;
+ }
+ 
+ static const struct v4l2_subdev_core_ops venc_core_ops = {
+       .ioctl      = venc_ioctl,
+ };
+ 
+ static const struct v4l2_subdev_video_ops venc_video_ops = {
+       .s_routing = venc_s_routing,
+       .s_std_output = venc_s_std_output,
+       .s_dv_preset = venc_s_dv_preset,
+ };
+ 
+ static const struct v4l2_subdev_ops venc_ops = {
+       .core = &venc_core_ops,
+       .video = &venc_video_ops,
+ };
+ 
+ static int venc_initialize(struct v4l2_subdev *sd)
+ {
+       struct venc_state *venc = to_state(sd);
+       int ret;
+ 
+       /* Set default to output to composite and std to NTSC */
+       venc->output = 0;
+       venc->std = V4L2_STD_525_60;
+ 
+       ret = venc_s_routing(sd, 0, venc->output, 0);
+       if (ret < 0) {
+               v4l2_err(sd, "Error setting output during init\n");
+               return -EINVAL;
+       }
+ 
+       ret = venc_s_std_output(sd, venc->std);
+       if (ret < 0) {
+               v4l2_err(sd, "Error setting std during init\n");
+               return -EINVAL;
+       }
+ 
+       return ret;
+ }
+ 
+ static int venc_device_get(struct device *dev, void *data)
+ {
+       struct platform_device *pdev = to_platform_device(dev);
+       struct venc_state **venc = data;
+ 
+       if (strcmp(MODULE_NAME, pdev->name) == 0)
+               *venc = platform_get_drvdata(pdev);
+ 
+       return 0;
+ }
+ 
+ struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev,
+               const char *venc_name)
+ {
+       struct venc_state *venc;
+       int err;
+ 
+       err = bus_for_each_dev(&platform_bus_type, NULL, &venc,
+                       venc_device_get);
+       if (venc == NULL)
+               return NULL;
+ 
+       v4l2_subdev_init(&venc->sd, &venc_ops);
+ 
+       strcpy(venc->sd.name, venc_name);
+       if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) {
+               v4l2_err(v4l2_dev,
+                       "vpbe unable to register venc sub device\n");
+               return NULL;
+       }
+       if (venc_initialize(&venc->sd)) {
+               v4l2_err(v4l2_dev,
+                       "vpbe venc initialization failed\n");
+               return NULL;
+       }
+ 
+       return &venc->sd;
+ }
+ EXPORT_SYMBOL(venc_sub_dev_init);
+ 
+ static int venc_probe(struct platform_device *pdev)
+ {
+       struct venc_state *venc;
+       struct resource *res;
+       int ret;
+ 
+       venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
+       if (venc == NULL)
+               return -ENOMEM;
+ 
+       venc->pdev = &pdev->dev;
+       venc->pdata = pdev->dev.platform_data;
+       if (NULL == venc->pdata) {
+               dev_err(venc->pdev, "Unable to get platform data for"
+                       " VENC sub device");
+               ret = -ENOENT;
+               goto free_mem;
+       }
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(venc->pdev,
+                       "Unable to get VENC register address map\n");
+               ret = -ENODEV;
+               goto free_mem;
+       }
+ 
+       if (!request_mem_region(res->start, resource_size(res), "venc")) {
+               dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
+               ret = -ENODEV;
+               goto free_mem;
+       }
+ 
+       venc->venc_base = ioremap_nocache(res->start, resource_size(res));
+       if (!venc->venc_base) {
+               dev_err(venc->pdev, "Unable to map VENC IO space\n");
+               ret = -ENODEV;
+               goto release_venc_mem_region;
+       }
+ 
+       if (venc->pdata->venc_type != VPBE_VERSION_1) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               if (!res) {
+                       dev_err(venc->pdev,
+                               "Unable to get VDAC_CONFIG address map\n");
+                       ret = -ENODEV;
+                       goto unmap_venc_io;
+               }
+ 
+               if (!request_mem_region(res->start,
+                                       resource_size(res), "venc")) {
+                       dev_err(venc->pdev,
+                               "Unable to reserve VDAC_CONFIG  MMIO region\n");
+                       ret = -ENODEV;
+                       goto unmap_venc_io;
+               }
+ 
+               venc->vdaccfg_reg = ioremap_nocache(res->start,
+                                                   resource_size(res));
+               if (!venc->vdaccfg_reg) {
+                       dev_err(venc->pdev,
+                               "Unable to map VDAC_CONFIG IO space\n");
+                       ret = -ENODEV;
+                       goto release_vdaccfg_mem_region;
+               }
+       }
+       spin_lock_init(&venc->lock);
+       platform_set_drvdata(pdev, venc);
+       dev_notice(venc->pdev, "VENC sub device probe success\n");
+       return 0;
+ 
+ release_vdaccfg_mem_region:
+       release_mem_region(res->start, resource_size(res));
+ unmap_venc_io:
+       iounmap(venc->venc_base);
+ release_venc_mem_region:
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+ free_mem:
+       kfree(venc);
+       return ret;
+ }
+ 
+ static int venc_remove(struct platform_device *pdev)
+ {
+       struct venc_state *venc = platform_get_drvdata(pdev);
+       struct resource *res;
+ 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       iounmap((void *)venc->venc_base);
+       release_mem_region(res->start, resource_size(res));
+       if (venc->pdata->venc_type != VPBE_VERSION_1) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+               iounmap((void *)venc->vdaccfg_reg);
+               release_mem_region(res->start, resource_size(res));
+       }
+       kfree(venc);
+ 
+       return 0;
+ }
+ 
+ static struct platform_driver venc_driver = {
+       .probe          = venc_probe,
+       .remove         = venc_remove,
+       .driver         = {
+               .name   = MODULE_NAME,
+               .owner  = THIS_MODULE,
+       },
+ };
+ 
+ module_platform_driver(venc_driver);
+ 
+ MODULE_LICENSE("GPL");
+ MODULE_DESCRIPTION("VPBE VENC Driver");
+ MODULE_AUTHOR("Texas Instruments");
 
--- /dev/null
+ /*
+  * omap_vout.c
+  *
+  * Copyright (C) 2005-2010 Texas Instruments.
+  *
+  * This file is licensed under the terms of the GNU General Public License
+  * version 2. This program is licensed "as is" without any warranty of any
+  * kind, whether express or implied.
+  *
+  * Leveraged code from the OMAP2 camera driver
+  * Video-for-Linux (Version 2) camera capture driver for
+  * the OMAP24xx camera controller.
+  *
+  * Author: Andy Lowe (source@mvista.com)
+  *
+  * Copyright (C) 2004 MontaVista Software, Inc.
+  * Copyright (C) 2010 Texas Instruments.
+  *
+  * History:
+  * 20-APR-2006 Khasim         Modified VRFB based Rotation,
+  *                            The image data is always read from 0 degree
+  *                            view and written
+  *                            to the virtual space of desired rotation angle
+  * 4-DEC-2006  Jian           Changed to support better memory management
+  *
+  * 17-Nov-2008 Hardik         Changed driver to use video_ioctl2
+  *
+  * 23-Feb-2010 Vaibhav H      Modified to use new DSS2 interface
+  *
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/vmalloc.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+ #include <linux/platform_device.h>
+ #include <linux/irq.h>
+ #include <linux/videodev2.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/slab.h>
+ 
+ #include <media/videobuf-dma-contig.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+ 
++#include <plat/cpu.h>
+ #include <plat/dma.h>
+ #include <plat/vrfb.h>
+ #include <video/omapdss.h>
+ 
+ #include "omap_voutlib.h"
+ #include "omap_voutdef.h"
+ #include "omap_vout_vrfb.h"
+ 
+ MODULE_AUTHOR("Texas Instruments");
+ MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
+ MODULE_LICENSE("GPL");
+ 
+ /* Driver Configuration macros */
+ #define VOUT_NAME             "omap_vout"
+ 
+ enum omap_vout_channels {
+       OMAP_VIDEO1,
+       OMAP_VIDEO2,
+ };
+ 
+ static struct videobuf_queue_ops video_vbq_ops;
+ /* Variables configurable through module params*/
+ static u32 video1_numbuffers = 3;
+ static u32 video2_numbuffers = 3;
+ static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+ static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+ static bool vid1_static_vrfb_alloc;
+ static bool vid2_static_vrfb_alloc;
+ static bool debug;
+ 
+ /* Module parameters */
+ module_param(video1_numbuffers, uint, S_IRUGO);
+ MODULE_PARM_DESC(video1_numbuffers,
+       "Number of buffers to be allocated at init time for Video1 device.");
+ 
+ module_param(video2_numbuffers, uint, S_IRUGO);
+ MODULE_PARM_DESC(video2_numbuffers,
+       "Number of buffers to be allocated at init time for Video2 device.");
+ 
+ module_param(video1_bufsize, uint, S_IRUGO);
+ MODULE_PARM_DESC(video1_bufsize,
+       "Size of the buffer to be allocated for video1 device");
+ 
+ module_param(video2_bufsize, uint, S_IRUGO);
+ MODULE_PARM_DESC(video2_bufsize,
+       "Size of the buffer to be allocated for video2 device");
+ 
+ module_param(vid1_static_vrfb_alloc, bool, S_IRUGO);
+ MODULE_PARM_DESC(vid1_static_vrfb_alloc,
+       "Static allocation of the VRFB buffer for video1 device");
+ 
+ module_param(vid2_static_vrfb_alloc, bool, S_IRUGO);
+ MODULE_PARM_DESC(vid2_static_vrfb_alloc,
+       "Static allocation of the VRFB buffer for video2 device");
+ 
+ module_param(debug, bool, S_IRUGO);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+ 
+ /* list of image formats supported by OMAP2 video pipelines */
+ static const struct v4l2_fmtdesc omap_formats[] = {
+       {
+               /* Note:  V4L2 defines RGB565 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 r4 r3 r2 r1 r0   b4 b3 b2 b1 b0 g5 g4 g3
+                *
+                * We interpret RGB565 as:
+                *
+                *      Byte 0                    Byte 1
+                *      g2 g1 g0 b4 b3 b2 b1 b0   r4 r3 r2 r1 r0 g5 g4 g3
+                */
+               .description = "RGB565, le",
+               .pixelformat = V4L2_PIX_FMT_RGB565,
+       },
+       {
+               /* Note:  V4L2 defines RGB32 as: RGB-8-8-8-8  we use
+                *  this for RGB24 unpack mode, the last 8 bits are ignored
+                * */
+               .description = "RGB32, le",
+               .pixelformat = V4L2_PIX_FMT_RGB32,
+       },
+       {
+               /* Note:  V4L2 defines RGB24 as: RGB-8-8-8  we use
+                *        this for RGB24 packed mode
+                *
+                */
+               .description = "RGB24, le",
+               .pixelformat = V4L2_PIX_FMT_RGB24,
+       },
+       {
+               .description = "YUYV (YUV 4:2:2), packed",
+               .pixelformat = V4L2_PIX_FMT_YUYV,
+       },
+       {
+               .description = "UYVY, packed",
+               .pixelformat = V4L2_PIX_FMT_UYVY,
+       },
+ };
+ 
+ #define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
+ 
+ /*
+  * Try format
+  */
+ static int omap_vout_try_format(struct v4l2_pix_format *pix)
+ {
+       int ifmt, bpp = 0;
+ 
+       pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT,
+                                               (u32)VID_MAX_HEIGHT);
+       pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH);
+ 
+       for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) {
+               if (pix->pixelformat == omap_formats[ifmt].pixelformat)
+                       break;
+       }
+ 
+       if (ifmt == NUM_OUTPUT_FORMATS)
+               ifmt = 0;
+ 
+       pix->pixelformat = omap_formats[ifmt].pixelformat;
+       pix->field = V4L2_FIELD_ANY;
+       pix->priv = 0;
+ 
+       switch (pix->pixelformat) {
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_UYVY:
+       default:
+               pix->colorspace = V4L2_COLORSPACE_JPEG;
+               bpp = YUYV_BPP;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB565X:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               bpp = RGB565_BPP;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               bpp = RGB24_BPP;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+       case V4L2_PIX_FMT_BGR32:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               bpp = RGB32_BPP;
+               break;
+       }
+       pix->bytesperline = pix->width * bpp;
+       pix->sizeimage = pix->bytesperline * pix->height;
+ 
+       return bpp;
+ }
+ 
+ /*
+  * omap_vout_uservirt_to_phys: This inline function is used to convert user
+  * space virtual address to physical address.
+  */
+ static u32 omap_vout_uservirt_to_phys(u32 virtp)
+ {
+       unsigned long physp = 0;
+       struct vm_area_struct *vma;
+       struct mm_struct *mm = current->mm;
+ 
+       vma = find_vma(mm, virtp);
+       /* For kernel direct-mapped memory, take the easy way */
+       if (virtp >= PAGE_OFFSET) {
+               physp = virt_to_phys((void *) virtp);
+       } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
+               /* this will catch, kernel-allocated, mmaped-to-usermode
+                  addresses */
+               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+       } else {
+               /* otherwise, use get_user_pages() for general userland pages */
+               int res, nr_pages = 1;
+               struct page *pages;
+               down_read(¤t->mm->mmap_sem);
+ 
+               res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
+                               0, &pages, NULL);
+               up_read(¤t->mm->mmap_sem);
+ 
+               if (res == nr_pages) {
+                       physp =  __pa(page_address(&pages[0]) +
+                                       (virtp & ~PAGE_MASK));
+               } else {
+                       printk(KERN_WARNING VOUT_NAME
+                                       "get_user_pages failed\n");
+                       return 0;
+               }
+       }
+ 
+       return physp;
+ }
+ 
+ /*
+  * Free the V4L2 buffers
+  */
+ void omap_vout_free_buffers(struct omap_vout_device *vout)
+ {
+       int i, numbuffers;
+ 
+       /* Allocate memory for the buffers */
+       numbuffers = (vout->vid) ?  video2_numbuffers : video1_numbuffers;
+       vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize;
+ 
+       for (i = 0; i < numbuffers; i++) {
+               omap_vout_free_buffer(vout->buf_virt_addr[i],
+                               vout->buffer_size);
+               vout->buf_phy_addr[i] = 0;
+               vout->buf_virt_addr[i] = 0;
+       }
+ }
+ 
+ /*
+  * Convert V4L2 rotation to DSS rotation
+  *    V4L2 understand 0, 90, 180, 270.
+  *    Convert to 0, 1, 2 and 3 respectively for DSS
+  */
+ static int v4l2_rot_to_dss_rot(int v4l2_rotation,
+                       enum dss_rotation *rotation, bool mirror)
+ {
+       int ret = 0;
+ 
+       switch (v4l2_rotation) {
+       case 90:
+               *rotation = dss_rotation_90_degree;
+               break;
+       case 180:
+               *rotation = dss_rotation_180_degree;
+               break;
+       case 270:
+               *rotation = dss_rotation_270_degree;
+               break;
+       case 0:
+               *rotation = dss_rotation_0_degree;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ 
+ static int omap_vout_calculate_offset(struct omap_vout_device *vout)
+ {
+       struct omapvideo_info *ovid;
+       struct v4l2_rect *crop = &vout->crop;
+       struct v4l2_pix_format *pix = &vout->pix;
+       int *cropped_offset = &vout->cropped_offset;
+       int ps = 2, line_length = 0;
+ 
+       ovid = &vout->vid_info;
+ 
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               omap_vout_calculate_vrfb_offset(vout);
+       } else {
+               vout->line_length = line_length = pix->width;
+ 
+               if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+                       V4L2_PIX_FMT_UYVY == pix->pixelformat)
+                       ps = 2;
+               else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
+                       ps = 4;
+               else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
+                       ps = 3;
+ 
+               vout->ps = ps;
+ 
+               *cropped_offset = (line_length * ps) *
+                       crop->top + crop->left * ps;
+       }
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
+                       __func__, vout->cropped_offset);
+ 
+       return 0;
+ }
+ 
+ /*
+  * Convert V4L2 pixel format to DSS pixel format
+  */
+ static int video_mode_to_dss_mode(struct omap_vout_device *vout)
+ {
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct v4l2_pix_format *pix = &vout->pix;
+       enum omap_color_mode mode;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       switch (pix->pixelformat) {
+       case 0:
+               break;
+       case V4L2_PIX_FMT_YUYV:
+               mode = OMAP_DSS_COLOR_YUV2;
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               mode = OMAP_DSS_COLOR_UYVY;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               mode = OMAP_DSS_COLOR_RGB16;
+               break;
+       case V4L2_PIX_FMT_RGB24:
+               mode = OMAP_DSS_COLOR_RGB24P;
+               break;
+       case V4L2_PIX_FMT_RGB32:
+               mode = (ovl->id == OMAP_DSS_VIDEO1) ?
+                       OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32;
+               break;
+       case V4L2_PIX_FMT_BGR32:
+               mode = OMAP_DSS_COLOR_RGBX32;
+               break;
+       default:
+               mode = -EINVAL;
+       }
+       return mode;
+ }
+ 
+ /*
+  * Setup the overlay
+  */
+ static int omapvid_setup_overlay(struct omap_vout_device *vout,
+               struct omap_overlay *ovl, int posx, int posy, int outw,
+               int outh, u32 addr)
+ {
+       int ret = 0;
+       struct omap_overlay_info info;
+       int cropheight, cropwidth, pixheight, pixwidth;
+ 
+       if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
+                       (outw != vout->pix.width || outh != vout->pix.height)) {
+               ret = -EINVAL;
+               goto setup_ovl_err;
+       }
+ 
+       vout->dss_mode = video_mode_to_dss_mode(vout);
+       if (vout->dss_mode == -EINVAL) {
+               ret = -EINVAL;
+               goto setup_ovl_err;
+       }
+ 
+       /* Setup the input plane parameters according to
+        * rotation value selected.
+        */
+       if (is_rotation_90_or_270(vout)) {
+               cropheight = vout->crop.width;
+               cropwidth = vout->crop.height;
+               pixheight = vout->pix.width;
+               pixwidth = vout->pix.height;
+       } else {
+               cropheight = vout->crop.height;
+               cropwidth = vout->crop.width;
+               pixheight = vout->pix.height;
+               pixwidth = vout->pix.width;
+       }
+ 
+       ovl->get_overlay_info(ovl, &info);
+       info.paddr = addr;
+       info.width = cropwidth;
+       info.height = cropheight;
+       info.color_mode = vout->dss_mode;
+       info.mirror = vout->mirror;
+       info.pos_x = posx;
+       info.pos_y = posy;
+       info.out_width = outw;
+       info.out_height = outh;
+       info.global_alpha = vout->win.global_alpha;
+       if (!is_rotation_enabled(vout)) {
+               info.rotation = 0;
+               info.rotation_type = OMAP_DSS_ROT_DMA;
+               info.screen_width = pixwidth;
+       } else {
+               info.rotation = vout->rotation;
+               info.rotation_type = OMAP_DSS_ROT_VRFB;
+               info.screen_width = 2048;
+       }
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+               "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
+               "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
+               "out_height=%d rotation_type=%d screen_width=%d\n",
+               __func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
+               info.color_mode, info.rotation, info.mirror, info.pos_x,
+               info.pos_y, info.out_width, info.out_height, info.rotation_type,
+               info.screen_width);
+ 
+       ret = ovl->set_overlay_info(ovl, &info);
+       if (ret)
+               goto setup_ovl_err;
+ 
+       return 0;
+ 
+ setup_ovl_err:
+       v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n");
+       return ret;
+ }
+ 
+ /*
+  * Initialize the overlay structure
+  */
+ static int omapvid_init(struct omap_vout_device *vout, u32 addr)
+ {
+       int ret = 0, i;
+       struct v4l2_window *win;
+       struct omap_overlay *ovl;
+       int posx, posy, outw, outh, temp;
+       struct omap_video_timings *timing;
+       struct omapvideo_info *ovid = &vout->vid_info;
+ 
+       win = &vout->win;
+       for (i = 0; i < ovid->num_overlays; i++) {
+               ovl = ovid->overlays[i];
+               if (!ovl->manager || !ovl->manager->device)
+                       return -EINVAL;
+ 
+               timing = &ovl->manager->device->panel.timings;
+ 
+               outw = win->w.width;
+               outh = win->w.height;
+               switch (vout->rotation) {
+               case dss_rotation_90_degree:
+                       /* Invert the height and width for 90
+                        * and 270 degree rotation
+                        */
+                       temp = outw;
+                       outw = outh;
+                       outh = temp;
+                       posy = (timing->y_res - win->w.width) - win->w.left;
+                       posx = win->w.top;
+                       break;
+ 
+               case dss_rotation_180_degree:
+                       posx = (timing->x_res - win->w.width) - win->w.left;
+                       posy = (timing->y_res - win->w.height) - win->w.top;
+                       break;
+ 
+               case dss_rotation_270_degree:
+                       temp = outw;
+                       outw = outh;
+                       outh = temp;
+                       posy = win->w.left;
+                       posx = (timing->x_res - win->w.height) - win->w.top;
+                       break;
+ 
+               default:
+                       posx = win->w.left;
+                       posy = win->w.top;
+                       break;
+               }
+ 
+               ret = omapvid_setup_overlay(vout, ovl, posx, posy,
+                               outw, outh, addr);
+               if (ret)
+                       goto omapvid_init_err;
+       }
+       return 0;
+ 
+ omapvid_init_err:
+       v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n");
+       return ret;
+ }
+ 
+ /*
+  * Apply the changes set the go bit of DSS
+  */
+ static int omapvid_apply_changes(struct omap_vout_device *vout)
+ {
+       int i;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid = &vout->vid_info;
+ 
+       for (i = 0; i < ovid->num_overlays; i++) {
+               ovl = ovid->overlays[i];
+               if (!ovl->manager || !ovl->manager->device)
+                       return -EINVAL;
+               ovl->manager->apply(ovl->manager);
+       }
+ 
+       return 0;
+ }
+ 
+ static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
+               unsigned int irqstatus, struct timeval timevalue)
+ {
+       u32 fid;
+ 
+       if (vout->first_int) {
+               vout->first_int = 0;
+               goto err;
+       }
+ 
+       if (irqstatus & DISPC_IRQ_EVSYNC_ODD)
+               fid = 1;
+       else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN)
+               fid = 0;
+       else
+               goto err;
+ 
+       vout->field_id ^= 1;
+       if (fid != vout->field_id) {
+               if (fid == 0)
+                       vout->field_id = fid;
+       } else if (0 == fid) {
+               if (vout->cur_frm == vout->next_frm)
+                       goto err;
+ 
+               vout->cur_frm->ts = timevalue;
+               vout->cur_frm->state = VIDEOBUF_DONE;
+               wake_up_interruptible(&vout->cur_frm->done);
+               vout->cur_frm = vout->next_frm;
+       } else {
+               if (list_empty(&vout->dma_queue) ||
+                               (vout->cur_frm != vout->next_frm))
+                       goto err;
+       }
+ 
+       return vout->field_id;
+ err:
+       return 0;
+ }
+ 
+ static void omap_vout_isr(void *arg, unsigned int irqstatus)
+ {
+       int ret, fid, mgr_id;
+       u32 addr, irq;
+       struct omap_overlay *ovl;
+       struct timeval timevalue;
+       struct omapvideo_info *ovid;
+       struct omap_dss_device *cur_display;
+       struct omap_vout_device *vout = (struct omap_vout_device *)arg;
+ 
+       if (!vout->streaming)
+               return;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+       /* get the display device attached to the overlay */
+       if (!ovl->manager || !ovl->manager->device)
+               return;
+ 
+       mgr_id = ovl->manager->id;
+       cur_display = ovl->manager->device;
+ 
+       spin_lock(&vout->vbq_lock);
+       do_gettimeofday(&timevalue);
+ 
+       switch (cur_display->type) {
+       case OMAP_DISPLAY_TYPE_DSI:
+       case OMAP_DISPLAY_TYPE_DPI:
+               if (mgr_id == OMAP_DSS_CHANNEL_LCD)
+                       irq = DISPC_IRQ_VSYNC;
+               else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
+                       irq = DISPC_IRQ_VSYNC2;
+               else
+                       goto vout_isr_err;
+ 
+               if (!(irqstatus & irq))
+                       goto vout_isr_err;
+               break;
+       case OMAP_DISPLAY_TYPE_VENC:
+               fid = omapvid_handle_interlace_display(vout, irqstatus,
+                               timevalue);
+               if (!fid)
+                       goto vout_isr_err;
+               break;
+       case OMAP_DISPLAY_TYPE_HDMI:
+               if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
+                       goto vout_isr_err;
+               break;
+       default:
+               goto vout_isr_err;
+       }
+ 
+       if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
+               vout->cur_frm->ts = timevalue;
+               vout->cur_frm->state = VIDEOBUF_DONE;
+               wake_up_interruptible(&vout->cur_frm->done);
+               vout->cur_frm = vout->next_frm;
+       }
+ 
+       vout->first_int = 0;
+       if (list_empty(&vout->dma_queue))
+               goto vout_isr_err;
+ 
+       vout->next_frm = list_entry(vout->dma_queue.next,
+                       struct videobuf_buffer, queue);
+       list_del(&vout->next_frm->queue);
+ 
+       vout->next_frm->state = VIDEOBUF_ACTIVE;
+ 
+       addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i]
+               + vout->cropped_offset;
+ 
+       /* First save the configuration in ovelray structure */
+       ret = omapvid_init(vout, addr);
+       if (ret)
+               printk(KERN_ERR VOUT_NAME
+                       "failed to set overlay info\n");
+       /* Enable the pipeline and set the Go bit */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               printk(KERN_ERR VOUT_NAME "failed to change mode\n");
+ 
+ vout_isr_err:
+       spin_unlock(&vout->vbq_lock);
+ }
+ 
+ /* Video buffer call backs */
+ 
+ /*
+  * Buffer setup function is called by videobuf layer when REQBUF ioctl is
+  * called. This is used to setup buffers and return size and count of
+  * buffers allocated. After the call to this buffer, videobuf layer will
+  * setup buffer queue depending on the size and count of buffers
+  */
+ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+                         unsigned int *size)
+ {
+       int startindex = 0, i, j;
+       u32 phy_addr = 0, virt_addr = 0;
+       struct omap_vout_device *vout = q->priv_data;
+       struct omapvideo_info *ovid = &vout->vid_info;
+       int vid_max_buf_size;
+ 
+       if (!vout)
+               return -EINVAL;
+ 
+       vid_max_buf_size = vout->vid == OMAP_VIDEO1 ? video1_bufsize :
+               video2_bufsize;
+ 
+       if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
+               return -EINVAL;
+ 
+       startindex = (vout->vid == OMAP_VIDEO1) ?
+               video1_numbuffers : video2_numbuffers;
+       if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
+               *count = startindex;
+ 
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
+                       return -ENOMEM;
+       }
+ 
+       if (V4L2_MEMORY_MMAP != vout->memory)
+               return 0;
+ 
+       /* Now allocated the V4L2 buffers */
+       *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp);
+       startindex = (vout->vid == OMAP_VIDEO1) ?
+               video1_numbuffers : video2_numbuffers;
+ 
+       /* Check the size of the buffer */
+       if (*size > vid_max_buf_size) {
+               v4l2_err(&vout->vid_dev->v4l2_dev,
+                               "buffer allocation mismatch [%u] [%u]\n",
+                               *size, vout->buffer_size);
+               return -ENOMEM;
+       }
+ 
+       for (i = startindex; i < *count; i++) {
+               vout->buffer_size = *size;
+ 
+               virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
+                               &phy_addr);
+               if (!virt_addr) {
+                       if (ovid->rotation_type == VOUT_ROT_NONE) {
+                               break;
+                       } else {
+                               if (!is_rotation_enabled(vout))
+                                       break;
+                       /* Free the VRFB buffers if no space for V4L2 buffers */
+                       for (j = i; j < *count; j++) {
+                               omap_vout_free_buffer(
+                                               vout->smsshado_virt_addr[j],
+                                               vout->smsshado_size);
+                               vout->smsshado_virt_addr[j] = 0;
+                               vout->smsshado_phy_addr[j] = 0;
+                               }
+                       }
+               }
+               vout->buf_virt_addr[i] = virt_addr;
+               vout->buf_phy_addr[i] = phy_addr;
+       }
+       *count = vout->buffer_allocated = i;
+ 
+       return 0;
+ }
+ 
+ /*
+  * Free the V4L2 buffers additionally allocated than default
+  * number of buffers
+  */
+ static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
+ {
+       int num_buffers = 0, i;
+ 
+       num_buffers = (vout->vid == OMAP_VIDEO1) ?
+               video1_numbuffers : video2_numbuffers;
+ 
+       for (i = num_buffers; i < vout->buffer_allocated; i++) {
+               if (vout->buf_virt_addr[i])
+                       omap_vout_free_buffer(vout->buf_virt_addr[i],
+                                       vout->buffer_size);
+ 
+               vout->buf_virt_addr[i] = 0;
+               vout->buf_phy_addr[i] = 0;
+       }
+       vout->buffer_allocated = num_buffers;
+ }
+ 
+ /*
+  * This function will be called when VIDIOC_QBUF ioctl is called.
+  * It prepare buffers before give out for the display. This function
+  * converts user space virtual address into physical address if userptr memory
+  * exchange mechanism is used. If rotation is enabled, it copies entire
+  * buffer into VRFB memory space before giving it to the DSS.
+  */
+ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
+                       struct videobuf_buffer *vb,
+                       enum v4l2_field field)
+ {
+       struct omap_vout_device *vout = q->priv_data;
+       struct omapvideo_info *ovid = &vout->vid_info;
+ 
+       if (VIDEOBUF_NEEDS_INIT == vb->state) {
+               vb->width = vout->pix.width;
+               vb->height = vout->pix.height;
+               vb->size = vb->width * vb->height * vout->bpp;
+               vb->field = field;
+       }
+       vb->state = VIDEOBUF_PREPARED;
+       /* if user pointer memory mechanism is used, get the physical
+        * address of the buffer
+        */
+       if (V4L2_MEMORY_USERPTR == vb->memory) {
+               if (0 == vb->baddr)
+                       return -EINVAL;
+               /* Physical address */
+               vout->queued_buf_addr[vb->i] = (u8 *)
+                       omap_vout_uservirt_to_phys(vb->baddr);
+       } else {
+               u32 addr, dma_addr;
+               unsigned long size;
+ 
+               addr = (unsigned long) vout->buf_virt_addr[vb->i];
+               size = (unsigned long) vb->size;
+ 
+               dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
+                               size, DMA_TO_DEVICE);
+               if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
+                       v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
+ 
+               vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
+       }
+ 
+       if (ovid->rotation_type == VOUT_ROT_VRFB)
+               return omap_vout_prepare_vrfb(vout, vb);
+       else
+               return 0;
+ }
+ 
+ /*
+  * Buffer queue function will be called from the videobuf layer when _QBUF
+  * ioctl is called. It is used to enqueue buffer, which is ready to be
+  * displayed.
+  */
+ static void omap_vout_buffer_queue(struct videobuf_queue *q,
+                         struct videobuf_buffer *vb)
+ {
+       struct omap_vout_device *vout = q->priv_data;
+ 
+       /* Driver is also maintainig a queue. So enqueue buffer in the driver
+        * queue */
+       list_add_tail(&vb->queue, &vout->dma_queue);
+ 
+       vb->state = VIDEOBUF_QUEUED;
+ }
+ 
+ /*
+  * Buffer release function is called from videobuf layer to release buffer
+  * which are already allocated
+  */
+ static void omap_vout_buffer_release(struct videobuf_queue *q,
+                           struct videobuf_buffer *vb)
+ {
+       struct omap_vout_device *vout = q->priv_data;
+ 
+       vb->state = VIDEOBUF_NEEDS_INIT;
+ 
+       if (V4L2_MEMORY_MMAP != vout->memory)
+               return;
+ }
+ 
+ /*
+  *  File operations
+  */
+ static unsigned int omap_vout_poll(struct file *file,
+                                  struct poll_table_struct *wait)
+ {
+       struct omap_vout_device *vout = file->private_data;
+       struct videobuf_queue *q = &vout->vbq;
+ 
+       return videobuf_poll_stream(file, q, wait);
+ }
+ 
+ static void omap_vout_vm_open(struct vm_area_struct *vma)
+ {
+       struct omap_vout_device *vout = vma->vm_private_data;
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+               "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+       vout->mmap_count++;
+ }
+ 
+ static void omap_vout_vm_close(struct vm_area_struct *vma)
+ {
+       struct omap_vout_device *vout = vma->vm_private_data;
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+               "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+       vout->mmap_count--;
+ }
+ 
+ static struct vm_operations_struct omap_vout_vm_ops = {
+       .open   = omap_vout_vm_open,
+       .close  = omap_vout_vm_close,
+ };
+ 
+ static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+       int i;
+       void *pos;
+       unsigned long start = vma->vm_start;
+       unsigned long size = (vma->vm_end - vma->vm_start);
+       struct omap_vout_device *vout = file->private_data;
+       struct videobuf_queue *q = &vout->vbq;
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+                       " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__,
+                       vma->vm_pgoff, vma->vm_start, vma->vm_end);
+ 
+       /* look for the buffer to map */
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
+               if (V4L2_MEMORY_MMAP != q->bufs[i]->memory)
+                       continue;
+               if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+                       break;
+       }
+ 
+       if (VIDEO_MAX_FRAME == i) {
+               v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+                               "offset invalid [offset=0x%lx]\n",
+                               (vma->vm_pgoff << PAGE_SHIFT));
+               return -EINVAL;
+       }
+       /* Check the size of the buffer */
+       if (size > vout->buffer_size) {
+               v4l2_err(&vout->vid_dev->v4l2_dev,
+                               "insufficient memory [%lu] [%u]\n",
+                               size, vout->buffer_size);
+               return -ENOMEM;
+       }
+ 
+       q->bufs[i]->baddr = vma->vm_start;
+ 
+       vma->vm_flags |= VM_RESERVED;
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+       vma->vm_ops = &omap_vout_vm_ops;
+       vma->vm_private_data = (void *) vout;
+       pos = (void *)vout->buf_virt_addr[i];
+       vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT;
+       while (size > 0) {
+               unsigned long pfn;
+               pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT;
+               if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
+                       return -EAGAIN;
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vout->mmap_count++;
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+ 
+       return 0;
+ }
+ 
+ static int omap_vout_release(struct file *file)
+ {
+       unsigned int ret, i;
+       struct videobuf_queue *q;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = file->private_data;
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+       ovid = &vout->vid_info;
+ 
+       if (!vout)
+               return 0;
+ 
+       q = &vout->vbq;
+       /* Disable all the overlay managers connected with this interface */
+       for (i = 0; i < ovid->num_overlays; i++) {
+               struct omap_overlay *ovl = ovid->overlays[i];
+               if (ovl->manager && ovl->manager->device)
+                       ovl->disable(ovl);
+       }
+       /* Turn off the pipeline */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               v4l2_warn(&vout->vid_dev->v4l2_dev,
+                               "Unable to apply changes\n");
+ 
+       /* Free all buffers */
+       omap_vout_free_extra_buffers(vout);
+ 
+       /* Free the VRFB buffers only if they are allocated
+        * during reqbufs.  Don't free if init time allocated
+        */
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               if (!vout->vrfb_static_allocation)
+                       omap_vout_free_vrfb_buffers(vout);
+       }
+       videobuf_mmap_free(q);
+ 
+       /* Even if apply changes fails we should continue
+          freeing allocated memory */
+       if (vout->streaming) {
+               u32 mask = 0;
+ 
+               mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
+                       DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
+               omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+               vout->streaming = 0;
+ 
+               videobuf_streamoff(q);
+               videobuf_queue_cancel(q);
+       }
+ 
+       if (vout->mmap_count != 0)
+               vout->mmap_count = 0;
+ 
+       vout->opened -= 1;
+       file->private_data = NULL;
+ 
+       if (vout->buffer_allocated)
+               videobuf_mmap_free(q);
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+       return ret;
+ }
+ 
+ static int omap_vout_open(struct file *file)
+ {
+       struct videobuf_queue *q;
+       struct omap_vout_device *vout = NULL;
+ 
+       vout = video_drvdata(file);
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+ 
+       if (vout == NULL)
+               return -ENODEV;
+ 
+       /* for now, we only support single open */
+       if (vout->opened)
+               return -EBUSY;
+ 
+       vout->opened += 1;
+ 
+       file->private_data = vout;
+       vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ 
+       q = &vout->vbq;
+       video_vbq_ops.buf_setup = omap_vout_buffer_setup;
+       video_vbq_ops.buf_prepare = omap_vout_buffer_prepare;
+       video_vbq_ops.buf_release = omap_vout_buffer_release;
+       video_vbq_ops.buf_queue = omap_vout_buffer_queue;
+       spin_lock_init(&vout->vbq_lock);
+ 
+       videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev,
+                       &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
+                       sizeof(struct videobuf_buffer), vout, NULL);
+ 
+       v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+       return 0;
+ }
+ 
+ /*
+  * V4L2 ioctls
+  */
+ static int vidioc_querycap(struct file *file, void *fh,
+               struct v4l2_capability *cap)
+ {
+       struct omap_vout_device *vout = fh;
+ 
+       strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
+       strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
+       cap->bus_info[0] = '\0';
+       cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
+               V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+ 
+       return 0;
+ }
+ 
+ static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_fmtdesc *fmt)
+ {
+       int index = fmt->index;
+ 
+       if (index >= NUM_OUTPUT_FORMATS)
+               return -EINVAL;
+ 
+       fmt->flags = omap_formats[index].flags;
+       strlcpy(fmt->description, omap_formats[index].description,
+                       sizeof(fmt->description));
+       fmt->pixelformat = omap_formats[index].pixelformat;
+ 
+       return 0;
+ }
+ 
+ static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       struct omap_vout_device *vout = fh;
+ 
+       f->fmt.pix = vout->pix;
+       return 0;
+ 
+ }
+ 
+ static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_video_timings *timing;
+       struct omap_vout_device *vout = fh;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       if (!ovl->manager || !ovl->manager->device)
+               return -EINVAL;
+       /* get the display device attached to the overlay */
+       timing = &ovl->manager->device->panel.timings;
+ 
+       vout->fbuf.fmt.height = timing->y_res;
+       vout->fbuf.fmt.width = timing->x_res;
+ 
+       omap_vout_try_format(&f->fmt.pix);
+       return 0;
+ }
+ 
+ static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       int ret, bpp;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_video_timings *timing;
+       struct omap_vout_device *vout = fh;
+ 
+       if (vout->streaming)
+               return -EBUSY;
+ 
+       mutex_lock(&vout->lock);
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       /* get the display device attached to the overlay */
+       if (!ovl->manager || !ovl->manager->device) {
+               ret = -EINVAL;
+               goto s_fmt_vid_out_exit;
+       }
+       timing = &ovl->manager->device->panel.timings;
+ 
+       /* We dont support RGB24-packed mode if vrfb rotation
+        * is enabled*/
+       if ((is_rotation_enabled(vout)) &&
+                       f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+               ret = -EINVAL;
+               goto s_fmt_vid_out_exit;
+       }
+ 
+       /* get the framebuffer parameters */
+ 
+       if (is_rotation_90_or_270(vout)) {
+               vout->fbuf.fmt.height = timing->x_res;
+               vout->fbuf.fmt.width = timing->y_res;
+       } else {
+               vout->fbuf.fmt.height = timing->y_res;
+               vout->fbuf.fmt.width = timing->x_res;
+       }
+ 
+       /* change to samller size is OK */
+ 
+       bpp = omap_vout_try_format(&f->fmt.pix);
+       f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp;
+ 
+       /* try & set the new output format */
+       vout->bpp = bpp;
+       vout->pix = f->fmt.pix;
+       vout->vrfb_bpp = 1;
+ 
+       /* If YUYV then vrfb bpp is 2, for  others its 1 */
+       if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat ||
+                       V4L2_PIX_FMT_UYVY == vout->pix.pixelformat)
+               vout->vrfb_bpp = 2;
+ 
+       /* set default crop and win */
+       omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win);
+ 
+       /* Save the changes in the overlay strcuture */
+       ret = omapvid_init(vout, 0);
+       if (ret) {
+               v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+               goto s_fmt_vid_out_exit;
+       }
+ 
+       ret = 0;
+ 
+ s_fmt_vid_out_exit:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ 
+ static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       int ret = 0;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct v4l2_window *win = &f->fmt.win;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       ret = omap_vout_try_window(&vout->fbuf, win);
+ 
+       if (!ret) {
+               if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+                       win->global_alpha = 255;
+               else
+                       win->global_alpha = f->fmt.win.global_alpha;
+       }
+ 
+       return ret;
+ }
+ 
+ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       int ret = 0;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct v4l2_window *win = &f->fmt.win;
+ 
+       mutex_lock(&vout->lock);
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
+       if (!ret) {
+               /* Video1 plane does not support global alpha on OMAP3 */
+               if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+                       vout->win.global_alpha = 255;
+               else
+                       vout->win.global_alpha = f->fmt.win.global_alpha;
+ 
+               vout->win.chromakey = f->fmt.win.chromakey;
+       }
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ 
+ static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_fmtdesc *fmt)
+ {
+       int index = fmt->index;
+ 
+       if (index >= NUM_OUTPUT_FORMATS)
+               return -EINVAL;
+ 
+       fmt->flags = omap_formats[index].flags;
+       strlcpy(fmt->description, omap_formats[index].description,
+                       sizeof(fmt->description));
+       fmt->pixelformat = omap_formats[index].pixelformat;
+       return 0;
+ }
+ 
+ static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh,
+                       struct v4l2_format *f)
+ {
+       u32 key_value =  0;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay_manager_info info;
+       struct v4l2_window *win = &f->fmt.win;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       win->w = vout->win.w;
+       win->field = vout->win.field;
+       win->global_alpha = vout->win.global_alpha;
+ 
+       if (ovl->manager && ovl->manager->get_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               key_value = info.trans_key;
+       }
+       win->chromakey = key_value;
+       return 0;
+ }
+ 
+ static int vidioc_cropcap(struct file *file, void *fh,
+               struct v4l2_cropcap *cropcap)
+ {
+       struct omap_vout_device *vout = fh;
+       struct v4l2_pix_format *pix = &vout->pix;
+ 
+       if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+ 
+       /* Width and height are always even */
+       cropcap->bounds.width = pix->width & ~1;
+       cropcap->bounds.height = pix->height & ~1;
+ 
+       omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect);
+       cropcap->pixelaspect.numerator = 1;
+       cropcap->pixelaspect.denominator = 1;
+       return 0;
+ }
+ 
+ static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+ {
+       struct omap_vout_device *vout = fh;
+ 
+       if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               return -EINVAL;
+       crop->c = vout->crop;
+       return 0;
+ }
+ 
+ static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+ {
+       int ret = -EINVAL;
+       struct omap_vout_device *vout = fh;
+       struct omapvideo_info *ovid;
+       struct omap_overlay *ovl;
+       struct omap_video_timings *timing;
+ 
+       if (vout->streaming)
+               return -EBUSY;
+ 
+       mutex_lock(&vout->lock);
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       if (!ovl->manager || !ovl->manager->device) {
+               ret = -EINVAL;
+               goto s_crop_err;
+       }
+       /* get the display device attached to the overlay */
+       timing = &ovl->manager->device->panel.timings;
+ 
+       if (is_rotation_90_or_270(vout)) {
+               vout->fbuf.fmt.height = timing->x_res;
+               vout->fbuf.fmt.width = timing->y_res;
+       } else {
+               vout->fbuf.fmt.height = timing->y_res;
+               vout->fbuf.fmt.width = timing->x_res;
+       }
+ 
+       if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+               ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win,
+                               &vout->fbuf, &crop->c);
+ 
+ s_crop_err:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ 
+ static int vidioc_queryctrl(struct file *file, void *fh,
+               struct v4l2_queryctrl *ctrl)
+ {
+       int ret = 0;
+ 
+       switch (ctrl->id) {
+       case V4L2_CID_ROTATE:
+               ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
+               break;
+       case V4L2_CID_BG_COLOR:
+               ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0);
+               break;
+       case V4L2_CID_VFLIP:
+               ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
+               break;
+       default:
+               ctrl->name[0] = '\0';
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ 
+ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+ {
+       int ret = 0;
+       struct omap_vout_device *vout = fh;
+ 
+       switch (ctrl->id) {
+       case V4L2_CID_ROTATE:
+               ctrl->value = vout->control[0].value;
+               break;
+       case V4L2_CID_BG_COLOR:
+       {
+               struct omap_overlay_manager_info info;
+               struct omap_overlay *ovl;
+ 
+               ovl = vout->vid_info.overlays[0];
+               if (!ovl->manager || !ovl->manager->get_manager_info) {
+                       ret = -EINVAL;
+                       break;
+               }
+ 
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               ctrl->value = info.default_color;
+               break;
+       }
+       case V4L2_CID_VFLIP:
+               ctrl->value = vout->control[2].value;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ 
+ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+ {
+       int ret = 0;
+       struct omap_vout_device *vout = fh;
+ 
+       switch (a->id) {
+       case V4L2_CID_ROTATE:
+       {
+               struct omapvideo_info *ovid;
+               int rotation = a->value;
+ 
+               ovid = &vout->vid_info;
+ 
+               mutex_lock(&vout->lock);
+               if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
+                       mutex_unlock(&vout->lock);
+                       ret = -ERANGE;
+                       break;
+               }
+ 
+               if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+ 
+               if (v4l2_rot_to_dss_rot(rotation, &vout->rotation,
+                                                       vout->mirror)) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+ 
+               vout->control[0].value = rotation;
+               mutex_unlock(&vout->lock);
+               break;
+       }
+       case V4L2_CID_BG_COLOR:
+       {
+               struct omap_overlay *ovl;
+               unsigned int  color = a->value;
+               struct omap_overlay_manager_info info;
+ 
+               ovl = vout->vid_info.overlays[0];
+ 
+               mutex_lock(&vout->lock);
+               if (!ovl->manager || !ovl->manager->get_manager_info) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+ 
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               info.default_color = color;
+               if (ovl->manager->set_manager_info(ovl->manager, &info)) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+ 
+               vout->control[1].value = color;
+               mutex_unlock(&vout->lock);
+               break;
+       }
+       case V4L2_CID_VFLIP:
+       {
+               struct omap_overlay *ovl;
+               struct omapvideo_info *ovid;
+               unsigned int  mirror = a->value;
+ 
+               ovid = &vout->vid_info;
+               ovl = ovid->overlays[0];
+ 
+               mutex_lock(&vout->lock);
+               if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
+                       mutex_unlock(&vout->lock);
+                       ret = -ERANGE;
+                       break;
+               }
+ 
+               if (mirror  && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+                       mutex_unlock(&vout->lock);
+                       ret = -EINVAL;
+                       break;
+               }
+               vout->mirror = mirror;
+               vout->control[2].value = mirror;
+               mutex_unlock(&vout->lock);
+               break;
+       }
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+ }
+ 
+ static int vidioc_reqbufs(struct file *file, void *fh,
+                       struct v4l2_requestbuffers *req)
+ {
+       int ret = 0;
+       unsigned int i, num_buffers = 0;
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+ 
+       if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
+               return -EINVAL;
+       /* if memory is not mmp or userptr
+          return error */
+       if ((V4L2_MEMORY_MMAP != req->memory) &&
+                       (V4L2_MEMORY_USERPTR != req->memory))
+               return -EINVAL;
+ 
+       mutex_lock(&vout->lock);
+       /* Cannot be requested when streaming is on */
+       if (vout->streaming) {
+               ret = -EBUSY;
+               goto reqbuf_err;
+       }
+ 
+       /* If buffers are already allocated free them */
+       if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) {
+               if (vout->mmap_count) {
+                       ret = -EBUSY;
+                       goto reqbuf_err;
+               }
+               num_buffers = (vout->vid == OMAP_VIDEO1) ?
+                       video1_numbuffers : video2_numbuffers;
+               for (i = num_buffers; i < vout->buffer_allocated; i++) {
+                       omap_vout_free_buffer(vout->buf_virt_addr[i],
+                                       vout->buffer_size);
+                       vout->buf_virt_addr[i] = 0;
+                       vout->buf_phy_addr[i] = 0;
+               }
+               vout->buffer_allocated = num_buffers;
+               videobuf_mmap_free(q);
+       } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) {
+               if (vout->buffer_allocated) {
+                       videobuf_mmap_free(q);
+                       for (i = 0; i < vout->buffer_allocated; i++) {
+                               kfree(q->bufs[i]);
+                               q->bufs[i] = NULL;
+                       }
+                       vout->buffer_allocated = 0;
+               }
+       }
+ 
+       /*store the memory type in data structure */
+       vout->memory = req->memory;
+ 
+       INIT_LIST_HEAD(&vout->dma_queue);
+ 
+       /* call videobuf_reqbufs api */
+       ret = videobuf_reqbufs(q, req);
+       if (ret < 0)
+               goto reqbuf_err;
+ 
+       vout->buffer_allocated = req->count;
+ 
+ reqbuf_err:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ 
+ static int vidioc_querybuf(struct file *file, void *fh,
+                       struct v4l2_buffer *b)
+ {
+       struct omap_vout_device *vout = fh;
+ 
+       return videobuf_querybuf(&vout->vbq, b);
+ }
+ 
+ static int vidioc_qbuf(struct file *file, void *fh,
+                       struct v4l2_buffer *buffer)
+ {
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+ 
+       if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) ||
+                       (buffer->index >= vout->buffer_allocated) ||
+                       (q->bufs[buffer->index]->memory != buffer->memory)) {
+               return -EINVAL;
+       }
+       if (V4L2_MEMORY_USERPTR == buffer->memory) {
+               if ((buffer->length < vout->pix.sizeimage) ||
+                               (0 == buffer->m.userptr)) {
+                       return -EINVAL;
+               }
+       }
+ 
+       if ((is_rotation_enabled(vout)) &&
+                       vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
+               v4l2_warn(&vout->vid_dev->v4l2_dev,
+                               "DMA Channel not allocated for Rotation\n");
+               return -EINVAL;
+       }
+ 
+       return videobuf_qbuf(q, buffer);
+ }
+ 
+ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+ {
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+ 
+       int ret;
+       u32 addr;
+       unsigned long size;
+       struct videobuf_buffer *vb;
+ 
+       vb = q->bufs[b->index];
+ 
+       if (!vout->streaming)
+               return -EINVAL;
+ 
+       if (file->f_flags & O_NONBLOCK)
+               /* Call videobuf_dqbuf for non blocking mode */
+               ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
+       else
+               /* Call videobuf_dqbuf for  blocking mode */
+               ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+ 
+       addr = (unsigned long) vout->buf_phy_addr[vb->i];
+       size = (unsigned long) vb->size;
+       dma_unmap_single(vout->vid_dev->v4l2_dev.dev,  addr,
+                               size, DMA_TO_DEVICE);
+       return ret;
+ }
+ 
+ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       int ret = 0, j;
+       u32 addr = 0, mask = 0;
+       struct omap_vout_device *vout = fh;
+       struct videobuf_queue *q = &vout->vbq;
+       struct omapvideo_info *ovid = &vout->vid_info;
+ 
+       mutex_lock(&vout->lock);
+ 
+       if (vout->streaming) {
+               ret = -EBUSY;
+               goto streamon_err;
+       }
+ 
+       ret = videobuf_streamon(q);
+       if (ret)
+               goto streamon_err;
+ 
+       if (list_empty(&vout->dma_queue)) {
+               ret = -EIO;
+               goto streamon_err1;
+       }
+ 
+       /* Get the next frame from the buffer queue */
+       vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next,
+                       struct videobuf_buffer, queue);
+       /* Remove buffer from the buffer queue */
+       list_del(&vout->cur_frm->queue);
+       /* Mark state of the current frame to active */
+       vout->cur_frm->state = VIDEOBUF_ACTIVE;
+       /* Initialize field_id and started member */
+       vout->field_id = 0;
+ 
+       /* set flag here. Next QBUF will start DMA */
+       vout->streaming = 1;
+ 
+       vout->first_int = 1;
+ 
+       if (omap_vout_calculate_offset(vout)) {
+               ret = -EINVAL;
+               goto streamon_err1;
+       }
+       addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
+               + vout->cropped_offset;
+ 
+       mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+               | DISPC_IRQ_VSYNC2;
+ 
+       omap_dispc_register_isr(omap_vout_isr, vout, mask);
+ 
+       for (j = 0; j < ovid->num_overlays; j++) {
+               struct omap_overlay *ovl = ovid->overlays[j];
+ 
+               if (ovl->manager && ovl->manager->device) {
+                       struct omap_overlay_info info;
+                       ovl->get_overlay_info(ovl, &info);
+                       info.paddr = addr;
+                       if (ovl->set_overlay_info(ovl, &info)) {
+                               ret = -EINVAL;
+                               goto streamon_err1;
+                       }
+               }
+       }
+ 
+       /* First save the configuration in ovelray structure */
+       ret = omapvid_init(vout, addr);
+       if (ret)
+               v4l2_err(&vout->vid_dev->v4l2_dev,
+                               "failed to set overlay info\n");
+       /* Enable the pipeline and set the Go bit */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+ 
+       for (j = 0; j < ovid->num_overlays; j++) {
+               struct omap_overlay *ovl = ovid->overlays[j];
+ 
+               if (ovl->manager && ovl->manager->device) {
+                       ret = ovl->enable(ovl);
+                       if (ret)
+                               goto streamon_err1;
+               }
+       }
+ 
+       ret = 0;
+ 
+ streamon_err1:
+       if (ret)
+               ret = videobuf_streamoff(q);
+ streamon_err:
+       mutex_unlock(&vout->lock);
+       return ret;
+ }
+ 
+ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       u32 mask = 0;
+       int ret = 0, j;
+       struct omap_vout_device *vout = fh;
+       struct omapvideo_info *ovid = &vout->vid_info;
+ 
+       if (!vout->streaming)
+               return -EINVAL;
+ 
+       vout->streaming = 0;
+       mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+               | DISPC_IRQ_VSYNC2;
+ 
+       omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+ 
+       for (j = 0; j < ovid->num_overlays; j++) {
+               struct omap_overlay *ovl = ovid->overlays[j];
+ 
+               if (ovl->manager && ovl->manager->device)
+                       ovl->disable(ovl);
+       }
+ 
+       /* Turn of the pipeline */
+       ret = omapvid_apply_changes(vout);
+       if (ret)
+               v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in"
+                               " streamoff\n");
+ 
+       INIT_LIST_HEAD(&vout->dma_queue);
+       ret = videobuf_streamoff(&vout->vbq);
+ 
+       return ret;
+ }
+ 
+ static int vidioc_s_fbuf(struct file *file, void *fh,
+                               const struct v4l2_framebuffer *a)
+ {
+       int enable = 0;
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay_manager_info info;
+       enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       /* OMAP DSS doesn't support Source and Destination color
+          key together */
+       if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
+                       (a->flags & V4L2_FBUF_FLAG_CHROMAKEY))
+               return -EINVAL;
+       /* OMAP DSS Doesn't support the Destination color key
+          and alpha blending together */
+       if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
+                       (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA))
+               return -EINVAL;
+ 
+       if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) {
+               vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+               key_type =  OMAP_DSS_COLOR_KEY_VID_SRC;
+       } else
+               vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+ 
+       if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) {
+               vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+               key_type =  OMAP_DSS_COLOR_KEY_GFX_DST;
+       } else
+               vout->fbuf.flags &=  ~V4L2_FBUF_FLAG_CHROMAKEY;
+ 
+       if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY |
+                               V4L2_FBUF_FLAG_SRC_CHROMAKEY))
+               enable = 1;
+       else
+               enable = 0;
+       if (ovl->manager && ovl->manager->get_manager_info &&
+                       ovl->manager->set_manager_info) {
+ 
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               info.trans_enabled = enable;
+               info.trans_key_type = key_type;
+               info.trans_key = vout->win.chromakey;
+ 
+               if (ovl->manager->set_manager_info(ovl->manager, &info))
+                       return -EINVAL;
+       }
+       if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) {
+               vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+               enable = 1;
+       } else {
+               vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA;
+               enable = 0;
+       }
+       if (ovl->manager && ovl->manager->get_manager_info &&
+                       ovl->manager->set_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               /* enable this only if there is no zorder cap */
+               if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+                       info.partial_alpha_enabled = enable;
+               if (ovl->manager->set_manager_info(ovl->manager, &info))
+                       return -EINVAL;
+       }
+ 
+       return 0;
+ }
+ 
+ static int vidioc_g_fbuf(struct file *file, void *fh,
+               struct v4l2_framebuffer *a)
+ {
+       struct omap_overlay *ovl;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout = fh;
+       struct omap_overlay_manager_info info;
+ 
+       ovid = &vout->vid_info;
+       ovl = ovid->overlays[0];
+ 
+       /* The video overlay must stay within the framebuffer and can't be
+          positioned independently. */
+       a->flags = V4L2_FBUF_FLAG_OVERLAY;
+       a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY
+               | V4L2_FBUF_CAP_SRC_CHROMAKEY;
+ 
+       if (ovl->manager && ovl->manager->get_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC)
+                       a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+               if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST)
+                       a->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+       }
+       if (ovl->manager && ovl->manager->get_manager_info) {
+               ovl->manager->get_manager_info(ovl->manager, &info);
+               if (info.partial_alpha_enabled)
+                       a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+       }
+ 
+       return 0;
+ }
+ 
+ static const struct v4l2_ioctl_ops vout_ioctl_ops = {
+       .vidioc_querycap                        = vidioc_querycap,
+       .vidioc_enum_fmt_vid_out                = vidioc_enum_fmt_vid_out,
+       .vidioc_g_fmt_vid_out                   = vidioc_g_fmt_vid_out,
+       .vidioc_try_fmt_vid_out                 = vidioc_try_fmt_vid_out,
+       .vidioc_s_fmt_vid_out                   = vidioc_s_fmt_vid_out,
+       .vidioc_queryctrl                       = vidioc_queryctrl,
+       .vidioc_g_ctrl                          = vidioc_g_ctrl,
+       .vidioc_s_fbuf                          = vidioc_s_fbuf,
+       .vidioc_g_fbuf                          = vidioc_g_fbuf,
+       .vidioc_s_ctrl                          = vidioc_s_ctrl,
+       .vidioc_try_fmt_vid_overlay             = vidioc_try_fmt_vid_overlay,
+       .vidioc_s_fmt_vid_overlay               = vidioc_s_fmt_vid_overlay,
+       .vidioc_enum_fmt_vid_overlay            = vidioc_enum_fmt_vid_overlay,
+       .vidioc_g_fmt_vid_overlay               = vidioc_g_fmt_vid_overlay,
+       .vidioc_cropcap                         = vidioc_cropcap,
+       .vidioc_g_crop                          = vidioc_g_crop,
+       .vidioc_s_crop                          = vidioc_s_crop,
+       .vidioc_reqbufs                         = vidioc_reqbufs,
+       .vidioc_querybuf                        = vidioc_querybuf,
+       .vidioc_qbuf                            = vidioc_qbuf,
+       .vidioc_dqbuf                           = vidioc_dqbuf,
+       .vidioc_streamon                        = vidioc_streamon,
+       .vidioc_streamoff                       = vidioc_streamoff,
+ };
+ 
+ static const struct v4l2_file_operations omap_vout_fops = {
+       .owner          = THIS_MODULE,
+       .poll           = omap_vout_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap           = omap_vout_mmap,
+       .open           = omap_vout_open,
+       .release        = omap_vout_release,
+ };
+ 
+ /* Init functions used during driver initialization */
+ /* Initial setup of video_data */
+ static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
+ {
+       struct video_device *vfd;
+       struct v4l2_pix_format *pix;
+       struct v4l2_control *control;
+       struct omap_dss_device *display =
+               vout->vid_info.overlays[0]->manager->device;
+ 
+       /* set the default pix */
+       pix = &vout->pix;
+ 
+       /* Set the default picture of QVGA  */
+       pix->width = QQVGA_WIDTH;
+       pix->height = QQVGA_HEIGHT;
+ 
+       /* Default pixel format is RGB 5-6-5 */
+       pix->pixelformat = V4L2_PIX_FMT_RGB565;
+       pix->field = V4L2_FIELD_ANY;
+       pix->bytesperline = pix->width * 2;
+       pix->sizeimage = pix->bytesperline * pix->height;
+       pix->priv = 0;
+       pix->colorspace = V4L2_COLORSPACE_JPEG;
+ 
+       vout->bpp = RGB565_BPP;
+       vout->fbuf.fmt.width  =  display->panel.timings.x_res;
+       vout->fbuf.fmt.height =  display->panel.timings.y_res;
+ 
+       /* Set the data structures for the overlay parameters*/
+       vout->win.global_alpha = 255;
+       vout->fbuf.flags = 0;
+       vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA |
+               V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY;
+       vout->win.chromakey = 0;
+ 
+       omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win);
+ 
+       /*Initialize the control variables for
+         rotation, flipping and background color. */
+       control = vout->control;
+       control[0].id = V4L2_CID_ROTATE;
+       control[0].value = 0;
+       vout->rotation = 0;
+       vout->mirror = 0;
+       vout->control[2].id = V4L2_CID_HFLIP;
+       vout->control[2].value = 0;
+       if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+               vout->vrfb_bpp = 2;
+ 
+       control[1].id = V4L2_CID_BG_COLOR;
+       control[1].value = 0;
+ 
+       /* initialize the video_device struct */
+       vfd = vout->vfd = video_device_alloc();
+ 
+       if (!vfd) {
+               printk(KERN_ERR VOUT_NAME ": could not allocate"
+                               " video device struct\n");
+               return -ENOMEM;
+       }
+       vfd->release = video_device_release;
+       vfd->ioctl_ops = &vout_ioctl_ops;
+ 
+       strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name));
+ 
+       vfd->fops = &omap_vout_fops;
+       vfd->v4l2_dev = &vout->vid_dev->v4l2_dev;
+       vfd->vfl_dir = VFL_DIR_TX;
+       mutex_init(&vout->lock);
+ 
+       vfd->minor = -1;
+       return 0;
+ 
+ }
+ 
+ /* Setup video buffers */
+ static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
+               int vid_num)
+ {
+       u32 numbuffers;
+       int ret = 0, i;
+       struct omapvideo_info *ovid;
+       struct omap_vout_device *vout;
+       struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+       struct omap2video_device *vid_dev =
+               container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+ 
+       vout = vid_dev->vouts[vid_num];
+       ovid = &vout->vid_info;
+ 
+       numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
+       vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
+       dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size);
+ 
+       for (i = 0; i < numbuffers; i++) {
+               vout->buf_virt_addr[i] =
+                       omap_vout_alloc_buffer(vout->buffer_size,
+                                       (u32 *) &vout->buf_phy_addr[i]);
+               if (!vout->buf_virt_addr[i]) {
+                       numbuffers = i;
+                       ret = -ENOMEM;
+                       goto free_buffers;
+               }
+       }
+ 
+       vout->cropped_offset = 0;
+ 
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               int static_vrfb_allocation = (vid_num == 0) ?
+                       vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
+               ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
+                               static_vrfb_allocation);
+       }
+ 
+       return ret;
+ 
+ free_buffers:
+       for (i = 0; i < numbuffers; i++) {
+               omap_vout_free_buffer(vout->buf_virt_addr[i],
+                                               vout->buffer_size);
+               vout->buf_virt_addr[i] = 0;
+               vout->buf_phy_addr[i] = 0;
+       }
+       return ret;
+ 
+ }
+ 
+ /* Create video out devices */
+ static int __init omap_vout_create_video_devices(struct platform_device *pdev)
+ {
+       int ret = 0, k;
+       struct omap_vout_device *vout;
+       struct video_device *vfd = NULL;
+       struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+       struct omap2video_device *vid_dev = container_of(v4l2_dev,
+                       struct omap2video_device, v4l2_dev);
+ 
+       for (k = 0; k < pdev->num_resources; k++) {
+ 
+               vout = kzalloc(sizeof(struct omap_vout_device), GFP_KERNEL);
+               if (!vout) {
+                       dev_err(&pdev->dev, ": could not allocate memory\n");
+                       return -ENOMEM;
+               }
+ 
+               vout->vid = k;
+               vid_dev->vouts[k] = vout;
+               vout->vid_dev = vid_dev;
+               /* Select video2 if only 1 overlay is controlled by V4L2 */
+               if (pdev->num_resources == 1)
+                       vout->vid_info.overlays[0] = vid_dev->overlays[k + 2];
+               else
+                       /* Else select video1 and video2 one by one. */
+                       vout->vid_info.overlays[0] = vid_dev->overlays[k + 1];
+               vout->vid_info.num_overlays = 1;
+               vout->vid_info.id = k + 1;
+ 
+               /* Set VRFB as rotation_type for omap2 and omap3 */
+               if (cpu_is_omap24xx() || cpu_is_omap34xx())
+                       vout->vid_info.rotation_type = VOUT_ROT_VRFB;
+ 
+               /* Setup the default configuration for the video devices
+                */
+               if (omap_vout_setup_video_data(vout) != 0) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+ 
+               /* Allocate default number of buffers for the video streaming
+                * and reserve the VRFB space for rotation
+                */
+               if (omap_vout_setup_video_bufs(pdev, k) != 0) {
+                       ret = -ENOMEM;
+                       goto error1;
+               }
+ 
+               /* Register the Video device with V4L2
+                */
+               vfd = vout->vfd;
+               if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) {
+                       dev_err(&pdev->dev, ": Could not register "
+                                       "Video for Linux device\n");
+                       vfd->minor = -1;
+                       ret = -ENODEV;
+                       goto error2;
+               }
+               video_set_drvdata(vfd, vout);
+ 
+               /* Configure the overlay structure */
+               ret = omapvid_init(vid_dev->vouts[k], 0);
+               if (!ret)
+                       goto success;
+ 
+ error2:
+               if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+                       omap_vout_release_vrfb(vout);
+               omap_vout_free_buffers(vout);
+ error1:
+               video_device_release(vfd);
+ error:
+               kfree(vout);
+               return ret;
+ 
+ success:
+               dev_info(&pdev->dev, ": registered and initialized"
+                               " video device %d\n", vfd->minor);
+               if (k == (pdev->num_resources - 1))
+                       return 0;
+       }
+ 
+       return -ENODEV;
+ }
+ /* Driver functions */
+ static void omap_vout_cleanup_device(struct omap_vout_device *vout)
+ {
+       struct video_device *vfd;
+       struct omapvideo_info *ovid;
+ 
+       if (!vout)
+               return;
+ 
+       vfd = vout->vfd;
+       ovid = &vout->vid_info;
+       if (vfd) {
+               if (!video_is_registered(vfd)) {
+                       /*
+                        * The device was never registered, so release the
+                        * video_device struct directly.
+                        */
+                       video_device_release(vfd);
+               } else {
+                       /*
+                        * The unregister function will release the video_device
+                        * struct as well as unregistering it.
+                        */
+                       video_unregister_device(vfd);
+               }
+       }
+       if (ovid->rotation_type == VOUT_ROT_VRFB) {
+               omap_vout_release_vrfb(vout);
+               /* Free the VRFB buffer if allocated
+                * init time
+                */
+               if (vout->vrfb_static_allocation)
+                       omap_vout_free_vrfb_buffers(vout);
+       }
+       omap_vout_free_buffers(vout);
+ 
+       kfree(vout);
+ }
+ 
+ static int omap_vout_remove(struct platform_device *pdev)
+ {
+       int k;
+       struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+       struct omap2video_device *vid_dev = container_of(v4l2_dev, struct
+                       omap2video_device, v4l2_dev);
+ 
+       v4l2_device_unregister(v4l2_dev);
+       for (k = 0; k < pdev->num_resources; k++)
+               omap_vout_cleanup_device(vid_dev->vouts[k]);
+ 
+       for (k = 0; k < vid_dev->num_displays; k++) {
+               if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED)
+                       vid_dev->displays[k]->driver->disable(vid_dev->displays[k]);
+ 
+               omap_dss_put_device(vid_dev->displays[k]);
+       }
+       kfree(vid_dev);
+       return 0;
+ }
+ 
+ static int __init omap_vout_probe(struct platform_device *pdev)
+ {
+       int ret = 0, i;
+       struct omap_overlay *ovl;
+       struct omap_dss_device *dssdev = NULL;
+       struct omap_dss_device *def_display;
+       struct omap2video_device *vid_dev = NULL;
+ 
+       if (pdev->num_resources == 0) {
+               dev_err(&pdev->dev, "probed for an unknown device\n");
+               return -ENODEV;
+       }
+ 
+       vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL);
+       if (vid_dev == NULL)
+               return -ENOMEM;
+ 
+       vid_dev->num_displays = 0;
+       for_each_dss_dev(dssdev) {
+               omap_dss_get_device(dssdev);
+ 
+               if (!dssdev->driver) {
+                       dev_warn(&pdev->dev, "no driver for display: %s\n",
+                                       dssdev->name);
+                       omap_dss_put_device(dssdev);
+                       continue;
+               }
+ 
+               vid_dev->displays[vid_dev->num_displays++] = dssdev;
+       }
+ 
+       if (vid_dev->num_displays == 0) {
+               dev_err(&pdev->dev, "no displays\n");
+               ret = -EINVAL;
+               goto probe_err0;
+       }
+ 
+       vid_dev->num_overlays = omap_dss_get_num_overlays();
+       for (i = 0; i < vid_dev->num_overlays; i++)
+               vid_dev->overlays[i] = omap_dss_get_overlay(i);
+ 
+       vid_dev->num_managers = omap_dss_get_num_overlay_managers();
+       for (i = 0; i < vid_dev->num_managers; i++)
+               vid_dev->managers[i] = omap_dss_get_overlay_manager(i);
+ 
+       /* Get the Video1 overlay and video2 overlay.
+        * Setup the Display attached to that overlays
+        */
+       for (i = 1; i < vid_dev->num_overlays; i++) {
+               ovl = omap_dss_get_overlay(i);
+               if (ovl->manager && ovl->manager->device) {
+                       def_display = ovl->manager->device;
+               } else {
+                       dev_warn(&pdev->dev, "cannot find display\n");
+                       def_display = NULL;
+               }
+               if (def_display) {
+                       struct omap_dss_driver *dssdrv = def_display->driver;
+ 
+                       ret = dssdrv->enable(def_display);
+                       if (ret) {
+                               /* Here we are not considering a error
+                                *  as display may be enabled by frame
+                                *  buffer driver
+                                */
+                               dev_warn(&pdev->dev,
+                                       "'%s' Display already enabled\n",
+                                       def_display->name);
+                       }
+               }
+       }
+ 
+       if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) {
+               dev_err(&pdev->dev, "v4l2_device_register failed\n");
+               ret = -ENODEV;
+               goto probe_err1;
+       }
+ 
+       ret = omap_vout_create_video_devices(pdev);
+       if (ret)
+               goto probe_err2;
+ 
+       for (i = 0; i < vid_dev->num_displays; i++) {
+               struct omap_dss_device *display = vid_dev->displays[i];
+ 
+               if (display->driver->update)
+                       display->driver->update(display, 0, 0,
+                                       display->panel.timings.x_res,
+                                       display->panel.timings.y_res);
+       }
+       return 0;
+ 
+ probe_err2:
+       v4l2_device_unregister(&vid_dev->v4l2_dev);
+ probe_err1:
+       for (i = 1; i < vid_dev->num_overlays; i++) {
+               def_display = NULL;
+               ovl = omap_dss_get_overlay(i);
+               if (ovl->manager && ovl->manager->device)
+                       def_display = ovl->manager->device;
+ 
+               if (def_display && def_display->driver)
+                       def_display->driver->disable(def_display);
+       }
+ probe_err0:
+       kfree(vid_dev);
+       return ret;
+ }
+ 
+ static struct platform_driver omap_vout_driver = {
+       .driver = {
+               .name = VOUT_NAME,
+       },
+       .remove = omap_vout_remove,
+ };
+ 
+ static int __init omap_vout_init(void)
+ {
+       if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
+               printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
+               return -EINVAL;
+       }
+       return 0;
+ }
+ 
+ static void omap_vout_cleanup(void)
+ {
+       platform_driver_unregister(&omap_vout_driver);
+ }
+ 
+ late_initcall(omap_vout_init);
+ module_exit(omap_vout_cleanup);
 
--- /dev/null
 -      flush_work_sync(&cam->sensor_reset_work);
+ /*
+  * drivers/media/platform/omap24xxcam.c
+  *
+  * OMAP 2 camera block driver.
+  *
+  * Copyright (C) 2004 MontaVista Software, Inc.
+  * Copyright (C) 2004 Texas Instruments.
+  * Copyright (C) 2007-2008 Nokia Corporation.
+  *
+  * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+  *
+  * Based on code from Andy Lowe <source@mvista.com>
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * version 2 as published by the Free Software Foundation.
+  *
+  * 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., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA
+  */
+ 
+ #include <linux/delay.h>
+ #include <linux/kernel.h>
+ #include <linux/interrupt.h>
+ #include <linux/videodev2.h>
+ #include <linux/pci.h>                /* needed for videobufs */
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+ #include <linux/io.h>
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/module.h>
+ 
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+ 
+ #include "omap24xxcam.h"
+ 
+ #define OMAP24XXCAM_VERSION "0.0.1"
+ 
+ #define RESET_TIMEOUT_NS 10000
+ 
+ static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+ static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+ static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+ static int omap24xxcam_remove(struct platform_device *pdev);
+ 
+ /* module parameters */
+ static int video_nr = -1;     /* video device minor (-1 ==> auto assign) */
+ /*
+  * Maximum amount of memory to use for capture buffers.
+  * Default is 4800KB, enough to double-buffer SXGA.
+  */
+ static int capture_mem = 1280 * 960 * 2 * 2;
+ 
+ static struct v4l2_int_device omap24xxcam;
+ 
+ /*
+  *
+  * Clocks.
+  *
+  */
+ 
+ static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+ {
+       if (cam->ick != NULL && !IS_ERR(cam->ick))
+               clk_put(cam->ick);
+       if (cam->fck != NULL && !IS_ERR(cam->fck))
+               clk_put(cam->fck);
+ 
+       cam->ick = cam->fck = NULL;
+ }
+ 
+ static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+ {
+       int rval = 0;
+ 
+       cam->fck = clk_get(cam->dev, "fck");
+       if (IS_ERR(cam->fck)) {
+               dev_err(cam->dev, "can't get camera fck");
+               rval = PTR_ERR(cam->fck);
+               omap24xxcam_clock_put(cam);
+               return rval;
+       }
+ 
+       cam->ick = clk_get(cam->dev, "ick");
+       if (IS_ERR(cam->ick)) {
+               dev_err(cam->dev, "can't get camera ick");
+               rval = PTR_ERR(cam->ick);
+               omap24xxcam_clock_put(cam);
+       }
+ 
+       return rval;
+ }
+ 
+ static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+ {
+       clk_enable(cam->fck);
+       clk_enable(cam->ick);
+ }
+ 
+ static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+ {
+       clk_disable(cam->fck);
+       clk_disable(cam->ick);
+ }
+ 
+ /*
+  *
+  * Camera core
+  *
+  */
+ 
+ /*
+  * Set xclk.
+  *
+  * To disable xclk, use value zero.
+  */
+ static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+                                     u32 xclk)
+ {
+       if (xclk) {
+               u32 divisor = CAM_MCLK / xclk;
+ 
+               if (divisor == 1)
+                       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+                                           CC_CTRL_XCLK,
+                                           CC_CTRL_XCLK_DIV_BYPASS);
+               else
+                       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+                                           CC_CTRL_XCLK, divisor);
+       } else
+               omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+                                   CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+ }
+ 
+ static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+ {
+       /*
+        * Setting the camera core AUTOIDLE bit causes problems with frame
+        * synchronization, so we will clear the AUTOIDLE bit instead.
+        */
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+                           CC_SYSCONFIG_AUTOIDLE);
+ 
+       /* program the camera interface DMA packet size */
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+                           CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+ 
+       /* enable camera core error interrupts */
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+                           CC_IRQENABLE_FW_ERR_IRQ
+                           | CC_IRQENABLE_FSC_ERR_IRQ
+                           | CC_IRQENABLE_SSC_ERR_IRQ
+                           | CC_IRQENABLE_FIFO_OF_IRQ);
+ }
+ 
+ /*
+  * Enable the camera core.
+  *
+  * Data transfer to the camera DMA starts from next starting frame.
+  */
+ static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+ {
+ 
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+                           cam->cc_ctrl);
+ }
+ 
+ /*
+  * Disable camera core.
+  *
+  * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+  * core internal state machines will be reset. Use
+  * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+  * frame completely.
+  */
+ static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+                           CC_CTRL_CC_RST);
+ }
+ 
+ /* Interrupt service routine for camera core interrupts. */
+ static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+ {
+       u32 cc_irqstatus;
+       const u32 cc_irqstatus_err =
+               CC_IRQSTATUS_FW_ERR_IRQ
+               | CC_IRQSTATUS_FSC_ERR_IRQ
+               | CC_IRQSTATUS_SSC_ERR_IRQ
+               | CC_IRQSTATUS_FIFO_UF_IRQ
+               | CC_IRQSTATUS_FIFO_OF_IRQ;
+ 
+       cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+                                         CC_IRQSTATUS);
+       omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+                           cc_irqstatus);
+ 
+       if (cc_irqstatus & cc_irqstatus_err
+           && !atomic_read(&cam->in_reset)) {
+               dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+                       cc_irqstatus);
+               omap24xxcam_reset(cam);
+       }
+ }
+ 
+ /*
+  *
+  * videobuf_buffer handling.
+  *
+  * Memory for mmapped videobuf_buffers is not allocated
+  * conventionally, but by several kmalloc allocations and then
+  * creating the scatterlist on our own. User-space buffers are handled
+  * normally.
+  *
+  */
+ 
+ /*
+  * Free the memory-mapped buffer memory allocated for a
+  * videobuf_buffer and the associated scatterlist.
+  */
+ static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+ {
+       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+       size_t alloc_size;
+       struct page *page;
+       int i;
+ 
+       if (dma->sglist == NULL)
+               return;
+ 
+       i = dma->sglen;
+       while (i) {
+               i--;
+               alloc_size = sg_dma_len(&dma->sglist[i]);
+               page = sg_page(&dma->sglist[i]);
+               do {
+                       ClearPageReserved(page++);
+               } while (alloc_size -= PAGE_SIZE);
+               __free_pages(sg_page(&dma->sglist[i]),
+                            get_order(sg_dma_len(&dma->sglist[i])));
+       }
+ 
+       kfree(dma->sglist);
+       dma->sglist = NULL;
+ }
+ 
+ /* Release all memory related to the videobuf_queue. */
+ static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+ {
+       int i;
+ 
+       mutex_lock(&vbq->vb_lock);
+ 
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == vbq->bufs[i])
+                       continue;
+               if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+                       continue;
+               vbq->ops->buf_release(vbq, vbq->bufs[i]);
+               omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+               kfree(vbq->bufs[i]);
+               vbq->bufs[i] = NULL;
+       }
+ 
+       mutex_unlock(&vbq->vb_lock);
+ 
+       videobuf_mmap_free(vbq);
+ }
+ 
+ /*
+  * Allocate physically as contiguous as possible buffer for video
+  * frame and allocate and build DMA scatter-gather list for it.
+  */
+ static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+ {
+       unsigned int order;
+       size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+       struct page *page;
+       int max_pages, err = 0, i = 0;
+       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ 
+       /*
+        * allocate maximum size scatter-gather list. Note this is
+        * overhead. We may not use as many entries as we allocate
+        */
+       max_pages = vb->bsize >> PAGE_SHIFT;
+       dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+       if (dma->sglist == NULL) {
+               err = -ENOMEM;
+               goto out;
+       }
+ 
+       while (size) {
+               order = get_order(size);
+               /*
+                * do not over-allocate even if we would get larger
+                * contiguous chunk that way
+                */
+               if ((PAGE_SIZE << order) > size)
+                       order--;
+ 
+               /* try to allocate as many contiguous pages as possible */
+               page = alloc_pages(GFP_KERNEL, order);
+               /* if allocation fails, try to allocate smaller amount */
+               while (page == NULL) {
+                       order--;
+                       page = alloc_pages(GFP_KERNEL, order);
+                       if (page == NULL && !order) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
+               }
+               size -= (PAGE_SIZE << order);
+ 
+               /* append allocated chunk of pages into scatter-gather list */
+               sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+               dma->sglen++;
+               i++;
+ 
+               alloc_size = (PAGE_SIZE << order);
+ 
+               /* clear pages before giving them to user space */
+               memset(page_address(page), 0, alloc_size);
+ 
+               /* mark allocated pages reserved */
+               do {
+                       SetPageReserved(page++);
+               } while (alloc_size -= PAGE_SIZE);
+       }
+       /*
+        * REVISIT: not fully correct to assign nr_pages == sglen but
+        * video-buf is passing nr_pages for e.g. unmap_sg calls
+        */
+       dma->nr_pages = dma->sglen;
+       dma->direction = PCI_DMA_FROMDEVICE;
+ 
+       return 0;
+ 
+ out:
+       omap24xxcam_vbq_free_mmap_buffer(vb);
+       return err;
+ }
+ 
+ static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+                                             unsigned int count)
+ {
+       int i, err = 0;
+       struct omap24xxcam_fh *fh =
+               container_of(vbq, struct omap24xxcam_fh, vbq);
+ 
+       mutex_lock(&vbq->vb_lock);
+ 
+       for (i = 0; i < count; i++) {
+               err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+               if (err)
+                       goto out;
+               dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+                       videobuf_to_dma(vbq->bufs[i])->sglen, i);
+       }
+ 
+       mutex_unlock(&vbq->vb_lock);
+ 
+       return 0;
+ out:
+       while (i) {
+               i--;
+               omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+       }
+ 
+       mutex_unlock(&vbq->vb_lock);
+ 
+       return err;
+ }
+ 
+ /*
+  * This routine is called from interrupt context when a scatter-gather DMA
+  * transfer of a videobuf_buffer completes.
+  */
+ static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+                                    u32 csr, void *arg)
+ {
+       struct omap24xxcam_device *cam =
+               container_of(sgdma, struct omap24xxcam_device, sgdma);
+       struct omap24xxcam_fh *fh = cam->streaming->private_data;
+       struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+       const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+               | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+               | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+       if (--cam->sgdma_in_queue == 0)
+               omap24xxcam_core_disable(cam);
+       spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ 
+       do_gettimeofday(&vb->ts);
+       vb->field_count = atomic_add_return(2, &fh->field_count);
+       if (csr & csr_error) {
+               vb->state = VIDEOBUF_ERROR;
+               if (!atomic_read(&fh->cam->in_reset)) {
+                       dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+                       omap24xxcam_reset(cam);
+               }
+       } else
+               vb->state = VIDEOBUF_DONE;
+       wake_up(&vb->done);
+ }
+ 
+ static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+                                   struct videobuf_buffer *vb)
+ {
+       struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ 
+       /* wait for buffer, especially to get out of the sgdma queue */
+       videobuf_waiton(vbq, vb, 0, 0);
+       if (vb->memory == V4L2_MEMORY_MMAP) {
+               dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+                            dma->direction);
+               dma->direction = DMA_NONE;
+       } else {
+               videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb));
+               videobuf_dma_free(videobuf_to_dma(vb));
+       }
+ 
+       vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+ 
+ /*
+  * Limit the number of available kernel image capture buffers based on the
+  * number requested, the currently selected image size, and the maximum
+  * amount of memory permitted for kernel capture buffers.
+  */
+ static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+                                unsigned int *size)
+ {
+       struct omap24xxcam_fh *fh = vbq->priv_data;
+ 
+       if (*cnt <= 0)
+               *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+ 
+       if (*cnt > VIDEO_MAX_FRAME)
+               *cnt = VIDEO_MAX_FRAME;
+ 
+       *size = fh->pix.sizeimage;
+ 
+       /* accessing fh->cam->capture_mem is ok, it's constant */
+       if (*size * *cnt > fh->cam->capture_mem)
+               *cnt = fh->cam->capture_mem / *size;
+ 
+       return 0;
+ }
+ 
+ static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+                                 struct videobuf_dmabuf *dma)
+ {
+       int err = 0;
+ 
+       dma->direction = PCI_DMA_FROMDEVICE;
+       if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+               kfree(dma->sglist);
+               dma->sglist = NULL;
+               dma->sglen = 0;
+               err = -EIO;
+       }
+ 
+       return err;
+ }
+ 
+ static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+                                  struct videobuf_buffer *vb,
+                                  enum v4l2_field field)
+ {
+       struct omap24xxcam_fh *fh = vbq->priv_data;
+       int err = 0;
+ 
+       /*
+        * Accessing pix here is okay since it's constant while
+        * streaming is on (and we only get called then).
+        */
+       if (vb->baddr) {
+               /* This is a userspace buffer. */
+               if (fh->pix.sizeimage > vb->bsize) {
+                       /* The buffer isn't big enough. */
+                       err = -EINVAL;
+               } else
+                       vb->size = fh->pix.sizeimage;
+       } else {
+               if (vb->state != VIDEOBUF_NEEDS_INIT) {
+                       /*
+                        * We have a kernel bounce buffer that has
+                        * already been allocated.
+                        */
+                       if (fh->pix.sizeimage > vb->size) {
+                               /*
+                                * The image size has been changed to
+                                * a larger size since this buffer was
+                                * allocated, so we need to free and
+                                * reallocate it.
+                                */
+                               omap24xxcam_vbq_release(vbq, vb);
+                               vb->size = fh->pix.sizeimage;
+                       }
+               } else {
+                       /* We need to allocate a new kernel bounce buffer. */
+                       vb->size = fh->pix.sizeimage;
+               }
+       }
+ 
+       if (err)
+               return err;
+ 
+       vb->width = fh->pix.width;
+       vb->height = fh->pix.height;
+       vb->field = field;
+ 
+       if (vb->state == VIDEOBUF_NEEDS_INIT) {
+               if (vb->memory == V4L2_MEMORY_MMAP)
+                       /*
+                        * we have built the scatter-gather list by ourself so
+                        * do the scatter-gather mapping as well
+                        */
+                       err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+               else
+                       err = videobuf_iolock(vbq, vb, NULL);
+       }
+ 
+       if (!err)
+               vb->state = VIDEOBUF_PREPARED;
+       else
+               omap24xxcam_vbq_release(vbq, vb);
+ 
+       return err;
+ }
+ 
+ static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+                                 struct videobuf_buffer *vb)
+ {
+       struct omap24xxcam_fh *fh = vbq->priv_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       enum videobuf_state state = vb->state;
+       unsigned long flags;
+       int err;
+ 
+       /*
+        * FIXME: We're marking the buffer active since we have no
+        * pretty way of marking it active exactly when the
+        * scatter-gather transfer starts.
+        */
+       vb->state = VIDEOBUF_ACTIVE;
+ 
+       err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+                                     videobuf_to_dma(vb)->sglist,
+                                     videobuf_to_dma(vb)->sglen, vb->size,
+                                     omap24xxcam_vbq_complete, vb);
+ 
+       if (!err) {
+               spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+               if (++cam->sgdma_in_queue == 1
+                   && !atomic_read(&cam->in_reset))
+                       omap24xxcam_core_enable(cam);
+               spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+       } else {
+               /*
+                * Oops. We're not supposed to get any errors here.
+                * The only way we could get an error is if we ran out
+                * of scatter-gather DMA slots, but we are supposed to
+                * have at least as many scatter-gather DMA slots as
+                * video buffers so that can't happen.
+                */
+               dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+               dev_err(cam->dev, "likely a bug in the driver!\n");
+               vb->state = state;
+       }
+ }
+ 
+ static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+       .buf_setup   = omap24xxcam_vbq_setup,
+       .buf_prepare = omap24xxcam_vbq_prepare,
+       .buf_queue   = omap24xxcam_vbq_queue,
+       .buf_release = omap24xxcam_vbq_release,
+ };
+ 
+ /*
+  *
+  * OMAP main camera system
+  *
+  */
+ 
+ /*
+  * Reset camera block to power-on state.
+  */
+ static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+ {
+       int max_loop = RESET_TIMEOUT_NS;
+ 
+       /* Reset whole camera subsystem */
+       omap24xxcam_reg_out(cam->mmio_base,
+                           CAM_SYSCONFIG,
+                           CAM_SYSCONFIG_SOFTRESET);
+ 
+       /* Wait till it's finished */
+       while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+                & CAM_SYSSTATUS_RESETDONE)
+              && --max_loop) {
+               ndelay(1);
+       }
+ 
+       if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+             & CAM_SYSSTATUS_RESETDONE))
+               dev_err(cam->dev, "camera soft reset timeout\n");
+ }
+ 
+ /*
+  * (Re)initialise the camera block.
+  */
+ static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_poweron_reset(cam);
+ 
+       /* set the camera subsystem autoidle bit */
+       omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+                           CAM_SYSCONFIG_AUTOIDLE);
+ 
+       /* set the camera MMU autoidle bit */
+       omap24xxcam_reg_out(cam->mmio_base,
+                           CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+                           CAMMMU_SYSCONFIG_AUTOIDLE);
+ 
+       omap24xxcam_core_hwinit(cam);
+ 
+       omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+ }
+ 
+ /*
+  * Callback for dma transfer stalling.
+  */
+ static void omap24xxcam_stalled_dma_reset(unsigned long data)
+ {
+       struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+ 
+       if (!atomic_read(&cam->in_reset)) {
+               dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+               omap24xxcam_reset(cam);
+       }
+ }
+ 
+ /*
+  * Stop capture. Mark we're doing a reset, stop DMA transfers and
+  * core. (No new scatter-gather transfers will be queued whilst
+  * in_reset is non-zero.)
+  *
+  * If omap24xxcam_capture_stop is called from several places at
+  * once, only the first call will have an effect. Similarly, the last
+  * call omap24xxcam_streaming_cont will have effect.
+  *
+  * Serialisation is ensured by using cam->core_enable_disable_lock.
+  */
+ static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+ {
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ 
+       if (atomic_inc_return(&cam->in_reset) != 1) {
+               spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+               return;
+       }
+ 
+       omap24xxcam_core_disable(cam);
+ 
+       spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ 
+       omap24xxcam_sgdma_sync(&cam->sgdma);
+ }
+ 
+ /*
+  * Reset and continue streaming.
+  *
+  * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+  * register is supposed to be sufficient to recover from a camera
+  * interface error, but it doesn't seem to be enough. If we only do
+  * that then subsequent image captures are out of sync by either one
+  * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+  * entire camera subsystem prevents the problem with frame
+  * synchronization.
+  */
+ static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+ {
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ 
+       if (atomic_read(&cam->in_reset) != 1)
+               goto out;
+ 
+       omap24xxcam_hwinit(cam);
+ 
+       omap24xxcam_sensor_if_enable(cam);
+ 
+       omap24xxcam_sgdma_process(&cam->sgdma);
+ 
+       if (cam->sgdma_in_queue)
+               omap24xxcam_core_enable(cam);
+ 
+ out:
+       atomic_dec(&cam->in_reset);
+       spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ }
+ 
+ static ssize_t
+ omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+               char *buf)
+ {
+       struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+ 
+       return sprintf(buf, "%s\n", cam->streaming ?  "active" : "inactive");
+ }
+ static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+ 
+ /*
+  * Stop capture and restart it. I.e. reset the camera during use.
+  */
+ static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_capture_stop(cam);
+       omap24xxcam_capture_cont(cam);
+ }
+ 
+ /*
+  * The main interrupt handler.
+  */
+ static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+ {
+       struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+       u32 irqstatus;
+       unsigned int irqhandled = 0;
+ 
+       irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+ 
+       if (irqstatus &
+           (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+            | CAM_IRQSTATUS_DMA_IRQ0)) {
+               omap24xxcam_dma_isr(&cam->sgdma.dma);
+               irqhandled = 1;
+       }
+       if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+               omap24xxcam_core_isr(cam);
+               irqhandled = 1;
+       }
+       if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+               dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+ 
+       return IRQ_RETVAL(irqhandled);
+ }
+ 
+ /*
+  *
+  * Sensor handling.
+  *
+  */
+ 
+ /*
+  * Enable the external sensor interface. Try to negotiate interface
+  * parameters with the sensor and start using the new ones. The calls
+  * to sensor_if_enable and sensor_if_disable need not to be balanced.
+  */
+ static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+ {
+       int rval;
+       struct v4l2_ifparm p;
+ 
+       rval = vidioc_int_g_ifparm(cam->sdev, &p);
+       if (rval) {
+               dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+               return rval;
+       }
+ 
+       cam->if_type = p.if_type;
+ 
+       cam->cc_ctrl = CC_CTRL_CC_EN;
+ 
+       switch (p.if_type) {
+       case V4L2_IF_TYPE_BT656:
+               if (p.u.bt656.frame_start_on_rising_vs)
+                       cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+               if (p.u.bt656.bt_sync_correct)
+                       cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+               if (p.u.bt656.swap)
+                       cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+               if (p.u.bt656.latch_clk_inv)
+                       cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+               if (p.u.bt656.nobt_hs_inv)
+                       cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+               if (p.u.bt656.nobt_vs_inv)
+                       cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+ 
+               switch (p.u.bt656.mode) {
+               case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+                       break;
+               case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+                       cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+                       break;
+               default:
+                       dev_err(cam->dev,
+                               "bt656 interface mode %d not supported\n",
+                               p.u.bt656.mode);
+                       return -EINVAL;
+               }
+               /*
+                * The clock rate that the sensor wants has changed.
+                * We have to adjust the xclk from OMAP 2 side to
+                * match the sensor's wish as closely as possible.
+                */
+               if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+                       u32 xclk = p.u.bt656.clock_curr;
+                       u32 divisor;
+ 
+                       if (xclk == 0)
+                               return -EINVAL;
+ 
+                       if (xclk > CAM_MCLK)
+                               xclk = CAM_MCLK;
+ 
+                       divisor = CAM_MCLK / xclk;
+                       if (divisor * xclk < CAM_MCLK)
+                               divisor++;
+                       if (CAM_MCLK / divisor < p.u.bt656.clock_min
+                           && divisor > 1)
+                               divisor--;
+                       if (divisor > 30)
+                               divisor = 30;
+ 
+                       xclk = CAM_MCLK / divisor;
+ 
+                       if (xclk < p.u.bt656.clock_min
+                           || xclk > p.u.bt656.clock_max)
+                               return -EINVAL;
+ 
+                       cam->if_u.bt656.xclk = xclk;
+               }
+               omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+               break;
+       default:
+               /* FIXME: how about other interfaces? */
+               dev_err(cam->dev, "interface type %d not supported\n",
+                       p.if_type);
+               return -EINVAL;
+       }
+ 
+       return 0;
+ }
+ 
+ static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+ {
+       switch (cam->if_type) {
+       case V4L2_IF_TYPE_BT656:
+               omap24xxcam_core_xclk_set(cam, 0);
+               break;
+       }
+ }
+ 
+ /*
+  * Initialise the sensor hardware.
+  */
+ static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+ {
+       int err = 0;
+       struct v4l2_int_device *sdev = cam->sdev;
+ 
+       omap24xxcam_clock_on(cam);
+       err = omap24xxcam_sensor_if_enable(cam);
+       if (err) {
+               dev_err(cam->dev, "sensor interface could not be enabled at "
+                       "initialisation, %d\n", err);
+               cam->sdev = NULL;
+               goto out;
+       }
+ 
+       /* power up sensor during sensor initialization */
+       vidioc_int_s_power(sdev, 1);
+ 
+       err = vidioc_int_dev_init(sdev);
+       if (err) {
+               dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+               /* Sensor init failed --- it's nonexistent to us! */
+               cam->sdev = NULL;
+               goto out;
+       }
+ 
+       dev_info(cam->dev, "sensor is %s\n", sdev->name);
+ 
+ out:
+       omap24xxcam_sensor_if_disable(cam);
+       omap24xxcam_clock_off(cam);
+ 
+       vidioc_int_s_power(sdev, 0);
+ 
+       return err;
+ }
+ 
+ static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+ {
+       if (cam->sdev)
+               vidioc_int_dev_exit(cam->sdev);
+ }
+ 
+ static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+ {
+       omap24xxcam_sensor_if_disable(cam);
+       omap24xxcam_clock_off(cam);
+       vidioc_int_s_power(cam->sdev, 0);
+ }
+ 
+ /*
+  * Power-up and configure camera sensor. It's ready for capturing now.
+  */
+ static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+ {
+       int rval;
+ 
+       omap24xxcam_clock_on(cam);
+ 
+       omap24xxcam_sensor_if_enable(cam);
+ 
+       rval = vidioc_int_s_power(cam->sdev, 1);
+       if (rval)
+               goto out;
+ 
+       rval = vidioc_int_init(cam->sdev);
+       if (rval)
+               goto out;
+ 
+       return 0;
+ 
+ out:
+       omap24xxcam_sensor_disable(cam);
+ 
+       return rval;
+ }
+ 
+ static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+ {
+       struct omap24xxcam_device *cam =
+               container_of(work, struct omap24xxcam_device,
+                            sensor_reset_work);
+ 
+       if (atomic_read(&cam->reset_disable))
+               return;
+ 
+       omap24xxcam_capture_stop(cam);
+ 
+       if (vidioc_int_reset(cam->sdev) == 0) {
+               vidioc_int_init(cam->sdev);
+       } else {
+               /* Can't reset it by vidioc_int_reset. */
+               omap24xxcam_sensor_disable(cam);
+               omap24xxcam_sensor_enable(cam);
+       }
+ 
+       omap24xxcam_capture_cont(cam);
+ }
+ 
+ /*
+  *
+  * IOCTL interface.
+  *
+  */
+ 
+ static int vidioc_querycap(struct file *file, void *fh,
+                          struct v4l2_capability *cap)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+ 
+       strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+       strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ 
+       return 0;
+ }
+ 
+ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+                                  struct v4l2_fmtdesc *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+                               struct v4l2_format *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               rval = -EBUSY;
+               goto out;
+       }
+ 
+       rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+ 
+ out:
+       mutex_unlock(&cam->mutex);
+ 
+       if (!rval) {
+               mutex_lock(&ofh->vbq.vb_lock);
+               ofh->pix = f->fmt.pix;
+               mutex_unlock(&ofh->vbq.vb_lock);
+       }
+ 
+       memset(f, 0, sizeof(*f));
+       vidioc_g_fmt_vid_cap(file, fh, f);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+                                 struct v4l2_format *f)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_reqbufs(struct file *file, void *fh,
+                         struct v4l2_requestbuffers *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               mutex_unlock(&cam->mutex);
+               return -EBUSY;
+       }
+ 
+       omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+       mutex_unlock(&cam->mutex);
+ 
+       rval = videobuf_reqbufs(&ofh->vbq, b);
+ 
+       /*
+        * Either videobuf_reqbufs failed or the buffers are not
+        * memory-mapped (which would need special attention).
+        */
+       if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+               goto out;
+ 
+       rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+       if (rval)
+               omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ 
+ out:
+       return rval;
+ }
+ 
+ static int vidioc_querybuf(struct file *file, void *fh,
+                          struct v4l2_buffer *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+ 
+       return videobuf_querybuf(&ofh->vbq, b);
+ }
+ 
+ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+ 
+       return videobuf_qbuf(&ofh->vbq, b);
+ }
+ 
+ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       struct videobuf_buffer *vb;
+       int rval;
+ 
+ videobuf_dqbuf_again:
+       rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+       if (rval)
+               goto out;
+ 
+       vb = ofh->vbq.bufs[b->index];
+ 
+       mutex_lock(&cam->mutex);
+       /* _needs_reset returns -EIO if reset is required. */
+       rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+       mutex_unlock(&cam->mutex);
+       if (rval == -EIO)
+               schedule_work(&cam->sensor_reset_work);
+       else
+               rval = 0;
+ 
+ out:
+       /*
+        * This is a hack. We don't want to show -EIO to the user
+        * space. Requeue the buffer and try again if we're not doing
+        * this in non-blocking mode.
+        */
+       if (rval == -EIO) {
+               videobuf_qbuf(&ofh->vbq, b);
+               if (!(file->f_flags & O_NONBLOCK))
+                       goto videobuf_dqbuf_again;
+               /*
+                * We don't have a videobuf_buffer now --- maybe next
+                * time...
+                */
+               rval = -EAGAIN;
+       }
+ 
+       return rval;
+ }
+ 
+ static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               rval = -EBUSY;
+               goto out;
+       }
+ 
+       rval = omap24xxcam_sensor_if_enable(cam);
+       if (rval) {
+               dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+               goto out;
+       }
+ 
+       rval = videobuf_streamon(&ofh->vbq);
+       if (!rval) {
+               cam->streaming = file;
+               sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+       }
+ 
+ out:
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       struct videobuf_queue *q = &ofh->vbq;
+       int rval;
+ 
+       atomic_inc(&cam->reset_disable);
+ 
 -      flush_work_sync(&cam->sensor_reset_work);
++      flush_work(&cam->sensor_reset_work);
+ 
+       rval = videobuf_streamoff(q);
+       if (!rval) {
+               mutex_lock(&cam->mutex);
+               cam->streaming = NULL;
+               mutex_unlock(&cam->mutex);
+               sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+       }
+ 
+       atomic_dec(&cam->reset_disable);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_enum_input(struct file *file, void *fh,
+                            struct v4l2_input *inp)
+ {
+       if (inp->index > 0)
+               return -EINVAL;
+ 
+       strlcpy(inp->name, "camera", sizeof(inp->name));
+       inp->type = V4L2_INPUT_TYPE_CAMERA;
+ 
+       return 0;
+ }
+ 
+ static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+ {
+       *i = 0;
+ 
+       return 0;
+ }
+ 
+ static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+ {
+       if (i > 0)
+               return -EINVAL;
+ 
+       return 0;
+ }
+ 
+ static int vidioc_queryctrl(struct file *file, void *fh,
+                           struct v4l2_queryctrl *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       rval = vidioc_int_queryctrl(cam->sdev, a);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_g_ctrl(struct file *file, void *fh,
+                        struct v4l2_control *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_g_ctrl(cam->sdev, a);
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_s_ctrl(struct file *file, void *fh,
+                        struct v4l2_control *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_s_ctrl(cam->sdev, a);
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_g_parm(struct file *file, void *fh,
+                        struct v4l2_streamparm *a) {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       rval = vidioc_int_g_parm(cam->sdev, a);
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ static int vidioc_s_parm(struct file *file, void *fh,
+                        struct v4l2_streamparm *a)
+ {
+       struct omap24xxcam_fh *ofh = fh;
+       struct omap24xxcam_device *cam = ofh->cam;
+       struct v4l2_streamparm old_streamparm;
+       int rval;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               rval = -EBUSY;
+               goto out;
+       }
+ 
+       old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+       if (rval)
+               goto out;
+ 
+       rval = vidioc_int_s_parm(cam->sdev, a);
+       if (rval)
+               goto out;
+ 
+       rval = omap24xxcam_sensor_if_enable(cam);
+       /*
+        * Revert to old streaming parameters if enabling sensor
+        * interface with the new ones failed.
+        */
+       if (rval)
+               vidioc_int_s_parm(cam->sdev, &old_streamparm);
+ 
+ out:
+       mutex_unlock(&cam->mutex);
+ 
+       return rval;
+ }
+ 
+ /*
+  *
+  * File operations.
+  *
+  */
+ 
+ static unsigned int omap24xxcam_poll(struct file *file,
+                                    struct poll_table_struct *wait)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       struct videobuf_buffer *vb;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming != file) {
+               mutex_unlock(&cam->mutex);
+               return POLLERR;
+       }
+       mutex_unlock(&cam->mutex);
+ 
+       mutex_lock(&fh->vbq.vb_lock);
+       if (list_empty(&fh->vbq.stream)) {
+               mutex_unlock(&fh->vbq.vb_lock);
+               return POLLERR;
+       }
+       vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+       mutex_unlock(&fh->vbq.vb_lock);
+ 
+       poll_wait(file, &vb->done, wait);
+ 
+       if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+               return POLLIN | POLLRDNORM;
+ 
+       return 0;
+ }
+ 
+ static int omap24xxcam_mmap_buffers(struct file *file,
+                                   struct vm_area_struct *vma)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       struct omap24xxcam_device *cam = fh->cam;
+       struct videobuf_queue *vbq = &fh->vbq;
+       unsigned int first, last, size, i, j;
+       int err = 0;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming) {
+               mutex_unlock(&cam->mutex);
+               return -EBUSY;
+       }
+       mutex_unlock(&cam->mutex);
+       mutex_lock(&vbq->vb_lock);
+ 
+       /* look for first buffer to map */
+       for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+               if (NULL == vbq->bufs[first])
+                       continue;
+               if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+                       continue;
+               if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+                       break;
+       }
+ 
+       /* look for last buffer to map */
+       for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+               if (NULL == vbq->bufs[last])
+                       continue;
+               if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+                       continue;
+               size += vbq->bufs[last]->bsize;
+               if (size == (vma->vm_end - vma->vm_start))
+                       break;
+       }
+ 
+       size = 0;
+       for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) {
+               struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+ 
+               for (j = 0; j < dma->sglen; j++) {
+                       err = remap_pfn_range(
+                               vma, vma->vm_start + size,
+                               page_to_pfn(sg_page(&dma->sglist[j])),
+                               sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+                       if (err)
+                               goto out;
+                       size += sg_dma_len(&dma->sglist[j]);
+               }
+       }
+ 
+ out:
+       mutex_unlock(&vbq->vb_lock);
+ 
+       return err;
+ }
+ 
+ static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       int rval;
+ 
+       /* let the video-buf mapper check arguments and set-up structures */
+       rval = videobuf_mmap_mapper(&fh->vbq, vma);
+       if (rval)
+               return rval;
+ 
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ 
+       /* do mapping to our allocated buffers */
+       rval = omap24xxcam_mmap_buffers(file, vma);
+       /*
+        * In case of error, free vma->vm_private_data allocated by
+        * videobuf_mmap_mapper.
+        */
+       if (rval)
+               kfree(vma->vm_private_data);
+ 
+       return rval;
+ }
+ 
+ static int omap24xxcam_open(struct file *file)
+ {
+       struct omap24xxcam_device *cam = omap24xxcam.priv;
+       struct omap24xxcam_fh *fh;
+       struct v4l2_format format;
+ 
+       if (!cam || !cam->vfd)
+               return -ENODEV;
+ 
+       fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+       if (fh == NULL)
+               return -ENOMEM;
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+               mutex_unlock(&cam->mutex);
+               goto out_try_module_get;
+       }
+ 
+       if (atomic_inc_return(&cam->users) == 1) {
+               omap24xxcam_hwinit(cam);
+               if (omap24xxcam_sensor_enable(cam)) {
+                       mutex_unlock(&cam->mutex);
+                       goto out_omap24xxcam_sensor_enable;
+               }
+       }
+       mutex_unlock(&cam->mutex);
+ 
+       fh->cam = cam;
+       mutex_lock(&cam->mutex);
+       vidioc_int_g_fmt_cap(cam->sdev, &format);
+       mutex_unlock(&cam->mutex);
+       /* FIXME: how about fh->pix when there are more users? */
+       fh->pix = format.fmt.pix;
+ 
+       file->private_data = fh;
+ 
+       spin_lock_init(&fh->vbq_lock);
+ 
+       videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+                               &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                               V4L2_FIELD_NONE,
+                               sizeof(struct videobuf_buffer), fh, NULL);
+ 
+       return 0;
+ 
+ out_omap24xxcam_sensor_enable:
+       omap24xxcam_poweron_reset(cam);
+       module_put(cam->sdev->module);
+ 
+ out_try_module_get:
+       kfree(fh);
+ 
+       return -ENODEV;
+ }
+ 
+ static int omap24xxcam_release(struct file *file)
+ {
+       struct omap24xxcam_fh *fh = file->private_data;
+       struct omap24xxcam_device *cam = fh->cam;
+ 
+       atomic_inc(&cam->reset_disable);
+ 
 -      flush_work_sync(&cam->sensor_reset_work);
++      flush_work(&cam->sensor_reset_work);
+ 
+       /* stop streaming capture */
+       videobuf_streamoff(&fh->vbq);
+ 
+       mutex_lock(&cam->mutex);
+       if (cam->streaming == file) {
+               cam->streaming = NULL;
+               mutex_unlock(&cam->mutex);
+               sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+       } else {
+               mutex_unlock(&cam->mutex);
+       }
+ 
+       atomic_dec(&cam->reset_disable);
+ 
+       omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+ 
+       /*
+        * Make sure the reset work we might have scheduled is not
+        * pending! It may be run *only* if we have users. (And it may
+        * not be scheduled anymore since streaming is already
+        * disabled.)
+        */
++      flush_work(&cam->sensor_reset_work);
+ 
+       mutex_lock(&cam->mutex);
+       if (atomic_dec_return(&cam->users) == 0) {
+               omap24xxcam_sensor_disable(cam);
+               omap24xxcam_poweron_reset(cam);
+       }
+       mutex_unlock(&cam->mutex);
+ 
+       file->private_data = NULL;
+ 
+       module_put(cam->sdev->module);
+       kfree(fh);
+ 
+       return 0;
+ }
+ 
+ static struct v4l2_file_operations omap24xxcam_fops = {
+       .ioctl   = video_ioctl2,
+       .poll    = omap24xxcam_poll,
+       .mmap    = omap24xxcam_mmap,
+       .open    = omap24xxcam_open,
+       .release = omap24xxcam_release,
+ };
+ 
+ /*
+  *
+  * Power management.
+  *
+  */
+ 
+ #ifdef CONFIG_PM
+ static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+ {
+       struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+ 
+       if (atomic_read(&cam->users) == 0)
+               return 0;
+ 
+       if (!atomic_read(&cam->reset_disable))
+               omap24xxcam_capture_stop(cam);
+ 
+       omap24xxcam_sensor_disable(cam);
+       omap24xxcam_poweron_reset(cam);
+ 
+       return 0;
+ }
+ 
+ static int omap24xxcam_resume(struct platform_device *pdev)
+ {
+       struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+ 
+       if (atomic_read(&cam->users) == 0)
+               return 0;
+ 
+       omap24xxcam_hwinit(cam);
+       omap24xxcam_sensor_enable(cam);
+ 
+       if (!atomic_read(&cam->reset_disable))
+               omap24xxcam_capture_cont(cam);
+ 
+       return 0;
+ }
+ #endif /* CONFIG_PM */
+ 
+ static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+       .vidioc_querycap        = vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+       .vidioc_reqbufs         = vidioc_reqbufs,
+       .vidioc_querybuf        = vidioc_querybuf,
+       .vidioc_qbuf            = vidioc_qbuf,
+       .vidioc_dqbuf           = vidioc_dqbuf,
+       .vidioc_streamon        = vidioc_streamon,
+       .vidioc_streamoff       = vidioc_streamoff,
+       .vidioc_enum_input      = vidioc_enum_input,
+       .vidioc_g_input         = vidioc_g_input,
+       .vidioc_s_input         = vidioc_s_input,
+       .vidioc_queryctrl       = vidioc_queryctrl,
+       .vidioc_g_ctrl          = vidioc_g_ctrl,
+       .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_g_parm          = vidioc_g_parm,
+       .vidioc_s_parm          = vidioc_s_parm,
+ };
+ 
+ /*
+  *
+  * Camera device (i.e. /dev/video).
+  *
+  */
+ 
+ static int omap24xxcam_device_register(struct v4l2_int_device *s)
+ {
+       struct omap24xxcam_device *cam = s->u.slave->master->priv;
+       struct video_device *vfd;
+       int rval;
+ 
+       /* We already have a slave. */
+       if (cam->sdev)
+               return -EBUSY;
+ 
+       cam->sdev = s;
+ 
+       if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+               dev_err(cam->dev, "could not register sysfs entry\n");
+               rval = -EBUSY;
+               goto err;
+       }
+ 
+       /* initialize the video_device struct */
+       vfd = cam->vfd = video_device_alloc();
+       if (!vfd) {
+               dev_err(cam->dev, "could not allocate video device struct\n");
+               rval = -ENOMEM;
+               goto err;
+       }
+       vfd->release = video_device_release;
+ 
+       vfd->parent = cam->dev;
+ 
+       strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+       vfd->fops                = &omap24xxcam_fops;
+       vfd->ioctl_ops           = &omap24xxcam_ioctl_fops;
+ 
+       omap24xxcam_hwinit(cam);
+ 
+       rval = omap24xxcam_sensor_init(cam);
+       if (rval)
+               goto err;
+ 
+       if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+               dev_err(cam->dev, "could not register V4L device\n");
+               rval = -EBUSY;
+               goto err;
+       }
+ 
+       omap24xxcam_poweron_reset(cam);
+ 
+       dev_info(cam->dev, "registered device %s\n",
+                video_device_node_name(vfd));
+ 
+       return 0;
+ 
+ err:
+       omap24xxcam_device_unregister(s);
+ 
+       return rval;
+ }
+ 
+ static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+ {
+       struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ 
+       omap24xxcam_sensor_exit(cam);
+ 
+       if (cam->vfd) {
+               if (!video_is_registered(cam->vfd)) {
+                       /*
+                        * The device was never registered, so release the
+                        * video_device struct directly.
+                        */
+                       video_device_release(cam->vfd);
+               } else {
+                       /*
+                        * The unregister function will release the
+                        * video_device struct as well as
+                        * unregistering it.
+                        */
+                       video_unregister_device(cam->vfd);
+               }
+               cam->vfd = NULL;
+       }
+ 
+       device_remove_file(cam->dev, &dev_attr_streaming);
+ 
+       cam->sdev = NULL;
+ }
+ 
+ static struct v4l2_int_master omap24xxcam_master = {
+       .attach = omap24xxcam_device_register,
+       .detach = omap24xxcam_device_unregister,
+ };
+ 
+ static struct v4l2_int_device omap24xxcam = {
+       .module = THIS_MODULE,
+       .name   = CAM_NAME,
+       .type   = v4l2_int_type_master,
+       .u      = {
+               .master = &omap24xxcam_master
+       },
+ };
+ 
+ /*
+  *
+  * Driver initialisation and deinitialisation.
+  *
+  */
+ 
+ static int __devinit omap24xxcam_probe(struct platform_device *pdev)
+ {
+       struct omap24xxcam_device *cam;
+       struct resource *mem;
+       int irq;
+ 
+       cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+       if (!cam) {
+               dev_err(&pdev->dev, "could not allocate memory\n");
+               goto err;
+       }
+ 
+       platform_set_drvdata(pdev, cam);
+ 
+       cam->dev = &pdev->dev;
+ 
+       /*
+        * Impose a lower limit on the amount of memory allocated for
+        * capture. We require at least enough memory to double-buffer
+        * QVGA (300KB).
+        */
+       if (capture_mem < 320 * 240 * 2 * 2)
+               capture_mem = 320 * 240 * 2 * 2;
+       cam->capture_mem = capture_mem;
+ 
+       /* request the mem region for the camera registers */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(cam->dev, "no mem resource?\n");
+               goto err;
+       }
+       if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+               dev_err(cam->dev,
+                       "cannot reserve camera register I/O region\n");
+               goto err;
+       }
+       cam->mmio_base_phys = mem->start;
+       cam->mmio_size = resource_size(mem);
+ 
+       /* map the region */
+       cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+       if (!cam->mmio_base) {
+               dev_err(cam->dev, "cannot map camera register I/O region\n");
+               goto err;
+       }
+ 
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(cam->dev, "no irq for camera?\n");
+               goto err;
+       }
+ 
+       /* install the interrupt service routine */
+       if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+               dev_err(cam->dev,
+                       "could not install interrupt service routine\n");
+               goto err;
+       }
+       cam->irq = irq;
+ 
+       if (omap24xxcam_clock_get(cam))
+               goto err;
+ 
+       INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+ 
+       mutex_init(&cam->mutex);
+       spin_lock_init(&cam->core_enable_disable_lock);
+ 
+       omap24xxcam_sgdma_init(&cam->sgdma,
+                              cam->mmio_base + CAMDMA_REG_OFFSET,
+                              omap24xxcam_stalled_dma_reset,
+                              (unsigned long)cam);
+ 
+       omap24xxcam.priv = cam;
+ 
+       if (v4l2_int_device_register(&omap24xxcam))
+               goto err;
+ 
+       return 0;
+ 
+ err:
+       omap24xxcam_remove(pdev);
+       return -ENODEV;
+ }
+ 
+ static int omap24xxcam_remove(struct platform_device *pdev)
+ {
+       struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+ 
+       if (!cam)
+               return 0;
+ 
+       if (omap24xxcam.priv != NULL)
+               v4l2_int_device_unregister(&omap24xxcam);
+       omap24xxcam.priv = NULL;
+ 
+       omap24xxcam_clock_put(cam);
+ 
+       if (cam->irq) {
+               free_irq(cam->irq, cam);
+               cam->irq = 0;
+       }
+ 
+       if (cam->mmio_base) {
+               iounmap((void *)cam->mmio_base);
+               cam->mmio_base = 0;
+       }
+ 
+       if (cam->mmio_base_phys) {
+               release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+               cam->mmio_base_phys = 0;
+       }
+ 
+       kfree(cam);
+ 
+       return 0;
+ }
+ 
+ static struct platform_driver omap24xxcam_driver = {
+       .probe   = omap24xxcam_probe,
+       .remove  = omap24xxcam_remove,
+ #ifdef CONFIG_PM
+       .suspend = omap24xxcam_suspend,
+       .resume  = omap24xxcam_resume,
+ #endif
+       .driver  = {
+               .name = CAM_NAME,
+               .owner = THIS_MODULE,
+       },
+ };
+ 
+ module_platform_driver(omap24xxcam_driver);
+ 
+ MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+ MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(OMAP24XXCAM_VERSION);
+ module_param(video_nr, int, 0);
+ MODULE_PARM_DESC(video_nr,
+                "Minor number for video device (-1 ==> auto assign)");
+ module_param(capture_mem, int, 0);
+ MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+                "buffers (default 4800kiB)");
 
--- /dev/null
+ /*
+  * isp.c
+  *
+  * TI OMAP3 ISP - Core
+  *
+  * Copyright (C) 2006-2010 Nokia Corporation
+  * Copyright (C) 2007-2009 Texas Instruments, Inc.
+  *
+  * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+  *         Sakari Ailus <sakari.ailus@iki.fi>
+  *
+  * Contributors:
+  *    Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+  *    Sakari Ailus <sakari.ailus@iki.fi>
+  *    David Cohen <dacohen@gmail.com>
+  *    Stanimir Varbanov <svarbanov@mm-sol.com>
+  *    Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
+  *    Tuukka Toivonen <tuukkat76@gmail.com>
+  *    Sergio Aguirre <saaguirre@ti.com>
+  *    Antti Koskipaa <akoskipa@gmail.com>
+  *    Ivan T. Ivanov <iivanov@mm-sol.com>
+  *    RaniSuneela <r-m@ti.com>
+  *    Atanas Filipov <afilipov@mm-sol.com>
+  *    Gjorgji Rosikopulos <grosikopulos@mm-sol.com>
+  *    Hiroshi DOYU <hiroshi.doyu@nokia.com>
+  *    Nayden Kanchev <nkanchev@mm-sol.com>
+  *    Phil Carmody <ext-phil.2.carmody@nokia.com>
+  *    Artem Bityutskiy <artem.bityutskiy@nokia.com>
+  *    Dominic Curran <dcurran@ti.com>
+  *    Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi>
+  *    Pallavi Kulkarni <p-kulkarni@ti.com>
+  *    Vaibhav Hiremath <hvaibhav@ti.com>
+  *    Mohit Jalori <mjalori@ti.com>
+  *    Sameer Venkatraman <sameerv@ti.com>
+  *    Senthilvadivu Guruswamy <svadivu@ti.com>
+  *    Thara Gopinath <thara@ti.com>
+  *    Toni Leinonen <toni.leinonen@nokia.com>
+  *    Troy Laramy <t-laramy@ti.com>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  *
+  * 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., 51 Franklin St, Fifth Floor, Boston, MA
+  * 02110-1301 USA
+  */
+ 
+ #include <asm/cacheflush.h>
+ 
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/i2c.h>
+ #include <linux/interrupt.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/vmalloc.h>
+ 
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-device.h>
+ 
++#include <plat/cpu.h>
++
+ #include "isp.h"
+ #include "ispreg.h"
+ #include "ispccdc.h"
+ #include "isppreview.h"
+ #include "ispresizer.h"
+ #include "ispcsi2.h"
+ #include "ispccp2.h"
+ #include "isph3a.h"
+ #include "isphist.h"
+ 
+ static unsigned int autoidle;
+ module_param(autoidle, int, 0444);
+ MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
+ 
+ static void isp_save_ctx(struct isp_device *isp);
+ 
+ static void isp_restore_ctx(struct isp_device *isp);
+ 
+ static const struct isp_res_mapping isp_res_maps[] = {
+       {
+               .isp_rev = ISP_REVISION_2_0,
+               .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+                      1 << OMAP3_ISP_IOMEM_CCP2 |
+                      1 << OMAP3_ISP_IOMEM_CCDC |
+                      1 << OMAP3_ISP_IOMEM_HIST |
+                      1 << OMAP3_ISP_IOMEM_H3A |
+                      1 << OMAP3_ISP_IOMEM_PREV |
+                      1 << OMAP3_ISP_IOMEM_RESZ |
+                      1 << OMAP3_ISP_IOMEM_SBL |
+                      1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+                      1 << OMAP3_ISP_IOMEM_CSIPHY2,
+       },
+       {
+               .isp_rev = ISP_REVISION_15_0,
+               .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+                      1 << OMAP3_ISP_IOMEM_CCP2 |
+                      1 << OMAP3_ISP_IOMEM_CCDC |
+                      1 << OMAP3_ISP_IOMEM_HIST |
+                      1 << OMAP3_ISP_IOMEM_H3A |
+                      1 << OMAP3_ISP_IOMEM_PREV |
+                      1 << OMAP3_ISP_IOMEM_RESZ |
+                      1 << OMAP3_ISP_IOMEM_SBL |
+                      1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+                      1 << OMAP3_ISP_IOMEM_CSIPHY2 |
+                      1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
+                      1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
+                      1 << OMAP3_ISP_IOMEM_CSIPHY1 |
+                      1 << OMAP3_ISP_IOMEM_CSI2C_REGS2,
+       },
+ };
+ 
+ /* Structure for saving/restoring ISP module registers */
+ static struct isp_reg isp_reg_list[] = {
+       {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
+       {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
+       {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
+       {0, ISP_TOK_TERM, 0}
+ };
+ 
+ /*
+  * omap3isp_flush - Post pending L3 bus writes by doing a register readback
+  * @isp: OMAP3 ISP device
+  *
+  * In order to force posting of pending writes, we need to write and
+  * readback the same register, in this case the revision register.
+  *
+  * See this link for reference:
+  *   http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+  */
+ void omap3isp_flush(struct isp_device *isp)
+ {
+       isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+       isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ }
+ 
+ /*
+  * isp_enable_interrupts - Enable ISP interrupts.
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_enable_interrupts(struct isp_device *isp)
+ {
+       static const u32 irq = IRQ0ENABLE_CSIA_IRQ
+                            | IRQ0ENABLE_CSIB_IRQ
+                            | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ
+                            | IRQ0ENABLE_CCDC_LSC_DONE_IRQ
+                            | IRQ0ENABLE_CCDC_VD0_IRQ
+                            | IRQ0ENABLE_CCDC_VD1_IRQ
+                            | IRQ0ENABLE_HS_VS_IRQ
+                            | IRQ0ENABLE_HIST_DONE_IRQ
+                            | IRQ0ENABLE_H3A_AWB_DONE_IRQ
+                            | IRQ0ENABLE_H3A_AF_DONE_IRQ
+                            | IRQ0ENABLE_PRV_DONE_IRQ
+                            | IRQ0ENABLE_RSZ_DONE_IRQ;
+ 
+       isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+       isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+ }
+ 
+ /*
+  * isp_disable_interrupts - Disable ISP interrupts.
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_disable_interrupts(struct isp_device *isp)
+ {
+       isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+ }
+ 
+ /**
+  * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
+  * @isp: OMAP3 ISP device
+  * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
+  * @xclksel: XCLK to configure (0 = A, 1 = B).
+  *
+  * Configures the specified MCLK divisor in the ISP timing control register
+  * (TCTRL_CTRL) to generate the desired xclk clock value.
+  *
+  * Divisor = cam_mclk_hz / xclk
+  *
+  * Returns the final frequency that is actually being generated
+  **/
+ static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
+ {
+       u32 divisor;
+       u32 currentxclk;
+       unsigned long mclk_hz;
+ 
+       if (!omap3isp_get(isp))
+               return 0;
+ 
+       mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+ 
+       if (xclk >= mclk_hz) {
+               divisor = ISPTCTRL_CTRL_DIV_BYPASS;
+               currentxclk = mclk_hz;
+       } else if (xclk >= 2) {
+               divisor = mclk_hz / xclk;
+               if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
+                       divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
+               currentxclk = mclk_hz / divisor;
+       } else {
+               divisor = xclk;
+               currentxclk = 0;
+       }
+ 
+       switch (xclksel) {
+       case ISP_XCLK_A:
+               isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+                               ISPTCTRL_CTRL_DIVA_MASK,
+                               divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
+               dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
+                       currentxclk);
+               break;
+       case ISP_XCLK_B:
+               isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+                               ISPTCTRL_CTRL_DIVB_MASK,
+                               divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
+               dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
+                       currentxclk);
+               break;
+       case ISP_XCLK_NONE:
+       default:
+               omap3isp_put(isp);
+               dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
+                       "xclk. Must be 0 (A) or 1 (B).\n");
+               return -EINVAL;
+       }
+ 
+       /* Do we go from stable whatever to clock? */
+       if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
+               omap3isp_get(isp);
+       /* Stopping the clock. */
+       else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
+               omap3isp_put(isp);
+ 
+       isp->xclk_divisor[xclksel - 1] = divisor;
+ 
+       omap3isp_put(isp);
+ 
+       return currentxclk;
+ }
+ 
+ /*
+  * isp_core_init - ISP core settings
+  * @isp: OMAP3 ISP device
+  * @idle: Consider idle state.
+  *
+  * Set the power settings for the ISP and SBL bus and cConfigure the HS/VS
+  * interrupt source.
+  *
+  * We need to configure the HS/VS interrupt source before interrupts get
+  * enabled, as the sensor might be free-running and the ISP default setting
+  * (HS edge) would put an unnecessary burden on the CPU.
+  */
+ static void isp_core_init(struct isp_device *isp, int idle)
+ {
+       isp_reg_writel(isp,
+                      ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY :
+                               ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) <<
+                       ISP_SYSCONFIG_MIDLEMODE_SHIFT) |
+                       ((isp->revision == ISP_REVISION_15_0) ?
+                         ISP_SYSCONFIG_AUTOIDLE : 0),
+                      OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+ 
+       isp_reg_writel(isp,
+                      (isp->autoidle ? ISPCTRL_SBL_AUTOIDLE : 0) |
+                      ISPCTRL_SYNC_DETECT_VSRISE,
+                      OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+ }
+ 
+ /*
+  * Configure the bridge and lane shifter. Valid inputs are
+  *
+  * CCDC_INPUT_PARALLEL: Parallel interface
+  * CCDC_INPUT_CSI2A: CSI2a receiver
+  * CCDC_INPUT_CCP2B: CCP2b receiver
+  * CCDC_INPUT_CSI2C: CSI2c receiver
+  *
+  * The bridge and lane shifter are configured according to the selected input
+  * and the ISP platform data.
+  */
+ void omap3isp_configure_bridge(struct isp_device *isp,
+                              enum ccdc_input_entity input,
+                              const struct isp_parallel_platform_data *pdata,
+                              unsigned int shift, unsigned int bridge)
+ {
+       u32 ispctrl_val;
+ 
+       ispctrl_val  = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+       ispctrl_val &= ~ISPCTRL_SHIFT_MASK;
+       ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
+       ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK;
+       ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK;
+       ispctrl_val |= bridge;
+ 
+       switch (input) {
+       case CCDC_INPUT_PARALLEL:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
+               ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
+               shift += pdata->data_lane_shift * 2;
+               break;
+ 
+       case CCDC_INPUT_CSI2A:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA;
+               break;
+ 
+       case CCDC_INPUT_CCP2B:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB;
+               break;
+ 
+       case CCDC_INPUT_CSI2C:
+               ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC;
+               break;
+ 
+       default:
+               return;
+       }
+ 
+       ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
+ 
+       isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+ }
+ 
+ void omap3isp_hist_dma_done(struct isp_device *isp)
+ {
+       if (omap3isp_ccdc_busy(&isp->isp_ccdc) ||
+           omap3isp_stat_pcr_busy(&isp->isp_hist)) {
+               /* Histogram cannot be enabled in this frame anymore */
+               atomic_set(&isp->isp_hist.buf_err, 1);
+               dev_dbg(isp->dev, "hist: Out of synchronization with "
+                                 "CCDC. Ignoring next buffer.\n");
+       }
+ }
+ 
+ static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus)
+ {
+       static const char *name[] = {
+               "CSIA_IRQ",
+               "res1",
+               "res2",
+               "CSIB_LCM_IRQ",
+               "CSIB_IRQ",
+               "res5",
+               "res6",
+               "res7",
+               "CCDC_VD0_IRQ",
+               "CCDC_VD1_IRQ",
+               "CCDC_VD2_IRQ",
+               "CCDC_ERR_IRQ",
+               "H3A_AF_DONE_IRQ",
+               "H3A_AWB_DONE_IRQ",
+               "res14",
+               "res15",
+               "HIST_DONE_IRQ",
+               "CCDC_LSC_DONE",
+               "CCDC_LSC_PREFETCH_COMPLETED",
+               "CCDC_LSC_PREFETCH_ERROR",
+               "PRV_DONE_IRQ",
+               "CBUFF_IRQ",
+               "res22",
+               "res23",
+               "RSZ_DONE_IRQ",
+               "OVF_IRQ",
+               "res26",
+               "res27",
+               "MMU_ERR_IRQ",
+               "OCP_ERR_IRQ",
+               "SEC_ERR_IRQ",
+               "HS_VS_IRQ",
+       };
+       int i;
+ 
+       dev_dbg(isp->dev, "ISP IRQ: ");
+ 
+       for (i = 0; i < ARRAY_SIZE(name); i++) {
+               if ((1 << i) & irqstatus)
+                       printk(KERN_CONT "%s ", name[i]);
+       }
+       printk(KERN_CONT "\n");
+ }
+ 
+ static void isp_isr_sbl(struct isp_device *isp)
+ {
+       struct device *dev = isp->dev;
+       struct isp_pipeline *pipe;
+       u32 sbl_pcr;
+ 
+       /*
+        * Handle shared buffer logic overflows for video buffers.
+        * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored.
+        */
+       sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+       isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+       sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF;
+ 
+       if (sbl_pcr)
+               dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr);
+ 
+       if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+ 
+       if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+ 
+       if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+ 
+       if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) {
+               pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+ 
+       if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF
+                      | ISPSBL_PCR_RSZ2_WBL_OVF
+                      | ISPSBL_PCR_RSZ3_WBL_OVF
+                      | ISPSBL_PCR_RSZ4_WBL_OVF)) {
+               pipe = to_isp_pipeline(&isp->isp_res.subdev.entity);
+               if (pipe != NULL)
+                       pipe->error = true;
+       }
+ 
+       if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF)
+               omap3isp_stat_sbl_overflow(&isp->isp_af);
+ 
+       if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF)
+               omap3isp_stat_sbl_overflow(&isp->isp_aewb);
+ }
+ 
+ /*
+  * isp_isr - Interrupt Service Routine for Camera ISP module.
+  * @irq: Not used currently.
+  * @_isp: Pointer to the OMAP3 ISP device
+  *
+  * Handles the corresponding callback if plugged in.
+  *
+  * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+  * IRQ wasn't handled.
+  */
+ static irqreturn_t isp_isr(int irq, void *_isp)
+ {
+       static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ |
+                                      IRQ0STATUS_CCDC_LSC_DONE_IRQ |
+                                      IRQ0STATUS_CCDC_VD0_IRQ |
+                                      IRQ0STATUS_CCDC_VD1_IRQ |
+                                      IRQ0STATUS_HS_VS_IRQ;
+       struct isp_device *isp = _isp;
+       u32 irqstatus;
+ 
+       irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+       isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ 
+       isp_isr_sbl(isp);
+ 
+       if (irqstatus & IRQ0STATUS_CSIA_IRQ)
+               omap3isp_csi2_isr(&isp->isp_csi2a);
+ 
+       if (irqstatus & IRQ0STATUS_CSIB_IRQ)
+               omap3isp_ccp2_isr(&isp->isp_ccp2);
+ 
+       if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) {
+               if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW)
+                       omap3isp_preview_isr_frame_sync(&isp->isp_prev);
+               if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)
+                       omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+               omap3isp_stat_isr_frame_sync(&isp->isp_aewb);
+               omap3isp_stat_isr_frame_sync(&isp->isp_af);
+               omap3isp_stat_isr_frame_sync(&isp->isp_hist);
+       }
+ 
+       if (irqstatus & ccdc_events)
+               omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events);
+ 
+       if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) {
+               if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER)
+                       omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+               omap3isp_preview_isr(&isp->isp_prev);
+       }
+ 
+       if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ)
+               omap3isp_resizer_isr(&isp->isp_res);
+ 
+       if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ)
+               omap3isp_stat_isr(&isp->isp_aewb);
+ 
+       if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ)
+               omap3isp_stat_isr(&isp->isp_af);
+ 
+       if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ)
+               omap3isp_stat_isr(&isp->isp_hist);
+ 
+       omap3isp_flush(isp);
+ 
+ #if defined(DEBUG) && defined(ISP_ISR_DEBUG)
+       isp_isr_dbg(isp, irqstatus);
+ #endif
+ 
+       return IRQ_HANDLED;
+ }
+ 
+ /* -----------------------------------------------------------------------------
+  * Pipeline power management
+  *
+  * Entities must be powered up when part of a pipeline that contains at least
+  * one open video device node.
+  *
+  * To achieve this use the entity use_count field to track the number of users.
+  * For entities corresponding to video device nodes the use_count field stores
+  * the users count of the node. For entities corresponding to subdevs the
+  * use_count field stores the total number of users of all video device nodes
+  * in the pipeline.
+  *
+  * The omap3isp_pipeline_pm_use() function must be called in the open() and
+  * close() handlers of video device nodes. It increments or decrements the use
+  * count of all subdev entities in the pipeline.
+  *
+  * To react to link management on powered pipelines, the link setup notification
+  * callback updates the use count of all entities in the source and sink sides
+  * of the link.
+  */
+ 
+ /*
+  * isp_pipeline_pm_use_count - Count the number of users of a pipeline
+  * @entity: The entity
+  *
+  * Return the total number of users of all video device nodes in the pipeline.
+  */
+ static int isp_pipeline_pm_use_count(struct media_entity *entity)
+ {
+       struct media_entity_graph graph;
+       int use = 0;
+ 
+       media_entity_graph_walk_start(&graph, entity);
+ 
+       while ((entity = media_entity_graph_walk_next(&graph))) {
+               if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+                       use += entity->use_count;
+       }
+ 
+       return use;
+ }
+ 
+ /*
+  * isp_pipeline_pm_power_one - Apply power change to an entity
+  * @entity: The entity
+  * @change: Use count change
+  *
+  * Change the entity use count by @change. If the entity is a subdev update its
+  * power state by calling the core::s_power operation when the use count goes
+  * from 0 to != 0 or from != 0 to 0.
+  *
+  * Return 0 on success or a negative error code on failure.
+  */
+ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
+ {
+       struct v4l2_subdev *subdev;
+       int ret;
+ 
+       subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+              ? media_entity_to_v4l2_subdev(entity) : NULL;
+ 
+       if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+               ret = v4l2_subdev_call(subdev, core, s_power, 1);
+               if (ret < 0 && ret != -ENOIOCTLCMD)
+                       return ret;
+       }
+ 
+       entity->use_count += change;
+       WARN_ON(entity->use_count < 0);
+ 
+       if (entity->use_count == 0 && change < 0 && subdev != NULL)
+               v4l2_subdev_call(subdev, core, s_power, 0);
+ 
+       return 0;
+ }
+ 
+ /*
+  * isp_pipeline_pm_power - Apply power change to all entities in a pipeline
+  * @entity: The entity
+  * @change: Use count change
+  *
+  * Walk the pipeline to update the use count and the power state of all non-node
+  * entities.
+  *
+  * Return 0 on success or a negative error code on failure.
+  */
+ static int isp_pipeline_pm_power(struct media_entity *entity, int change)
+ {
+       struct media_entity_graph graph;
+       struct media_entity *first = entity;
+       int ret = 0;
+ 
+       if (!change)
+               return 0;
+ 
+       media_entity_graph_walk_start(&graph, entity);
+ 
+       while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+               if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+                       ret = isp_pipeline_pm_power_one(entity, change);
+ 
+       if (!ret)
+               return 0;
+ 
+       media_entity_graph_walk_start(&graph, first);
+ 
+       while ((first = media_entity_graph_walk_next(&graph))
+              && first != entity)
+               if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+                       isp_pipeline_pm_power_one(first, -change);
+ 
+       return ret;
+ }
+ 
+ /*
+  * omap3isp_pipeline_pm_use - Update the use count of an entity
+  * @entity: The entity
+  * @use: Use (1) or stop using (0) the entity
+  *
+  * Update the use count of all entities in the pipeline and power entities on or
+  * off accordingly.
+  *
+  * Return 0 on success or a negative error code on failure. Powering entities
+  * off is assumed to never fail. No failure can occur when the use parameter is
+  * set to 0.
+  */
+ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
+ {
+       int change = use ? 1 : -1;
+       int ret;
+ 
+       mutex_lock(&entity->parent->graph_mutex);
+ 
+       /* Apply use count to node. */
+       entity->use_count += change;
+       WARN_ON(entity->use_count < 0);
+ 
+       /* Apply power change to connected non-nodes. */
+       ret = isp_pipeline_pm_power(entity, change);
+       if (ret < 0)
+               entity->use_count -= change;
+ 
+       mutex_unlock(&entity->parent->graph_mutex);
+ 
+       return ret;
+ }
+ 
+ /*
+  * isp_pipeline_link_notify - Link management notification callback
+  * @source: Pad at the start of the link
+  * @sink: Pad at the end of the link
+  * @flags: New link flags that will be applied
+  *
+  * React to link management on powered pipelines by updating the use count of
+  * all entities in the source and sink sides of the link. Entities are powered
+  * on or off accordingly.
+  *
+  * Return 0 on success or a negative error code on failure. Powering entities
+  * off is assumed to never fail. This function will not fail for disconnection
+  * events.
+  */
+ static int isp_pipeline_link_notify(struct media_pad *source,
+                                   struct media_pad *sink, u32 flags)
+ {
+       int source_use = isp_pipeline_pm_use_count(source->entity);
+       int sink_use = isp_pipeline_pm_use_count(sink->entity);
+       int ret;
+ 
+       if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+               /* Powering off entities is assumed to never fail. */
+               isp_pipeline_pm_power(source->entity, -sink_use);
+               isp_pipeline_pm_power(sink->entity, -source_use);
+               return 0;
+       }
+ 
+       ret = isp_pipeline_pm_power(source->entity, sink_use);
+       if (ret < 0)
+               return ret;
+ 
+       ret = isp_pipeline_pm_power(sink->entity, source_use);
+       if (ret < 0)
+               isp_pipeline_pm_power(source->entity, -sink_use);
+ 
+       return ret;
+ }
+ 
+ /* -----------------------------------------------------------------------------
+  * Pipeline stream management
+  */
+ 
+ /*
+  * isp_pipeline_enable - Enable streaming on a pipeline
+  * @pipe: ISP pipeline
+  * @mode: Stream mode (single shot or continuous)
+  *
+  * Walk the entities chain starting at the pipeline output video node and start
+  * all modules in the chain in the given mode.
+  *
+  * Return 0 if successful, or the return value of the failed video::s_stream
+  * operation otherwise.
+  */
+ static int isp_pipeline_enable(struct isp_pipeline *pipe,
+                              enum isp_pipeline_stream_state mode)
+ {
+       struct isp_device *isp = pipe->output->isp;
+       struct media_entity *entity;
+       struct media_pad *pad;
+       struct v4l2_subdev *subdev;
+       unsigned long flags;
+       int ret;
+ 
+       /* If the preview engine crashed it might not respond to read/write
+        * operations on the L4 bus. This would result in a bus fault and a
+        * kernel oops. Refuse to start streaming in that case. This check must
+        * be performed before the loop below to avoid starting entities if the
+        * pipeline won't start anyway (those entities would then likely fail to
+        * stop, making the problem worse).
+        */
+       if ((pipe->entities & isp->crashed) &
+           (1U << isp->isp_prev.subdev.entity.id))
+               return -EIO;
+ 
+       spin_lock_irqsave(&pipe->lock, flags);
+       pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
+       spin_unlock_irqrestore(&pipe->lock, flags);
+ 
+       pipe->do_propagation = false;
+ 
+       entity = &pipe->output->video.entity;
+       while (1) {
+               pad = &entity->pads[0];
+               if (!(pad->flags & MEDIA_PAD_FL_SINK))
+                       break;
+ 
+               pad = media_entity_remote_source(pad);
+               if (pad == NULL ||
+                   media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+ 
+               entity = pad->entity;
+               subdev = media_entity_to_v4l2_subdev(entity);
+ 
+               ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+               if (ret < 0 && ret != -ENOIOCTLCMD)
+                       return ret;
+ 
+               if (subdev == &isp->isp_ccdc.subdev) {
+                       v4l2_subdev_call(&isp->isp_aewb.subdev, video,
+                                       s_stream, mode);
+                       v4l2_subdev_call(&isp->isp_af.subdev, video,
+                                       s_stream, mode);
+                       v4l2_subdev_call(&isp->isp_hist.subdev, video,
+                                       s_stream, mode);
+                       pipe->do_propagation = true;
+               }
+       }
+ 
+       return 0;
+ }
+ 
+ static int isp_pipeline_wait_resizer(struct isp_device *isp)
+ {
+       return omap3isp_resizer_busy(&isp->isp_res);
+ }
+ 
+ static int isp_pipeline_wait_preview(struct isp_device *isp)
+ {
+       return omap3isp_preview_busy(&isp->isp_prev);
+ }
+ 
+ static int isp_pipeline_wait_ccdc(struct isp_device *isp)
+ {
+       return omap3isp_stat_busy(&isp->isp_af)
+           || omap3isp_stat_busy(&isp->isp_aewb)
+           || omap3isp_stat_busy(&isp->isp_hist)
+           || omap3isp_ccdc_busy(&isp->isp_ccdc);
+ }
+ 
+ #define ISP_STOP_TIMEOUT      msecs_to_jiffies(1000)
+ 
+ static int isp_pipeline_wait(struct isp_device *isp,
+                            int(*busy)(struct isp_device *isp))
+ {
+       unsigned long timeout = jiffies + ISP_STOP_TIMEOUT;
+ 
+       while (!time_after(jiffies, timeout)) {
+               if (!busy(isp))
+                       return 0;
+       }
+ 
+       return 1;
+ }
+ 
+ /*
+  * isp_pipeline_disable - Disable streaming on a pipeline
+  * @pipe: ISP pipeline
+  *
+  * Walk the entities chain starting at the pipeline output video node and stop
+  * all modules in the chain. Wait synchronously for the modules to be stopped if
+  * necessary.
+  *
+  * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
+  * can't be stopped (in which case a software reset of the ISP is probably
+  * necessary).
+  */
+ static int isp_pipeline_disable(struct isp_pipeline *pipe)
+ {
+       struct isp_device *isp = pipe->output->isp;
+       struct media_entity *entity;
+       struct media_pad *pad;
+       struct v4l2_subdev *subdev;
+       int failure = 0;
+       int ret;
+ 
+       /*
+        * We need to stop all the modules after CCDC first or they'll
+        * never stop since they may not get a full frame from CCDC.
+        */
+       entity = &pipe->output->video.entity;
+       while (1) {
+               pad = &entity->pads[0];
+               if (!(pad->flags & MEDIA_PAD_FL_SINK))
+                       break;
+ 
+               pad = media_entity_remote_source(pad);
+               if (pad == NULL ||
+                   media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+                       break;
+ 
+               entity = pad->entity;
+               subdev = media_entity_to_v4l2_subdev(entity);
+ 
+               if (subdev == &isp->isp_ccdc.subdev) {
+                       v4l2_subdev_call(&isp->isp_aewb.subdev,
+                                        video, s_stream, 0);
+                       v4l2_subdev_call(&isp->isp_af.subdev,
+                                        video, s_stream, 0);
+                       v4l2_subdev_call(&isp->isp_hist.subdev,
+                                        video, s_stream, 0);
+               }
+ 
+               v4l2_subdev_call(subdev, video, s_stream, 0);
+ 
+               if (subdev == &isp->isp_res.subdev)
+                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
+               else if (subdev == &isp->isp_prev.subdev)
+                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
+               else if (subdev == &isp->isp_ccdc.subdev)
+                       ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
+               else
+                       ret = 0;
+ 
+               if (ret) {
+                       dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+                       /* If the entity failed to stopped, assume it has
+                        * crashed. Mark it as such, the ISP will be reset when
+                        * applications will release it.
+                        */
+                       isp->crashed |= 1U << subdev->entity.id;
+                       failure = -ETIMEDOUT;
+               }
+       }
+ 
+       return failure;
+ }
+ 
+ /*
+  * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline
+  * @pipe: ISP pipeline
+  * @state: Stream state (stopped, single shot or continuous)
+  *
+  * Set the pipeline to the given stream state. Pipelines can be started in
+  * single-shot or continuous mode.
+  *
+  * Return 0 if successful, or the return value of the failed video::s_stream
+  * operation otherwise. The pipeline state is not updated when the operation
+  * fails, except when stopping the pipeline.
+  */
+ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
+                                enum isp_pipeline_stream_state state)
+ {
+       int ret;
+ 
+       if (state == ISP_PIPELINE_STREAM_STOPPED)
+               ret = isp_pipeline_disable(pipe);
+       else
+               ret = isp_pipeline_enable(pipe, state);
+ 
+       if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
+               pipe->stream_state = state;
+ 
+       return ret;
+ }
+ 
+ /*
+  * isp_pipeline_resume - Resume streaming on a pipeline
+  * @pipe: ISP pipeline
+  *
+  * Resume video output and input and re-enable pipeline.
+  */
+ static void isp_pipeline_resume(struct isp_pipeline *pipe)
+ {
+       int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT;
+ 
+       omap3isp_video_resume(pipe->output, !singleshot);
+       if (singleshot)
+               omap3isp_video_resume(pipe->input, 0);
+       isp_pipeline_enable(pipe, pipe->stream_state);
+ }
+ 
+ /*
+  * isp_pipeline_suspend - Suspend streaming on a pipeline
+  * @pipe: ISP pipeline
+  *
+  * Suspend pipeline.
+  */
+ static void isp_pipeline_suspend(struct isp_pipeline *pipe)
+ {
+       isp_pipeline_disable(pipe);
+ }
+ 
+ /*
+  * isp_pipeline_is_last - Verify if entity has an enabled link to the output
+  *                      video node
+  * @me: ISP module's media entity
+  *
+  * Returns 1 if the entity has an enabled link to the output video node or 0
+  * otherwise. It's true only while pipeline can have no more than one output
+  * node.
+  */
+ static int isp_pipeline_is_last(struct media_entity *me)
+ {
+       struct isp_pipeline *pipe;
+       struct media_pad *pad;
+ 
+       if (!me->pipe)
+               return 0;
+       pipe = to_isp_pipeline(me);
+       if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
+               return 0;
+       pad = media_entity_remote_source(&pipe->output->pad);
+       return pad->entity == me;
+ }
+ 
+ /*
+  * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module
+  * @me: ISP module's media entity
+  *
+  * Suspend the whole pipeline if module's entity has an enabled link to the
+  * output video node. It works only while pipeline can have no more than one
+  * output node.
+  */
+ static void isp_suspend_module_pipeline(struct media_entity *me)
+ {
+       if (isp_pipeline_is_last(me))
+               isp_pipeline_suspend(to_isp_pipeline(me));
+ }
+ 
+ /*
+  * isp_resume_module_pipeline - Resume pipeline to which belongs the module
+  * @me: ISP module's media entity
+  *
+  * Resume the whole pipeline if module's entity has an enabled link to the
+  * output video node. It works only while pipeline can have no more than one
+  * output node.
+  */
+ static void isp_resume_module_pipeline(struct media_entity *me)
+ {
+       if (isp_pipeline_is_last(me))
+               isp_pipeline_resume(to_isp_pipeline(me));
+ }
+ 
+ /*
+  * isp_suspend_modules - Suspend ISP submodules.
+  * @isp: OMAP3 ISP device
+  *
+  * Returns 0 if suspend left in idle state all the submodules properly,
+  * or returns 1 if a general Reset is required to suspend the submodules.
+  */
+ static int isp_suspend_modules(struct isp_device *isp)
+ {
+       unsigned long timeout;
+ 
+       omap3isp_stat_suspend(&isp->isp_aewb);
+       omap3isp_stat_suspend(&isp->isp_af);
+       omap3isp_stat_suspend(&isp->isp_hist);
+       isp_suspend_module_pipeline(&isp->isp_res.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity);
+       isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity);
+ 
+       timeout = jiffies + ISP_STOP_TIMEOUT;
+       while (omap3isp_stat_busy(&isp->isp_af)
+           || omap3isp_stat_busy(&isp->isp_aewb)
+           || omap3isp_stat_busy(&isp->isp_hist)
+           || omap3isp_preview_busy(&isp->isp_prev)
+           || omap3isp_resizer_busy(&isp->isp_res)
+           || omap3isp_ccdc_busy(&isp->isp_ccdc)) {
+               if (time_after(jiffies, timeout)) {
+                       dev_info(isp->dev, "can't stop modules.\n");
+                       return 1;
+               }
+               msleep(1);
+       }
+ 
+       return 0;
+ }
+ 
+ /*
+  * isp_resume_modules - Resume ISP submodules.
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_resume_modules(struct isp_device *isp)
+ {
+       omap3isp_stat_resume(&isp->isp_aewb);
+       omap3isp_stat_resume(&isp->isp_af);
+       omap3isp_stat_resume(&isp->isp_hist);
+       isp_resume_module_pipeline(&isp->isp_res.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_prev.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity);
+       isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity);
+ }
+ 
+ /*
+  * isp_reset - Reset ISP with a timeout wait for idle.
+  * @isp: OMAP3 ISP device
+  */
+ static int isp_reset(struct isp_device *isp)
+ {
+       unsigned long timeout = 0;
+ 
+       isp_reg_writel(isp,
+                      isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)
+                      | ISP_SYSCONFIG_SOFTRESET,
+                      OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+       while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN,
+                              ISP_SYSSTATUS) & 0x1)) {
+               if (timeout++ > 10000) {
+                       dev_alert(isp->dev, "cannot reset ISP\n");
+                       return -ETIMEDOUT;
+               }
+               udelay(1);
+       }
+ 
+       isp->crashed = 0;
+       return 0;
+ }
+ 
+ /*
+  * isp_save_context - Saves the values of the ISP module registers.
+  * @isp: OMAP3 ISP device
+  * @reg_list: Structure containing pairs of register address and value to
+  *            modify on OMAP.
+  */
+ static void
+ isp_save_context(struct isp_device *isp, struct isp_reg *reg_list)
+ {
+       struct isp_reg *next = reg_list;
+ 
+       for (; next->reg != ISP_TOK_TERM; next++)
+               next->val = isp_reg_readl(isp, next->mmio_range, next->reg);
+ }
+ 
+ /*
+  * isp_restore_context - Restores the values of the ISP module registers.
+  * @isp: OMAP3 ISP device
+  * @reg_list: Structure containing pairs of register address and value to
+  *            modify on OMAP.
+  */
+ static void
+ isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list)
+ {
+       struct isp_reg *next = reg_list;
+ 
+       for (; next->reg != ISP_TOK_TERM; next++)
+               isp_reg_writel(isp, next->val, next->mmio_range, next->reg);
+ }
+ 
+ /*
+  * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+  * @isp: OMAP3 ISP device
+  *
+  * Routine for saving the context of each module in the ISP.
+  * CCDC, HIST, H3A, PREV, RESZ and MMU.
+  */
+ static void isp_save_ctx(struct isp_device *isp)
+ {
+       isp_save_context(isp, isp_reg_list);
+       omap_iommu_save_ctx(isp->dev);
+ }
+ 
+ /*
+  * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+  * @isp: OMAP3 ISP device
+  *
+  * Routine for restoring the context of each module in the ISP.
+  * CCDC, HIST, H3A, PREV, RESZ and MMU.
+  */
+ static void isp_restore_ctx(struct isp_device *isp)
+ {
+       isp_restore_context(isp, isp_reg_list);
+       omap_iommu_restore_ctx(isp->dev);
+       omap3isp_ccdc_restore_context(isp);
+       omap3isp_preview_restore_context(isp);
+ }
+ 
+ /* -----------------------------------------------------------------------------
+  * SBL resources management
+  */
+ #define OMAP3_ISP_SBL_READ    (OMAP3_ISP_SBL_CSI1_READ | \
+                                OMAP3_ISP_SBL_CCDC_LSC_READ | \
+                                OMAP3_ISP_SBL_PREVIEW_READ | \
+                                OMAP3_ISP_SBL_RESIZER_READ)
+ #define OMAP3_ISP_SBL_WRITE   (OMAP3_ISP_SBL_CSI1_WRITE | \
+                                OMAP3_ISP_SBL_CSI2A_WRITE | \
+                                OMAP3_ISP_SBL_CSI2C_WRITE | \
+                                OMAP3_ISP_SBL_CCDC_WRITE | \
+                                OMAP3_ISP_SBL_PREVIEW_WRITE)
+ 
+ void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res)
+ {
+       u32 sbl = 0;
+ 
+       isp->sbl_resources |= res;
+ 
+       if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)
+               sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+ 
+       if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)
+               sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+ 
+       if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)
+               sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+ 
+       if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)
+               sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+ 
+       if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE)
+               sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+ 
+       if (isp->sbl_resources & OMAP3_ISP_SBL_READ)
+               sbl |= ISPCTRL_SBL_RD_RAM_EN;
+ 
+       isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+ }
+ 
+ void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res)
+ {
+       u32 sbl = 0;
+ 
+       isp->sbl_resources &= ~res;
+ 
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ))
+               sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+ 
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ))
+               sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+ 
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE))
+               sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+ 
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE))
+               sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+ 
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE))
+               sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+ 
+       if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ))
+               sbl |= ISPCTRL_SBL_RD_RAM_EN;
+ 
+       isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+ }
+ 
+ /*
+  * isp_module_sync_idle - Helper to sync module with its idle state
+  * @me: ISP submodule's media entity
+  * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+  * @stopping: flag which tells module wants to stop
+  *
+  * This function checks if ISP submodule needs to wait for next interrupt. If
+  * yes, makes the caller to sleep while waiting for such event.
+  */
+ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+                             atomic_t *stopping)
+ {
+       struct isp_pipeline *pipe = to_isp_pipeline(me);
+ 
+       if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED ||
+           (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT &&
+            !isp_pipeline_ready(pipe)))
+               return 0;
+ 
+       /*
+        * atomic_set() doesn't include memory barrier on ARM platform for SMP
+        * scenario. We'll call it here to avoid race conditions.
+        */
+       atomic_set(stopping, 1);
+       smp_mb();
+ 
+       /*
+        * If module is the last one, it's writing to memory. In this case,
+        * it's necessary to check if the module is already paused due to
+        * DMA queue underrun or if it has to wait for next interrupt to be
+        * idle.
+        * If it isn't the last one, the function won't sleep but *stopping
+        * will still be set to warn next submodule caller's interrupt the
+        * module wants to be idle.
+        */
+       if (isp_pipeline_is_last(me)) {
+               struct isp_video *video = pipe->output;
+               unsigned long flags;
+               spin_lock_irqsave(&video->queue->irqlock, flags);
+               if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
+                       spin_unlock_irqrestore(&video->queue->irqlock, flags);
+                       atomic_set(stopping, 0);
+                       smp_mb();
+                       return 0;
+               }
+               spin_unlock_irqrestore(&video->queue->irqlock, flags);
+               if (!wait_event_timeout(*wait, !atomic_read(stopping),
+                                       msecs_to_jiffies(1000))) {
+                       atomic_set(stopping, 0);
+                       smp_mb();
+                       return -ETIMEDOUT;
+               }
+       }
+ 
+       return 0;
+ }
+ 
+ /*
+  * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
+  * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+  * @stopping: flag which tells module wants to stop
+  *
+  * This function checks if ISP submodule was stopping. In case of yes, it
+  * notices the caller by setting stopping to 0 and waking up the wait queue.
+  * Returns 1 if it was stopping or 0 otherwise.
+  */
+ int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
+                                    atomic_t *stopping)
+ {
+       if (atomic_cmpxchg(stopping, 1, 0)) {
+               wake_up(wait);
+               return 1;
+       }
+ 
+       return 0;
+ }
+ 
+ /* --------------------------------------------------------------------------
+  * Clock management
+  */
+ 
+ #define ISPCTRL_CLKS_MASK     (ISPCTRL_H3A_CLK_EN | \
+                                ISPCTRL_HIST_CLK_EN | \
+                                ISPCTRL_RSZ_CLK_EN | \
+                                (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \
+                                (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN))
+ 
+ static void __isp_subclk_update(struct isp_device *isp)
+ {
+       u32 clk = 0;
+ 
+       /* AEWB and AF share the same clock. */
+       if (isp->subclk_resources &
+           (OMAP3_ISP_SUBCLK_AEWB | OMAP3_ISP_SUBCLK_AF))
+               clk |= ISPCTRL_H3A_CLK_EN;
+ 
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST)
+               clk |= ISPCTRL_HIST_CLK_EN;
+ 
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER)
+               clk |= ISPCTRL_RSZ_CLK_EN;
+ 
+       /* NOTE: For CCDC & Preview submodules, we need to affect internal
+        *       RAM as well.
+        */
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC)
+               clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN;
+ 
+       if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW)
+               clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN;
+ 
+       isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+                       ISPCTRL_CLKS_MASK, clk);
+ }
+ 
+ void omap3isp_subclk_enable(struct isp_device *isp,
+                           enum isp_subclk_resource res)
+ {
+       isp->subclk_resources |= res;
+ 
+       __isp_subclk_update(isp);
+ }
+ 
+ void omap3isp_subclk_disable(struct isp_device *isp,
+                            enum isp_subclk_resource res)
+ {
+       isp->subclk_resources &= ~res;
+ 
+       __isp_subclk_update(isp);
+ }
+ 
+ /*
+  * isp_enable_clocks - Enable ISP clocks
+  * @isp: OMAP3 ISP device
+  *
+  * Return 0 if successful, or clk_enable return value if any of tthem fails.
+  */
+ static int isp_enable_clocks(struct isp_device *isp)
+ {
+       int r;
+       unsigned long rate;
+       int divisor;
+ 
+       /*
+        * cam_mclk clock chain:
+        *   dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk
+        *
+        * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are
+        * set to the same value. Hence the rate set for dpll4_m5
+        * has to be twice of what is set on OMAP3430 to get
+        * the required value for cam_mclk
+        */
+       if (cpu_is_omap3630())
+               divisor = 1;
+       else
+               divisor = 2;
+ 
+       r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
+       if (r) {
+               dev_err(isp->dev, "clk_enable cam_ick failed\n");
+               goto out_clk_enable_ick;
+       }
+       r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
+                        CM_CAM_MCLK_HZ/divisor);
+       if (r) {
+               dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
+               goto out_clk_enable_mclk;
+       }
+       r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]);
+       if (r) {
+               dev_err(isp->dev, "clk_enable cam_mclk failed\n");
+               goto out_clk_enable_mclk;
+       }
+       rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+       if (rate != CM_CAM_MCLK_HZ)
+               dev_warn(isp->dev, "unexpected cam_mclk rate:\n"
+                                  " expected : %d\n"
+                                  " actual   : %ld\n", CM_CAM_MCLK_HZ, rate);
+       r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]);
+       if (r) {
+               dev_err(isp->dev, "clk_enable csi2_fck failed\n");
+               goto out_clk_enable_csi2_fclk;
+       }
+       return 0;
+ 
+ out_clk_enable_csi2_fclk:
+       clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+ out_clk_enable_mclk:
+       clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+ out_clk_enable_ick:
+       return r;
+ }
+ 
+ /*
+  * isp_disable_clocks - Disable ISP clocks
+  * @isp: OMAP3 ISP device
+  */
+ static void isp_disable_clocks(struct isp_device *isp)
+ {
+       clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+       clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+       clk_disable(isp->clock[ISP_CLK_CSI2_FCK]);
+ }
+ 
+ static const char *isp_clocks[] = {
+       "cam_ick",
+       "cam_mclk",
+       "dpll4_m5_ck",
+       "csi2_96m_fck",
+       "l3_ick",
+ };
+ 
+ static void isp_put_clocks(struct isp_device *isp)
+ {
+       unsigned int i;
+ 
+       for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+               if (isp->clock[i]) {
+                       clk_put(isp->clock[i]);
+                       isp->clock[i] = NULL;
+               }
+       }
+ }
+ 
+ static int isp_get_clocks(struct isp_device *isp)
+ {
+       struct clk *clk;
+       unsigned int i;
+ 
+       for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+               clk = clk_get(isp->dev, isp_clocks[i]);
+               if (IS_ERR(clk)) {
+                       dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]);
+                       isp_put_clocks(isp);
+                       return PTR_ERR(clk);
+               }
+ 
+               isp->clock[i] = clk;
+       }
+ 
+       return 0;
+ }
+ 
+ /*
+  * omap3isp_get - Acquire the ISP resource.
+  *
+  * Initializes the clocks for the first acquire.
+  *
+  * Increment the reference count on the ISP. If the first reference is taken,
+  * enable clocks and power-up all submodules.
+  *
+  * Return a pointer to the ISP device structure, or NULL if an error occurred.
+  */
+ static struct isp_device *__omap3isp_get(struct isp_device *isp, bool irq)
+ {
+       struct isp_device *__isp = isp;
+ 
+       if (isp == NULL)
+               return NULL;
+ 
+       mutex_lock(&isp->isp_mutex);
+       if (isp->ref_count > 0)
+               goto out;
+ 
+       if (isp_enable_clocks(isp) < 0) {
+               __isp = NULL;
+               goto out;
+       }
+ 
+       /* We don't want to restore context before saving it! */
+       if (isp->has_context)
+               isp_restore_ctx(isp);
+ 
+       if (irq)
+               isp_enable_interrupts(isp);
+ 
+ out:
+       if (__isp != NULL)
+               isp->ref_count++;
+       mutex_unlock(&isp->isp_mutex);
+ 
+       return __isp;
+ }
+ 
+ struct isp_device *omap3isp_get(struct isp_device *isp)
+ {
+       return __omap3isp_get(isp, true);
+ }
+ 
+ /*
+  * omap3isp_put - Release the ISP
+  *
+  * Decrement the reference count on the ISP. If the last reference is released,
+  * power-down all submodules, disable clocks and free temporary buffers.
+  */
+ void omap3isp_put(struct isp_device *isp)
+ {
+       if (isp == NULL)
+               return;
+ 
+       mutex_lock(&isp->isp_mutex);
+       BUG_ON(isp->ref_count == 0);
+       if (--isp->ref_count == 0) {
+               isp_disable_interrupts(isp);
+               if (isp->domain) {
+                       isp_save_ctx(isp);
+                       isp->has_context = 1;
+               }
+               /* Reset the ISP if an entity has failed to stop. This is the
+                * only way to recover from such conditions.
+                */
+               if (isp->crashed)
+                       isp_reset(isp);
+               isp_disable_clocks(isp);
+       }
+       mutex_unlock(&isp->isp_mutex);
+ }
+ 
+ /* --------------------------------------------------------------------------
+  * Platform device driver
+  */
+ 
+ /*
+  * omap3isp_print_status - Prints the values of the ISP Control Module registers
+  * @isp: OMAP3 ISP device
+  */
+ #define ISP_PRINT_REGISTER(isp, name)\
+       dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
+               isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
+ #define SBL_PRINT_REGISTER(isp, name)\
+       dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
+               isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
+ 
+ void omap3isp_print_status(struct isp_device *isp)
+ {
+       dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
+ 
+       ISP_PRINT_REGISTER(isp, SYSCONFIG);
+       ISP_PRINT_REGISTER(isp, SYSSTATUS);
+       ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
+       ISP_PRINT_REGISTER(isp, IRQ0STATUS);
+       ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
+       ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
+       ISP_PRINT_REGISTER(isp, CTRL);
+       ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
+       ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
+       ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
+       ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
+       ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
+       ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
+       ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
+       ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
+ 
+       SBL_PRINT_REGISTER(isp, PCR);
+       SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
+ 
+       dev_dbg(isp->dev, "--------------------------------------------\n");
+ }
+ 
+ #ifdef CONFIG_PM
+ 
+ /*
+  * Power management support.
+  *
+  * As the ISP can't properly handle an input video stream interruption on a non
+  * frame boundary, the ISP pipelines need to be stopped before sensors get
+  * suspended. However, as suspending the sensors can require a running clock,
+  * which can be provided by the ISP, the ISP can't be completely suspended
+  * before the sensor.
+  *
+  * To solve this problem power management support is split into prepare/complete
+  * and suspend/resume operations. The pipelines are stopped in prepare() and the
+  * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
+  * resume(), and the the pipelines are restarted in complete().
+  *
+  * TODO: PM dependencies between the ISP and sensors are not modeled explicitly
+  * yet.
+  */
+ static int isp_pm_prepare(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+       int reset;
+ 
+       WARN_ON(mutex_is_locked(&isp->isp_mutex));
+ 
+       if (isp->ref_count == 0)
+               return 0;
+ 
+       reset = isp_suspend_modules(isp);
+       isp_disable_interrupts(isp);
+       isp_save_ctx(isp);
+       if (reset)
+               isp_reset(isp);
+ 
+       return 0;
+ }
+ 
+ static int isp_pm_suspend(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+ 
+       WARN_ON(mutex_is_locked(&isp->isp_mutex));
+ 
+       if (isp->ref_count)
+               isp_disable_clocks(isp);
+ 
+       return 0;
+ }
+ 
+ static int isp_pm_resume(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+ 
+       if (isp->ref_count == 0)
+               return 0;
+ 
+       return isp_enable_clocks(isp);
+ }
+ 
+ static void isp_pm_complete(struct device *dev)
+ {
+       struct isp_device *isp = dev_get_drvdata(dev);
+ 
+       if (isp->ref_count == 0)
+               return;
+ 
+       isp_restore_ctx(isp);
+       isp_enable_interrupts(isp);
+       isp_resume_modules(isp);
+ }
+ 
+ #else
+ 
+ #define isp_pm_prepare        NULL
+ #define isp_pm_suspend        NULL
+ #define isp_pm_resume NULL
+ #define isp_pm_complete       NULL
+ 
+ #endif /* CONFIG_PM */
+ 
+ static void isp_unregister_entities(struct isp_device *isp)
+ {
+       omap3isp_csi2_unregister_entities(&isp->isp_csi2a);
+       omap3isp_ccp2_unregister_entities(&isp->isp_ccp2);
+       omap3isp_ccdc_unregister_entities(&isp->isp_ccdc);
+       omap3isp_preview_unregister_entities(&isp->isp_prev);
+       omap3isp_resizer_unregister_entities(&isp->isp_res);
+       omap3isp_stat_unregister_entities(&isp->isp_aewb);
+       omap3isp_stat_unregister_entities(&isp->isp_af);
+       omap3isp_stat_unregister_entities(&isp->isp_hist);
+ 
+       v4l2_device_unregister(&isp->v4l2_dev);
+       media_device_unregister(&isp->media_dev);
+ }
+ 
+ /*
+  * isp_register_subdev_group - Register a group of subdevices
+  * @isp: OMAP3 ISP device
+  * @board_info: I2C subdevs board information array
+  *
+  * Register all I2C subdevices in the board_info array. The array must be
+  * terminated by a NULL entry, and the first entry must be the sensor.
+  *
+  * Return a pointer to the sensor media entity if it has been successfully
+  * registered, or NULL otherwise.
+  */
+ static struct v4l2_subdev *
+ isp_register_subdev_group(struct isp_device *isp,
+                    struct isp_subdev_i2c_board_info *board_info)
+ {
+       struct v4l2_subdev *sensor = NULL;
+       unsigned int first;
+ 
+       if (board_info->board_info == NULL)
+               return NULL;
+ 
+       for (first = 1; board_info->board_info; ++board_info, first = 0) {
+               struct v4l2_subdev *subdev;
+               struct i2c_adapter *adapter;
+ 
+               adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+               if (adapter == NULL) {
+                       printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+                               "device %s\n", __func__,
+                               board_info->i2c_adapter_id,
+                               board_info->board_info->type);
+                       continue;
+               }
+ 
+               subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
+                               board_info->board_info, NULL);
+               if (subdev == NULL) {
+                       printk(KERN_ERR "%s: Unable to register subdev %s\n",
+                               __func__, board_info->board_info->type);
+                       continue;
+               }
+ 
+               if (first)
+                       sensor = subdev;
+       }
+ 
+       return sensor;
+ }
+ 
+ static int isp_register_entities(struct isp_device *isp)
+ {
+       struct isp_platform_data *pdata = isp->pdata;
+       struct isp_v4l2_subdevs_group *subdevs;
+       int ret;
+ 
+       isp->media_dev.dev = isp->dev;
+       strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
+               sizeof(isp->media_dev.model));
+       isp->media_dev.hw_revision = isp->revision;
+       isp->media_dev.link_notify = isp_pipeline_link_notify;
+       ret = media_device_register(&isp->media_dev);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+                       __func__, ret);
+               return ret;
+       }
+ 
+       isp->v4l2_dev.mdev = &isp->media_dev;
+       ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+                       __func__, ret);
+               goto done;
+       }
+ 
+       /* Register internal entities */
+       ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_preview_register_entities(&isp->isp_prev,
+                                                &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev);
+       if (ret < 0)
+               goto done;
+ 
+       /* Register external entities */
+       for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+               struct v4l2_subdev *sensor;
+               struct media_entity *input;
+               unsigned int flags;
+               unsigned int pad;
+ 
+               sensor = isp_register_subdev_group(isp, subdevs->subdevs);
+               if (sensor == NULL)
+                       continue;
+ 
+               sensor->host_priv = subdevs;
+ 
+               /* Connect the sensor to the correct interface module. Parallel
+                * sensors are connected directly to the CCDC, while serial
+                * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
+                * through CSIPHY1 or CSIPHY2.
+                */
+               switch (subdevs->interface) {
+               case ISP_INTERFACE_PARALLEL:
+                       input = &isp->isp_ccdc.subdev.entity;
+                       pad = CCDC_PAD_SINK;
+                       flags = 0;
+                       break;
+ 
+               case ISP_INTERFACE_CSI2A_PHY2:
+                       input = &isp->isp_csi2a.subdev.entity;
+                       pad = CSI2_PAD_SINK;
+                       flags = MEDIA_LNK_FL_IMMUTABLE
+                             | MEDIA_LNK_FL_ENABLED;
+                       break;
+ 
+               case ISP_INTERFACE_CCP2B_PHY1:
+               case ISP_INTERFACE_CCP2B_PHY2:
+                       input = &isp->isp_ccp2.subdev.entity;
+                       pad = CCP2_PAD_SINK;
+                       flags = 0;
+                       break;
+ 
+               case ISP_INTERFACE_CSI2C_PHY1:
+                       input = &isp->isp_csi2c.subdev.entity;
+                       pad = CSI2_PAD_SINK;
+                       flags = MEDIA_LNK_FL_IMMUTABLE
+                             | MEDIA_LNK_FL_ENABLED;
+                       break;
+ 
+               default:
+                       printk(KERN_ERR "%s: invalid interface type %u\n",
+                              __func__, subdevs->interface);
+                       ret = -EINVAL;
+                       goto done;
+               }
+ 
+               ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+                                              flags);
+               if (ret < 0)
+                       goto done;
+       }
+ 
+       ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+ 
+ done:
+       if (ret < 0)
+               isp_unregister_entities(isp);
+ 
+       return ret;
+ }
+ 
+ static void isp_cleanup_modules(struct isp_device *isp)
+ {
+       omap3isp_h3a_aewb_cleanup(isp);
+       omap3isp_h3a_af_cleanup(isp);
+       omap3isp_hist_cleanup(isp);
+       omap3isp_resizer_cleanup(isp);
+       omap3isp_preview_cleanup(isp);
+       omap3isp_ccdc_cleanup(isp);
+       omap3isp_ccp2_cleanup(isp);
+       omap3isp_csi2_cleanup(isp);
+ }
+ 
+ static int isp_initialize_modules(struct isp_device *isp)
+ {
+       int ret;
+ 
+       ret = omap3isp_csiphy_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CSI PHY initialization failed\n");
+               goto error_csiphy;
+       }
+ 
+       ret = omap3isp_csi2_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CSI2 initialization failed\n");
+               goto error_csi2;
+       }
+ 
+       ret = omap3isp_ccp2_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CCP2 initialization failed\n");
+               goto error_ccp2;
+       }
+ 
+       ret = omap3isp_ccdc_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "CCDC initialization failed\n");
+               goto error_ccdc;
+       }
+ 
+       ret = omap3isp_preview_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "Preview initialization failed\n");
+               goto error_preview;
+       }
+ 
+       ret = omap3isp_resizer_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "Resizer initialization failed\n");
+               goto error_resizer;
+       }
+ 
+       ret = omap3isp_hist_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "Histogram initialization failed\n");
+               goto error_hist;
+       }
+ 
+       ret = omap3isp_h3a_aewb_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "H3A AEWB initialization failed\n");
+               goto error_h3a_aewb;
+       }
+ 
+       ret = omap3isp_h3a_af_init(isp);
+       if (ret < 0) {
+               dev_err(isp->dev, "H3A AF initialization failed\n");
+               goto error_h3a_af;
+       }
+ 
+       /* Connect the submodules. */
+       ret = media_entity_create_link(
+                       &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
+                       &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
+                       &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_aewb.subdev.entity, 0,
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_af.subdev.entity, 0,
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       if (ret < 0)
+               goto error_link;
+ 
+       ret = media_entity_create_link(
+                       &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+                       &isp->isp_hist.subdev.entity, 0,
+                       MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+       if (ret < 0)
+               goto error_link;
+ 
+       return 0;
+ 
+ error_link:
+       omap3isp_h3a_af_cleanup(isp);
+ error_h3a_af:
+       omap3isp_h3a_aewb_cleanup(isp);
+ error_h3a_aewb:
+       omap3isp_hist_cleanup(isp);
+ error_hist:
+       omap3isp_resizer_cleanup(isp);
+ error_resizer:
+       omap3isp_preview_cleanup(isp);
+ error_preview:
+       omap3isp_ccdc_cleanup(isp);
+ error_ccdc:
+       omap3isp_ccp2_cleanup(isp);
+ error_ccp2:
+       omap3isp_csi2_cleanup(isp);
+ error_csi2:
+ error_csiphy:
+       return ret;
+ }
+ 
+ /*
+  * isp_remove - Remove ISP platform device
+  * @pdev: Pointer to ISP platform device
+  *
+  * Always returns 0.
+  */
+ static int __devexit isp_remove(struct platform_device *pdev)
+ {
+       struct isp_device *isp = platform_get_drvdata(pdev);
+       int i;
+ 
+       isp_unregister_entities(isp);
+       isp_cleanup_modules(isp);
+ 
+       __omap3isp_get(isp, false);
+       iommu_detach_device(isp->domain, &pdev->dev);
+       iommu_domain_free(isp->domain);
+       isp->domain = NULL;
+       omap3isp_put(isp);
+ 
+       free_irq(isp->irq_num, isp);
+       isp_put_clocks(isp);
+ 
+       for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+               if (isp->mmio_base[i]) {
+                       iounmap(isp->mmio_base[i]);
+                       isp->mmio_base[i] = NULL;
+               }
+ 
+               if (isp->mmio_base_phys[i]) {
+                       release_mem_region(isp->mmio_base_phys[i],
+                                          isp->mmio_size[i]);
+                       isp->mmio_base_phys[i] = 0;
+               }
+       }
+ 
+       regulator_put(isp->isp_csiphy1.vdd);
+       regulator_put(isp->isp_csiphy2.vdd);
+       kfree(isp);
+ 
+       return 0;
+ }
+ 
+ static int isp_map_mem_resource(struct platform_device *pdev,
+                               struct isp_device *isp,
+                               enum isp_mem_resources res)
+ {
+       struct resource *mem;
+ 
+       /* request the mem region for the camera registers */
+ 
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+       if (!mem) {
+               dev_err(isp->dev, "no mem resource?\n");
+               return -ENODEV;
+       }
+ 
+       if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+               dev_err(isp->dev,
+                       "cannot reserve camera register I/O region\n");
+               return -ENODEV;
+       }
+       isp->mmio_base_phys[res] = mem->start;
+       isp->mmio_size[res] = resource_size(mem);
+ 
+       /* map the region */
+       isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res],
+                                             isp->mmio_size[res]);
+       if (!isp->mmio_base[res]) {
+               dev_err(isp->dev, "cannot map camera register I/O region\n");
+               return -ENODEV;
+       }
+ 
+       return 0;
+ }
+ 
+ /*
+  * isp_probe - Probe ISP platform device
+  * @pdev: Pointer to ISP platform device
+  *
+  * Returns 0 if successful,
+  *   -ENOMEM if no memory available,
+  *   -ENODEV if no platform device resources found
+  *     or no space for remapping registers,
+  *   -EINVAL if couldn't install ISR,
+  *   or clk_get return error value.
+  */
+ static int __devinit isp_probe(struct platform_device *pdev)
+ {
+       struct isp_platform_data *pdata = pdev->dev.platform_data;
+       struct isp_device *isp;
+       int ret;
+       int i, m;
+ 
+       if (pdata == NULL)
+               return -EINVAL;
+ 
+       isp = kzalloc(sizeof(*isp), GFP_KERNEL);
+       if (!isp) {
+               dev_err(&pdev->dev, "could not allocate memory\n");
+               return -ENOMEM;
+       }
+ 
+       isp->autoidle = autoidle;
+       isp->platform_cb.set_xclk = isp_set_xclk;
+ 
+       mutex_init(&isp->isp_mutex);
+       spin_lock_init(&isp->stat_lock);
+ 
+       isp->dev = &pdev->dev;
+       isp->pdata = pdata;
+       isp->ref_count = 0;
+ 
+       isp->raw_dmamask = DMA_BIT_MASK(32);
+       isp->dev->dma_mask = &isp->raw_dmamask;
+       isp->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+ 
+       platform_set_drvdata(pdev, isp);
+ 
+       /* Regulators */
+       isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
+       isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
+ 
+       /* Clocks */
+       ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
+       if (ret < 0)
+               goto error;
+ 
+       ret = isp_get_clocks(isp);
+       if (ret < 0)
+               goto error;
+ 
+       if (__omap3isp_get(isp, false) == NULL) {
+               ret = -ENODEV;
+               goto error;
+       }
+ 
+       ret = isp_reset(isp);
+       if (ret < 0)
+               goto error_isp;
+ 
+       /* Memory resources */
+       isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+       dev_info(isp->dev, "Revision %d.%d found\n",
+                (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
+ 
+       for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
+               if (isp->revision == isp_res_maps[m].isp_rev)
+                       break;
+ 
+       if (m == ARRAY_SIZE(isp_res_maps)) {
+               dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n",
+                       (isp->revision & 0xf0) >> 4, isp->revision & 0xf);
+               ret = -ENODEV;
+               goto error_isp;
+       }
+ 
+       for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
+               if (isp_res_maps[m].map & 1 << i) {
+                       ret = isp_map_mem_resource(pdev, isp, i);
+                       if (ret)
+                               goto error_isp;
+               }
+       }
+ 
+       isp->domain = iommu_domain_alloc(pdev->dev.bus);
+       if (!isp->domain) {
+               dev_err(isp->dev, "can't alloc iommu domain\n");
+               ret = -ENOMEM;
+               goto error_isp;
+       }
+ 
+       ret = iommu_attach_device(isp->domain, &pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+               goto free_domain;
+       }
+ 
+       /* Interrupt */
+       isp->irq_num = platform_get_irq(pdev, 0);
+       if (isp->irq_num <= 0) {
+               dev_err(isp->dev, "No IRQ resource\n");
+               ret = -ENODEV;
+               goto detach_dev;
+       }
+ 
+       if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
+               dev_err(isp->dev, "Unable to request IRQ\n");
+               ret = -EINVAL;
+               goto detach_dev;
+       }
+ 
+       /* Entities */
+       ret = isp_initialize_modules(isp);
+       if (ret < 0)
+               goto error_irq;
+ 
+       ret = isp_register_entities(isp);
+       if (ret < 0)
+               goto error_modules;
+ 
+       isp_core_init(isp, 1);
+       omap3isp_put(isp);
+ 
+       return 0;
+ 
+ error_modules:
+       isp_cleanup_modules(isp);
+ error_irq:
+       free_irq(isp->irq_num, isp);
+ detach_dev:
+       iommu_detach_device(isp->domain, &pdev->dev);
+ free_domain:
+       iommu_domain_free(isp->domain);
+ error_isp:
+       omap3isp_put(isp);
+ error:
+       isp_put_clocks(isp);
+ 
+       for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+               if (isp->mmio_base[i]) {
+                       iounmap(isp->mmio_base[i]);
+                       isp->mmio_base[i] = NULL;
+               }
+ 
+               if (isp->mmio_base_phys[i]) {
+                       release_mem_region(isp->mmio_base_phys[i],
+                                          isp->mmio_size[i]);
+                       isp->mmio_base_phys[i] = 0;
+               }
+       }
+       regulator_put(isp->isp_csiphy2.vdd);
+       regulator_put(isp->isp_csiphy1.vdd);
+       platform_set_drvdata(pdev, NULL);
+ 
+       mutex_destroy(&isp->isp_mutex);
+       kfree(isp);
+ 
+       return ret;
+ }
+ 
+ static const struct dev_pm_ops omap3isp_pm_ops = {
+       .prepare = isp_pm_prepare,
+       .suspend = isp_pm_suspend,
+       .resume = isp_pm_resume,
+       .complete = isp_pm_complete,
+ };
+ 
+ static struct platform_device_id omap3isp_id_table[] = {
+       { "omap3isp", 0 },
+       { },
+ };
+ MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
+ 
+ static struct platform_driver omap3isp_driver = {
+       .probe = isp_probe,
+       .remove = __devexit_p(isp_remove),
+       .id_table = omap3isp_id_table,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "omap3isp",
+               .pm     = &omap3isp_pm_ops,
+       },
+ };
+ 
+ module_platform_driver(omap3isp_driver);
+ 
+ MODULE_AUTHOR("Nokia Corporation");
+ MODULE_DESCRIPTION("TI OMAP3 ISP driver");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
 
--- /dev/null
 -#include <plat/mipi_csis.h>
+ /*
+  * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+  *
+  * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+  * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  */
+ 
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/irq.h>
+ #include <linux/kernel.h>
+ #include <linux/memory.h>
+ #include <linux/module.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/slab.h>
+ #include <linux/spinlock.h>
+ #include <linux/videodev2.h>
+ #include <media/v4l2-subdev.h>
++#include <linux/platform_data/mipi-csis.h>
+ #include "mipi-csis.h"
+ 
+ static int debug;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug, "Debug level (0-2)");
+ 
+ /* Register map definition */
+ 
+ /* CSIS global control */
+ #define S5PCSIS_CTRL                  0x00
+ #define S5PCSIS_CTRL_DPDN_DEFAULT     (0 << 31)
+ #define S5PCSIS_CTRL_DPDN_SWAP                (1 << 31)
+ #define S5PCSIS_CTRL_ALIGN_32BIT      (1 << 20)
+ #define S5PCSIS_CTRL_UPDATE_SHADOW    (1 << 16)
+ #define S5PCSIS_CTRL_WCLK_EXTCLK      (1 << 8)
+ #define S5PCSIS_CTRL_RESET            (1 << 4)
+ #define S5PCSIS_CTRL_ENABLE           (1 << 0)
+ 
+ /* D-PHY control */
+ #define S5PCSIS_DPHYCTRL              0x04
+ #define S5PCSIS_DPHYCTRL_HSS_MASK     (0x1f << 27)
+ #define S5PCSIS_DPHYCTRL_ENABLE               (0x1f << 0)
+ 
+ #define S5PCSIS_CONFIG                        0x08
+ #define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2)
+ #define S5PCSIS_CFG_FMT_RAW8          (0x2a << 2)
+ #define S5PCSIS_CFG_FMT_RAW10         (0x2b << 2)
+ #define S5PCSIS_CFG_FMT_RAW12         (0x2c << 2)
+ /* User defined formats, x = 1...4 */
+ #define S5PCSIS_CFG_FMT_USER(x)               ((0x30 + x - 1) << 2)
+ #define S5PCSIS_CFG_FMT_MASK          (0x3f << 2)
+ #define S5PCSIS_CFG_NR_LANE_MASK      3
+ 
+ /* Interrupt mask */
+ #define S5PCSIS_INTMSK                        0x10
+ #define S5PCSIS_INTMSK_EN_ALL         0xf000103f
+ #define S5PCSIS_INTMSK_EVEN_BEFORE    (1 << 31)
+ #define S5PCSIS_INTMSK_EVEN_AFTER     (1 << 30)
+ #define S5PCSIS_INTMSK_ODD_BEFORE     (1 << 29)
+ #define S5PCSIS_INTMSK_ODD_AFTER      (1 << 28)
+ #define S5PCSIS_INTMSK_ERR_SOT_HS     (1 << 12)
+ #define S5PCSIS_INTMSK_ERR_LOST_FS    (1 << 5)
+ #define S5PCSIS_INTMSK_ERR_LOST_FE    (1 << 4)
+ #define S5PCSIS_INTMSK_ERR_OVER               (1 << 3)
+ #define S5PCSIS_INTMSK_ERR_ECC                (1 << 2)
+ #define S5PCSIS_INTMSK_ERR_CRC                (1 << 1)
+ #define S5PCSIS_INTMSK_ERR_UNKNOWN    (1 << 0)
+ 
+ /* Interrupt source */
+ #define S5PCSIS_INTSRC                        0x14
+ #define S5PCSIS_INTSRC_EVEN_BEFORE    (1 << 31)
+ #define S5PCSIS_INTSRC_EVEN_AFTER     (1 << 30)
+ #define S5PCSIS_INTSRC_EVEN           (0x3 << 30)
+ #define S5PCSIS_INTSRC_ODD_BEFORE     (1 << 29)
+ #define S5PCSIS_INTSRC_ODD_AFTER      (1 << 28)
+ #define S5PCSIS_INTSRC_ODD            (0x3 << 28)
+ #define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28)
+ #define S5PCSIS_INTSRC_ERR_SOT_HS     (0xf << 12)
+ #define S5PCSIS_INTSRC_ERR_LOST_FS    (1 << 5)
+ #define S5PCSIS_INTSRC_ERR_LOST_FE    (1 << 4)
+ #define S5PCSIS_INTSRC_ERR_OVER               (1 << 3)
+ #define S5PCSIS_INTSRC_ERR_ECC                (1 << 2)
+ #define S5PCSIS_INTSRC_ERR_CRC                (1 << 1)
+ #define S5PCSIS_INTSRC_ERR_UNKNOWN    (1 << 0)
+ #define S5PCSIS_INTSRC_ERRORS         0xf03f
+ 
+ /* Pixel resolution */
+ #define S5PCSIS_RESOL                 0x2c
+ #define CSIS_MAX_PIX_WIDTH            0xffff
+ #define CSIS_MAX_PIX_HEIGHT           0xffff
+ 
+ enum {
+       CSIS_CLK_MUX,
+       CSIS_CLK_GATE,
+ };
+ 
+ static char *csi_clock_name[] = {
+       [CSIS_CLK_MUX]  = "sclk_csis",
+       [CSIS_CLK_GATE] = "csis",
+ };
+ #define NUM_CSIS_CLOCKS       ARRAY_SIZE(csi_clock_name)
+ 
+ static const char * const csis_supply_name[] = {
+       "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
+       "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
+ };
+ #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
+ 
+ enum {
+       ST_POWERED      = 1,
+       ST_STREAMING    = 2,
+       ST_SUSPENDED    = 4,
+ };
+ 
+ struct s5pcsis_event {
+       u32 mask;
+       const char * const name;
+       unsigned int counter;
+ };
+ 
+ static const struct s5pcsis_event s5pcsis_events[] = {
+       /* Errors */
+       { S5PCSIS_INTSRC_ERR_SOT_HS,    "SOT Error" },
+       { S5PCSIS_INTSRC_ERR_LOST_FS,   "Lost Frame Start Error" },
+       { S5PCSIS_INTSRC_ERR_LOST_FE,   "Lost Frame End Error" },
+       { S5PCSIS_INTSRC_ERR_OVER,      "FIFO Overflow Error" },
+       { S5PCSIS_INTSRC_ERR_ECC,       "ECC Error" },
+       { S5PCSIS_INTSRC_ERR_CRC,       "CRC Error" },
+       { S5PCSIS_INTSRC_ERR_UNKNOWN,   "Unknown Error" },
+       /* Non-image data receive events */
+       { S5PCSIS_INTSRC_EVEN_BEFORE,   "Non-image data before even frame" },
+       { S5PCSIS_INTSRC_EVEN_AFTER,    "Non-image data after even frame" },
+       { S5PCSIS_INTSRC_ODD_BEFORE,    "Non-image data before odd frame" },
+       { S5PCSIS_INTSRC_ODD_AFTER,     "Non-image data after odd frame" },
+ };
+ #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
+ 
+ /**
+  * struct csis_state - the driver's internal state data structure
+  * @lock: mutex serializing the subdev and power management operations,
+  *        protecting @format and @flags members
+  * @pads: CSIS pads array
+  * @sd: v4l2_subdev associated with CSIS device instance
+  * @pdev: CSIS platform device
+  * @regs: mmaped I/O registers memory
+  * @supplies: CSIS regulator supplies
+  * @clock: CSIS clocks
+  * @irq: requested s5p-mipi-csis irq number
+  * @flags: the state variable for power and streaming control
+  * @csis_fmt: current CSIS pixel format
+  * @format: common media bus format for the source and sink pad
+  * @slock: spinlock protecting structure members below
+  * @events: MIPI-CSIS event (error) counters
+  */
+ struct csis_state {
+       struct mutex lock;
+       struct media_pad pads[CSIS_PADS_NUM];
+       struct v4l2_subdev sd;
+       struct platform_device *pdev;
+       void __iomem *regs;
+       struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
+       struct clk *clock[NUM_CSIS_CLOCKS];
+       int irq;
+       u32 flags;
+       const struct csis_pix_format *csis_fmt;
+       struct v4l2_mbus_framefmt format;
+ 
+       struct spinlock slock;
+       struct s5pcsis_event events[S5PCSIS_NUM_EVENTS];
+ };
+ 
+ /**
+  * struct csis_pix_format - CSIS pixel format description
+  * @pix_width_alignment: horizontal pixel alignment, width will be
+  *                       multiple of 2^pix_width_alignment
+  * @code: corresponding media bus code
+  * @fmt_reg: S5PCSIS_CONFIG register value
+  * @data_alignment: MIPI-CSI data alignment in bits
+  */
+ struct csis_pix_format {
+       unsigned int pix_width_alignment;
+       enum v4l2_mbus_pixelcode code;
+       u32 fmt_reg;
+       u8 data_alignment;
+ };
+ 
+ static const struct csis_pix_format s5pcsis_formats[] = {
+       {
+               .code = V4L2_MBUS_FMT_VYUY8_2X8,
+               .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
+               .data_alignment = 32,
+       }, {
+               .code = V4L2_MBUS_FMT_JPEG_1X8,
+               .fmt_reg = S5PCSIS_CFG_FMT_USER(1),
+               .data_alignment = 32,
+       },
+ };
+ 
+ #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
+ #define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
+ 
+ static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
+ {
+       return container_of(sdev, struct csis_state, sd);
+ }
+ 
+ static const struct csis_pix_format *find_csis_format(
+       struct v4l2_mbus_framefmt *mf)
+ {
+       int i;
+ 
+       for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
+               if (mf->code == s5pcsis_formats[i].code)
+                       return &s5pcsis_formats[i];
+       return NULL;
+ }
+ 
+ static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
+ {
+       u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
+ 
+       val = on ? val | S5PCSIS_INTMSK_EN_ALL :
+                  val & ~S5PCSIS_INTMSK_EN_ALL;
+       s5pcsis_write(state, S5PCSIS_INTMSK, val);
+ }
+ 
+ static void s5pcsis_reset(struct csis_state *state)
+ {
+       u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
+ 
+       s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
+       udelay(10);
+ }
+ 
+ static void s5pcsis_system_enable(struct csis_state *state, int on)
+ {
+       u32 val;
+ 
+       val = s5pcsis_read(state, S5PCSIS_CTRL);
+       if (on)
+               val |= S5PCSIS_CTRL_ENABLE;
+       else
+               val &= ~S5PCSIS_CTRL_ENABLE;
+       s5pcsis_write(state, S5PCSIS_CTRL, val);
+ 
+       val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+       if (on)
+               val |= S5PCSIS_DPHYCTRL_ENABLE;
+       else
+               val &= ~S5PCSIS_DPHYCTRL_ENABLE;
+       s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+ }
+ 
+ /* Called with the state.lock mutex held */
+ static void __s5pcsis_set_format(struct csis_state *state)
+ {
+       struct v4l2_mbus_framefmt *mf = &state->format;
+       u32 val;
+ 
+       v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n",
+                mf->code, mf->width, mf->height);
+ 
+       /* Color format */
+       val = s5pcsis_read(state, S5PCSIS_CONFIG);
+       val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+       s5pcsis_write(state, S5PCSIS_CONFIG, val);
+ 
+       /* Pixel resolution */
+       val = (mf->width << 16) | mf->height;
+       s5pcsis_write(state, S5PCSIS_RESOL, val);
+ }
+ 
+ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
+ {
+       u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+ 
+       val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
+       s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+ }
+ 
+ static void s5pcsis_set_params(struct csis_state *state)
+ {
+       struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
+       u32 val;
+ 
+       val = s5pcsis_read(state, S5PCSIS_CONFIG);
+       val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
+       s5pcsis_write(state, S5PCSIS_CONFIG, val);
+ 
+       __s5pcsis_set_format(state);
+       s5pcsis_set_hsync_settle(state, pdata->hs_settle);
+ 
+       val = s5pcsis_read(state, S5PCSIS_CTRL);
+       if (state->csis_fmt->data_alignment == 32)
+               val |= S5PCSIS_CTRL_ALIGN_32BIT;
+       else /* 24-bits */
+               val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
+       /* Not using external clock. */
+       val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
+       s5pcsis_write(state, S5PCSIS_CTRL, val);
+ 
+       /* Update the shadow register. */
+       val = s5pcsis_read(state, S5PCSIS_CTRL);
+       s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
+ }
+ 
+ static void s5pcsis_clk_put(struct csis_state *state)
+ {
+       int i;
+ 
+       for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+               if (IS_ERR_OR_NULL(state->clock[i]))
+                       continue;
+               clk_unprepare(state->clock[i]);
+               clk_put(state->clock[i]);
+               state->clock[i] = NULL;
+       }
+ }
+ 
+ static int s5pcsis_clk_get(struct csis_state *state)
+ {
+       struct device *dev = &state->pdev->dev;
+       int i, ret;
+ 
+       for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+               state->clock[i] = clk_get(dev, csi_clock_name[i]);
+               if (IS_ERR(state->clock[i]))
+                       goto err;
+               ret = clk_prepare(state->clock[i]);
+               if (ret < 0) {
+                       clk_put(state->clock[i]);
+                       state->clock[i] = NULL;
+                       goto err;
+               }
+       }
+       return 0;
+ err:
+       s5pcsis_clk_put(state);
+       dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
+       return -ENXIO;
+ }
+ 
+ static void s5pcsis_start_stream(struct csis_state *state)
+ {
+       s5pcsis_reset(state);
+       s5pcsis_set_params(state);
+       s5pcsis_system_enable(state, true);
+       s5pcsis_enable_interrupts(state, true);
+ }
+ 
+ static void s5pcsis_stop_stream(struct csis_state *state)
+ {
+       s5pcsis_enable_interrupts(state, false);
+       s5pcsis_system_enable(state, false);
+ }
+ 
+ static void s5pcsis_clear_counters(struct csis_state *state)
+ {
+       unsigned long flags;
+       int i;
+ 
+       spin_lock_irqsave(&state->slock, flags);
+       for (i = 0; i < S5PCSIS_NUM_EVENTS; i++)
+               state->events[i].counter = 0;
+       spin_unlock_irqrestore(&state->slock, flags);
+ }
+ 
+ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors)
+ {
+       int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4;
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&state->slock, flags);
+ 
+       for (i--; i >= 0; i--)
+               if (state->events[i].counter >= 0)
+                       v4l2_info(&state->sd, "%s events: %d\n",
+                                 state->events[i].name,
+                                 state->events[i].counter);
+ 
+       spin_unlock_irqrestore(&state->slock, flags);
+ }
+ 
+ /*
+  * V4L2 subdev operations
+  */
+ static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       struct device *dev = &state->pdev->dev;
+ 
+       if (on)
+               return pm_runtime_get_sync(dev);
+ 
+       return pm_runtime_put_sync(dev);
+ }
+ 
+ static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       int ret = 0;
+ 
+       v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
+                __func__, enable, state->flags);
+ 
+       if (enable) {
+               s5pcsis_clear_counters(state);
+               ret = pm_runtime_get_sync(&state->pdev->dev);
+               if (ret && ret != 1)
+                       return ret;
+       }
+ 
+       mutex_lock(&state->lock);
+       if (enable) {
+               if (state->flags & ST_SUSPENDED) {
+                       ret = -EBUSY;
+                       goto unlock;
+               }
+               s5pcsis_start_stream(state);
+               state->flags |= ST_STREAMING;
+       } else {
+               s5pcsis_stop_stream(state);
+               state->flags &= ~ST_STREAMING;
+               if (debug > 0)
+                       s5pcsis_log_counters(state, true);
+       }
+ unlock:
+       mutex_unlock(&state->lock);
+       if (!enable)
+               pm_runtime_put(&state->pdev->dev);
+ 
+       return ret == 1 ? 0 : ret;
+ }
+ 
+ static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
+                                 struct v4l2_subdev_fh *fh,
+                                 struct v4l2_subdev_mbus_code_enum *code)
+ {
+       if (code->index >= ARRAY_SIZE(s5pcsis_formats))
+               return -EINVAL;
+ 
+       code->code = s5pcsis_formats[code->index].code;
+       return 0;
+ }
+ 
+ static struct csis_pix_format const *s5pcsis_try_format(
+       struct v4l2_mbus_framefmt *mf)
+ {
+       struct csis_pix_format const *csis_fmt;
+ 
+       csis_fmt = find_csis_format(mf);
+       if (csis_fmt == NULL)
+               csis_fmt = &s5pcsis_formats[0];
+ 
+       mf->code = csis_fmt->code;
+       v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+                             csis_fmt->pix_width_alignment,
+                             &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+                             0);
+       return csis_fmt;
+ }
+ 
+ static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
+               struct csis_state *state, struct v4l2_subdev_fh *fh,
+               u32 pad, enum v4l2_subdev_format_whence which)
+ {
+       if (which == V4L2_SUBDEV_FORMAT_TRY)
+               return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+ 
+       return &state->format;
+ }
+ 
+ static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+                          struct v4l2_subdev_format *fmt)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       struct csis_pix_format const *csis_fmt;
+       struct v4l2_mbus_framefmt *mf;
+ 
+       if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+               return -EINVAL;
+ 
+       mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+ 
+       if (fmt->pad == CSIS_PAD_SOURCE) {
+               if (mf) {
+                       mutex_lock(&state->lock);
+                       fmt->format = *mf;
+                       mutex_unlock(&state->lock);
+               }
+               return 0;
+       }
+       csis_fmt = s5pcsis_try_format(&fmt->format);
+       if (mf) {
+               mutex_lock(&state->lock);
+               *mf = fmt->format;
+               if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+                       state->csis_fmt = csis_fmt;
+               mutex_unlock(&state->lock);
+       }
+       return 0;
+ }
+ 
+ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+                          struct v4l2_subdev_format *fmt)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+       struct v4l2_mbus_framefmt *mf;
+ 
+       if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+               return -EINVAL;
+ 
+       mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+       if (!mf)
+               return -EINVAL;
+ 
+       mutex_lock(&state->lock);
+       fmt->format = *mf;
+       mutex_unlock(&state->lock);
+       return 0;
+ }
+ 
+ static int s5pcsis_log_status(struct v4l2_subdev *sd)
+ {
+       struct csis_state *state = sd_to_csis_state(sd);
+ 
+       s5pcsis_log_counters(state, true);
+       return 0;
+ }
+ 
+ static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+ {
+       struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+ 
+       format->colorspace = V4L2_COLORSPACE_JPEG;
+       format->code = s5pcsis_formats[0].code;
+       format->width = S5PCSIS_DEF_PIX_WIDTH;
+       format->height = S5PCSIS_DEF_PIX_HEIGHT;
+       format->field = V4L2_FIELD_NONE;
+ 
+       return 0;
+ }
+ 
+ static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = {
+       .open = s5pcsis_open,
+ };
+ 
+ static struct v4l2_subdev_core_ops s5pcsis_core_ops = {
+       .s_power = s5pcsis_s_power,
+       .log_status = s5pcsis_log_status,
+ };
+ 
+ static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
+       .enum_mbus_code = s5pcsis_enum_mbus_code,
+       .get_fmt = s5pcsis_get_fmt,
+       .set_fmt = s5pcsis_set_fmt,
+ };
+ 
+ static struct v4l2_subdev_video_ops s5pcsis_video_ops = {
+       .s_stream = s5pcsis_s_stream,
+ };
+ 
+ static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
+       .core = &s5pcsis_core_ops,
+       .pad = &s5pcsis_pad_ops,
+       .video = &s5pcsis_video_ops,
+ };
+ 
+ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
+ {
+       struct csis_state *state = dev_id;
+       unsigned long flags;
+       u32 status;
+ 
+       status = s5pcsis_read(state, S5PCSIS_INTSRC);
+ 
+       spin_lock_irqsave(&state->slock, flags);
+ 
+       /* Update the event/error counters */
+       if ((status & S5PCSIS_INTSRC_ERRORS) || debug) {
+               int i;
+               for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) {
+                       if (!(status & state->events[i].mask))
+                               continue;
+                       state->events[i].counter++;
+                       v4l2_dbg(2, debug, &state->sd, "%s: %d\n",
+                                state->events[i].name,
+                                state->events[i].counter);
+               }
+               v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status);
+       }
+       spin_unlock_irqrestore(&state->slock, flags);
+ 
+       s5pcsis_write(state, S5PCSIS_INTSRC, status);
+       return IRQ_HANDLED;
+ }
+ 
+ static int __devinit s5pcsis_probe(struct platform_device *pdev)
+ {
+       struct s5p_platform_mipi_csis *pdata;
+       struct resource *mem_res;
+       struct csis_state *state;
+       int ret = -ENOMEM;
+       int i;
+ 
+       state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+ 
+       mutex_init(&state->lock);
+       spin_lock_init(&state->slock);
+ 
+       state->pdev = pdev;
+ 
+       pdata = pdev->dev.platform_data;
+       if (pdata == NULL || pdata->phy_enable == NULL) {
+               dev_err(&pdev->dev, "Platform data not fully specified\n");
+               return -EINVAL;
+       }
+ 
+       if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
+           pdata->lanes > CSIS0_MAX_LANES) {
+               dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
+                       pdata->lanes);
+               return -EINVAL;
+       }
+ 
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
+       if (state->regs == NULL) {
+               dev_err(&pdev->dev, "Failed to request and remap io memory\n");
+               return -ENXIO;
+       }
+ 
+       state->irq = platform_get_irq(pdev, 0);
+       if (state->irq < 0) {
+               dev_err(&pdev->dev, "Failed to get irq\n");
+               return state->irq;
+       }
+ 
+       for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
+               state->supplies[i].supply = csis_supply_name[i];
+ 
+       ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+                                state->supplies);
+       if (ret)
+               return ret;
+ 
+       ret = s5pcsis_clk_get(state);
+       if (ret)
+               goto e_clkput;
+ 
+       clk_enable(state->clock[CSIS_CLK_MUX]);
+       if (pdata->clk_rate)
+               clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
+       else
+               dev_WARN(&pdev->dev, "No clock frequency specified!\n");
+ 
+       ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
+                              0, dev_name(&pdev->dev), state);
+       if (ret) {
+               dev_err(&pdev->dev, "Interrupt request failed\n");
+               goto e_regput;
+       }
+ 
+       v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
+       state->sd.owner = THIS_MODULE;
+       strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
+       state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+       state->csis_fmt = &s5pcsis_formats[0];
+ 
+       state->format.code = s5pcsis_formats[0].code;
+       state->format.width = S5PCSIS_DEF_PIX_WIDTH;
+       state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
+ 
+       state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+       state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+       ret = media_entity_init(&state->sd.entity,
+                               CSIS_PADS_NUM, state->pads, 0);
+       if (ret < 0)
+               goto e_clkput;
+ 
+       /* This allows to retrieve the platform device id by the host driver */
+       v4l2_set_subdevdata(&state->sd, pdev);
+ 
+       /* .. and a pointer to the subdev. */
+       platform_set_drvdata(pdev, &state->sd);
+ 
+       memcpy(state->events, s5pcsis_events, sizeof(state->events));
+ 
+       pm_runtime_enable(&pdev->dev);
+       return 0;
+ 
+ e_regput:
+       regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+ e_clkput:
+       clk_disable(state->clock[CSIS_CLK_MUX]);
+       s5pcsis_clk_put(state);
+       return ret;
+ }
+ 
+ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
+ {
+       struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+       struct csis_state *state = sd_to_csis_state(sd);
+       int ret = 0;
+ 
+       v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+                __func__, state->flags);
+ 
+       mutex_lock(&state->lock);
+       if (state->flags & ST_POWERED) {
+               s5pcsis_stop_stream(state);
+               ret = pdata->phy_enable(state->pdev, false);
+               if (ret)
+                       goto unlock;
+               ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+                                            state->supplies);
+               if (ret)
+                       goto unlock;
+               clk_disable(state->clock[CSIS_CLK_GATE]);
+               state->flags &= ~ST_POWERED;
+               if (!runtime)
+                       state->flags |= ST_SUSPENDED;
+       }
+  unlock:
+       mutex_unlock(&state->lock);
+       return ret ? -EAGAIN : 0;
+ }
+ 
+ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
+ {
+       struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+       struct csis_state *state = sd_to_csis_state(sd);
+       int ret = 0;
+ 
+       v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+                __func__, state->flags);
+ 
+       mutex_lock(&state->lock);
+       if (!runtime && !(state->flags & ST_SUSPENDED))
+               goto unlock;
+ 
+       if (!(state->flags & ST_POWERED)) {
+               ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
+                                           state->supplies);
+               if (ret)
+                       goto unlock;
+               ret = pdata->phy_enable(state->pdev, true);
+               if (!ret) {
+                       state->flags |= ST_POWERED;
+               } else {
+                       regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+                                              state->supplies);
+                       goto unlock;
+               }
+               clk_enable(state->clock[CSIS_CLK_GATE]);
+       }
+       if (state->flags & ST_STREAMING)
+               s5pcsis_start_stream(state);
+ 
+       state->flags &= ~ST_SUSPENDED;
+  unlock:
+       mutex_unlock(&state->lock);
+       return ret ? -EAGAIN : 0;
+ }
+ 
+ #ifdef CONFIG_PM_SLEEP
+ static int s5pcsis_suspend(struct device *dev)
+ {
+       return s5pcsis_pm_suspend(dev, false);
+ }
+ 
+ static int s5pcsis_resume(struct device *dev)
+ {
+       return s5pcsis_pm_resume(dev, false);
+ }
+ #endif
+ 
+ #ifdef CONFIG_PM_RUNTIME
+ static int s5pcsis_runtime_suspend(struct device *dev)
+ {
+       return s5pcsis_pm_suspend(dev, true);
+ }
+ 
+ static int s5pcsis_runtime_resume(struct device *dev)
+ {
+       return s5pcsis_pm_resume(dev, true);
+ }
+ #endif
+ 
+ static int __devexit s5pcsis_remove(struct platform_device *pdev)
+ {
+       struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+       struct csis_state *state = sd_to_csis_state(sd);
+ 
+       pm_runtime_disable(&pdev->dev);
+       s5pcsis_pm_suspend(&pdev->dev, false);
+       clk_disable(state->clock[CSIS_CLK_MUX]);
+       pm_runtime_set_suspended(&pdev->dev);
+       s5pcsis_clk_put(state);
+       regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+ 
+       media_entity_cleanup(&state->sd.entity);
+ 
+       return 0;
+ }
+ 
+ static const struct dev_pm_ops s5pcsis_pm_ops = {
+       SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
+                          NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
+ };
+ 
+ static struct platform_driver s5pcsis_driver = {
+       .probe          = s5pcsis_probe,
+       .remove         = __devexit_p(s5pcsis_remove),
+       .driver         = {
+               .name   = CSIS_DRIVER_NAME,
+               .owner  = THIS_MODULE,
+               .pm     = &s5pcsis_pm_ops,
+       },
+ };
+ 
+ module_platform_driver(s5pcsis_driver);
+ 
+ MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+ MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
+ MODULE_LICENSE("GPL");
 
--- /dev/null
 -#include <mach/mx1_camera.h>
+ /*
+  * V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+  *
+  * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+  * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+  *
+  * Based on PXA SoC camera driver
+  * Copyright (C) 2006, Sascha Hauer, Pengutronix
+  * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  */
+ 
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/kernel.h>
+ #include <linux/mm.h>
+ #include <linux/module.h>
+ #include <linux/moduleparam.h>
+ #include <linux/mutex.h>
+ #include <linux/platform_device.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ #include <linux/time.h>
+ #include <linux/videodev2.h>
+ 
+ #include <media/soc_camera.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-dev.h>
+ #include <media/videobuf-dma-contig.h>
+ #include <media/soc_mediabus.h>
+ 
+ #include <asm/dma.h>
+ #include <asm/fiq.h>
+ #include <mach/dma-mx1-mx2.h>
+ #include <mach/hardware.h>
+ #include <mach/irqs.h>
++#include <linux/platform_data/camera-mx1.h>
+ 
+ /*
+  * CSI registers
+  */
+ #define CSICR1                0x00                    /* CSI Control Register 1 */
+ #define CSISR         0x08                    /* CSI Status Register */
+ #define CSIRXR                0x10                    /* CSI RxFIFO Register */
+ 
+ #define CSICR1_RXFF_LEVEL(x)  (((x) & 0x3) << 19)
+ #define CSICR1_SOF_POL                (1 << 17)
+ #define CSICR1_SOF_INTEN      (1 << 16)
+ #define CSICR1_MCLKDIV(x)     (((x) & 0xf) << 12)
+ #define CSICR1_MCLKEN         (1 << 9)
+ #define CSICR1_FCC            (1 << 8)
+ #define CSICR1_BIG_ENDIAN     (1 << 7)
+ #define CSICR1_CLR_RXFIFO     (1 << 5)
+ #define CSICR1_GCLK_MODE      (1 << 4)
+ #define CSICR1_DATA_POL               (1 << 2)
+ #define CSICR1_REDGE          (1 << 1)
+ #define CSICR1_EN             (1 << 0)
+ 
+ #define CSISR_SFF_OR_INT      (1 << 25)
+ #define CSISR_RFF_OR_INT      (1 << 24)
+ #define CSISR_STATFF_INT      (1 << 21)
+ #define CSISR_RXFF_INT                (1 << 18)
+ #define CSISR_SOF_INT         (1 << 16)
+ #define CSISR_DRDY            (1 << 0)
+ 
+ #define DRIVER_VERSION "0.0.2"
+ #define DRIVER_NAME "mx1-camera"
+ 
+ #define CSI_IRQ_MASK  (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \
+                       CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT)
+ 
+ #define CSI_BUS_FLAGS (V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+                       V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+                       V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+                       V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_LOW)
+ 
+ #define MAX_VIDEO_MEM 16      /* Video memory limit in megabytes */
+ 
+ /*
+  * Structures
+  */
+ 
+ /* buffer for one video frame */
+ struct mx1_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct videobuf_buffer          vb;
+       enum v4l2_mbus_pixelcode        code;
+       int                             inwork;
+ };
+ 
+ /*
+  * i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor
+  * Interface. If anyone ever builds hardware to enable more than
+  * one camera, they will have to modify this driver too
+  */
+ struct mx1_camera_dev {
+       struct soc_camera_host          soc_host;
+       struct soc_camera_device        *icd;
+       struct mx1_camera_pdata         *pdata;
+       struct mx1_buffer               *active;
+       struct resource                 *res;
+       struct clk                      *clk;
+       struct list_head                capture;
+ 
+       void __iomem                    *base;
+       int                             dma_chan;
+       unsigned int                    irq;
+       unsigned long                   mclk;
+ 
+       spinlock_t                      lock;
+ };
+ 
+ /*
+  *  Videobuf operations
+  */
+ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+                             unsigned int *size)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+ 
+       *size = icd->sizeimage;
+ 
+       if (!*count)
+               *count = 32;
+ 
+       if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+               *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+ 
+       dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
+ 
+       return 0;
+ }
+ 
+ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct videobuf_buffer *vb = &buf->vb;
+ 
+       BUG_ON(in_interrupt());
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       /*
+        * This waits until this buffer is out of danger, i.e., until it is no
+        * longer in STATE_QUEUED or STATE_ACTIVE
+        */
+       videobuf_waiton(vq, vb, 0, 0);
+       videobuf_dma_contig_free(vq, vb);
+ 
+       vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+ 
+ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
+               struct videobuf_buffer *vb, enum v4l2_field field)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
+       int ret;
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       /* Added list head initialization on alloc */
+       WARN_ON(!list_empty(&vb->queue));
+ 
+       BUG_ON(NULL == icd->current_fmt);
+ 
+       /*
+        * I think, in buf_prepare you only have to protect global data,
+        * the actual buffer is yours
+        */
+       buf->inwork = 1;
+ 
+       if (buf->code   != icd->current_fmt->code ||
+           vb->width   != icd->user_width ||
+           vb->height  != icd->user_height ||
+           vb->field   != field) {
+               buf->code       = icd->current_fmt->code;
+               vb->width       = icd->user_width;
+               vb->height      = icd->user_height;
+               vb->field       = field;
+               vb->state       = VIDEOBUF_NEEDS_INIT;
+       }
+ 
+       vb->size = icd->sizeimage;
+       if (0 != vb->baddr && vb->bsize < vb->size) {
+               ret = -EINVAL;
+               goto out;
+       }
+ 
+       if (vb->state == VIDEOBUF_NEEDS_INIT) {
+               ret = videobuf_iolock(vq, vb, NULL);
+               if (ret)
+                       goto fail;
+ 
+               vb->state = VIDEOBUF_PREPARED;
+       }
+ 
+       buf->inwork = 0;
+ 
+       return 0;
+ 
+ fail:
+       free_buffer(vq, buf);
+ out:
+       buf->inwork = 0;
+       return ret;
+ }
+ 
+ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
+ {
+       struct videobuf_buffer *vbuf = &pcdev->active->vb;
+       struct device *dev = pcdev->icd->parent;
+       int ret;
+ 
+       if (unlikely(!pcdev->active)) {
+               dev_err(dev, "DMA End IRQ with no active buffer\n");
+               return -EFAULT;
+       }
+ 
+       /* setup sg list for future DMA */
+       ret = imx_dma_setup_single(pcdev->dma_chan,
+               videobuf_to_dma_contig(vbuf),
+               vbuf->size, pcdev->res->start +
+               CSIRXR, DMA_MODE_READ);
+       if (unlikely(ret))
+               dev_err(dev, "Failed to setup DMA sg list\n");
+ 
+       return ret;
+ }
+ 
+ /* Called under spinlock_irqsave(&pcdev->lock, ...) */
+ static void mx1_videobuf_queue(struct videobuf_queue *vq,
+                                               struct videobuf_buffer *vb)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx1_camera_dev *pcdev = ici->priv;
+       struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       list_add_tail(&vb->queue, &pcdev->capture);
+ 
+       vb->state = VIDEOBUF_ACTIVE;
+ 
+       if (!pcdev->active) {
+               pcdev->active = buf;
+ 
+               /* setup sg list for future DMA */
+               if (!mx1_camera_setup_dma(pcdev)) {
+                       unsigned int temp;
+                       /* enable SOF irq */
+                       temp = __raw_readl(pcdev->base + CSICR1) |
+                                                       CSICR1_SOF_INTEN;
+                       __raw_writel(temp, pcdev->base + CSICR1);
+               }
+       }
+ }
+ 
+ static void mx1_videobuf_release(struct videobuf_queue *vq,
+                                struct videobuf_buffer *vb)
+ {
+       struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
+ #ifdef DEBUG
+       struct soc_camera_device *icd = vq->priv_data;
+       struct device *dev = icd->parent;
+ 
+       dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       switch (vb->state) {
+       case VIDEOBUF_ACTIVE:
+               dev_dbg(dev, "%s (active)\n", __func__);
+               break;
+       case VIDEOBUF_QUEUED:
+               dev_dbg(dev, "%s (queued)\n", __func__);
+               break;
+       case VIDEOBUF_PREPARED:
+               dev_dbg(dev, "%s (prepared)\n", __func__);
+               break;
+       default:
+               dev_dbg(dev, "%s (unknown)\n", __func__);
+               break;
+       }
+ #endif
+ 
+       free_buffer(vq, buf);
+ }
+ 
+ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
+                             struct videobuf_buffer *vb,
+                             struct mx1_buffer *buf)
+ {
+       /* _init is used to debug races, see comment in mx1_camera_reqbufs() */
+       list_del_init(&vb->queue);
+       vb->state = VIDEOBUF_DONE;
+       do_gettimeofday(&vb->ts);
+       vb->field_count++;
+       wake_up(&vb->done);
+ 
+       if (list_empty(&pcdev->capture)) {
+               pcdev->active = NULL;
+               return;
+       }
+ 
+       pcdev->active = list_entry(pcdev->capture.next,
+                                  struct mx1_buffer, vb.queue);
+ 
+       /* setup sg list for future DMA */
+       if (likely(!mx1_camera_setup_dma(pcdev))) {
+               unsigned int temp;
+ 
+               /* enable SOF irq */
+               temp = __raw_readl(pcdev->base + CSICR1) | CSICR1_SOF_INTEN;
+               __raw_writel(temp, pcdev->base + CSICR1);
+       }
+ }
+ 
+ static void mx1_camera_dma_irq(int channel, void *data)
+ {
+       struct mx1_camera_dev *pcdev = data;
+       struct device *dev = pcdev->icd->parent;
+       struct mx1_buffer *buf;
+       struct videobuf_buffer *vb;
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&pcdev->lock, flags);
+ 
+       imx_dma_disable(channel);
+ 
+       if (unlikely(!pcdev->active)) {
+               dev_err(dev, "DMA End IRQ with no active buffer\n");
+               goto out;
+       }
+ 
+       vb = &pcdev->active->vb;
+       buf = container_of(vb, struct mx1_buffer, vb);
+       WARN_ON(buf->inwork || list_empty(&vb->queue));
+       dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       mx1_camera_wakeup(pcdev, vb, buf);
+ out:
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ 
+ static struct videobuf_queue_ops mx1_videobuf_ops = {
+       .buf_setup      = mx1_videobuf_setup,
+       .buf_prepare    = mx1_videobuf_prepare,
+       .buf_queue      = mx1_videobuf_queue,
+       .buf_release    = mx1_videobuf_release,
+ };
+ 
+ static void mx1_camera_init_videobuf(struct videobuf_queue *q,
+                                    struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx1_camera_dev *pcdev = ici->priv;
+ 
+       videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent,
+                               &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                               V4L2_FIELD_NONE,
+                               sizeof(struct mx1_buffer), icd, &icd->video_lock);
+ }
+ 
+ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
+ {
+       unsigned int mclk = pcdev->mclk;
+       unsigned long div;
+       unsigned long lcdclk;
+ 
+       lcdclk = clk_get_rate(pcdev->clk);
+ 
+       /*
+        * We verify platform_mclk_10khz != 0, so if anyone breaks it, here
+        * they get a nice Oops
+        */
+       div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
+ 
+       dev_dbg(pcdev->icd->parent,
+               "System clock %lukHz, target freq %dkHz, divisor %lu\n",
+               lcdclk / 1000, mclk / 1000, div);
+ 
+       return div;
+ }
+ 
+ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
+ {
+       unsigned int csicr1 = CSICR1_EN;
+ 
+       dev_dbg(pcdev->icd->parent, "Activate device\n");
+ 
+       clk_prepare_enable(pcdev->clk);
+ 
+       /* enable CSI before doing anything else */
+       __raw_writel(csicr1, pcdev->base + CSICR1);
+ 
+       csicr1 |= CSICR1_MCLKEN | CSICR1_FCC | CSICR1_GCLK_MODE;
+       csicr1 |= CSICR1_MCLKDIV(mclk_get_divisor(pcdev));
+       csicr1 |= CSICR1_RXFF_LEVEL(2); /* 16 words */
+ 
+       __raw_writel(csicr1, pcdev->base + CSICR1);
+ }
+ 
+ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
+ {
+       dev_dbg(pcdev->icd->parent, "Deactivate device\n");
+ 
+       /* Disable all CSI interface */
+       __raw_writel(0x00, pcdev->base + CSICR1);
+ 
+       clk_disable_unprepare(pcdev->clk);
+ }
+ 
+ /*
+  * The following two functions absolutely depend on the fact, that
+  * there can be only one camera on i.MX1/i.MXL camera sensor interface
+  */
+ static int mx1_camera_add_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx1_camera_dev *pcdev = ici->priv;
+ 
+       if (pcdev->icd)
+               return -EBUSY;
+ 
+       dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n",
+                icd->devnum);
+ 
+       mx1_camera_activate(pcdev);
+ 
+       pcdev->icd = icd;
+ 
+       return 0;
+ }
+ 
+ static void mx1_camera_remove_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx1_camera_dev *pcdev = ici->priv;
+       unsigned int csicr1;
+ 
+       BUG_ON(icd != pcdev->icd);
+ 
+       /* disable interrupts */
+       csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK;
+       __raw_writel(csicr1, pcdev->base + CSICR1);
+ 
+       /* Stop DMA engine */
+       imx_dma_disable(pcdev->dma_chan);
+ 
+       dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n",
+                icd->devnum);
+ 
+       mx1_camera_deactivate(pcdev);
+ 
+       pcdev->icd = NULL;
+ }
+ 
+ static int mx1_camera_set_crop(struct soc_camera_device *icd,
+                              struct v4l2_crop *a)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ 
+       return v4l2_subdev_call(sd, video, s_crop, a);
+ }
+ 
+ static int mx1_camera_set_bus_param(struct soc_camera_device *icd)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx1_camera_dev *pcdev = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long common_flags;
+       unsigned int csicr1;
+       int ret;
+ 
+       /* MX1 supports only 8bit buswidth */
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg, CSI_BUS_FLAGS);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%x\n",
+                                cfg.flags, CSI_BUS_FLAGS);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       } else {
+               common_flags = CSI_BUS_FLAGS;
+       }
+ 
+       /* Make choises, based on platform choice */
+       if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+               (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+                       if (!pcdev->pdata ||
+                            pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH)
+                               common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+                       else
+                               common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+               (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+                       if (!pcdev->pdata ||
+                            pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING)
+                               common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+                       else
+                               common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+               (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+                       if (!pcdev->pdata ||
+                            pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH)
+                               common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+                       else
+                               common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+       }
+ 
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+                       common_flags, ret);
+               return ret;
+       }
+ 
+       csicr1 = __raw_readl(pcdev->base + CSICR1);
+ 
+       if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+               csicr1 |= CSICR1_REDGE;
+       if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+               csicr1 |= CSICR1_SOF_POL;
+       if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+               csicr1 |= CSICR1_DATA_POL;
+ 
+       __raw_writel(csicr1, pcdev->base + CSICR1);
+ 
+       return 0;
+ }
+ 
+ static int mx1_camera_set_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret, buswidth;
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n",
+                        pix->pixelformat);
+               return -EINVAL;
+       }
+ 
+       buswidth = xlate->host_fmt->bits_per_sample;
+       if (buswidth > 8) {
+               dev_warn(icd->parent,
+                        "bits-per-sample %d for format %x unsupported\n",
+                        buswidth, pix->pixelformat);
+               return -EINVAL;
+       }
+ 
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       if (mf.code != xlate->code)
+               return -EINVAL;
+ 
+       pix->width              = mf.width;
+       pix->height             = mf.height;
+       pix->field              = mf.field;
+       pix->colorspace         = mf.colorspace;
+       icd->current_fmt        = xlate;
+ 
+       return ret;
+ }
+ 
+ static int mx1_camera_try_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+       /* TODO: limit to mx1 hardware capabilities */
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n",
+                        pix->pixelformat);
+               return -EINVAL;
+       }
+ 
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       /* limit to sensor capabilities */
+       ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       pix->width      = mf.width;
+       pix->height     = mf.height;
+       pix->field      = mf.field;
+       pix->colorspace = mf.colorspace;
+ 
+       return 0;
+ }
+ 
+ static int mx1_camera_reqbufs(struct soc_camera_device *icd,
+                             struct v4l2_requestbuffers *p)
+ {
+       int i;
+ 
+       /*
+        * This is for locking debugging only. I removed spinlocks and now I
+        * check whether .prepare is ever called on a linked buffer, or whether
+        * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+        * it hadn't triggered
+        */
+       for (i = 0; i < p->count; i++) {
+               struct mx1_buffer *buf = container_of(icd->vb_vidq.bufs[i],
+                                                     struct mx1_buffer, vb);
+               buf->inwork = 0;
+               INIT_LIST_HEAD(&buf->vb.queue);
+       }
+ 
+       return 0;
+ }
+ 
+ static unsigned int mx1_camera_poll(struct file *file, poll_table *pt)
+ {
+       struct soc_camera_device *icd = file->private_data;
+       struct mx1_buffer *buf;
+ 
+       buf = list_entry(icd->vb_vidq.stream.next, struct mx1_buffer,
+                        vb.stream);
+ 
+       poll_wait(file, &buf->vb.done, pt);
+ 
+       if (buf->vb.state == VIDEOBUF_DONE ||
+           buf->vb.state == VIDEOBUF_ERROR)
+               return POLLIN | POLLRDNORM;
+ 
+       return 0;
+ }
+ 
+ static int mx1_camera_querycap(struct soc_camera_host *ici,
+                              struct v4l2_capability *cap)
+ {
+       /* cap->name is set by the friendly caller:-> */
+       strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ 
+       return 0;
+ }
+ 
+ static struct soc_camera_host_ops mx1_soc_camera_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = mx1_camera_add_device,
+       .remove         = mx1_camera_remove_device,
+       .set_bus_param  = mx1_camera_set_bus_param,
+       .set_crop       = mx1_camera_set_crop,
+       .set_fmt        = mx1_camera_set_fmt,
+       .try_fmt        = mx1_camera_try_fmt,
+       .init_videobuf  = mx1_camera_init_videobuf,
+       .reqbufs        = mx1_camera_reqbufs,
+       .poll           = mx1_camera_poll,
+       .querycap       = mx1_camera_querycap,
+ };
+ 
+ static struct fiq_handler fh = {
+       .name           = "csi_sof"
+ };
+ 
+ static int __init mx1_camera_probe(struct platform_device *pdev)
+ {
+       struct mx1_camera_dev *pcdev;
+       struct resource *res;
+       struct pt_regs regs;
+       struct clk *clk;
+       void __iomem *base;
+       unsigned int irq;
+       int err = 0;
+ 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       if (!res || (int)irq <= 0) {
+               err = -ENODEV;
+               goto exit;
+       }
+ 
+       clk = clk_get(&pdev->dev, "csi_clk");
+       if (IS_ERR(clk)) {
+               err = PTR_ERR(clk);
+               goto exit;
+       }
+ 
+       pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+       if (!pcdev) {
+               dev_err(&pdev->dev, "Could not allocate pcdev\n");
+               err = -ENOMEM;
+               goto exit_put_clk;
+       }
+ 
+       pcdev->res = res;
+       pcdev->clk = clk;
+ 
+       pcdev->pdata = pdev->dev.platform_data;
+ 
+       if (pcdev->pdata)
+               pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+ 
+       if (!pcdev->mclk) {
+               dev_warn(&pdev->dev,
+                        "mclk_10khz == 0! Please, fix your platform data. "
+                        "Using default 20MHz\n");
+               pcdev->mclk = 20000000;
+       }
+ 
+       INIT_LIST_HEAD(&pcdev->capture);
+       spin_lock_init(&pcdev->lock);
+ 
+       /*
+        * Request the regions.
+        */
+       if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+               err = -EBUSY;
+               goto exit_kfree;
+       }
+ 
+       base = ioremap(res->start, resource_size(res));
+       if (!base) {
+               err = -ENOMEM;
+               goto exit_release;
+       }
+       pcdev->irq = irq;
+       pcdev->base = base;
+ 
+       /* request dma */
+       pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH);
+       if (pcdev->dma_chan < 0) {
+               dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n");
+               err = -EBUSY;
+               goto exit_iounmap;
+       }
+       dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan);
+ 
+       imx_dma_setup_handlers(pcdev->dma_chan, mx1_camera_dma_irq, NULL,
+                              pcdev);
+ 
+       imx_dma_config_channel(pcdev->dma_chan, IMX_DMA_TYPE_FIFO,
+                              IMX_DMA_MEMSIZE_32, MX1_DMA_REQ_CSI_R, 0);
+       /* burst length : 16 words = 64 bytes */
+       imx_dma_config_burstlen(pcdev->dma_chan, 0);
+ 
+       /* request irq */
+       err = claim_fiq(&fh);
+       if (err) {
+               dev_err(&pdev->dev, "Camera interrupt register failed \n");
+               goto exit_free_dma;
+       }
+ 
+       set_fiq_handler(&mx1_camera_sof_fiq_start, &mx1_camera_sof_fiq_end -
+                                                  &mx1_camera_sof_fiq_start);
+ 
+       regs.ARM_r8 = (long)MX1_DMA_DIMR;
+       regs.ARM_r9 = (long)MX1_DMA_CCR(pcdev->dma_chan);
+       regs.ARM_r10 = (long)pcdev->base + CSICR1;
+       regs.ARM_fp = (long)pcdev->base + CSISR;
+       regs.ARM_sp = 1 << pcdev->dma_chan;
+       set_fiq_regs(®s);
+ 
+       mxc_set_irq_fiq(irq, 1);
+       enable_fiq(irq);
+ 
+       pcdev->soc_host.drv_name        = DRIVER_NAME;
+       pcdev->soc_host.ops             = &mx1_soc_camera_host_ops;
+       pcdev->soc_host.priv            = pcdev;
+       pcdev->soc_host.v4l2_dev.dev    = &pdev->dev;
+       pcdev->soc_host.nr              = pdev->id;
+       err = soc_camera_host_register(&pcdev->soc_host);
+       if (err)
+               goto exit_free_irq;
+ 
+       dev_info(&pdev->dev, "MX1 Camera driver loaded\n");
+ 
+       return 0;
+ 
+ exit_free_irq:
+       disable_fiq(irq);
+       mxc_set_irq_fiq(irq, 0);
+       release_fiq(&fh);
+ exit_free_dma:
+       imx_dma_free(pcdev->dma_chan);
+ exit_iounmap:
+       iounmap(base);
+ exit_release:
+       release_mem_region(res->start, resource_size(res));
+ exit_kfree:
+       kfree(pcdev);
+ exit_put_clk:
+       clk_put(clk);
+ exit:
+       return err;
+ }
+ 
+ static int __exit mx1_camera_remove(struct platform_device *pdev)
+ {
+       struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+       struct mx1_camera_dev *pcdev = container_of(soc_host,
+                                       struct mx1_camera_dev, soc_host);
+       struct resource *res;
+ 
+       imx_dma_free(pcdev->dma_chan);
+       disable_fiq(pcdev->irq);
+       mxc_set_irq_fiq(pcdev->irq, 0);
+       release_fiq(&fh);
+ 
+       clk_put(pcdev->clk);
+ 
+       soc_camera_host_unregister(soc_host);
+ 
+       iounmap(pcdev->base);
+ 
+       res = pcdev->res;
+       release_mem_region(res->start, resource_size(res));
+ 
+       kfree(pcdev);
+ 
+       dev_info(&pdev->dev, "MX1 Camera driver unloaded\n");
+ 
+       return 0;
+ }
+ 
+ static struct platform_driver mx1_camera_driver = {
+       .driver         = {
+               .name   = DRIVER_NAME,
+       },
+       .remove         = __exit_p(mx1_camera_remove),
+ };
+ 
+ static int __init mx1_camera_init(void)
+ {
+       return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe);
+ }
+ 
+ static void __exit mx1_camera_exit(void)
+ {
+       return platform_driver_unregister(&mx1_camera_driver);
+ }
+ 
+ module_init(mx1_camera_init);
+ module_exit(mx1_camera_exit);
+ 
+ MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver");
+ MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>");
+ MODULE_LICENSE("GPL v2");
+ MODULE_VERSION(DRIVER_VERSION);
+ MODULE_ALIAS("platform:" DRIVER_NAME);
 
--- /dev/null
 -#include <mach/mx2_cam.h>
+ /*
+  * V4L2 Driver for i.MX27/i.MX25 camera host
+  *
+  * Copyright (C) 2008, Sascha Hauer, Pengutronix
+  * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+  * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
+  *
+  * 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.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/io.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+ #include <linux/gcd.h>
+ #include <linux/interrupt.h>
+ #include <linux/kernel.h>
+ #include <linux/math64.h>
+ #include <linux/mm.h>
+ #include <linux/moduleparam.h>
+ #include <linux/time.h>
+ #include <linux/device.h>
+ #include <linux/platform_device.h>
+ #include <linux/mutex.h>
+ #include <linux/clk.h>
+ 
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-dev.h>
+ #include <media/videobuf2-core.h>
+ #include <media/videobuf2-dma-contig.h>
+ #include <media/soc_camera.h>
+ #include <media/soc_mediabus.h>
+ 
+ #include <linux/videodev2.h>
+ 
++#include <linux/platform_data/camera-mx2.h>
+ #include <mach/hardware.h>
+ 
+ #include <asm/dma.h>
+ 
+ #define MX2_CAM_DRV_NAME "mx2-camera"
+ #define MX2_CAM_VERSION "0.0.6"
+ #define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
+ 
+ /* reset values */
+ #define CSICR1_RESET_VAL      0x40000800
+ #define CSICR2_RESET_VAL      0x0
+ #define CSICR3_RESET_VAL      0x0
+ 
+ /* csi control reg 1 */
+ #define CSICR1_SWAP16_EN      (1 << 31)
+ #define CSICR1_EXT_VSYNC      (1 << 30)
+ #define CSICR1_EOF_INTEN      (1 << 29)
+ #define CSICR1_PRP_IF_EN      (1 << 28)
+ #define CSICR1_CCIR_MODE      (1 << 27)
+ #define CSICR1_COF_INTEN      (1 << 26)
+ #define CSICR1_SF_OR_INTEN    (1 << 25)
+ #define CSICR1_RF_OR_INTEN    (1 << 24)
+ #define CSICR1_STATFF_LEVEL   (3 << 22)
+ #define CSICR1_STATFF_INTEN   (1 << 21)
+ #define CSICR1_RXFF_LEVEL(l)  (((l) & 3) << 19)       /* MX27 */
+ #define CSICR1_FB2_DMA_INTEN  (1 << 20)               /* MX25 */
+ #define CSICR1_FB1_DMA_INTEN  (1 << 19)               /* MX25 */
+ #define CSICR1_RXFF_INTEN     (1 << 18)
+ #define CSICR1_SOF_POL                (1 << 17)
+ #define CSICR1_SOF_INTEN      (1 << 16)
+ #define CSICR1_MCLKDIV(d)     (((d) & 0xF) << 12)
+ #define CSICR1_HSYNC_POL      (1 << 11)
+ #define CSICR1_CCIR_EN                (1 << 10)
+ #define CSICR1_MCLKEN         (1 << 9)
+ #define CSICR1_FCC            (1 << 8)
+ #define CSICR1_PACK_DIR               (1 << 7)
+ #define CSICR1_CLR_STATFIFO   (1 << 6)
+ #define CSICR1_CLR_RXFIFO     (1 << 5)
+ #define CSICR1_GCLK_MODE      (1 << 4)
+ #define CSICR1_INV_DATA               (1 << 3)
+ #define CSICR1_INV_PCLK               (1 << 2)
+ #define CSICR1_REDGE          (1 << 1)
+ #define CSICR1_FMT_MASK               (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
+ 
+ #define SHIFT_STATFF_LEVEL    22
+ #define SHIFT_RXFF_LEVEL      19
+ #define SHIFT_MCLKDIV         12
+ 
+ /* control reg 3 */
+ #define CSICR3_FRMCNT         (0xFFFF << 16)
+ #define CSICR3_FRMCNT_RST     (1 << 15)
+ #define CSICR3_DMA_REFLASH_RFF        (1 << 14)
+ #define CSICR3_DMA_REFLASH_SFF        (1 << 13)
+ #define CSICR3_DMA_REQ_EN_RFF (1 << 12)
+ #define CSICR3_DMA_REQ_EN_SFF (1 << 11)
+ #define CSICR3_RXFF_LEVEL(l)  (((l) & 7) << 4)        /* MX25 */
+ #define CSICR3_CSI_SUP                (1 << 3)
+ #define CSICR3_ZERO_PACK_EN   (1 << 2)
+ #define CSICR3_ECC_INT_EN     (1 << 1)
+ #define CSICR3_ECC_AUTO_EN    (1 << 0)
+ 
+ #define SHIFT_FRMCNT          16
+ 
+ /* csi status reg */
+ #define CSISR_SFF_OR_INT      (1 << 25)
+ #define CSISR_RFF_OR_INT      (1 << 24)
+ #define CSISR_STATFF_INT      (1 << 21)
+ #define CSISR_DMA_TSF_FB2_INT (1 << 20)       /* MX25 */
+ #define CSISR_DMA_TSF_FB1_INT (1 << 19)       /* MX25 */
+ #define CSISR_RXFF_INT                (1 << 18)
+ #define CSISR_EOF_INT         (1 << 17)
+ #define CSISR_SOF_INT         (1 << 16)
+ #define CSISR_F2_INT          (1 << 15)
+ #define CSISR_F1_INT          (1 << 14)
+ #define CSISR_COF_INT         (1 << 13)
+ #define CSISR_ECC_INT         (1 << 1)
+ #define CSISR_DRDY            (1 << 0)
+ 
+ #define CSICR1                        0x00
+ #define CSICR2                        0x04
+ #define CSISR                 (cpu_is_mx27() ? 0x08 : 0x18)
+ #define CSISTATFIFO           0x0c
+ #define CSIRFIFO              0x10
+ #define CSIRXCNT              0x14
+ #define CSICR3                        (cpu_is_mx27() ? 0x1C : 0x08)
+ #define CSIDMASA_STATFIFO     0x20
+ #define CSIDMATA_STATFIFO     0x24
+ #define CSIDMASA_FB1          0x28
+ #define CSIDMASA_FB2          0x2c
+ #define CSIFBUF_PARA          0x30
+ #define CSIIMAG_PARA          0x34
+ 
+ /* EMMA PrP */
+ #define PRP_CNTL                      0x00
+ #define PRP_INTR_CNTL                 0x04
+ #define PRP_INTRSTATUS                        0x08
+ #define PRP_SOURCE_Y_PTR              0x0c
+ #define PRP_SOURCE_CB_PTR             0x10
+ #define PRP_SOURCE_CR_PTR             0x14
+ #define PRP_DEST_RGB1_PTR             0x18
+ #define PRP_DEST_RGB2_PTR             0x1c
+ #define PRP_DEST_Y_PTR                        0x20
+ #define PRP_DEST_CB_PTR                       0x24
+ #define PRP_DEST_CR_PTR                       0x28
+ #define PRP_SRC_FRAME_SIZE            0x2c
+ #define PRP_DEST_CH1_LINE_STRIDE      0x30
+ #define PRP_SRC_PIXEL_FORMAT_CNTL     0x34
+ #define PRP_CH1_PIXEL_FORMAT_CNTL     0x38
+ #define PRP_CH1_OUT_IMAGE_SIZE                0x3c
+ #define PRP_CH2_OUT_IMAGE_SIZE                0x40
+ #define PRP_SRC_LINE_STRIDE           0x44
+ #define PRP_CSC_COEF_012              0x48
+ #define PRP_CSC_COEF_345              0x4c
+ #define PRP_CSC_COEF_678              0x50
+ #define PRP_CH1_RZ_HORI_COEF1         0x54
+ #define PRP_CH1_RZ_HORI_COEF2         0x58
+ #define PRP_CH1_RZ_HORI_VALID         0x5c
+ #define PRP_CH1_RZ_VERT_COEF1         0x60
+ #define PRP_CH1_RZ_VERT_COEF2         0x64
+ #define PRP_CH1_RZ_VERT_VALID         0x68
+ #define PRP_CH2_RZ_HORI_COEF1         0x6c
+ #define PRP_CH2_RZ_HORI_COEF2         0x70
+ #define PRP_CH2_RZ_HORI_VALID         0x74
+ #define PRP_CH2_RZ_VERT_COEF1         0x78
+ #define PRP_CH2_RZ_VERT_COEF2         0x7c
+ #define PRP_CH2_RZ_VERT_VALID         0x80
+ 
+ #define PRP_CNTL_CH1EN                (1 << 0)
+ #define PRP_CNTL_CH2EN                (1 << 1)
+ #define PRP_CNTL_CSIEN                (1 << 2)
+ #define PRP_CNTL_DATA_IN_YUV420       (0 << 3)
+ #define PRP_CNTL_DATA_IN_YUV422       (1 << 3)
+ #define PRP_CNTL_DATA_IN_RGB16        (2 << 3)
+ #define PRP_CNTL_DATA_IN_RGB32        (3 << 3)
+ #define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+ #define PRP_CNTL_CH1_OUT_RGB16        (1 << 5)
+ #define PRP_CNTL_CH1_OUT_RGB32        (2 << 5)
+ #define PRP_CNTL_CH1_OUT_YUV422       (3 << 5)
+ #define PRP_CNTL_CH2_OUT_YUV420       (0 << 7)
+ #define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+ #define PRP_CNTL_CH2_OUT_YUV444       (2 << 7)
+ #define PRP_CNTL_CH1_LEN      (1 << 9)
+ #define PRP_CNTL_CH2_LEN      (1 << 10)
+ #define PRP_CNTL_SKIP_FRAME   (1 << 11)
+ #define PRP_CNTL_SWRST                (1 << 12)
+ #define PRP_CNTL_CLKEN                (1 << 13)
+ #define PRP_CNTL_WEN          (1 << 14)
+ #define PRP_CNTL_CH1BYP               (1 << 15)
+ #define PRP_CNTL_IN_TSKIP(x)  ((x) << 16)
+ #define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+ #define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+ #define PRP_CNTL_INPUT_FIFO_LEVEL(x)  ((x) << 25)
+ #define PRP_CNTL_RZ_FIFO_LEVEL(x)     ((x) << 27)
+ #define PRP_CNTL_CH2B1EN      (1 << 29)
+ #define PRP_CNTL_CH2B2EN      (1 << 30)
+ #define PRP_CNTL_CH2FEN               (1 << 31)
+ 
+ /* IRQ Enable and status register */
+ #define PRP_INTR_RDERR                (1 << 0)
+ #define PRP_INTR_CH1WERR      (1 << 1)
+ #define PRP_INTR_CH2WERR      (1 << 2)
+ #define PRP_INTR_CH1FC                (1 << 3)
+ #define PRP_INTR_CH2FC                (1 << 5)
+ #define PRP_INTR_LBOVF                (1 << 7)
+ #define PRP_INTR_CH2OVF               (1 << 8)
+ 
+ /* Resizing registers */
+ #define PRP_RZ_VALID_TBL_LEN(x)       ((x) << 24)
+ #define PRP_RZ_VALID_BILINEAR (1 << 31)
+ 
+ #define MAX_VIDEO_MEM 16
+ 
+ #define RESIZE_NUM_MIN        1
+ #define RESIZE_NUM_MAX        20
+ #define BC_COEF               3
+ #define SZ_COEF               (1 << BC_COEF)
+ 
+ #define RESIZE_DIR_H  0
+ #define RESIZE_DIR_V  1
+ 
+ #define RESIZE_ALGO_BILINEAR 0
+ #define RESIZE_ALGO_AVERAGING 1
+ 
+ struct mx2_prp_cfg {
+       int channel;
+       u32 in_fmt;
+       u32 out_fmt;
+       u32 src_pixel;
+       u32 ch1_pixel;
+       u32 irq_flags;
+       u32 csicr1;
+ };
+ 
+ /* prp resizing parameters */
+ struct emma_prp_resize {
+       int             algo; /* type of algorithm used */
+       int             len; /* number of coefficients */
+       unsigned char   s[RESIZE_NUM_MAX]; /* table of coefficients */
+ };
+ 
+ /* prp configuration for a client-host fmt pair */
+ struct mx2_fmt_cfg {
+       enum v4l2_mbus_pixelcode        in_fmt;
+       u32                             out_fmt;
+       struct mx2_prp_cfg              cfg;
+ };
+ 
+ enum mx2_buffer_state {
+       MX2_STATE_QUEUED,
+       MX2_STATE_ACTIVE,
+       MX2_STATE_DONE,
+ };
+ 
+ struct mx2_buf_internal {
+       struct list_head        queue;
+       int                     bufnum;
+       bool                    discard;
+ };
+ 
+ /* buffer for one video frame */
+ struct mx2_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct vb2_buffer               vb;
+       enum mx2_buffer_state           state;
+       struct mx2_buf_internal         internal;
+ };
+ 
+ struct mx2_camera_dev {
+       struct device           *dev;
+       struct soc_camera_host  soc_host;
+       struct soc_camera_device *icd;
+       struct clk              *clk_csi, *clk_emma_ahb, *clk_emma_ipg;
+ 
+       void __iomem            *base_csi, *base_emma;
+ 
+       struct mx2_camera_platform_data *pdata;
+       unsigned long           platform_flags;
+ 
+       struct list_head        capture;
+       struct list_head        active_bufs;
+       struct list_head        discard;
+ 
+       spinlock_t              lock;
+ 
+       int                     dma;
+       struct mx2_buffer       *active;
+       struct mx2_buffer       *fb1_active;
+       struct mx2_buffer       *fb2_active;
+ 
+       u32                     csicr1;
+ 
+       struct mx2_buf_internal buf_discard[2];
+       void                    *discard_buffer;
+       dma_addr_t              discard_buffer_dma;
+       size_t                  discard_size;
+       struct mx2_fmt_cfg      *emma_prp;
+       struct emma_prp_resize  resizing[2];
+       unsigned int            s_width, s_height;
+       u32                     frame_count;
+       struct vb2_alloc_ctx    *alloc_ctx;
+ };
+ 
+ static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+ {
+       return container_of(int_buf, struct mx2_buffer, internal);
+ }
+ 
+ static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
+       /*
+        * This is a generic configuration which is valid for most
+        * prp input-output format combinations.
+        * We set the incomming and outgoing pixelformat to a
+        * 16 Bit wide format and adjust the bytesperline
+        * accordingly. With this configuration the inputdata
+        * will not be changed by the emma and could be any type
+        * of 16 Bit Pixelformat.
+        */
+       {
+               .in_fmt         = 0,
+               .out_fmt        = 0,
+               .cfg            = {
+                       .channel        = 1,
+                       .in_fmt         = PRP_CNTL_DATA_IN_RGB16,
+                       .out_fmt        = PRP_CNTL_CH1_OUT_RGB16,
+                       .src_pixel      = 0x2ca00565, /* RGB565 */
+                       .ch1_pixel      = 0x2ca00565, /* RGB565 */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+                                               PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+                       .csicr1         = 0,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_UYVY8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUYV,
+               .cfg            = {
+                       .channel        = 1,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH1_OUT_YUV422,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .ch1_pixel      = 0x62000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+                                               PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+                       .csicr1         = CSICR1_SWAP16_EN,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_YUYV8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUYV,
+               .cfg            = {
+                       .channel        = 1,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH1_OUT_YUV422,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .ch1_pixel      = 0x62000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+                                               PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+                       .csicr1         = CSICR1_PACK_DIR,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_YUYV8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUV420,
+               .cfg            = {
+                       .channel        = 2,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH2_OUT_YUV420,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+                                       PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+                                       PRP_INTR_CH2OVF,
+                       .csicr1         = CSICR1_PACK_DIR,
+               }
+       },
+       {
+               .in_fmt         = V4L2_MBUS_FMT_UYVY8_2X8,
+               .out_fmt        = V4L2_PIX_FMT_YUV420,
+               .cfg            = {
+                       .channel        = 2,
+                       .in_fmt         = PRP_CNTL_DATA_IN_YUV422,
+                       .out_fmt        = PRP_CNTL_CH2_OUT_YUV420,
+                       .src_pixel      = 0x22000888, /* YUV422 (YUYV) */
+                       .irq_flags      = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+                                       PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+                                       PRP_INTR_CH2OVF,
+                       .csicr1         = CSICR1_SWAP16_EN,
+               }
+       },
+ };
+ 
+ static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
+                                       enum v4l2_mbus_pixelcode in_fmt,
+                                       u32 out_fmt)
+ {
+       int i;
+ 
+       for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
+               if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
+                               (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
+                       return &mx27_emma_prp_table[i];
+               }
+       /* If no match return the most generic configuration */
+       return &mx27_emma_prp_table[0];
+ };
+ 
+ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+                                unsigned long phys, int bufnum)
+ {
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ 
+       if (prp->cfg.channel == 1) {
+               writel(phys, pcdev->base_emma +
+                               PRP_DEST_RGB1_PTR + 4 * bufnum);
+       } else {
+               writel(phys, pcdev->base_emma +
+                       PRP_DEST_Y_PTR - 0x14 * bufnum);
+               if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+                       u32 imgsize = pcdev->icd->user_height *
+                                       pcdev->icd->user_width;
+ 
+                       writel(phys + imgsize, pcdev->base_emma +
+                               PRP_DEST_CB_PTR - 0x14 * bufnum);
+                       writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+                               PRP_DEST_CR_PTR - 0x14 * bufnum);
+               }
+       }
+ }
+ 
+ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
+ {
+       unsigned long flags;
+ 
+       clk_disable_unprepare(pcdev->clk_csi);
+       writel(0, pcdev->base_csi + CSICR1);
+       if (cpu_is_mx27()) {
+               writel(0, pcdev->base_emma + PRP_CNTL);
+       } else if (cpu_is_mx25()) {
+               spin_lock_irqsave(&pcdev->lock, flags);
+               pcdev->fb1_active = NULL;
+               pcdev->fb2_active = NULL;
+               writel(0, pcdev->base_csi + CSIDMASA_FB1);
+               writel(0, pcdev->base_csi + CSIDMASA_FB2);
+               spin_unlock_irqrestore(&pcdev->lock, flags);
+       }
+ }
+ 
+ /*
+  * The following two functions absolutely depend on the fact, that
+  * there can be only one camera on mx2 camera sensor interface
+  */
+ static int mx2_camera_add_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       int ret;
+       u32 csicr1;
+ 
+       if (pcdev->icd)
+               return -EBUSY;
+ 
+       ret = clk_prepare_enable(pcdev->clk_csi);
+       if (ret < 0)
+               return ret;
+ 
+       csicr1 = CSICR1_MCLKEN;
+ 
+       if (cpu_is_mx27())
+               csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
+                       CSICR1_RXFF_LEVEL(0);
+ 
+       pcdev->csicr1 = csicr1;
+       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+ 
+       pcdev->icd = icd;
+       pcdev->frame_count = 0;
+ 
+       dev_info(icd->parent, "Camera driver attached to camera %d\n",
+                icd->devnum);
+ 
+       return 0;
+ }
+ 
+ static void mx2_camera_remove_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+ 
+       BUG_ON(icd != pcdev->icd);
+ 
+       dev_info(icd->parent, "Camera driver detached from camera %d\n",
+                icd->devnum);
+ 
+       mx2_camera_deactivate(pcdev);
+ 
+       pcdev->icd = NULL;
+ }
+ 
+ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
+               int state)
+ {
+       struct vb2_buffer *vb;
+       struct mx2_buffer *buf;
+       struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
+               &pcdev->fb2_active;
+       u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2;
+       unsigned long flags;
+ 
+       spin_lock_irqsave(&pcdev->lock, flags);
+ 
+       if (*fb_active == NULL)
+               goto out;
+ 
+       vb = &(*fb_active)->vb;
+       dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+ 
+       do_gettimeofday(&vb->v4l2_buf.timestamp);
+       vb->v4l2_buf.sequence++;
+       vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ 
+       if (list_empty(&pcdev->capture)) {
+               buf = NULL;
+               writel(0, pcdev->base_csi + fb_reg);
+       } else {
+               buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                               internal.queue);
+               vb = &buf->vb;
+               list_del(&buf->internal.queue);
+               buf->state = MX2_STATE_ACTIVE;
+               writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+                      pcdev->base_csi + fb_reg);
+       }
+ 
+       *fb_active = buf;
+ 
+ out:
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ 
+ static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
+ {
+       struct mx2_camera_dev *pcdev = data;
+       u32 status = readl(pcdev->base_csi + CSISR);
+ 
+       if (status & CSISR_DMA_TSF_FB1_INT)
+               mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
+       else if (status & CSISR_DMA_TSF_FB2_INT)
+               mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
+ 
+       /* FIXME: handle CSISR_RFF_OR_INT */
+ 
+       writel(status, pcdev->base_csi + CSISR);
+ 
+       return IRQ_HANDLED;
+ }
+ 
+ /*
+  *  Videobuf operations
+  */
+ static int mx2_videobuf_setup(struct vb2_queue *vq,
+                       const struct v4l2_format *fmt,
+                       unsigned int *count, unsigned int *num_planes,
+                       unsigned int sizes[], void *alloc_ctxs[])
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+ 
+       dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+ 
+       /* TODO: support for VIDIOC_CREATE_BUFS not ready */
+       if (fmt != NULL)
+               return -ENOTTY;
+ 
+       alloc_ctxs[0] = pcdev->alloc_ctx;
+ 
+       sizes[0] = icd->sizeimage;
+ 
+       if (0 == *count)
+               *count = 32;
+       if (!*num_planes &&
+           sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+               *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+ 
+       *num_planes = 1;
+ 
+       return 0;
+ }
+ 
+ static int mx2_videobuf_prepare(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       int ret = 0;
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+ 
+ #ifdef DEBUG
+       /*
+        * This can be useful if you want to see if we actually fill
+        * the buffer with something
+        */
+       memset((void *)vb2_plane_vaddr(vb, 0),
+              0xaa, vb2_get_plane_payload(vb, 0));
+ #endif
+ 
+       vb2_set_plane_payload(vb, 0, icd->sizeimage);
+       if (vb2_plane_vaddr(vb, 0) &&
+           vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+               ret = -EINVAL;
+               goto out;
+       }
+ 
+       return 0;
+ 
+ out:
+       return ret;
+ }
+ 
+ static void mx2_videobuf_queue(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+       unsigned long flags;
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+ 
+       spin_lock_irqsave(&pcdev->lock, flags);
+ 
+       buf->state = MX2_STATE_QUEUED;
+       list_add_tail(&buf->internal.queue, &pcdev->capture);
+ 
+       if (cpu_is_mx25()) {
+               u32 csicr3, dma_inten = 0;
+ 
+               if (pcdev->fb1_active == NULL) {
+                       writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+                                       pcdev->base_csi + CSIDMASA_FB1);
+                       pcdev->fb1_active = buf;
+                       dma_inten = CSICR1_FB1_DMA_INTEN;
+               } else if (pcdev->fb2_active == NULL) {
+                       writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+                                       pcdev->base_csi + CSIDMASA_FB2);
+                       pcdev->fb2_active = buf;
+                       dma_inten = CSICR1_FB2_DMA_INTEN;
+               }
+ 
+               if (dma_inten) {
+                       list_del(&buf->internal.queue);
+                       buf->state = MX2_STATE_ACTIVE;
+ 
+                       csicr3 = readl(pcdev->base_csi + CSICR3);
+ 
+                       /* Reflash DMA */
+                       writel(csicr3 | CSICR3_DMA_REFLASH_RFF,
+                                       pcdev->base_csi + CSICR3);
+ 
+                       /* clear & enable interrupts */
+                       writel(dma_inten, pcdev->base_csi + CSISR);
+                       pcdev->csicr1 |= dma_inten;
+                       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+ 
+                       /* enable DMA */
+                       csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1);
+                       writel(csicr3, pcdev->base_csi + CSICR3);
+               }
+       }
+ 
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ 
+ static void mx2_videobuf_release(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+       unsigned long flags;
+ 
+ #ifdef DEBUG
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+               vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+ 
+       switch (buf->state) {
+       case MX2_STATE_ACTIVE:
+               dev_info(icd->parent, "%s (active)\n", __func__);
+               break;
+       case MX2_STATE_QUEUED:
+               dev_info(icd->parent, "%s (queued)\n", __func__);
+               break;
+       default:
+               dev_info(icd->parent, "%s (unknown) %d\n", __func__,
+                               buf->state);
+               break;
+       }
+ #endif
+ 
+       /*
+        * Terminate only queued but inactive buffers. Active buffers are
+        * released when they become inactive after videobuf_waiton().
+        *
+        * FIXME: implement forced termination of active buffers for mx27 and
+        * mx27 eMMA, so that the user won't get stuck in an uninterruptible
+        * state. This requires a specific handling for each of the these DMA
+        * types.
+        */
+ 
+       spin_lock_irqsave(&pcdev->lock, flags);
+       if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
+               if (pcdev->fb1_active == buf) {
+                       pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
+                       writel(0, pcdev->base_csi + CSIDMASA_FB1);
+                       pcdev->fb1_active = NULL;
+               } else if (pcdev->fb2_active == buf) {
+                       pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
+                       writel(0, pcdev->base_csi + CSIDMASA_FB2);
+                       pcdev->fb2_active = NULL;
+               }
+               writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+       }
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ 
+ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+               int bytesperline)
+ {
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ 
+       writel((pcdev->s_width << 16) | pcdev->s_height,
+              pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+       writel(prp->cfg.src_pixel,
+              pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+       if (prp->cfg.channel == 1) {
+               writel((icd->user_width << 16) | icd->user_height,
+                       pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+               writel(bytesperline,
+                       pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+               writel(prp->cfg.ch1_pixel,
+                       pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+       } else { /* channel 2 */
+               writel((icd->user_width << 16) | icd->user_height,
+                       pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+       }
+ 
+       /* Enable interrupts */
+       writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+ }
+ 
+ static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
+ {
+       int dir;
+ 
+       for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+               unsigned char *s = pcdev->resizing[dir].s;
+               int len = pcdev->resizing[dir].len;
+               unsigned int coeff[2] = {0, 0};
+               unsigned int valid  = 0;
+               int i;
+ 
+               if (len == 0)
+                       continue;
+ 
+               for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+                       int j;
+ 
+                       j = i > 9 ? 1 : 0;
+                       coeff[j] = (coeff[j] << BC_COEF) |
+                                       (s[i] & (SZ_COEF - 1));
+ 
+                       if (i == 5 || i == 15)
+                               coeff[j] <<= 1;
+ 
+                       valid = (valid << 1) | (s[i] >> BC_COEF);
+               }
+ 
+               valid |= PRP_RZ_VALID_TBL_LEN(len);
+ 
+               if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+                       valid |= PRP_RZ_VALID_BILINEAR;
+ 
+               if (pcdev->emma_prp->cfg.channel == 1) {
+                       if (dir == RESIZE_DIR_H) {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH1_RZ_HORI_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH1_RZ_HORI_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH1_RZ_HORI_VALID);
+                       } else {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH1_RZ_VERT_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH1_RZ_VERT_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH1_RZ_VERT_VALID);
+                       }
+               } else {
+                       if (dir == RESIZE_DIR_H) {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH2_RZ_HORI_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH2_RZ_HORI_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH2_RZ_HORI_VALID);
+                       } else {
+                               writel(coeff[0], pcdev->base_emma +
+                                                       PRP_CH2_RZ_VERT_COEF1);
+                               writel(coeff[1], pcdev->base_emma +
+                                                       PRP_CH2_RZ_VERT_COEF2);
+                               writel(valid, pcdev->base_emma +
+                                                       PRP_CH2_RZ_VERT_VALID);
+                       }
+               }
+       }
+ }
+ 
+ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+       struct vb2_buffer *vb;
+       struct mx2_buffer *buf;
+       unsigned long phys;
+       int bytesperline;
+ 
+       if (cpu_is_mx27()) {
+               unsigned long flags;
+               if (count < 2)
+                       return -EINVAL;
+ 
+               spin_lock_irqsave(&pcdev->lock, flags);
+ 
+               buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                                      internal.queue);
+               buf->internal.bufnum = 0;
+               vb = &buf->vb;
+               buf->state = MX2_STATE_ACTIVE;
+ 
+               phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+               mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+               list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+ 
+               buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                                      internal.queue);
+               buf->internal.bufnum = 1;
+               vb = &buf->vb;
+               buf->state = MX2_STATE_ACTIVE;
+ 
+               phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+               mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+               list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+ 
+               bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+                               icd->current_fmt->host_fmt);
+               if (bytesperline < 0)
+                       return bytesperline;
+ 
+               /*
+                * I didn't manage to properly enable/disable the prp
+                * on a per frame basis during running transfers,
+                * thus we allocate a buffer here and use it to
+                * discard frames when no buffer is available.
+                * Feel free to work on this ;)
+                */
+               pcdev->discard_size = icd->user_height * bytesperline;
+               pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+                               pcdev->discard_size, &pcdev->discard_buffer_dma,
+                               GFP_KERNEL);
+               if (!pcdev->discard_buffer)
+                       return -ENOMEM;
+ 
+               pcdev->buf_discard[0].discard = true;
+               list_add_tail(&pcdev->buf_discard[0].queue,
+                                     &pcdev->discard);
+ 
+               pcdev->buf_discard[1].discard = true;
+               list_add_tail(&pcdev->buf_discard[1].queue,
+                                     &pcdev->discard);
+ 
+               mx2_prp_resize_commit(pcdev);
+ 
+               mx27_camera_emma_buf_init(icd, bytesperline);
+ 
+               if (prp->cfg.channel == 1) {
+                       writel(PRP_CNTL_CH1EN |
+                               PRP_CNTL_CSIEN |
+                               prp->cfg.in_fmt |
+                               prp->cfg.out_fmt |
+                               PRP_CNTL_CH1_LEN |
+                               PRP_CNTL_CH1BYP |
+                               PRP_CNTL_CH1_TSKIP(0) |
+                               PRP_CNTL_IN_TSKIP(0),
+                               pcdev->base_emma + PRP_CNTL);
+               } else {
+                       writel(PRP_CNTL_CH2EN |
+                               PRP_CNTL_CSIEN |
+                               prp->cfg.in_fmt |
+                               prp->cfg.out_fmt |
+                               PRP_CNTL_CH2_LEN |
+                               PRP_CNTL_CH2_TSKIP(0) |
+                               PRP_CNTL_IN_TSKIP(0),
+                               pcdev->base_emma + PRP_CNTL);
+               }
+               spin_unlock_irqrestore(&pcdev->lock, flags);
+       }
+ 
+       return 0;
+ }
+ 
+ static int mx2_stop_streaming(struct vb2_queue *q)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+       struct soc_camera_host *ici =
+               to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+       unsigned long flags;
+       void *b;
+       u32 cntl;
+ 
+       if (cpu_is_mx27()) {
+               spin_lock_irqsave(&pcdev->lock, flags);
+ 
+               cntl = readl(pcdev->base_emma + PRP_CNTL);
+               if (prp->cfg.channel == 1) {
+                       writel(cntl & ~PRP_CNTL_CH1EN,
+                              pcdev->base_emma + PRP_CNTL);
+               } else {
+                       writel(cntl & ~PRP_CNTL_CH2EN,
+                              pcdev->base_emma + PRP_CNTL);
+               }
+               INIT_LIST_HEAD(&pcdev->capture);
+               INIT_LIST_HEAD(&pcdev->active_bufs);
+               INIT_LIST_HEAD(&pcdev->discard);
+ 
+               b = pcdev->discard_buffer;
+               pcdev->discard_buffer = NULL;
+ 
+               spin_unlock_irqrestore(&pcdev->lock, flags);
+ 
+               dma_free_coherent(ici->v4l2_dev.dev,
+                       pcdev->discard_size, b, pcdev->discard_buffer_dma);
+       }
+ 
+       return 0;
+ }
+ 
+ static struct vb2_ops mx2_videobuf_ops = {
+       .queue_setup     = mx2_videobuf_setup,
+       .buf_prepare     = mx2_videobuf_prepare,
+       .buf_queue       = mx2_videobuf_queue,
+       .buf_cleanup     = mx2_videobuf_release,
+       .start_streaming = mx2_start_streaming,
+       .stop_streaming  = mx2_stop_streaming,
+ };
+ 
+ static int mx2_camera_init_videobuf(struct vb2_queue *q,
+                             struct soc_camera_device *icd)
+ {
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = icd;
+       q->ops = &mx2_videobuf_ops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct mx2_buffer);
+ 
+       return vb2_queue_init(q);
+ }
+ 
+ #define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+                       V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+                       V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+                       V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+                       V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+                       V4L2_MBUS_PCLK_SAMPLE_RISING | \
+                       V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+                       V4L2_MBUS_DATA_ACTIVE_HIGH | \
+                       V4L2_MBUS_DATA_ACTIVE_LOW)
+ 
+ static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+ {
+       u32 cntl;
+       int count = 0;
+ 
+       cntl = readl(pcdev->base_emma + PRP_CNTL);
+       writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+       while (count++ < 100) {
+               if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+                       return 0;
+               barrier();
+               udelay(1);
+       }
+ 
+       return -ETIMEDOUT;
+ }
+ 
+ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long common_flags;
+       int ret;
+       int bytesperline;
+       u32 csicr1 = pcdev->csicr1;
+ 
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%x\n",
+                                cfg.flags, MX2_BUS_FLAGS);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       } else {
+               common_flags = MX2_BUS_FLAGS;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+               if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+               else
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+           (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+               if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+               else
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+       }
+ 
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+                       common_flags, ret);
+               return ret;
+       }
+ 
+       csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
+ 
+       if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+               csicr1 |= CSICR1_REDGE;
+       if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+               csicr1 |= CSICR1_SOF_POL;
+       if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+               csicr1 |= CSICR1_HSYNC_POL;
+       if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
+               csicr1 |= CSICR1_EXT_VSYNC;
+       if (pcdev->platform_flags & MX2_CAMERA_CCIR)
+               csicr1 |= CSICR1_CCIR_EN;
+       if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
+               csicr1 |= CSICR1_CCIR_MODE;
+       if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
+               csicr1 |= CSICR1_GCLK_MODE;
+       if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
+               csicr1 |= CSICR1_INV_DATA;
+ 
+       pcdev->csicr1 = csicr1;
+ 
+       bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+                       icd->current_fmt->host_fmt);
+       if (bytesperline < 0)
+               return bytesperline;
+ 
+       if (cpu_is_mx27()) {
+               ret = mx27_camera_emma_prp_reset(pcdev);
+               if (ret)
+                       return ret;
+       } else if (cpu_is_mx25()) {
+               writel((bytesperline * icd->user_height) >> 2,
+                               pcdev->base_csi + CSIRXCNT);
+               writel((bytesperline << 16) | icd->user_height,
+                               pcdev->base_csi + CSIIMAG_PARA);
+       }
+ 
+       writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+ 
+       return 0;
+ }
+ 
+ static int mx2_camera_set_crop(struct soc_camera_device *icd,
+                               struct v4l2_crop *a)
+ {
+       struct v4l2_rect *rect = &a->c;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+ 
+       soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+       soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+ 
+       ret = v4l2_subdev_call(sd, video, s_crop, a);
+       if (ret < 0)
+               return ret;
+ 
+       /* The capture device might have changed its output  */
+       ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+               mf.width, mf.height);
+ 
+       icd->user_width         = mf.width;
+       icd->user_height        = mf.height;
+ 
+       return ret;
+ }
+ 
+ static int mx2_camera_get_formats(struct soc_camera_device *icd,
+                                 unsigned int idx,
+                                 struct soc_camera_format_xlate *xlate)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_mbus_pixelfmt *fmt;
+       struct device *dev = icd->parent;
+       enum v4l2_mbus_pixelcode code;
+       int ret, formats = 0;
+ 
+       ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+       if (ret < 0)
+               /* no more formats */
+               return 0;
+ 
+       fmt = soc_mbus_get_fmtdesc(code);
+       if (!fmt) {
+               dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
+               return 0;
+       }
+ 
+       if (code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+           code == V4L2_MBUS_FMT_UYVY8_2X8) {
+               formats++;
+               if (xlate) {
+                       /*
+                        * CH2 can output YUV420 which is a standard format in
+                        * soc_mediabus.c
+                        */
+                       xlate->host_fmt =
+                               soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_1_5X8);
+                       xlate->code     = code;
+                       dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+                              xlate->host_fmt->name, code);
+                       xlate++;
+               }
+       }
+ 
+       if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt =
+                               soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8);
+                       xlate->code     = code;
+                       dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+                               xlate->host_fmt->name, code);
+                       xlate++;
+               }
+       }
+ 
+       /* Generic pass-trough */
+       formats++;
+       if (xlate) {
+               xlate->host_fmt = fmt;
+               xlate->code     = code;
+               xlate++;
+       }
+       return formats;
+ }
+ 
+ static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+                             struct v4l2_mbus_framefmt *mf_in,
+                             struct v4l2_pix_format *pix_out, bool apply)
+ {
+       int num, den;
+       unsigned long m;
+       int i, dir;
+ 
+       for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+               struct emma_prp_resize tmprsz;
+               unsigned char *s = tmprsz.s;
+               int len = 0;
+               int in, out;
+ 
+               if (dir == RESIZE_DIR_H) {
+                       in = mf_in->width;
+                       out = pix_out->width;
+               } else {
+                       in = mf_in->height;
+                       out = pix_out->height;
+               }
+ 
+               if (in < out)
+                       return -EINVAL;
+               else if (in == out)
+                       continue;
+ 
+               /* Calculate ratio */
+               m = gcd(in, out);
+               num = in / m;
+               den = out / m;
+               if (num > RESIZE_NUM_MAX)
+                       return -EINVAL;
+ 
+               if ((num >= 2 * den) && (den == 1) &&
+                   (num < 9) && (!(num & 0x01))) {
+                       int sum = 0;
+                       int j;
+ 
+                       /* Average scaling for >= 2:1 ratios */
+                       /* Support can be added for num >=9 and odd values */
+ 
+                       tmprsz.algo = RESIZE_ALGO_AVERAGING;
+                       len = num;
+ 
+                       for (i = 0; i < (len / 2); i++)
+                               s[i] = 8;
+ 
+                       do {
+                               for (i = 0; i < (len / 2); i++) {
+                                       s[i] = s[i] >> 1;
+                                       sum = 0;
+                                       for (j = 0; j < (len / 2); j++)
+                                               sum += s[j];
+                                       if (sum == 4)
+                                               break;
+                               }
+                       } while (sum != 4);
+ 
+                       for (i = (len / 2); i < len; i++)
+                               s[i] = s[len - i - 1];
+ 
+                       s[len - 1] |= SZ_COEF;
+               } else {
+                       /* bilinear scaling for < 2:1 ratios */
+                       int v; /* overflow counter */
+                       int coeff, nxt; /* table output */
+                       int in_pos_inc = 2 * den;
+                       int out_pos = num;
+                       int out_pos_inc = 2 * num;
+                       int init_carry = num - den;
+                       int carry = init_carry;
+ 
+                       tmprsz.algo = RESIZE_ALGO_BILINEAR;
+                       v = den + in_pos_inc;
+                       do {
+                               coeff = v - out_pos;
+                               out_pos += out_pos_inc;
+                               carry += out_pos_inc;
+                               for (nxt = 0; v < out_pos; nxt++) {
+                                       v += in_pos_inc;
+                                       carry -= in_pos_inc;
+                               }
+ 
+                               if (len > RESIZE_NUM_MAX)
+                                       return -EINVAL;
+ 
+                               coeff = ((coeff << BC_COEF) +
+                                       (in_pos_inc >> 1)) / in_pos_inc;
+ 
+                               if (coeff >= (SZ_COEF - 1))
+                                       coeff--;
+ 
+                               coeff |= SZ_COEF;
+                               s[len] = (unsigned char)coeff;
+                               len++;
+ 
+                               for (i = 1; i < nxt; i++) {
+                                       if (len >= RESIZE_NUM_MAX)
+                                               return -EINVAL;
+                                       s[len] = 0;
+                                       len++;
+                               }
+                       } while (carry != init_carry);
+               }
+               tmprsz.len = len;
+               if (dir == RESIZE_DIR_H)
+                       mf_in->width = pix_out->width;
+               else
+                       mf_in->height = pix_out->height;
+ 
+               if (apply)
+                       memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+       }
+       return 0;
+ }
+ 
+ static int mx2_camera_set_fmt(struct soc_camera_device *icd,
+                              struct v4l2_format *f)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+ 
+       dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n",
+                               pix->pixelformat);
+               return -EINVAL;
+       }
+ 
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+       if (ret < 0 && ret != -ENOIOCTLCMD)
+               return ret;
+ 
+       /* Store width and height returned by the sensor for resizing */
+       pcdev->s_width = mf.width;
+       pcdev->s_height = mf.height;
+       dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+               __func__, pcdev->s_width, pcdev->s_height);
+ 
+       pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+                                                  xlate->host_fmt->fourcc);
+ 
+       memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+       if ((mf.width != pix->width || mf.height != pix->height) &&
+               pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+               if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
+                       dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+       }
+ 
+       if (mf.code != xlate->code)
+               return -EINVAL;
+ 
+       pix->width              = mf.width;
+       pix->height             = mf.height;
+       pix->field              = mf.field;
+       pix->colorspace         = mf.colorspace;
+       icd->current_fmt        = xlate;
+ 
+       dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+ 
+       return 0;
+ }
+ 
+ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
+                                 struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       __u32 pixfmt = pix->pixelformat;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx2_camera_dev *pcdev = ici->priv;
+       unsigned int width_limit;
+       int ret;
+ 
+       dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (pixfmt && !xlate) {
+               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+ 
+       /* FIXME: implement MX27 limits */
+ 
+       /* limit to MX25 hardware capabilities */
+       if (cpu_is_mx25()) {
+               if (xlate->host_fmt->bits_per_sample <= 8)
+                       width_limit = 0xffff * 4;
+               else
+                       width_limit = 0xffff * 2;
+               /* CSIIMAG_PARA limit */
+               if (pix->width > width_limit)
+                       pix->width = width_limit;
+               if (pix->height > 0xffff)
+                       pix->height = 0xffff;
+ 
+               pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+                               xlate->host_fmt);
+               if (pix->bytesperline < 0)
+                       return pix->bytesperline;
+               pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+                                               pix->bytesperline, pix->height);
+               /* Check against the CSIRXCNT limit */
+               if (pix->sizeimage > 4 * 0x3ffff) {
+                       /* Adjust geometry, preserve aspect ratio */
+                       unsigned int new_height = int_sqrt(div_u64(0x3ffffULL *
+                                       4 * pix->height, pix->bytesperline));
+                       pix->width = new_height * pix->width / pix->height;
+                       pix->height = new_height;
+                       pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+                                                       xlate->host_fmt);
+                       BUG_ON(pix->bytesperline < 0);
+                       pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+                                               pix->bytesperline, pix->height);
+               }
+       }
+ 
+       /* limit to sensor capabilities */
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+               __func__, pcdev->s_width, pcdev->s_height);
+ 
+       /* If the sensor does not support image size try PrP resizing */
+       pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+                                                  xlate->host_fmt->fourcc);
+ 
+       memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+       if ((mf.width != pix->width || mf.height != pix->height) &&
+               pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+               if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
+                       dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+       }
+ 
+       if (mf.field == V4L2_FIELD_ANY)
+               mf.field = V4L2_FIELD_NONE;
+       /*
+        * Driver supports interlaced images provided they have
+        * both fields so that they can be processed as if they
+        * were progressive.
+        */
+       if (mf.field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf.field)) {
+               dev_err(icd->parent, "Field type %d unsupported.\n",
+                               mf.field);
+               return -EINVAL;
+       }
+ 
+       pix->width      = mf.width;
+       pix->height     = mf.height;
+       pix->field      = mf.field;
+       pix->colorspace = mf.colorspace;
+ 
+       dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+               __func__, pix->width, pix->height);
+ 
+       return 0;
+ }
+ 
+ static int mx2_camera_querycap(struct soc_camera_host *ici,
+                              struct v4l2_capability *cap)
+ {
+       /* cap->name is set by the friendly caller:-> */
+       strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ 
+       return 0;
+ }
+ 
+ static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
+ {
+       struct soc_camera_device *icd = file->private_data;
+ 
+       return vb2_poll(&icd->vb2_vidq, file, pt);
+ }
+ 
+ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = mx2_camera_add_device,
+       .remove         = mx2_camera_remove_device,
+       .set_fmt        = mx2_camera_set_fmt,
+       .set_crop       = mx2_camera_set_crop,
+       .get_formats    = mx2_camera_get_formats,
+       .try_fmt        = mx2_camera_try_fmt,
+       .init_videobuf2 = mx2_camera_init_videobuf,
+       .poll           = mx2_camera_poll,
+       .querycap       = mx2_camera_querycap,
+       .set_bus_param  = mx2_camera_set_bus_param,
+ };
+ 
+ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
+               int bufnum, bool err)
+ {
+ #ifdef DEBUG
+       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ #endif
+       struct mx2_buf_internal *ibuf;
+       struct mx2_buffer *buf;
+       struct vb2_buffer *vb;
+       unsigned long phys;
+ 
+       ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+                              queue);
+ 
+       BUG_ON(ibuf->bufnum != bufnum);
+ 
+       if (ibuf->discard) {
+               /*
+                * Discard buffer must not be returned to user space.
+                * Just return it to the discard queue.
+                */
+               list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+       } else {
+               buf = mx2_ibuf_to_buf(ibuf);
+ 
+               vb = &buf->vb;
+ #ifdef DEBUG
+               phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+               if (prp->cfg.channel == 1) {
+                       if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
+                               4 * bufnum) != phys) {
+                               dev_err(pcdev->dev, "%lx != %x\n", phys,
+                                       readl(pcdev->base_emma +
+                                       PRP_DEST_RGB1_PTR + 4 * bufnum));
+                       }
+               } else {
+                       if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
+                               0x14 * bufnum) != phys) {
+                               dev_err(pcdev->dev, "%lx != %x\n", phys,
+                                       readl(pcdev->base_emma +
+                                       PRP_DEST_Y_PTR - 0x14 * bufnum));
+                       }
+               }
+ #endif
+               dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+                               vb2_plane_vaddr(vb, 0),
+                               vb2_get_plane_payload(vb, 0));
+ 
+               list_del_init(&buf->internal.queue);
+               do_gettimeofday(&vb->v4l2_buf.timestamp);
+               vb->v4l2_buf.sequence = pcdev->frame_count;
+               if (err)
+                       vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+               else
+                       vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+       }
+ 
+       pcdev->frame_count++;
+ 
+       if (list_empty(&pcdev->capture)) {
+               if (list_empty(&pcdev->discard)) {
+                       dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+                                __func__);
+                       return;
+               }
+ 
+               ibuf = list_first_entry(&pcdev->discard,
+                                       struct mx2_buf_internal, queue);
+               ibuf->bufnum = bufnum;
+ 
+               list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+               mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
+               return;
+       }
+ 
+       buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+                              internal.queue);
+ 
+       buf->internal.bufnum = bufnum;
+ 
+       list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+ 
+       vb = &buf->vb;
+       buf->state = MX2_STATE_ACTIVE;
+ 
+       phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+       mx27_update_emma_buf(pcdev, phys, bufnum);
+ }
+ 
+ static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
+ {
+       struct mx2_camera_dev *pcdev = data;
+       unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
+       struct mx2_buf_internal *ibuf;
+ 
+       spin_lock(&pcdev->lock);
+ 
+       if (list_empty(&pcdev->active_bufs)) {
+               dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+                       __func__);
+ 
+               if (!status) {
+                       spin_unlock(&pcdev->lock);
+                       return IRQ_NONE;
+               }
+       }
+ 
+       if (status & (1 << 7)) { /* overflow */
+               u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
+               writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
+                      pcdev->base_emma + PRP_CNTL);
+               writel(cntl, pcdev->base_emma + PRP_CNTL);
+ 
+               ibuf = list_first_entry(&pcdev->active_bufs,
+                                       struct mx2_buf_internal, queue);
+               mx27_camera_frame_done_emma(pcdev,
+                                       ibuf->bufnum, true);
+ 
+               status &= ~(1 << 7);
+       } else if (((status & (3 << 5)) == (3 << 5)) ||
+               ((status & (3 << 3)) == (3 << 3))) {
+               /*
+                * Both buffers have triggered, process the one we're expecting
+                * to first
+                */
+               ibuf = list_first_entry(&pcdev->active_bufs,
+                                       struct mx2_buf_internal, queue);
+               mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+               status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+       } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+               mx27_camera_frame_done_emma(pcdev, 0, false);
+       } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+               mx27_camera_frame_done_emma(pcdev, 1, false);
+       }
+ 
+       spin_unlock(&pcdev->lock);
+       writel(status, pcdev->base_emma + PRP_INTRSTATUS);
+ 
+       return IRQ_HANDLED;
+ }
+ 
+ static int __devinit mx27_camera_emma_init(struct platform_device *pdev)
+ {
+       struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev);
+       struct resource *res_emma;
+       int irq_emma;
+       int err = 0;
+ 
+       res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       irq_emma = platform_get_irq(pdev, 1);
+       if (!res_emma || !irq_emma) {
+               dev_err(pcdev->dev, "no EMMA resources\n");
+               goto out;
+       }
+ 
+       pcdev->base_emma = devm_request_and_ioremap(pcdev->dev, res_emma);
+       if (!pcdev->base_emma) {
+               err = -EADDRNOTAVAIL;
+               goto out;
+       }
+ 
+       err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
+                              MX2_CAM_DRV_NAME, pcdev);
+       if (err) {
+               dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n");
+               goto out;
+       }
+ 
+       pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg");
+       if (IS_ERR(pcdev->clk_emma_ipg)) {
+               err = PTR_ERR(pcdev->clk_emma_ipg);
+               goto out;
+       }
+ 
+       clk_prepare_enable(pcdev->clk_emma_ipg);
+ 
+       pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb");
+       if (IS_ERR(pcdev->clk_emma_ahb)) {
+               err = PTR_ERR(pcdev->clk_emma_ahb);
+               goto exit_clk_emma_ipg;
+       }
+ 
+       clk_prepare_enable(pcdev->clk_emma_ahb);
+ 
+       err = mx27_camera_emma_prp_reset(pcdev);
+       if (err)
+               goto exit_clk_emma_ahb;
+ 
+       return err;
+ 
+ exit_clk_emma_ahb:
+       clk_disable_unprepare(pcdev->clk_emma_ahb);
+ exit_clk_emma_ipg:
+       clk_disable_unprepare(pcdev->clk_emma_ipg);
+ out:
+       return err;
+ }
+ 
+ static int __devinit mx2_camera_probe(struct platform_device *pdev)
+ {
+       struct mx2_camera_dev *pcdev;
+       struct resource *res_csi;
+       int irq_csi;
+       int err = 0;
+ 
+       dev_dbg(&pdev->dev, "initialising\n");
+ 
+       res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq_csi = platform_get_irq(pdev, 0);
+       if (res_csi == NULL || irq_csi < 0) {
+               dev_err(&pdev->dev, "Missing platform resources data\n");
+               err = -ENODEV;
+               goto exit;
+       }
+ 
+       pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+       if (!pcdev) {
+               dev_err(&pdev->dev, "Could not allocate pcdev\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+ 
+       pcdev->clk_csi = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(pcdev->clk_csi)) {
+               dev_err(&pdev->dev, "Could not get csi clock\n");
+               err = PTR_ERR(pcdev->clk_csi);
+               goto exit;
+       }
+ 
+       pcdev->pdata = pdev->dev.platform_data;
+       if (pcdev->pdata) {
+               long rate;
+ 
+               pcdev->platform_flags = pcdev->pdata->flags;
+ 
+               rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2);
+               if (rate <= 0) {
+                       err = -ENODEV;
+                       goto exit;
+               }
+               err = clk_set_rate(pcdev->clk_csi, rate);
+               if (err < 0)
+                       goto exit;
+       }
+ 
+       INIT_LIST_HEAD(&pcdev->capture);
+       INIT_LIST_HEAD(&pcdev->active_bufs);
+       INIT_LIST_HEAD(&pcdev->discard);
+       spin_lock_init(&pcdev->lock);
+ 
+       pcdev->base_csi = devm_request_and_ioremap(&pdev->dev, res_csi);
+       if (!pcdev->base_csi) {
+               err = -EADDRNOTAVAIL;
+               goto exit;
+       }
+ 
+       pcdev->dev = &pdev->dev;
+       platform_set_drvdata(pdev, pcdev);
+ 
+       if (cpu_is_mx25()) {
+               err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0,
+                                      MX2_CAM_DRV_NAME, pcdev);
+               if (err) {
+                       dev_err(pcdev->dev, "Camera interrupt register failed \n");
+                       goto exit;
+               }
+       }
+ 
+       if (cpu_is_mx27()) {
+               err = mx27_camera_emma_init(pdev);
+               if (err)
+                       goto exit;
+       }
+ 
+       /*
+        * We're done with drvdata here.  Clear the pointer so that
+        * v4l2 core can start using drvdata on its purpose.
+        */
+       platform_set_drvdata(pdev, NULL);
+ 
+       pcdev->soc_host.drv_name        = MX2_CAM_DRV_NAME,
+       pcdev->soc_host.ops             = &mx2_soc_camera_host_ops,
+       pcdev->soc_host.priv            = pcdev;
+       pcdev->soc_host.v4l2_dev.dev    = &pdev->dev;
+       pcdev->soc_host.nr              = pdev->id;
+       if (cpu_is_mx25())
+               pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE;
+ 
+       pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(pcdev->alloc_ctx)) {
+               err = PTR_ERR(pcdev->alloc_ctx);
+               goto eallocctx;
+       }
+       err = soc_camera_host_register(&pcdev->soc_host);
+       if (err)
+               goto exit_free_emma;
+ 
+       dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+                       clk_get_rate(pcdev->clk_csi));
+ 
+       return 0;
+ 
+ exit_free_emma:
+       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ eallocctx:
+       if (cpu_is_mx27()) {
+               clk_disable_unprepare(pcdev->clk_emma_ipg);
+               clk_disable_unprepare(pcdev->clk_emma_ahb);
+       }
+ exit:
+       return err;
+ }
+ 
+ static int __devexit mx2_camera_remove(struct platform_device *pdev)
+ {
+       struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+       struct mx2_camera_dev *pcdev = container_of(soc_host,
+                       struct mx2_camera_dev, soc_host);
+ 
+       soc_camera_host_unregister(&pcdev->soc_host);
+ 
+       vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ 
+       if (cpu_is_mx27()) {
+               clk_disable_unprepare(pcdev->clk_emma_ipg);
+               clk_disable_unprepare(pcdev->clk_emma_ahb);
+       }
+ 
+       dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
+ 
+       return 0;
+ }
+ 
+ static struct platform_driver mx2_camera_driver = {
+       .driver         = {
+               .name   = MX2_CAM_DRV_NAME,
+       },
+       .remove         = __devexit_p(mx2_camera_remove),
+ };
+ 
+ 
+ static int __init mx2_camera_init(void)
+ {
+       return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe);
+ }
+ 
+ static void __exit mx2_camera_exit(void)
+ {
+       return platform_driver_unregister(&mx2_camera_driver);
+ }
+ 
+ module_init(mx2_camera_init);
+ module_exit(mx2_camera_exit);
+ 
+ MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
+ MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(MX2_CAM_VERSION);
 
--- /dev/null
 -#include <mach/mx3_camera.h>
 -#include <mach/dma.h>
+ /*
+  * V4L2 Driver for i.MX3x camera host
+  *
+  * Copyright (C) 2008
+  * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/videodev2.h>
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+ #include <linux/vmalloc.h>
+ #include <linux/interrupt.h>
+ #include <linux/sched.h>
+ 
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-dev.h>
+ #include <media/videobuf2-dma-contig.h>
+ #include <media/soc_camera.h>
+ #include <media/soc_mediabus.h>
+ 
+ #include <mach/ipu.h>
++#include <linux/platform_data/camera-mx3.h>
++#include <linux/platform_data/dma-imx.h>
+ 
+ #define MX3_CAM_DRV_NAME "mx3-camera"
+ 
+ /* CMOS Sensor Interface Registers */
+ #define CSI_REG_START         0x60
+ 
+ #define CSI_SENS_CONF         (0x60 - CSI_REG_START)
+ #define CSI_SENS_FRM_SIZE     (0x64 - CSI_REG_START)
+ #define CSI_ACT_FRM_SIZE      (0x68 - CSI_REG_START)
+ #define CSI_OUT_FRM_CTRL      (0x6C - CSI_REG_START)
+ #define CSI_TST_CTRL          (0x70 - CSI_REG_START)
+ #define CSI_CCIR_CODE_1               (0x74 - CSI_REG_START)
+ #define CSI_CCIR_CODE_2               (0x78 - CSI_REG_START)
+ #define CSI_CCIR_CODE_3               (0x7C - CSI_REG_START)
+ #define CSI_FLASH_STROBE_1    (0x80 - CSI_REG_START)
+ #define CSI_FLASH_STROBE_2    (0x84 - CSI_REG_START)
+ 
+ #define CSI_SENS_CONF_VSYNC_POL_SHIFT         0
+ #define CSI_SENS_CONF_HSYNC_POL_SHIFT         1
+ #define CSI_SENS_CONF_DATA_POL_SHIFT          2
+ #define CSI_SENS_CONF_PIX_CLK_POL_SHIFT               3
+ #define CSI_SENS_CONF_SENS_PRTCL_SHIFT                4
+ #define CSI_SENS_CONF_SENS_CLKSRC_SHIFT               7
+ #define CSI_SENS_CONF_DATA_FMT_SHIFT          8
+ #define CSI_SENS_CONF_DATA_WIDTH_SHIFT                10
+ #define CSI_SENS_CONF_EXT_VSYNC_SHIFT         15
+ #define CSI_SENS_CONF_DIVRATIO_SHIFT          16
+ 
+ #define CSI_SENS_CONF_DATA_FMT_RGB_YUV444     (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+ #define CSI_SENS_CONF_DATA_FMT_YUV422         (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+ #define CSI_SENS_CONF_DATA_FMT_BAYER          (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+ 
+ #define MAX_VIDEO_MEM 16
+ 
+ struct mx3_camera_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct vb2_buffer                       vb;
+       struct list_head                        queue;
+ 
+       /* One descriptot per scatterlist (per frame) */
+       struct dma_async_tx_descriptor          *txd;
+ 
+       /* We have to "build" a scatterlist ourselves - one element per frame */
+       struct scatterlist                      sg;
+ };
+ 
+ /**
+  * struct mx3_camera_dev - i.MX3x camera (CSI) object
+  * @dev:              camera device, to which the coherent buffer is attached
+  * @icd:              currently attached camera sensor
+  * @clk:              pointer to clock
+  * @base:             remapped register base address
+  * @pdata:            platform data
+  * @platform_flags:   platform flags
+  * @mclk:             master clock frequency in Hz
+  * @capture:          list of capture videobuffers
+  * @lock:             protects video buffer lists
+  * @active:           active video buffer
+  * @idmac_channel:    array of pointers to IPU DMAC DMA channels
+  * @soc_host:         embedded soc_host object
+  */
+ struct mx3_camera_dev {
+       /*
+        * i.MX3x is only supposed to handle one camera on its Camera Sensor
+        * Interface. If anyone ever builds hardware to enable more than one
+        * camera _simultaneously_, they will have to modify this driver too
+        */
+       struct soc_camera_device *icd;
+       struct clk              *clk;
+ 
+       void __iomem            *base;
+ 
+       struct mx3_camera_pdata *pdata;
+ 
+       unsigned long           platform_flags;
+       unsigned long           mclk;
+       u16                     width_flags;    /* max 15 bits */
+ 
+       struct list_head        capture;
+       spinlock_t              lock;           /* Protects video buffer lists */
+       struct mx3_camera_buffer *active;
+       size_t                  buf_total;
+       struct vb2_alloc_ctx    *alloc_ctx;
+       enum v4l2_field         field;
+       int                     sequence;
+ 
+       /* IDMAC / dmaengine interface */
+       struct idmac_channel    *idmac_channel[1];      /* We need one channel */
+ 
+       struct soc_camera_host  soc_host;
+ };
+ 
+ struct dma_chan_request {
+       struct mx3_camera_dev   *mx3_cam;
+       enum ipu_channel        id;
+ };
+ 
+ static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
+ {
+       return __raw_readl(mx3->base + reg);
+ }
+ 
+ static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
+ {
+       __raw_writel(value, mx3->base + reg);
+ }
+ 
+ static struct mx3_camera_buffer *to_mx3_vb(struct vb2_buffer *vb)
+ {
+       return container_of(vb, struct mx3_camera_buffer, vb);
+ }
+ 
+ /* Called from the IPU IDMAC ISR */
+ static void mx3_cam_dma_done(void *arg)
+ {
+       struct idmac_tx_desc *desc = to_tx_desc(arg);
+       struct dma_chan *chan = desc->txd.chan;
+       struct idmac_channel *ichannel = to_idmac_chan(chan);
+       struct mx3_camera_dev *mx3_cam = ichannel->client;
+ 
+       dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
+               desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0);
+ 
+       spin_lock(&mx3_cam->lock);
+       if (mx3_cam->active) {
+               struct vb2_buffer *vb = &mx3_cam->active->vb;
+               struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ 
+               list_del_init(&buf->queue);
+               do_gettimeofday(&vb->v4l2_buf.timestamp);
+               vb->v4l2_buf.field = mx3_cam->field;
+               vb->v4l2_buf.sequence = mx3_cam->sequence++;
+               vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+       }
+ 
+       if (list_empty(&mx3_cam->capture)) {
+               mx3_cam->active = NULL;
+               spin_unlock(&mx3_cam->lock);
+ 
+               /*
+                * stop capture - without further buffers IPU_CHA_BUF0_RDY will
+                * not get updated
+                */
+               return;
+       }
+ 
+       mx3_cam->active = list_entry(mx3_cam->capture.next,
+                                    struct mx3_camera_buffer, queue);
+       spin_unlock(&mx3_cam->lock);
+ }
+ 
+ /*
+  * Videobuf operations
+  */
+ 
+ /*
+  * Calculate the __buffer__ (not data) size and number of buffers.
+  */
+ static int mx3_videobuf_setup(struct vb2_queue *vq,
+                       const struct v4l2_format *fmt,
+                       unsigned int *count, unsigned int *num_planes,
+                       unsigned int sizes[], void *alloc_ctxs[])
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+ 
+       if (!mx3_cam->idmac_channel[0])
+               return -EINVAL;
+ 
+       if (fmt) {
+               const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+                                                               fmt->fmt.pix.pixelformat);
+               unsigned int bytes_per_line;
+               int ret;
+ 
+               if (!xlate)
+                       return -EINVAL;
+ 
+               ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+                                             xlate->host_fmt);
+               if (ret < 0)
+                       return ret;
+ 
+               bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+ 
+               ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+                                         fmt->fmt.pix.height);
+               if (ret < 0)
+                       return ret;
+ 
+               sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
+       } else {
+               /* Called from VIDIOC_REQBUFS or in compatibility mode */
+               sizes[0] = icd->sizeimage;
+       }
+ 
+       alloc_ctxs[0] = mx3_cam->alloc_ctx;
+ 
+       if (!vq->num_buffers)
+               mx3_cam->sequence = 0;
+ 
+       if (!*count)
+               *count = 2;
+ 
+       /* If *num_planes != 0, we have already verified *count. */
+       if (!*num_planes &&
+           sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+               *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+                       sizes[0];
+ 
+       *num_planes = 1;
+ 
+       return 0;
+ }
+ 
+ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
+ {
+       /* Add more formats as need arises and test possibilities appear... */
+       switch (fourcc) {
+       case V4L2_PIX_FMT_RGB24:
+               return IPU_PIX_FMT_RGB24;
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_RGB565:
+       default:
+               return IPU_PIX_FMT_GENERIC;
+       }
+ }
+ 
+ static void mx3_videobuf_queue(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+       struct scatterlist *sg = &buf->sg;
+       struct dma_async_tx_descriptor *txd;
+       struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+       struct idmac_video_param *video = &ichan->params.video;
+       const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+       unsigned long flags;
+       dma_cookie_t cookie;
+       size_t new_size;
+ 
+       new_size = icd->sizeimage;
+ 
+       if (vb2_plane_size(vb, 0) < new_size) {
+               dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+                       vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+               goto error;
+       }
+ 
+       if (!buf->txd) {
+               sg_dma_address(sg)      = vb2_dma_contig_plane_dma_addr(vb, 0);
+               sg_dma_len(sg)          = new_size;
+ 
+               txd = dmaengine_prep_slave_sg(
+                       &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
+                       DMA_PREP_INTERRUPT);
+               if (!txd)
+                       goto error;
+ 
+               txd->callback_param     = txd;
+               txd->callback           = mx3_cam_dma_done;
+ 
+               buf->txd                = txd;
+       } else {
+               txd = buf->txd;
+       }
+ 
+       vb2_set_plane_payload(vb, 0, new_size);
+ 
+       /* This is the configuration of one sg-element */
+       video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+ 
+       if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+               /*
+                * If the IPU DMA channel is configured to transfer generic
+                * 8-bit data, we have to set up the geometry parameters
+                * correctly, according to the current pixel format. The DMA
+                * horizontal parameters in this case are expressed in bytes,
+                * not in pixels.
+                */
+               video->out_width        = icd->bytesperline;
+               video->out_height       = icd->user_height;
+               video->out_stride       = icd->bytesperline;
+       } else {
+               /*
+                * For IPU known formats the pixel unit will be managed
+                * successfully by the IPU code
+                */
+               video->out_width        = icd->user_width;
+               video->out_height       = icd->user_height;
+               video->out_stride       = icd->user_width;
+       }
+ 
+ #ifdef DEBUG
+       /* helps to see what DMA actually has written */
+       if (vb2_plane_vaddr(vb, 0))
+               memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+ #endif
+ 
+       spin_lock_irqsave(&mx3_cam->lock, flags);
+       list_add_tail(&buf->queue, &mx3_cam->capture);
+ 
+       if (!mx3_cam->active)
+               mx3_cam->active = buf;
+ 
+       spin_unlock_irq(&mx3_cam->lock);
+ 
+       cookie = txd->tx_submit(txd);
+       dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
+               cookie, sg_dma_address(&buf->sg));
+ 
+       if (cookie >= 0)
+               return;
+ 
+       spin_lock_irq(&mx3_cam->lock);
+ 
+       /* Submit error */
+       list_del_init(&buf->queue);
+ 
+       if (mx3_cam->active == buf)
+               mx3_cam->active = NULL;
+ 
+       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ error:
+       vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ 
+ static void mx3_videobuf_release(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+       struct dma_async_tx_descriptor *txd = buf->txd;
+       unsigned long flags;
+ 
+       dev_dbg(icd->parent,
+               "Release%s DMA 0x%08x, queue %sempty\n",
+               mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
+               list_empty(&buf->queue) ? "" : "not ");
+ 
+       spin_lock_irqsave(&mx3_cam->lock, flags);
+ 
+       if (mx3_cam->active == buf)
+               mx3_cam->active = NULL;
+ 
+       /* Doesn't hurt also if the list is empty */
+       list_del_init(&buf->queue);
+ 
+       if (txd) {
+               buf->txd = NULL;
+               if (mx3_cam->idmac_channel[0])
+                       async_tx_ack(txd);
+       }
+ 
+       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ 
+       mx3_cam->buf_total -= vb2_plane_size(vb, 0);
+ }
+ 
+ static int mx3_videobuf_init(struct vb2_buffer *vb)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ 
+       if (!buf->txd) {
+               /* This is for locking debugging only */
+               INIT_LIST_HEAD(&buf->queue);
+               sg_init_table(&buf->sg, 1);
+ 
+               mx3_cam->buf_total += vb2_plane_size(vb, 0);
+       }
+ 
+       return 0;
+ }
+ 
+ static int mx3_stop_streaming(struct vb2_queue *q)
+ {
+       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+       struct mx3_camera_buffer *buf, *tmp;
+       unsigned long flags;
+ 
+       if (ichan) {
+               struct dma_chan *chan = &ichan->dma_chan;
+               chan->device->device_control(chan, DMA_PAUSE, 0);
+       }
+ 
+       spin_lock_irqsave(&mx3_cam->lock, flags);
+ 
+       mx3_cam->active = NULL;
+ 
+       list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
+               list_del_init(&buf->queue);
+               vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+       }
+ 
+       spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ 
+       return 0;
+ }
+ 
+ static struct vb2_ops mx3_videobuf_ops = {
+       .queue_setup    = mx3_videobuf_setup,
+       .buf_queue      = mx3_videobuf_queue,
+       .buf_cleanup    = mx3_videobuf_release,
+       .buf_init       = mx3_videobuf_init,
+       .wait_prepare   = soc_camera_unlock,
+       .wait_finish    = soc_camera_lock,
+       .stop_streaming = mx3_stop_streaming,
+ };
+ 
+ static int mx3_camera_init_videobuf(struct vb2_queue *q,
+                                    struct soc_camera_device *icd)
+ {
+       q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       q->io_modes = VB2_MMAP | VB2_USERPTR;
+       q->drv_priv = icd;
+       q->ops = &mx3_videobuf_ops;
+       q->mem_ops = &vb2_dma_contig_memops;
+       q->buf_struct_size = sizeof(struct mx3_camera_buffer);
+ 
+       return vb2_queue_init(q);
+ }
+ 
+ /* First part of ipu_csi_init_interface() */
+ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
+                               struct soc_camera_device *icd)
+ {
+       u32 conf;
+       long rate;
+ 
+       /* Set default size: ipu_csi_set_window_size() */
+       csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
+       /* ...and position to 0:0: ipu_csi_set_window_pos() */
+       conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+       csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
+ 
+       /* We use only gated clock synchronisation mode so far */
+       conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+ 
+       /* Set generic data, platform-biggest bus-width */
+       conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+ 
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+               conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+               conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+               conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
+               conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ 
+       if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
+               conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
+               conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+               conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+               conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+               conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+       if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+               conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+ 
+       /* ipu_csi_init_interface() */
+       csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
+ 
+       clk_prepare_enable(mx3_cam->clk);
+       rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
+       dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+       if (rate)
+               clk_set_rate(mx3_cam->clk, rate);
+ }
+ 
+ /* Called with .video_lock held */
+ static int mx3_camera_add_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+ 
+       if (mx3_cam->icd)
+               return -EBUSY;
+ 
+       mx3_camera_activate(mx3_cam, icd);
+ 
+       mx3_cam->buf_total = 0;
+       mx3_cam->icd = icd;
+ 
+       dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+                icd->devnum);
+ 
+       return 0;
+ }
+ 
+ /* Called with .video_lock held */
+ static void mx3_camera_remove_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+ 
+       BUG_ON(icd != mx3_cam->icd);
+ 
+       if (*ichan) {
+               dma_release_channel(&(*ichan)->dma_chan);
+               *ichan = NULL;
+       }
+ 
+       clk_disable_unprepare(mx3_cam->clk);
+ 
+       mx3_cam->icd = NULL;
+ 
+       dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+                icd->devnum);
+ }
+ 
+ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
+                              unsigned char buswidth, unsigned long *flags)
+ {
+       /*
+        * If requested data width is supported by the platform, use it or any
+        * possible lower value - i.MX31 is smart enough to shift bits
+        */
+       if (buswidth > fls(mx3_cam->width_flags))
+               return -EINVAL;
+ 
+       /*
+        * Platform specified synchronization and pixel clock polarities are
+        * only a recommendation and are only used during probing. MX3x
+        * camera interface only works in master mode, i.e., uses HSYNC and
+        * VSYNC signals from the sensor
+        */
+       *flags = V4L2_MBUS_MASTER |
+               V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_HSYNC_ACTIVE_LOW |
+               V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_VSYNC_ACTIVE_LOW |
+               V4L2_MBUS_PCLK_SAMPLE_RISING |
+               V4L2_MBUS_PCLK_SAMPLE_FALLING |
+               V4L2_MBUS_DATA_ACTIVE_HIGH |
+               V4L2_MBUS_DATA_ACTIVE_LOW;
+ 
+       return 0;
+ }
+ 
+ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
+                                   const unsigned int depth)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long bus_flags, common_flags;
+       int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+ 
+       dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+ 
+       if (ret < 0)
+               return ret;
+ 
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         bus_flags);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%lx\n",
+                                cfg.flags, bus_flags);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       }
+ 
+       return 0;
+ }
+ 
+ static bool chan_filter(struct dma_chan *chan, void *arg)
+ {
+       struct dma_chan_request *rq = arg;
+       struct mx3_camera_pdata *pdata;
+ 
+       if (!imx_dma_is_ipu(chan))
+               return false;
+ 
+       if (!rq)
+               return false;
+ 
+       pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
+ 
+       return rq->id == chan->chan_id &&
+               pdata->dma_dev == chan->device->dev;
+ }
+ 
+ static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+       {
+               .fourcc                 = V4L2_PIX_FMT_SBGGR8,
+               .name                   = "Bayer BGGR (sRGB) 8 bit",
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_NONE,
+               .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PACKED,
+       }, {
+               .fourcc                 = V4L2_PIX_FMT_GREY,
+               .name                   = "Monochrome 8 bit",
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_NONE,
+               .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PACKED,
+       },
+ };
+ 
+ /* This will be corrected as we get more formats */
+ static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+ {
+       return  fmt->packing == SOC_MBUS_PACKING_NONE ||
+               (fmt->bits_per_sample == 8 &&
+                fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+               (fmt->bits_per_sample > 8 &&
+                fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+ }
+ 
+ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+                                 struct soc_camera_format_xlate *xlate)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct device *dev = icd->parent;
+       int formats = 0, ret;
+       enum v4l2_mbus_pixelcode code;
+       const struct soc_mbus_pixelfmt *fmt;
+ 
+       ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+       if (ret < 0)
+               /* No more formats */
+               return 0;
+ 
+       fmt = soc_mbus_get_fmtdesc(code);
+       if (!fmt) {
+               dev_warn(icd->parent,
+                        "Unsupported format code #%u: %d\n", idx, code);
+               return 0;
+       }
+ 
+       /* This also checks support for the requested bits-per-sample */
+       ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
+       if (ret < 0)
+               return 0;
+ 
+       switch (code) {
+       case V4L2_MBUS_FMT_SBGGR10_1X10:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = &mx3_camera_formats[0];
+                       xlate->code     = code;
+                       xlate++;
+                       dev_dbg(dev, "Providing format %s using code %d\n",
+                               mx3_camera_formats[0].name, code);
+               }
+               break;
+       case V4L2_MBUS_FMT_Y10_1X10:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = &mx3_camera_formats[1];
+                       xlate->code     = code;
+                       xlate++;
+                       dev_dbg(dev, "Providing format %s using code %d\n",
+                               mx3_camera_formats[1].name, code);
+               }
+               break;
+       default:
+               if (!mx3_camera_packing_supported(fmt))
+                       return 0;
+       }
+ 
+       /* Generic pass-through */
+       formats++;
+       if (xlate) {
+               xlate->host_fmt = fmt;
+               xlate->code     = code;
+               dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
+                       (fmt->fourcc >> (0*8)) & 0xFF,
+                       (fmt->fourcc >> (1*8)) & 0xFF,
+                       (fmt->fourcc >> (2*8)) & 0xFF,
+                       (fmt->fourcc >> (3*8)) & 0xFF);
+               xlate++;
+       }
+ 
+       return formats;
+ }
+ 
+ static void configure_geometry(struct mx3_camera_dev *mx3_cam,
+                              unsigned int width, unsigned int height,
+                              const struct soc_mbus_pixelfmt *fmt)
+ {
+       u32 ctrl, width_field, height_field;
+ 
+       if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+               /*
+                * As the CSI will be configured to output BAYER, here
+                * the width parameter count the number of samples to
+                * capture to complete the whole image width.
+                */
+               unsigned int num, den;
+               int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
+               BUG_ON(ret < 0);
+               width = width * num / den;
+       }
+ 
+       /* Setup frame size - this cannot be changed on-the-fly... */
+       width_field = width - 1;
+       height_field = height - 1;
+       csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
+ 
+       csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
+       csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
+ 
+       csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
+ 
+       /* ...and position */
+       ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+       /* Sensor does the cropping */
+       csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
+ }
+ 
+ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
+ {
+       dma_cap_mask_t mask;
+       struct dma_chan *chan;
+       struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+       /* We have to use IDMAC_IC_7 for Bayer / generic data */
+       struct dma_chan_request rq = {.mx3_cam = mx3_cam,
+                                     .id = IDMAC_IC_7};
+ 
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_PRIVATE, mask);
+       chan = dma_request_channel(mask, chan_filter, &rq);
+       if (!chan)
+               return -EBUSY;
+ 
+       *ichan = to_idmac_chan(chan);
+       (*ichan)->client = mx3_cam;
+ 
+       return 0;
+ }
+ 
+ /*
+  * FIXME: learn to use stride != width, then we can keep stride properly aligned
+  * and support arbitrary (even) widths.
+  */
+ static inline void stride_align(__u32 *width)
+ {
+       if (ALIGN(*width, 8) < 4096)
+               *width = ALIGN(*width, 8);
+       else
+               *width = *width &  ~7;
+ }
+ 
+ /*
+  * As long as we don't implement host-side cropping and scaling, we can use
+  * default g_crop and cropcap from soc_camera.c
+  */
+ static int mx3_camera_set_crop(struct soc_camera_device *icd,
+                              struct v4l2_crop *a)
+ {
+       struct v4l2_rect *rect = &a->c;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+ 
+       soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+       soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+ 
+       ret = v4l2_subdev_call(sd, video, s_crop, a);
+       if (ret < 0)
+               return ret;
+ 
+       /* The capture device might have changed its output sizes */
+       ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       if (mf.code != icd->current_fmt->code)
+               return -EINVAL;
+ 
+       if (mf.width & 7) {
+               /* Ouch! We can only handle 8-byte aligned width... */
+               stride_align(&mf.width);
+               ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+               if (ret < 0)
+                       return ret;
+       }
+ 
+       if (mf.width != icd->user_width || mf.height != icd->user_height)
+               configure_geometry(mx3_cam, mf.width, mf.height,
+                                  icd->current_fmt->host_fmt);
+ 
+       dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+               mf.width, mf.height);
+ 
+       icd->user_width         = mf.width;
+       icd->user_height        = mf.height;
+ 
+       return ret;
+ }
+ 
+ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n",
+                        pix->pixelformat);
+               return -EINVAL;
+       }
+ 
+       stride_align(&pix->width);
+       dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
+ 
+       /*
+        * Might have to perform a complete interface initialisation like in
+        * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
+        * mxc_v4l2_s_fmt()
+        */
+ 
+       configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
+ 
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       if (mf.code != xlate->code)
+               return -EINVAL;
+ 
+       if (!mx3_cam->idmac_channel[0]) {
+               ret = acquire_dma_channel(mx3_cam);
+               if (ret < 0)
+                       return ret;
+       }
+ 
+       pix->width              = mf.width;
+       pix->height             = mf.height;
+       pix->field              = mf.field;
+       mx3_cam->field          = mf.field;
+       pix->colorspace         = mf.colorspace;
+       icd->current_fmt        = xlate;
+ 
+       dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
+ 
+       return ret;
+ }
+ 
+ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       __u32 pixfmt = pix->pixelformat;
+       int ret;
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (pixfmt && !xlate) {
+               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+ 
+       /* limit to MX3 hardware capabilities */
+       if (pix->height > 4096)
+               pix->height = 4096;
+       if (pix->width > 4096)
+               pix->width = 4096;
+ 
+       /* limit to sensor capabilities */
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       pix->width      = mf.width;
+       pix->height     = mf.height;
+       pix->colorspace = mf.colorspace;
+ 
+       switch (mf.field) {
+       case V4L2_FIELD_ANY:
+               pix->field = V4L2_FIELD_NONE;
+               break;
+       case V4L2_FIELD_NONE:
+               break;
+       default:
+               dev_err(icd->parent, "Field type %d unsupported.\n",
+                       mf.field);
+               ret = -EINVAL;
+       }
+ 
+       return ret;
+ }
+ 
+ static int mx3_camera_reqbufs(struct soc_camera_device *icd,
+                             struct v4l2_requestbuffers *p)
+ {
+       return 0;
+ }
+ 
+ static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
+ {
+       struct soc_camera_device *icd = file->private_data;
+ 
+       return vb2_poll(&icd->vb2_vidq, file, pt);
+ }
+ 
+ static int mx3_camera_querycap(struct soc_camera_host *ici,
+                              struct v4l2_capability *cap)
+ {
+       /* cap->name is set by the firendly caller:-> */
+       strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ 
+       return 0;
+ }
+ 
+ static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct mx3_camera_dev *mx3_cam = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+       unsigned long bus_flags, common_flags;
+       u32 dw, sens_conf;
+       const struct soc_mbus_pixelfmt *fmt;
+       int buswidth;
+       int ret;
+       const struct soc_camera_format_xlate *xlate;
+       struct device *dev = icd->parent;
+ 
+       fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+       if (!fmt)
+               return -EINVAL;
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (!xlate) {
+               dev_warn(dev, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+ 
+       buswidth = fmt->bits_per_sample;
+       ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+ 
+       dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
+ 
+       if (ret < 0)
+               return ret;
+ 
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         bus_flags);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%lx\n",
+                                cfg.flags, bus_flags);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       } else {
+               common_flags = bus_flags;
+       }
+ 
+       dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+               cfg.flags, bus_flags, common_flags);
+ 
+       /* Make choices, based on platform preferences */
+       if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+                       common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+           (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+               if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+               else
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+       }
+ 
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+                       common_flags, ret);
+               return ret;
+       }
+ 
+       /*
+        * So far only gated clock mode is supported. Add a line
+        *      (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
+        * below and select the required mode when supporting other
+        * synchronisation protocols.
+        */
+       sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
+               ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
+                 (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
+                 (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
+                 (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
+                 (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
+                 (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
+ 
+       /* TODO: Support RGB and YUV formats */
+ 
+       /* This has been set in mx3_camera_activate(), but we clear it above */
+       sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+ 
+       if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+               sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+       if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+               sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+       if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+               sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+       if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+               sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+ 
+       /* Just do what we're asked to do */
+       switch (xlate->host_fmt->bits_per_sample) {
+       case 4:
+               dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+               break;
+       case 8:
+               dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+               break;
+       case 10:
+               dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+               break;
+       default:
+               /*
+                * Actually it can only be 15 now, default is just to silence
+                * compiler warnings
+                */
+       case 15:
+               dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+       }
+ 
+       csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
+ 
+       dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+ 
+       return 0;
+ }
+ 
+ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = mx3_camera_add_device,
+       .remove         = mx3_camera_remove_device,
+       .set_crop       = mx3_camera_set_crop,
+       .set_fmt        = mx3_camera_set_fmt,
+       .try_fmt        = mx3_camera_try_fmt,
+       .get_formats    = mx3_camera_get_formats,
+       .init_videobuf2 = mx3_camera_init_videobuf,
+       .reqbufs        = mx3_camera_reqbufs,
+       .poll           = mx3_camera_poll,
+       .querycap       = mx3_camera_querycap,
+       .set_bus_param  = mx3_camera_set_bus_param,
+ };
+ 
+ static int __devinit mx3_camera_probe(struct platform_device *pdev)
+ {
+       struct mx3_camera_dev *mx3_cam;
+       struct resource *res;
+       void __iomem *base;
+       int err = 0;
+       struct soc_camera_host *soc_host;
+ 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               err = -ENODEV;
+               goto egetres;
+       }
+ 
+       mx3_cam = vzalloc(sizeof(*mx3_cam));
+       if (!mx3_cam) {
+               dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
+               err = -ENOMEM;
+               goto ealloc;
+       }
+ 
+       mx3_cam->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(mx3_cam->clk)) {
+               err = PTR_ERR(mx3_cam->clk);
+               goto eclkget;
+       }
+ 
+       mx3_cam->pdata = pdev->dev.platform_data;
+       mx3_cam->platform_flags = mx3_cam->pdata->flags;
+       if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
+               /*
+                * Platform hasn't set available data widths. This is bad.
+                * Warn and use a default.
+                */
+               dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+                        "data widths, using default 8 bit\n");
+               mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
+       }
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+               mx3_cam->width_flags = 1 << 3;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+               mx3_cam->width_flags |= 1 << 7;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+               mx3_cam->width_flags |= 1 << 9;
+       if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+               mx3_cam->width_flags |= 1 << 14;
+ 
+       mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
+       if (!mx3_cam->mclk) {
+               dev_warn(&pdev->dev,
+                        "mclk_10khz == 0! Please, fix your platform data. "
+                        "Using default 20MHz\n");
+               mx3_cam->mclk = 20000000;
+       }
+ 
+       /* list of video-buffers */
+       INIT_LIST_HEAD(&mx3_cam->capture);
+       spin_lock_init(&mx3_cam->lock);
+ 
+       base = ioremap(res->start, resource_size(res));
+       if (!base) {
+               pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
+               err = -ENOMEM;
+               goto eioremap;
+       }
+ 
+       mx3_cam->base   = base;
+ 
+       soc_host                = &mx3_cam->soc_host;
+       soc_host->drv_name      = MX3_CAM_DRV_NAME;
+       soc_host->ops           = &mx3_soc_camera_host_ops;
+       soc_host->priv          = mx3_cam;
+       soc_host->v4l2_dev.dev  = &pdev->dev;
+       soc_host->nr            = pdev->id;
+ 
+       mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+       if (IS_ERR(mx3_cam->alloc_ctx)) {
+               err = PTR_ERR(mx3_cam->alloc_ctx);
+               goto eallocctx;
+       }
+ 
+       err = soc_camera_host_register(soc_host);
+       if (err)
+               goto ecamhostreg;
+ 
+       /* IDMAC interface */
+       dmaengine_get();
+ 
+       return 0;
+ 
+ ecamhostreg:
+       vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+ eallocctx:
+       iounmap(base);
+ eioremap:
+       clk_put(mx3_cam->clk);
+ eclkget:
+       vfree(mx3_cam);
+ ealloc:
+ egetres:
+       return err;
+ }
+ 
+ static int __devexit mx3_camera_remove(struct platform_device *pdev)
+ {
+       struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+       struct mx3_camera_dev *mx3_cam = container_of(soc_host,
+                                       struct mx3_camera_dev, soc_host);
+ 
+       clk_put(mx3_cam->clk);
+ 
+       soc_camera_host_unregister(soc_host);
+ 
+       iounmap(mx3_cam->base);
+ 
+       /*
+        * The channel has either not been allocated,
+        * or should have been released
+        */
+       if (WARN_ON(mx3_cam->idmac_channel[0]))
+               dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
+ 
+       vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+ 
+       vfree(mx3_cam);
+ 
+       dmaengine_put();
+ 
+       return 0;
+ }
+ 
+ static struct platform_driver mx3_camera_driver = {
+       .driver         = {
+               .name   = MX3_CAM_DRV_NAME,
+       },
+       .probe          = mx3_camera_probe,
+       .remove         = __devexit_p(mx3_camera_remove),
+ };
+ 
+ module_platform_driver(mx3_camera_driver);
+ 
+ MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
+ MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+ MODULE_LICENSE("GPL v2");
+ MODULE_VERSION("0.2.3");
+ MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
 
--- /dev/null
 -#include <mach/camera.h>
+ /*
+  * V4L2 Driver for PXA camera host
+  *
+  * Copyright (C) 2006, Sascha Hauer, Pengutronix
+  * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.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.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/io.h>
+ #include <linux/delay.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+ #include <linux/interrupt.h>
+ #include <linux/kernel.h>
+ #include <linux/mm.h>
+ #include <linux/moduleparam.h>
+ #include <linux/time.h>
+ #include <linux/device.h>
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ 
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-dev.h>
+ #include <media/videobuf-dma-sg.h>
+ #include <media/soc_camera.h>
+ #include <media/soc_mediabus.h>
+ 
+ #include <linux/videodev2.h>
+ 
+ #include <mach/dma.h>
++#include <linux/platform_data/camera-pxa.h>
+ 
+ #define PXA_CAM_VERSION "0.0.6"
+ #define PXA_CAM_DRV_NAME "pxa27x-camera"
+ 
+ /* Camera Interface */
+ #define CICR0         0x0000
+ #define CICR1         0x0004
+ #define CICR2         0x0008
+ #define CICR3         0x000C
+ #define CICR4         0x0010
+ #define CISR          0x0014
+ #define CIFR          0x0018
+ #define CITOR         0x001C
+ #define CIBR0         0x0028
+ #define CIBR1         0x0030
+ #define CIBR2         0x0038
+ 
+ #define CICR0_DMAEN   (1 << 31)       /* DMA request enable */
+ #define CICR0_PAR_EN  (1 << 30)       /* Parity enable */
+ #define CICR0_SL_CAP_EN       (1 << 29)       /* Capture enable for slave mode */
+ #define CICR0_ENB     (1 << 28)       /* Camera interface enable */
+ #define CICR0_DIS     (1 << 27)       /* Camera interface disable */
+ #define CICR0_SIM     (0x7 << 24)     /* Sensor interface mode mask */
+ #define CICR0_TOM     (1 << 9)        /* Time-out mask */
+ #define CICR0_RDAVM   (1 << 8)        /* Receive-data-available mask */
+ #define CICR0_FEM     (1 << 7)        /* FIFO-empty mask */
+ #define CICR0_EOLM    (1 << 6)        /* End-of-line mask */
+ #define CICR0_PERRM   (1 << 5)        /* Parity-error mask */
+ #define CICR0_QDM     (1 << 4)        /* Quick-disable mask */
+ #define CICR0_CDM     (1 << 3)        /* Disable-done mask */
+ #define CICR0_SOFM    (1 << 2)        /* Start-of-frame mask */
+ #define CICR0_EOFM    (1 << 1)        /* End-of-frame mask */
+ #define CICR0_FOM     (1 << 0)        /* FIFO-overrun mask */
+ 
+ #define CICR1_TBIT    (1 << 31)       /* Transparency bit */
+ #define CICR1_RGBT_CONV       (0x3 << 29)     /* RGBT conversion mask */
+ #define CICR1_PPL     (0x7ff << 15)   /* Pixels per line mask */
+ #define CICR1_RGB_CONV        (0x7 << 12)     /* RGB conversion mask */
+ #define CICR1_RGB_F   (1 << 11)       /* RGB format */
+ #define CICR1_YCBCR_F (1 << 10)       /* YCbCr format */
+ #define CICR1_RGB_BPP (0x7 << 7)      /* RGB bis per pixel mask */
+ #define CICR1_RAW_BPP (0x3 << 5)      /* Raw bis per pixel mask */
+ #define CICR1_COLOR_SP        (0x3 << 3)      /* Color space mask */
+ #define CICR1_DW      (0x7 << 0)      /* Data width mask */
+ 
+ #define CICR2_BLW     (0xff << 24)    /* Beginning-of-line pixel clock
+                                          wait count mask */
+ #define CICR2_ELW     (0xff << 16)    /* End-of-line pixel clock
+                                          wait count mask */
+ #define CICR2_HSW     (0x3f << 10)    /* Horizontal sync pulse width mask */
+ #define CICR2_BFPW    (0x3f << 3)     /* Beginning-of-frame pixel clock
+                                          wait count mask */
+ #define CICR2_FSW     (0x7 << 0)      /* Frame stabilization
+                                          wait count mask */
+ 
+ #define CICR3_BFW     (0xff << 24)    /* Beginning-of-frame line clock
+                                          wait count mask */
+ #define CICR3_EFW     (0xff << 16)    /* End-of-frame line clock
+                                          wait count mask */
+ #define CICR3_VSW     (0x3f << 10)    /* Vertical sync pulse width mask */
+ #define CICR3_BFPW    (0x3f << 3)     /* Beginning-of-frame pixel clock
+                                          wait count mask */
+ #define CICR3_LPF     (0x7ff << 0)    /* Lines per frame mask */
+ 
+ #define CICR4_MCLK_DLY        (0x3 << 24)     /* MCLK Data Capture Delay mask */
+ #define CICR4_PCLK_EN (1 << 23)       /* Pixel clock enable */
+ #define CICR4_PCP     (1 << 22)       /* Pixel clock polarity */
+ #define CICR4_HSP     (1 << 21)       /* Horizontal sync polarity */
+ #define CICR4_VSP     (1 << 20)       /* Vertical sync polarity */
+ #define CICR4_MCLK_EN (1 << 19)       /* MCLK enable */
+ #define CICR4_FR_RATE (0x7 << 8)      /* Frame rate mask */
+ #define CICR4_DIV     (0xff << 0)     /* Clock divisor mask */
+ 
+ #define CISR_FTO      (1 << 15)       /* FIFO time-out */
+ #define CISR_RDAV_2   (1 << 14)       /* Channel 2 receive data available */
+ #define CISR_RDAV_1   (1 << 13)       /* Channel 1 receive data available */
+ #define CISR_RDAV_0   (1 << 12)       /* Channel 0 receive data available */
+ #define CISR_FEMPTY_2 (1 << 11)       /* Channel 2 FIFO empty */
+ #define CISR_FEMPTY_1 (1 << 10)       /* Channel 1 FIFO empty */
+ #define CISR_FEMPTY_0 (1 << 9)        /* Channel 0 FIFO empty */
+ #define CISR_EOL      (1 << 8)        /* End of line */
+ #define CISR_PAR_ERR  (1 << 7)        /* Parity error */
+ #define CISR_CQD      (1 << 6)        /* Camera interface quick disable */
+ #define CISR_CDD      (1 << 5)        /* Camera interface disable done */
+ #define CISR_SOF      (1 << 4)        /* Start of frame */
+ #define CISR_EOF      (1 << 3)        /* End of frame */
+ #define CISR_IFO_2    (1 << 2)        /* FIFO overrun for Channel 2 */
+ #define CISR_IFO_1    (1 << 1)        /* FIFO overrun for Channel 1 */
+ #define CISR_IFO_0    (1 << 0)        /* FIFO overrun for Channel 0 */
+ 
+ #define CIFR_FLVL2    (0x7f << 23)    /* FIFO 2 level mask */
+ #define CIFR_FLVL1    (0x7f << 16)    /* FIFO 1 level mask */
+ #define CIFR_FLVL0    (0xff << 8)     /* FIFO 0 level mask */
+ #define CIFR_THL_0    (0x3 << 4)      /* Threshold Level for Channel 0 FIFO */
+ #define CIFR_RESET_F  (1 << 3)        /* Reset input FIFOs */
+ #define CIFR_FEN2     (1 << 2)        /* FIFO enable for channel 2 */
+ #define CIFR_FEN1     (1 << 1)        /* FIFO enable for channel 1 */
+ #define CIFR_FEN0     (1 << 0)        /* FIFO enable for channel 0 */
+ 
+ #define CICR0_SIM_MP  (0 << 24)
+ #define CICR0_SIM_SP  (1 << 24)
+ #define CICR0_SIM_MS  (2 << 24)
+ #define CICR0_SIM_EP  (3 << 24)
+ #define CICR0_SIM_ES  (4 << 24)
+ 
+ #define CICR1_DW_VAL(x)   ((x) & CICR1_DW)        /* Data bus width */
+ #define CICR1_PPL_VAL(x)  (((x) << 15) & CICR1_PPL) /* Pixels per line */
+ #define CICR1_COLOR_SP_VAL(x) (((x) << 3) & CICR1_COLOR_SP)   /* color space */
+ #define CICR1_RGB_BPP_VAL(x)  (((x) << 7) & CICR1_RGB_BPP)    /* bpp for rgb */
+ #define CICR1_RGBT_CONV_VAL(x)        (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */
+ 
+ #define CICR2_BLW_VAL(x)  (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */
+ #define CICR2_ELW_VAL(x)  (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */
+ #define CICR2_HSW_VAL(x)  (((x) << 10) & CICR2_HSW) /* Horizontal sync pulse width */
+ #define CICR2_BFPW_VAL(x) (((x) << 3) & CICR2_BFPW) /* Beginning-of-frame pixel clock wait count */
+ #define CICR2_FSW_VAL(x)  (((x) << 0) & CICR2_FSW)  /* Frame stabilization wait count */
+ 
+ #define CICR3_BFW_VAL(x)  (((x) << 24) & CICR3_BFW) /* Beginning-of-frame line clock wait count  */
+ #define CICR3_EFW_VAL(x)  (((x) << 16) & CICR3_EFW) /* End-of-frame line clock wait count */
+ #define CICR3_VSW_VAL(x)  (((x) << 11) & CICR3_VSW) /* Vertical sync pulse width */
+ #define CICR3_LPF_VAL(x)  (((x) << 0) & CICR3_LPF)  /* Lines per frame */
+ 
+ #define CICR0_IRQ_MASK (CICR0_TOM | CICR0_RDAVM | CICR0_FEM | CICR0_EOLM | \
+                       CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \
+                       CICR0_EOFM | CICR0_FOM)
+ 
+ /*
+  * Structures
+  */
+ enum pxa_camera_active_dma {
+       DMA_Y = 0x1,
+       DMA_U = 0x2,
+       DMA_V = 0x4,
+ };
+ 
+ /* descriptor needed for the PXA DMA engine */
+ struct pxa_cam_dma {
+       dma_addr_t              sg_dma;
+       struct pxa_dma_desc     *sg_cpu;
+       size_t                  sg_size;
+       int                     sglen;
+ };
+ 
+ /* buffer for one video frame */
+ struct pxa_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct videobuf_buffer          vb;
+       enum v4l2_mbus_pixelcode        code;
+       /* our descriptor lists for Y, U and V channels */
+       struct pxa_cam_dma              dmas[3];
+       int                             inwork;
+       enum pxa_camera_active_dma      active_dma;
+ };
+ 
+ struct pxa_camera_dev {
+       struct soc_camera_host  soc_host;
+       /*
+        * PXA27x is only supposed to handle one camera on its Quick Capture
+        * interface. If anyone ever builds hardware to enable more than
+        * one camera, they will have to modify this driver too
+        */
+       struct soc_camera_device *icd;
+       struct clk              *clk;
+ 
+       unsigned int            irq;
+       void __iomem            *base;
+ 
+       int                     channels;
+       unsigned int            dma_chans[3];
+ 
+       struct pxacamera_platform_data *pdata;
+       struct resource         *res;
+       unsigned long           platform_flags;
+       unsigned long           ciclk;
+       unsigned long           mclk;
+       u32                     mclk_divisor;
+       u16                     width_flags;    /* max 10 bits */
+ 
+       struct list_head        capture;
+ 
+       spinlock_t              lock;
+ 
+       struct pxa_buffer       *active;
+       struct pxa_dma_desc     *sg_tail[3];
+ 
+       u32                     save_cicr[5];
+ };
+ 
+ struct pxa_cam {
+       unsigned long flags;
+ };
+ 
+ static const char *pxa_cam_driver_description = "PXA_Camera";
+ 
+ static unsigned int vid_limit = 16;   /* Video memory limit, in Mb */
+ 
+ /*
+  *  Videobuf operations
+  */
+ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+                             unsigned int *size)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+ 
+       dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
+ 
+       *size = icd->sizeimage;
+ 
+       if (0 == *count)
+               *count = 32;
+       if (*size * *count > vid_limit * 1024 * 1024)
+               *count = (vid_limit * 1024 * 1024) / *size;
+ 
+       return 0;
+ }
+ 
+ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+       int i;
+ 
+       BUG_ON(in_interrupt());
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               &buf->vb, buf->vb.baddr, buf->vb.bsize);
+ 
+       /*
+        * This waits until this buffer is out of danger, i.e., until it is no
+        * longer in STATE_QUEUED or STATE_ACTIVE
+        */
+       videobuf_waiton(vq, &buf->vb, 0, 0);
+       videobuf_dma_unmap(vq->dev, dma);
+       videobuf_dma_free(dma);
+ 
+       for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) {
+               if (buf->dmas[i].sg_cpu)
+                       dma_free_coherent(ici->v4l2_dev.dev,
+                                         buf->dmas[i].sg_size,
+                                         buf->dmas[i].sg_cpu,
+                                         buf->dmas[i].sg_dma);
+               buf->dmas[i].sg_cpu = NULL;
+       }
+ 
+       buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ }
+ 
+ static int calculate_dma_sglen(struct scatterlist *sglist, int sglen,
+                              int sg_first_ofs, int size)
+ {
+       int i, offset, dma_len, xfer_len;
+       struct scatterlist *sg;
+ 
+       offset = sg_first_ofs;
+       for_each_sg(sglist, sg, sglen, i) {
+               dma_len = sg_dma_len(sg);
+ 
+               /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
+               xfer_len = roundup(min(dma_len - offset, size), 8);
+ 
+               size = max(0, size - xfer_len);
+               offset = 0;
+               if (size == 0)
+                       break;
+       }
+ 
+       BUG_ON(size != 0);
+       return i + 1;
+ }
+ 
+ /**
+  * pxa_init_dma_channel - init dma descriptors
+  * @pcdev: pxa camera device
+  * @buf: pxa buffer to find pxa dma channel
+  * @dma: dma video buffer
+  * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V')
+  * @cibr: camera Receive Buffer Register
+  * @size: bytes to transfer
+  * @sg_first: first element of sg_list
+  * @sg_first_ofs: offset in first element of sg_list
+  *
+  * Prepares the pxa dma descriptors to transfer one camera channel.
+  * Beware sg_first and sg_first_ofs are both input and output parameters.
+  *
+  * Returns 0 or -ENOMEM if no coherent memory is available
+  */
+ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
+                               struct pxa_buffer *buf,
+                               struct videobuf_dmabuf *dma, int channel,
+                               int cibr, int size,
+                               struct scatterlist **sg_first, int *sg_first_ofs)
+ {
+       struct pxa_cam_dma *pxa_dma = &buf->dmas[channel];
+       struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+       struct scatterlist *sg;
+       int i, offset, sglen;
+       int dma_len = 0, xfer_len = 0;
+ 
+       if (pxa_dma->sg_cpu)
+               dma_free_coherent(dev, pxa_dma->sg_size,
+                                 pxa_dma->sg_cpu, pxa_dma->sg_dma);
+ 
+       sglen = calculate_dma_sglen(*sg_first, dma->sglen,
+                                   *sg_first_ofs, size);
+ 
+       pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc);
+       pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size,
+                                            &pxa_dma->sg_dma, GFP_KERNEL);
+       if (!pxa_dma->sg_cpu)
+               return -ENOMEM;
+ 
+       pxa_dma->sglen = sglen;
+       offset = *sg_first_ofs;
+ 
+       dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n",
+               *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma);
+ 
+ 
+       for_each_sg(*sg_first, sg, sglen, i) {
+               dma_len = sg_dma_len(sg);
+ 
+               /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */
+               xfer_len = roundup(min(dma_len - offset, size), 8);
+ 
+               size = max(0, size - xfer_len);
+ 
+               pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr;
+               pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset;
+               pxa_dma->sg_cpu[i].dcmd =
+                       DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len;
+ #ifdef DEBUG
+               if (!i)
+                       pxa_dma->sg_cpu[i].dcmd |= DCMD_STARTIRQEN;
+ #endif
+               pxa_dma->sg_cpu[i].ddadr =
+                       pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc);
+ 
+               dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n",
+                        pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc),
+                        sg_dma_address(sg) + offset, xfer_len);
+               offset = 0;
+ 
+               if (size == 0)
+                       break;
+       }
+ 
+       pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP;
+       pxa_dma->sg_cpu[sglen].dcmd  = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN;
+ 
+       /*
+        * Handle 1 special case :
+        *  - in 3 planes (YUV422P format), we might finish with xfer_len equal
+        *    to dma_len (end on PAGE boundary). In this case, the sg element
+        *    for next plane should be the next after the last used to store the
+        *    last scatter gather RAM page
+        */
+       if (xfer_len >= dma_len) {
+               *sg_first_ofs = xfer_len - dma_len;
+               *sg_first = sg_next(sg);
+       } else {
+               *sg_first_ofs = xfer_len;
+               *sg_first = sg;
+       }
+ 
+       return 0;
+ }
+ 
+ static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev,
+                                   struct pxa_buffer *buf)
+ {
+       buf->active_dma = DMA_Y;
+       if (pcdev->channels == 3)
+               buf->active_dma |= DMA_U | DMA_V;
+ }
+ 
+ /*
+  * Please check the DMA prepared buffer structure in :
+  *   Documentation/video4linux/pxa_camera.txt
+  * Please check also in pxa_camera_check_link_miss() to understand why DMA chain
+  * modification while DMA chain is running will work anyway.
+  */
+ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
+               struct videobuf_buffer *vb, enum v4l2_field field)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+       struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
+       int ret;
+       int size_y, size_u = 0, size_v = 0;
+ 
+       dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       /* Added list head initialization on alloc */
+       WARN_ON(!list_empty(&vb->queue));
+ 
+ #ifdef DEBUG
+       /*
+        * This can be useful if you want to see if we actually fill
+        * the buffer with something
+        */
+       memset((void *)vb->baddr, 0xaa, vb->bsize);
+ #endif
+ 
+       BUG_ON(NULL == icd->current_fmt);
+ 
+       /*
+        * I think, in buf_prepare you only have to protect global data,
+        * the actual buffer is yours
+        */
+       buf->inwork = 1;
+ 
+       if (buf->code   != icd->current_fmt->code ||
+           vb->width   != icd->user_width ||
+           vb->height  != icd->user_height ||
+           vb->field   != field) {
+               buf->code       = icd->current_fmt->code;
+               vb->width       = icd->user_width;
+               vb->height      = icd->user_height;
+               vb->field       = field;
+               vb->state       = VIDEOBUF_NEEDS_INIT;
+       }
+ 
+       vb->size = icd->sizeimage;
+       if (0 != vb->baddr && vb->bsize < vb->size) {
+               ret = -EINVAL;
+               goto out;
+       }
+ 
+       if (vb->state == VIDEOBUF_NEEDS_INIT) {
+               int size = vb->size;
+               int next_ofs = 0;
+               struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+               struct scatterlist *sg;
+ 
+               ret = videobuf_iolock(vq, vb, NULL);
+               if (ret)
+                       goto fail;
+ 
+               if (pcdev->channels == 3) {
+                       size_y = size / 2;
+                       size_u = size_v = size / 4;
+               } else {
+                       size_y = size;
+               }
+ 
+               sg = dma->sglist;
+ 
+               /* init DMA for Y channel */
+               ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y,
+                                          &sg, &next_ofs);
+               if (ret) {
+                       dev_err(dev, "DMA initialization for Y/RGB failed\n");
+                       goto fail;
+               }
+ 
+               /* init DMA for U channel */
+               if (size_u)
+                       ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1,
+                                                  size_u, &sg, &next_ofs);
+               if (ret) {
+                       dev_err(dev, "DMA initialization for U failed\n");
+                       goto fail_u;
+               }
+ 
+               /* init DMA for V channel */
+               if (size_v)
+                       ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2,
+                                                  size_v, &sg, &next_ofs);
+               if (ret) {
+                       dev_err(dev, "DMA initialization for V failed\n");
+                       goto fail_v;
+               }
+ 
+               vb->state = VIDEOBUF_PREPARED;
+       }
+ 
+       buf->inwork = 0;
+       pxa_videobuf_set_actdma(pcdev, buf);
+ 
+       return 0;
+ 
+ fail_v:
+       dma_free_coherent(dev, buf->dmas[1].sg_size,
+                         buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma);
+ fail_u:
+       dma_free_coherent(dev, buf->dmas[0].sg_size,
+                         buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma);
+ fail:
+       free_buffer(vq, buf);
+ out:
+       buf->inwork = 0;
+       return ret;
+ }
+ 
+ /**
+  * pxa_dma_start_channels - start DMA channel for active buffer
+  * @pcdev: pxa camera device
+  *
+  * Initialize DMA channels to the beginning of the active video buffer, and
+  * start these channels.
+  */
+ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev)
+ {
+       int i;
+       struct pxa_buffer *active;
+ 
+       active = pcdev->active;
+ 
+       for (i = 0; i < pcdev->channels; i++) {
+               dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+                       "%s (channel=%d) ddadr=%08x\n", __func__,
+                       i, active->dmas[i].sg_dma);
+               DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma;
+               DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
+       }
+ }
+ 
+ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev)
+ {
+       int i;
+ 
+       for (i = 0; i < pcdev->channels; i++) {
+               dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+                       "%s (channel=%d)\n", __func__, i);
+               DCSR(pcdev->dma_chans[i]) = 0;
+       }
+ }
+ 
+ static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev,
+                                struct pxa_buffer *buf)
+ {
+       int i;
+       struct pxa_dma_desc *buf_last_desc;
+ 
+       for (i = 0; i < pcdev->channels; i++) {
+               buf_last_desc = buf->dmas[i].sg_cpu + buf->dmas[i].sglen;
+               buf_last_desc->ddadr = DDADR_STOP;
+ 
+               if (pcdev->sg_tail[i])
+                       /* Link the new buffer to the old tail */
+                       pcdev->sg_tail[i]->ddadr = buf->dmas[i].sg_dma;
+ 
+               /* Update the channel tail */
+               pcdev->sg_tail[i] = buf_last_desc;
+       }
+ }
+ 
+ /**
+  * pxa_camera_start_capture - start video capturing
+  * @pcdev: camera device
+  *
+  * Launch capturing. DMA channels should not be active yet. They should get
+  * activated at the end of frame interrupt, to capture only whole frames, and
+  * never begin the capture of a partial frame.
+  */
+ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev)
+ {
+       unsigned long cicr0;
+ 
+       dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
+       /* Enable End-Of-Frame Interrupt */
+       cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB;
+       cicr0 &= ~CICR0_EOFM;
+       __raw_writel(cicr0, pcdev->base + CICR0);
+ }
+ 
+ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev)
+ {
+       unsigned long cicr0;
+ 
+       pxa_dma_stop_channels(pcdev);
+ 
+       cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB;
+       __raw_writel(cicr0, pcdev->base + CICR0);
+ 
+       pcdev->active = NULL;
+       dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
+ }
+ 
+ /* Called under spinlock_irqsave(&pcdev->lock, ...) */
+ static void pxa_videobuf_queue(struct videobuf_queue *vq,
+                              struct videobuf_buffer *vb)
+ {
+       struct soc_camera_device *icd = vq->priv_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
+ 
+       dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
+               __func__, vb, vb->baddr, vb->bsize, pcdev->active);
+ 
+       list_add_tail(&vb->queue, &pcdev->capture);
+ 
+       vb->state = VIDEOBUF_ACTIVE;
+       pxa_dma_add_tail_buf(pcdev, buf);
+ 
+       if (!pcdev->active)
+               pxa_camera_start_capture(pcdev);
+ }
+ 
+ static void pxa_videobuf_release(struct videobuf_queue *vq,
+                                struct videobuf_buffer *vb)
+ {
+       struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
+ #ifdef DEBUG
+       struct soc_camera_device *icd = vq->priv_data;
+       struct device *dev = icd->parent;
+ 
+       dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+               vb, vb->baddr, vb->bsize);
+ 
+       switch (vb->state) {
+       case VIDEOBUF_ACTIVE:
+               dev_dbg(dev, "%s (active)\n", __func__);
+               break;
+       case VIDEOBUF_QUEUED:
+               dev_dbg(dev, "%s (queued)\n", __func__);
+               break;
+       case VIDEOBUF_PREPARED:
+               dev_dbg(dev, "%s (prepared)\n", __func__);
+               break;
+       default:
+               dev_dbg(dev, "%s (unknown)\n", __func__);
+               break;
+       }
+ #endif
+ 
+       free_buffer(vq, buf);
+ }
+ 
+ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
+                             struct videobuf_buffer *vb,
+                             struct pxa_buffer *buf)
+ {
+       int i;
+ 
+       /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
+       list_del_init(&vb->queue);
+       vb->state = VIDEOBUF_DONE;
+       do_gettimeofday(&vb->ts);
+       vb->field_count++;
+       wake_up(&vb->done);
+       dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n",
+               __func__, vb);
+ 
+       if (list_empty(&pcdev->capture)) {
+               pxa_camera_stop_capture(pcdev);
+               for (i = 0; i < pcdev->channels; i++)
+                       pcdev->sg_tail[i] = NULL;
+               return;
+       }
+ 
+       pcdev->active = list_entry(pcdev->capture.next,
+                                  struct pxa_buffer, vb.queue);
+ }
+ 
+ /**
+  * pxa_camera_check_link_miss - check missed DMA linking
+  * @pcdev: camera device
+  *
+  * The DMA chaining is done with DMA running. This means a tiny temporal window
+  * remains, where a buffer is queued on the chain, while the chain is already
+  * stopped. This means the tailed buffer would never be transferred by DMA.
+  * This function restarts the capture for this corner case, where :
+  *  - DADR() == DADDR_STOP
+  *  - a videobuffer is queued on the pcdev->capture list
+  *
+  * Please check the "DMA hot chaining timeslice issue" in
+  *   Documentation/video4linux/pxa_camera.txt
+  *
+  * Context: should only be called within the dma irq handler
+  */
+ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev)
+ {
+       int i, is_dma_stopped = 1;
+ 
+       for (i = 0; i < pcdev->channels; i++)
+               if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP)
+                       is_dma_stopped = 0;
+       dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+               "%s : top queued buffer=%p, dma_stopped=%d\n",
+               __func__, pcdev->active, is_dma_stopped);
+       if (pcdev->active && is_dma_stopped)
+               pxa_camera_start_capture(pcdev);
+ }
+ 
+ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
+                              enum pxa_camera_active_dma act_dma)
+ {
+       struct device *dev = pcdev->soc_host.v4l2_dev.dev;
+       struct pxa_buffer *buf;
+       unsigned long flags;
+       u32 status, camera_status, overrun;
+       struct videobuf_buffer *vb;
+ 
+       spin_lock_irqsave(&pcdev->lock, flags);
+ 
+       status = DCSR(channel);
+       DCSR(channel) = status;
+ 
+       camera_status = __raw_readl(pcdev->base + CISR);
+       overrun = CISR_IFO_0;
+       if (pcdev->channels == 3)
+               overrun |= CISR_IFO_1 | CISR_IFO_2;
+ 
+       if (status & DCSR_BUSERR) {
+               dev_err(dev, "DMA Bus Error IRQ!\n");
+               goto out;
+       }
+ 
+       if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) {
+               dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n",
+                       status);
+               goto out;
+       }
+ 
+       /*
+        * pcdev->active should not be NULL in DMA irq handler.
+        *
+        * But there is one corner case : if capture was stopped due to an
+        * overrun of channel 1, and at that same channel 2 was completed.
+        *
+        * When handling the overrun in DMA irq for channel 1, we'll stop the
+        * capture and restart it (and thus set pcdev->active to NULL). But the
+        * DMA irq handler will already be pending for channel 2. So on entering
+        * the DMA irq handler for channel 2 there will be no active buffer, yet
+        * that is normal.
+        */
+       if (!pcdev->active)
+               goto out;
+ 
+       vb = &pcdev->active->vb;
+       buf = container_of(vb, struct pxa_buffer, vb);
+       WARN_ON(buf->inwork || list_empty(&vb->queue));
+ 
+       dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n",
+               __func__, channel, status & DCSR_STARTINTR ? "SOF " : "",
+               status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel));
+ 
+       if (status & DCSR_ENDINTR) {
+               /*
+                * It's normal if the last frame creates an overrun, as there
+                * are no more DMA descriptors to fetch from QCI fifos
+                */
+               if (camera_status & overrun &&
+                   !list_is_last(pcdev->capture.next, &pcdev->capture)) {
+                       dev_dbg(dev, "FIFO overrun! CISR: %x\n",
+                               camera_status);
+                       pxa_camera_stop_capture(pcdev);
+                       pxa_camera_start_capture(pcdev);
+                       goto out;
+               }
+               buf->active_dma &= ~act_dma;
+               if (!buf->active_dma) {
+                       pxa_camera_wakeup(pcdev, vb, buf);
+                       pxa_camera_check_link_miss(pcdev);
+               }
+       }
+ 
+ out:
+       spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+ 
+ static void pxa_camera_dma_irq_y(int channel, void *data)
+ {
+       struct pxa_camera_dev *pcdev = data;
+       pxa_camera_dma_irq(channel, pcdev, DMA_Y);
+ }
+ 
+ static void pxa_camera_dma_irq_u(int channel, void *data)
+ {
+       struct pxa_camera_dev *pcdev = data;
+       pxa_camera_dma_irq(channel, pcdev, DMA_U);
+ }
+ 
+ static void pxa_camera_dma_irq_v(int channel, void *data)
+ {
+       struct pxa_camera_dev *pcdev = data;
+       pxa_camera_dma_irq(channel, pcdev, DMA_V);
+ }
+ 
+ static struct videobuf_queue_ops pxa_videobuf_ops = {
+       .buf_setup      = pxa_videobuf_setup,
+       .buf_prepare    = pxa_videobuf_prepare,
+       .buf_queue      = pxa_videobuf_queue,
+       .buf_release    = pxa_videobuf_release,
+ };
+ 
+ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
+                             struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+ 
+       /*
+        * We must pass NULL as dev pointer, then all pci_* dma operations
+        * transform to normal dma_* ones.
+        */
+       videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock,
+                               V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+                               sizeof(struct pxa_buffer), icd, &icd->video_lock);
+ }
+ 
+ static u32 mclk_get_divisor(struct platform_device *pdev,
+                           struct pxa_camera_dev *pcdev)
+ {
+       unsigned long mclk = pcdev->mclk;
+       struct device *dev = &pdev->dev;
+       u32 div;
+       unsigned long lcdclk;
+ 
+       lcdclk = clk_get_rate(pcdev->clk);
+       pcdev->ciclk = lcdclk;
+ 
+       /* mclk <= ciclk / 4 (27.4.2) */
+       if (mclk > lcdclk / 4) {
+               mclk = lcdclk / 4;
+               dev_warn(dev, "Limiting master clock to %lu\n", mclk);
+       }
+ 
+       /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */
+       div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
+ 
+       /* If we're not supplying MCLK, leave it at 0 */
+       if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+               pcdev->mclk = lcdclk / (2 * (div + 1));
+ 
+       dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n",
+               lcdclk, mclk, div);
+ 
+       return div;
+ }
+ 
+ static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
+                                    unsigned long pclk)
+ {
+       /* We want a timeout > 1 pixel time, not ">=" */
+       u32 ciclk_per_pixel = pcdev->ciclk / pclk + 1;
+ 
+       __raw_writel(ciclk_per_pixel, pcdev->base + CITOR);
+ }
+ 
+ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
+ {
+       u32 cicr4 = 0;
+ 
+       /* disable all interrupts */
+       __raw_writel(0x3ff, pcdev->base + CICR0);
+ 
+       if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+               cicr4 |= CICR4_PCLK_EN;
+       if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+               cicr4 |= CICR4_MCLK_EN;
+       if (pcdev->platform_flags & PXA_CAMERA_PCP)
+               cicr4 |= CICR4_PCP;
+       if (pcdev->platform_flags & PXA_CAMERA_HSP)
+               cicr4 |= CICR4_HSP;
+       if (pcdev->platform_flags & PXA_CAMERA_VSP)
+               cicr4 |= CICR4_VSP;
+ 
+       __raw_writel(pcdev->mclk_divisor | cicr4, pcdev->base + CICR4);
+ 
+       if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+               /* Initialise the timeout under the assumption pclk = mclk */
+               recalculate_fifo_timeout(pcdev, pcdev->mclk);
+       else
+               /* "Safe default" - 13MHz */
+               recalculate_fifo_timeout(pcdev, 13000000);
+ 
+       clk_prepare_enable(pcdev->clk);
+ }
+ 
+ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
+ {
+       clk_disable_unprepare(pcdev->clk);
+ }
+ 
+ static irqreturn_t pxa_camera_irq(int irq, void *data)
+ {
+       struct pxa_camera_dev *pcdev = data;
+       unsigned long status, cifr, cicr0;
+       struct pxa_buffer *buf;
+       struct videobuf_buffer *vb;
+ 
+       status = __raw_readl(pcdev->base + CISR);
+       dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+               "Camera interrupt status 0x%lx\n", status);
+ 
+       if (!status)
+               return IRQ_NONE;
+ 
+       __raw_writel(status, pcdev->base + CISR);
+ 
+       if (status & CISR_EOF) {
+               /* Reset the FIFOs */
+               cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
+               __raw_writel(cifr, pcdev->base + CIFR);
+ 
+               pcdev->active = list_first_entry(&pcdev->capture,
+                                          struct pxa_buffer, vb.queue);
+               vb = &pcdev->active->vb;
+               buf = container_of(vb, struct pxa_buffer, vb);
+               pxa_videobuf_set_actdma(pcdev, buf);
+ 
+               pxa_dma_start_channels(pcdev);
+ 
+               cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
+               __raw_writel(cicr0, pcdev->base + CICR0);
+       }
+ 
+       return IRQ_HANDLED;
+ }
+ 
+ /*
+  * The following two functions absolutely depend on the fact, that
+  * there can be only one camera on PXA quick capture interface
+  * Called with .video_lock held
+  */
+ static int pxa_camera_add_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+ 
+       if (pcdev->icd)
+               return -EBUSY;
+ 
+       pxa_camera_activate(pcdev);
+ 
+       pcdev->icd = icd;
+ 
+       dev_info(icd->parent, "PXA Camera driver attached to camera %d\n",
+                icd->devnum);
+ 
+       return 0;
+ }
+ 
+ /* Called with .video_lock held */
+ static void pxa_camera_remove_device(struct soc_camera_device *icd)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+ 
+       BUG_ON(icd != pcdev->icd);
+ 
+       dev_info(icd->parent, "PXA Camera driver detached from camera %d\n",
+                icd->devnum);
+ 
+       /* disable capture, disable interrupts */
+       __raw_writel(0x3ff, pcdev->base + CICR0);
+ 
+       /* Stop DMA engine */
+       DCSR(pcdev->dma_chans[0]) = 0;
+       DCSR(pcdev->dma_chans[1]) = 0;
+       DCSR(pcdev->dma_chans[2]) = 0;
+ 
+       pxa_camera_deactivate(pcdev);
+ 
+       pcdev->icd = NULL;
+ }
+ 
+ static int test_platform_param(struct pxa_camera_dev *pcdev,
+                              unsigned char buswidth, unsigned long *flags)
+ {
+       /*
+        * Platform specified synchronization and pixel clock polarities are
+        * only a recommendation and are only used during probing. The PXA270
+        * quick capture interface supports both.
+        */
+       *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ?
+                 V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) |
+               V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_HSYNC_ACTIVE_LOW |
+               V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+               V4L2_MBUS_VSYNC_ACTIVE_LOW |
+               V4L2_MBUS_DATA_ACTIVE_HIGH |
+               V4L2_MBUS_PCLK_SAMPLE_RISING |
+               V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ 
+       /* If requested data width is supported by the platform, use it */
+       if ((1 << (buswidth - 1)) & pcdev->width_flags)
+               return 0;
+ 
+       return -EINVAL;
+ }
+ 
+ static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
+                                 unsigned long flags, __u32 pixfmt)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       unsigned long dw, bpp;
+       u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0, y_skip_top;
+       int ret = v4l2_subdev_call(sd, sensor, g_skip_top_lines, &y_skip_top);
+ 
+       if (ret < 0)
+               y_skip_top = 0;
+ 
+       /*
+        * Datawidth is now guaranteed to be equal to one of the three values.
+        * We fix bit-per-pixel equal to data-width...
+        */
+       switch (icd->current_fmt->host_fmt->bits_per_sample) {
+       case 10:
+               dw = 4;
+               bpp = 0x40;
+               break;
+       case 9:
+               dw = 3;
+               bpp = 0x20;
+               break;
+       default:
+               /*
+                * Actually it can only be 8 now,
+                * default is just to silence compiler warnings
+                */
+       case 8:
+               dw = 2;
+               bpp = 0;
+       }
+ 
+       if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+               cicr4 |= CICR4_PCLK_EN;
+       if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
+               cicr4 |= CICR4_MCLK_EN;
+       if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+               cicr4 |= CICR4_PCP;
+       if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+               cicr4 |= CICR4_HSP;
+       if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+               cicr4 |= CICR4_VSP;
+ 
+       cicr0 = __raw_readl(pcdev->base + CICR0);
+       if (cicr0 & CICR0_ENB)
+               __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0);
+ 
+       cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw;
+ 
+       switch (pixfmt) {
+       case V4L2_PIX_FMT_YUV422P:
+               pcdev->channels = 3;
+               cicr1 |= CICR1_YCBCR_F;
+               /*
+                * Normally, pxa bus wants as input UYVY format. We allow all
+                * reorderings of the YUV422 format, as no processing is done,
+                * and the YUV stream is just passed through without any
+                * transformation. Note that UYVY is the only format that
+                * should be used if pxa framebuffer Overlay2 is used.
+                */
+       case V4L2_PIX_FMT_UYVY:
+       case V4L2_PIX_FMT_VYUY:
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_YVYU:
+               cicr1 |= CICR1_COLOR_SP_VAL(2);
+               break;
+       case V4L2_PIX_FMT_RGB555:
+               cicr1 |= CICR1_RGB_BPP_VAL(1) | CICR1_RGBT_CONV_VAL(2) |
+                       CICR1_TBIT | CICR1_COLOR_SP_VAL(1);
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               cicr1 |= CICR1_COLOR_SP_VAL(1) | CICR1_RGB_BPP_VAL(2);
+               break;
+       }
+ 
+       cicr2 = 0;
+       cicr3 = CICR3_LPF_VAL(icd->user_height - 1) |
+               CICR3_BFW_VAL(min((u32)255, y_skip_top));
+       cicr4 |= pcdev->mclk_divisor;
+ 
+       __raw_writel(cicr1, pcdev->base + CICR1);
+       __raw_writel(cicr2, pcdev->base + CICR2);
+       __raw_writel(cicr3, pcdev->base + CICR3);
+       __raw_writel(cicr4, pcdev->base + CICR4);
+ 
+       /* CIF interrupts are not used, only DMA */
+       cicr0 = (cicr0 & CICR0_ENB) | (pcdev->platform_flags & PXA_CAMERA_MASTER ?
+               CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP));
+       cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK;
+       __raw_writel(cicr0, pcdev->base + CICR0);
+ }
+ 
+ static int pxa_camera_set_bus_param(struct soc_camera_device *icd)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+       unsigned long bus_flags, common_flags;
+       int ret;
+       struct pxa_cam *cam = icd->host_priv;
+ 
+       ret = test_platform_param(pcdev, icd->current_fmt->host_fmt->bits_per_sample,
+                                 &bus_flags);
+       if (ret < 0)
+               return ret;
+ 
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         bus_flags);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%lx\n",
+                                cfg.flags, bus_flags);
+                       return -EINVAL;
+               }
+       } else if (ret != -ENOIOCTLCMD) {
+               return ret;
+       } else {
+               common_flags = bus_flags;
+       }
+ 
+       pcdev->channels = 1;
+ 
+       /* Make choises, based on platform preferences */
+       if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+               if (pcdev->platform_flags & PXA_CAMERA_HSP)
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+           (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+               if (pcdev->platform_flags & PXA_CAMERA_VSP)
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+               else
+                       common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+       }
+ 
+       if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+           (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+               if (pcdev->platform_flags & PXA_CAMERA_PCP)
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+               else
+                       common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+       }
+ 
+       cfg.flags = common_flags;
+       ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+       if (ret < 0 && ret != -ENOIOCTLCMD) {
+               dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+                       common_flags, ret);
+               return ret;
+       }
+ 
+       cam->flags = common_flags;
+ 
+       pxa_camera_setup_cicr(icd, common_flags, pixfmt);
+ 
+       return 0;
+ }
+ 
+ static int pxa_camera_try_bus_param(struct soc_camera_device *icd,
+                                   unsigned char buswidth)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+       unsigned long bus_flags, common_flags;
+       int ret = test_platform_param(pcdev, buswidth, &bus_flags);
+ 
+       if (ret < 0)
+               return ret;
+ 
+       ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+       if (!ret) {
+               common_flags = soc_mbus_config_compatible(&cfg,
+                                                         bus_flags);
+               if (!common_flags) {
+                       dev_warn(icd->parent,
+                                "Flags incompatible: camera 0x%x, host 0x%lx\n",
+                                cfg.flags, bus_flags);
+                       return -EINVAL;
+               }
+       } else if (ret == -ENOIOCTLCMD) {
+               ret = 0;
+       }
+ 
+       return ret;
+ }
+ 
+ static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
+       {
+               .fourcc                 = V4L2_PIX_FMT_YUV422P,
+               .name                   = "Planar YUV422 16 bit",
+               .bits_per_sample        = 8,
+               .packing                = SOC_MBUS_PACKING_2X8_PADHI,
+               .order                  = SOC_MBUS_ORDER_LE,
+               .layout                 = SOC_MBUS_LAYOUT_PLANAR_2Y_U_V,
+       },
+ };
+ 
+ /* This will be corrected as we get more formats */
+ static bool pxa_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+ {
+       return  fmt->packing == SOC_MBUS_PACKING_NONE ||
+               (fmt->bits_per_sample == 8 &&
+                fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+               (fmt->bits_per_sample > 8 &&
+                fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+ }
+ 
+ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+                                 struct soc_camera_format_xlate *xlate)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct device *dev = icd->parent;
+       int formats = 0, ret;
+       struct pxa_cam *cam;
+       enum v4l2_mbus_pixelcode code;
+       const struct soc_mbus_pixelfmt *fmt;
+ 
+       ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+       if (ret < 0)
+               /* No more formats */
+               return 0;
+ 
+       fmt = soc_mbus_get_fmtdesc(code);
+       if (!fmt) {
+               dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
+               return 0;
+       }
+ 
+       /* This also checks support for the requested bits-per-sample */
+       ret = pxa_camera_try_bus_param(icd, fmt->bits_per_sample);
+       if (ret < 0)
+               return 0;
+ 
+       if (!icd->host_priv) {
+               cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+               if (!cam)
+                       return -ENOMEM;
+ 
+               icd->host_priv = cam;
+       } else {
+               cam = icd->host_priv;
+       }
+ 
+       switch (code) {
+       case V4L2_MBUS_FMT_UYVY8_2X8:
+               formats++;
+               if (xlate) {
+                       xlate->host_fmt = &pxa_camera_formats[0];
+                       xlate->code     = code;
+                       xlate++;
+                       dev_dbg(dev, "Providing format %s using code %d\n",
+                               pxa_camera_formats[0].name, code);
+               }
+       case V4L2_MBUS_FMT_VYUY8_2X8:
+       case V4L2_MBUS_FMT_YUYV8_2X8:
+       case V4L2_MBUS_FMT_YVYU8_2X8:
+       case V4L2_MBUS_FMT_RGB565_2X8_LE:
+       case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+               if (xlate)
+                       dev_dbg(dev, "Providing format %s packed\n",
+                               fmt->name);
+               break;
+       default:
+               if (!pxa_camera_packing_supported(fmt))
+                       return 0;
+               if (xlate)
+                       dev_dbg(dev,
+                               "Providing format %s in pass-through mode\n",
+                               fmt->name);
+       }
+ 
+       /* Generic pass-through */
+       formats++;
+       if (xlate) {
+               xlate->host_fmt = fmt;
+               xlate->code     = code;
+               xlate++;
+       }
+ 
+       return formats;
+ }
+ 
+ static void pxa_camera_put_formats(struct soc_camera_device *icd)
+ {
+       kfree(icd->host_priv);
+       icd->host_priv = NULL;
+ }
+ 
+ static int pxa_camera_check_frame(u32 width, u32 height)
+ {
+       /* limit to pxa hardware capabilities */
+       return height < 32 || height > 2048 || width < 48 || width > 2048 ||
+               (width & 0x01);
+ }
+ 
+ static int pxa_camera_set_crop(struct soc_camera_device *icd,
+                              struct v4l2_crop *a)
+ {
+       struct v4l2_rect *rect = &a->c;
+       struct device *dev = icd->parent;
+       struct soc_camera_host *ici = to_soc_camera_host(dev);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct soc_camera_sense sense = {
+               .master_clock = pcdev->mclk,
+               .pixel_clock_max = pcdev->ciclk / 4,
+       };
+       struct v4l2_mbus_framefmt mf;
+       struct pxa_cam *cam = icd->host_priv;
+       u32 fourcc = icd->current_fmt->host_fmt->fourcc;
+       int ret;
+ 
+       /* If PCLK is used to latch data from the sensor, check sense */
+       if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+               icd->sense = &sense;
+ 
+       ret = v4l2_subdev_call(sd, video, s_crop, a);
+ 
+       icd->sense = NULL;
+ 
+       if (ret < 0) {
+               dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n",
+                        rect->width, rect->height, rect->left, rect->top);
+               return ret;
+       }
+ 
+       ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       if (pxa_camera_check_frame(mf.width, mf.height)) {
+               /*
+                * Camera cropping produced a frame beyond our capabilities.
+                * FIXME: just extract a subframe, that we can process.
+                */
+               v4l_bound_align_image(&mf.width, 48, 2048, 1,
+                       &mf.height, 32, 2048, 0,
+                       fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+               ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+               if (ret < 0)
+                       return ret;
+ 
+               if (pxa_camera_check_frame(mf.width, mf.height)) {
+                       dev_warn(icd->parent,
+                                "Inconsistent state. Use S_FMT to repair\n");
+                       return -EINVAL;
+               }
+       }
+ 
+       if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+               if (sense.pixel_clock > sense.pixel_clock_max) {
+                       dev_err(dev,
+                               "pixel clock %lu set by the camera too high!",
+                               sense.pixel_clock);
+                       return -EIO;
+               }
+               recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+       }
+ 
+       icd->user_width         = mf.width;
+       icd->user_height        = mf.height;
+ 
+       pxa_camera_setup_cicr(icd, cam->flags, fourcc);
+ 
+       return ret;
+ }
+ 
+ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct device *dev = icd->parent;
+       struct soc_camera_host *ici = to_soc_camera_host(dev);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate = NULL;
+       struct soc_camera_sense sense = {
+               .master_clock = pcdev->mclk,
+               .pixel_clock_max = pcdev->ciclk / 4,
+       };
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       int ret;
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+       if (!xlate) {
+               dev_warn(dev, "Format %x not found\n", pix->pixelformat);
+               return -EINVAL;
+       }
+ 
+       /* If PCLK is used to latch data from the sensor, check sense */
+       if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+               /* The caller holds a mutex. */
+               icd->sense = &sense;
+ 
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       mf.field        = pix->field;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ 
+       if (mf.code != xlate->code)
+               return -EINVAL;
+ 
+       icd->sense = NULL;
+ 
+       if (ret < 0) {
+               dev_warn(dev, "Failed to configure for format %x\n",
+                        pix->pixelformat);
+       } else if (pxa_camera_check_frame(mf.width, mf.height)) {
+               dev_warn(dev,
+                        "Camera driver produced an unsupported frame %dx%d\n",
+                        mf.width, mf.height);
+               ret = -EINVAL;
+       } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+               if (sense.pixel_clock > sense.pixel_clock_max) {
+                       dev_err(dev,
+                               "pixel clock %lu set by the camera too high!",
+                               sense.pixel_clock);
+                       return -EIO;
+               }
+               recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+       }
+ 
+       if (ret < 0)
+               return ret;
+ 
+       pix->width              = mf.width;
+       pix->height             = mf.height;
+       pix->field              = mf.field;
+       pix->colorspace         = mf.colorspace;
+       icd->current_fmt        = xlate;
+ 
+       return ret;
+ }
+ 
+ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
+                             struct v4l2_format *f)
+ {
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct v4l2_mbus_framefmt mf;
+       __u32 pixfmt = pix->pixelformat;
+       int ret;
+ 
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (!xlate) {
+               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+               return -EINVAL;
+       }
+ 
+       /*
+        * Limit to pxa hardware capabilities.  YUV422P planar format requires
+        * images size to be a multiple of 16 bytes.  If not, zeros will be
+        * inserted between Y and U planes, and U and V planes, which violates
+        * the YUV422P standard.
+        */
+       v4l_bound_align_image(&pix->width, 48, 2048, 1,
+                             &pix->height, 32, 2048, 0,
+                             pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+ 
+       /* limit to sensor capabilities */
+       mf.width        = pix->width;
+       mf.height       = pix->height;
+       /* Only progressive video supported so far */
+       mf.field        = V4L2_FIELD_NONE;
+       mf.colorspace   = pix->colorspace;
+       mf.code         = xlate->code;
+ 
+       ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+       if (ret < 0)
+               return ret;
+ 
+       pix->width      = mf.width;
+       pix->height     = mf.height;
+       pix->colorspace = mf.colorspace;
+ 
+       switch (mf.field) {
+       case V4L2_FIELD_ANY:
+       case V4L2_FIELD_NONE:
+               pix->field      = V4L2_FIELD_NONE;
+               break;
+       default:
+               /* TODO: support interlaced at least in pass-through mode */
+               dev_err(icd->parent, "Field type %d unsupported.\n",
+                       mf.field);
+               return -EINVAL;
+       }
+ 
+       return ret;
+ }
+ 
+ static int pxa_camera_reqbufs(struct soc_camera_device *icd,
+                             struct v4l2_requestbuffers *p)
+ {
+       int i;
+ 
+       /*
+        * This is for locking debugging only. I removed spinlocks and now I
+        * check whether .prepare is ever called on a linked buffer, or whether
+        * a dma IRQ can occur for an in-work or unlinked buffer. Until now
+        * it hadn't triggered
+        */
+       for (i = 0; i < p->count; i++) {
+               struct pxa_buffer *buf = container_of(icd->vb_vidq.bufs[i],
+                                                     struct pxa_buffer, vb);
+               buf->inwork = 0;
+               INIT_LIST_HEAD(&buf->vb.queue);
+       }
+ 
+       return 0;
+ }
+ 
+ static unsigned int pxa_camera_poll(struct file *file, poll_table *pt)
+ {
+       struct soc_camera_device *icd = file->private_data;
+       struct pxa_buffer *buf;
+ 
+       buf = list_entry(icd->vb_vidq.stream.next, struct pxa_buffer,
+                        vb.stream);
+ 
+       poll_wait(file, &buf->vb.done, pt);
+ 
+       if (buf->vb.state == VIDEOBUF_DONE ||
+           buf->vb.state == VIDEOBUF_ERROR)
+               return POLLIN|POLLRDNORM;
+ 
+       return 0;
+ }
+ 
+ static int pxa_camera_querycap(struct soc_camera_host *ici,
+                              struct v4l2_capability *cap)
+ {
+       /* cap->name is set by the firendly caller:-> */
+       strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card));
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ 
+       return 0;
+ }
+ 
+ static int pxa_camera_suspend(struct device *dev)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(dev);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       int i = 0, ret = 0;
+ 
+       pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR0);
+       pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR1);
+       pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR2);
+       pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3);
+       pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4);
+ 
+       if (pcdev->icd) {
+               struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+               ret = v4l2_subdev_call(sd, core, s_power, 0);
+               if (ret == -ENOIOCTLCMD)
+                       ret = 0;
+       }
+ 
+       return ret;
+ }
+ 
+ static int pxa_camera_resume(struct device *dev)
+ {
+       struct soc_camera_host *ici = to_soc_camera_host(dev);
+       struct pxa_camera_dev *pcdev = ici->priv;
+       int i = 0, ret = 0;
+ 
+       DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
+       DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
+       DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
+ 
+       __raw_writel(pcdev->save_cicr[i++] & ~CICR0_ENB, pcdev->base + CICR0);
+       __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR1);
+       __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR2);
+       __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3);
+       __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4);
+ 
+       if (pcdev->icd) {
+               struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd);
+               ret = v4l2_subdev_call(sd, core, s_power, 1);
+               if (ret == -ENOIOCTLCMD)
+                       ret = 0;
+       }
+ 
+       /* Restart frame capture if active buffer exists */
+       if (!ret && pcdev->active)
+               pxa_camera_start_capture(pcdev);
+ 
+       return ret;
+ }
+ 
+ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
+       .owner          = THIS_MODULE,
+       .add            = pxa_camera_add_device,
+       .remove         = pxa_camera_remove_device,
+       .set_crop       = pxa_camera_set_crop,
+       .get_formats    = pxa_camera_get_formats,
+       .put_formats    = pxa_camera_put_formats,
+       .set_fmt        = pxa_camera_set_fmt,
+       .try_fmt        = pxa_camera_try_fmt,
+       .init_videobuf  = pxa_camera_init_videobuf,
+       .reqbufs        = pxa_camera_reqbufs,
+       .poll           = pxa_camera_poll,
+       .querycap       = pxa_camera_querycap,
+       .set_bus_param  = pxa_camera_set_bus_param,
+ };
+ 
+ static int __devinit pxa_camera_probe(struct platform_device *pdev)
+ {
+       struct pxa_camera_dev *pcdev;
+       struct resource *res;
+       void __iomem *base;
+       int irq;
+       int err = 0;
+ 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       if (!res || irq < 0) {
+               err = -ENODEV;
+               goto exit;
+       }
+ 
+       pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+       if (!pcdev) {
+               dev_err(&pdev->dev, "Could not allocate pcdev\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+ 
+       pcdev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(pcdev->clk)) {
+               err = PTR_ERR(pcdev->clk);
+               goto exit_kfree;
+       }
+ 
+       pcdev->res = res;
+ 
+       pcdev->pdata = pdev->dev.platform_data;
+       pcdev->platform_flags = pcdev->pdata->flags;
+       if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 |
+                       PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) {
+               /*
+                * Platform hasn't set available data widths. This is bad.
+                * Warn and use a default.
+                */
+               dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+                        "data widths, using default 10 bit\n");
+               pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10;
+       }
+       if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)
+               pcdev->width_flags = 1 << 7;
+       if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)
+               pcdev->width_flags |= 1 << 8;
+       if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)
+               pcdev->width_flags |= 1 << 9;
+       pcdev->mclk = pcdev->pdata->mclk_10khz * 10000;
+       if (!pcdev->mclk) {
+               dev_warn(&pdev->dev,
+                        "mclk == 0! Please, fix your platform data. "
+                        "Using default 20MHz\n");
+               pcdev->mclk = 20000000;
+       }
+ 
+       pcdev->mclk_divisor = mclk_get_divisor(pdev, pcdev);
+ 
+       INIT_LIST_HEAD(&pcdev->capture);
+       spin_lock_init(&pcdev->lock);
+ 
+       /*
+        * Request the regions.
+        */
+       if (!request_mem_region(res->start, resource_size(res),
+                               PXA_CAM_DRV_NAME)) {
+               err = -EBUSY;
+               goto exit_clk;
+       }
+ 
+       base = ioremap(res->start, resource_size(res));
+       if (!base) {
+               err = -ENOMEM;
+               goto exit_release;
+       }
+       pcdev->irq = irq;
+       pcdev->base = base;
+ 
+       /* request dma */
+       err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
+                             pxa_camera_dma_irq_y, pcdev);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Can't request DMA for Y\n");
+               goto exit_iounmap;
+       }
+       pcdev->dma_chans[0] = err;
+       dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
+ 
+       err = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
+                             pxa_camera_dma_irq_u, pcdev);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Can't request DMA for U\n");
+               goto exit_free_dma_y;
+       }
+       pcdev->dma_chans[1] = err;
+       dev_dbg(&pdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
+ 
+       err = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
+                             pxa_camera_dma_irq_v, pcdev);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Can't request DMA for V\n");
+               goto exit_free_dma_u;
+       }
+       pcdev->dma_chans[2] = err;
+       dev_dbg(&pdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
+ 
+       DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
+       DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD;
+       DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD;
+ 
+       /* request irq */
+       err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME,
+                         pcdev);
+       if (err) {
+               dev_err(&pdev->dev, "Camera interrupt register failed \n");
+               goto exit_free_dma;
+       }
+ 
+       pcdev->soc_host.drv_name        = PXA_CAM_DRV_NAME;
+       pcdev->soc_host.ops             = &pxa_soc_camera_host_ops;
+       pcdev->soc_host.priv            = pcdev;
+       pcdev->soc_host.v4l2_dev.dev    = &pdev->dev;
+       pcdev->soc_host.nr              = pdev->id;
+ 
+       err = soc_camera_host_register(&pcdev->soc_host);
+       if (err)
+               goto exit_free_irq;
+ 
+       return 0;
+ 
+ exit_free_irq:
+       free_irq(pcdev->irq, pcdev);
+ exit_free_dma:
+       pxa_free_dma(pcdev->dma_chans[2]);
+ exit_free_dma_u:
+       pxa_free_dma(pcdev->dma_chans[1]);
+ exit_free_dma_y:
+       pxa_free_dma(pcdev->dma_chans[0]);
+ exit_iounmap:
+       iounmap(base);
+ exit_release:
+       release_mem_region(res->start, resource_size(res));
+ exit_clk:
+       clk_put(pcdev->clk);
+ exit_kfree:
+       kfree(pcdev);
+ exit:
+       return err;
+ }
+ 
+ static int __devexit pxa_camera_remove(struct platform_device *pdev)
+ {
+       struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+       struct pxa_camera_dev *pcdev = container_of(soc_host,
+                                       struct pxa_camera_dev, soc_host);
+       struct resource *res;
+ 
+       clk_put(pcdev->clk);
+ 
+       pxa_free_dma(pcdev->dma_chans[0]);
+       pxa_free_dma(pcdev->dma_chans[1]);
+       pxa_free_dma(pcdev->dma_chans[2]);
+       free_irq(pcdev->irq, pcdev);
+ 
+       soc_camera_host_unregister(soc_host);
+ 
+       iounmap(pcdev->base);
+ 
+       res = pcdev->res;
+       release_mem_region(res->start, resource_size(res));
+ 
+       kfree(pcdev);
+ 
+       dev_info(&pdev->dev, "PXA Camera driver unloaded\n");
+ 
+       return 0;
+ }
+ 
+ static struct dev_pm_ops pxa_camera_pm = {
+       .suspend        = pxa_camera_suspend,
+       .resume         = pxa_camera_resume,
+ };
+ 
+ static struct platform_driver pxa_camera_driver = {
+       .driver         = {
+               .name   = PXA_CAM_DRV_NAME,
+               .pm     = &pxa_camera_pm,
+       },
+       .probe          = pxa_camera_probe,
+       .remove         = __devexit_p(pxa_camera_remove),
+ };
+ 
+ module_platform_driver(pxa_camera_driver);
+ 
+ MODULE_DESCRIPTION("PXA27x SoC Camera Host driver");
+ MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION(PXA_CAM_VERSION);
+ MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME);
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+    cx231xx-cards.c - driver for Conexant Cx23100/101/102
+                               USB video capture devices
+ 
+    Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+                               Based on em28xx driver
+ 
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/i2c.h>
+ #include <linux/usb.h>
+ #include <media/tuner.h>
+ #include <media/tveeprom.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+ 
+ #include <media/cx25840.h>
+ #include "dvb-usb-ids.h"
+ #include "xc5000.h"
+ #include "tda18271.h"
+ 
+ #include "cx231xx.h"
+ 
+ static int tuner = -1;
+ module_param(tuner, int, 0444);
+ MODULE_PARM_DESC(tuner, "tuner type");
+ 
+ static int transfer_mode = 1;
+ module_param(transfer_mode, int, 0444);
+ MODULE_PARM_DESC(transfer_mode, "transfer mode (1-ISO or 0-BULK)");
+ 
+ static unsigned int disable_ir;
+ module_param(disable_ir, int, 0444);
+ MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+ 
+ /* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */
+ static unsigned long cx231xx_devused;
+ 
+ /*
+  *  Reset sequences for analog/digital modes
+  */
+ 
+ static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = {
+       {0x03, 0x01, 10},
+       {0x03, 0x00, 30},
+       {0x03, 0x01, 10},
+       {-1, -1, -1},
+ };
+ 
+ /*
+  *  Board definitions
+  */
+ struct cx231xx_board cx231xx_boards[] = {
+       [CX231XX_BOARD_UNKNOWN] = {
+               .name = "Unknown CX231xx video grabber",
+               .tuner_type = TUNER_ABSENT,
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_3_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_CNXT_CARRAERA] = {
+               .name = "Conexant Hybrid TV - CARRAERA",
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x02,
+               .norm = V4L2_STD_PAL,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_3_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_CNXT_SHELBY] = {
+               .name = "Conexant Hybrid TV - SHELBY",
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x32,
+               .norm = V4L2_STD_NTSC,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_3_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_CNXT_RDE_253S] = {
+               .name = "Conexant Hybrid TV - RDE253S",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x02,
+               .norm = V4L2_STD_PAL,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_3_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+ 
+       [CX231XX_BOARD_CNXT_RDU_253S] = {
+               .name = "Conexant Hybrid TV - RDU253S",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x02,
+               .norm = V4L2_STD_PAL,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_3_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_CNXT_VIDEO_GRABBER] = {
+               .name = "Conexant VIDEO GRABBER",
+               .tuner_type = TUNER_ABSENT,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1c,
+               .gpio_pin_status_mask = 0x4001000,
+               .norm = V4L2_STD_PAL,
+               .no_alt_vanc = 1,
+               .external_av = 1,
+               .has_417 = 1,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_CNXT_RDE_250] = {
+               .name = "Conexant Hybrid TV - rde 250",
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x02,
+               .norm = V4L2_STD_PAL,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_CNXT_RDU_250] = {
+               .name = "Conexant Hybrid TV - RDU 250",
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x32,
+               .norm = V4L2_STD_NTSC,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_TELEVISION,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_VIDEO,
+                               .gpio = NULL,
+                       }
+               },
+       },
+       [CX231XX_BOARD_HAUPPAUGE_EXETER] = {
+               .name = "Hauppauge EXETER",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .demod_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x0e,
+               .norm = V4L2_STD_NTSC,
+ 
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+       [CX231XX_BOARD_HAUPPAUGE_USBLIVE2] = {
+               .name = "Hauppauge USB Live 2",
+               .tuner_type = TUNER_ABSENT,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .norm = V4L2_STD_NTSC,
+               .no_alt_vanc = 1,
+               .external_av = 1,
+               .dont_use_port_3 = 1,
+               .input = {{
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+       [CX231XX_BOARD_KWORLD_UB430_USB_HYBRID] = {
+               .name = "Kworld UB430 USB Hybrid",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */
+               .tuner_sif_gpio = -1,
+               .tuner_scl_gpio = -1,
+               .tuner_sda_gpio = -1,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 2,
+               .demod_i2c_master = 1,
+               .ir_i2c_master = 2,
+               .has_dvb = 1,
+               .demod_addr = 0x10,
+               .norm = V4L2_STD_PAL_M,
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+       [CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = {
+               .name = "Pixelview PlayTV USB Hybrid",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */
+               .tuner_sif_gpio = -1,
+               .tuner_scl_gpio = -1,
+               .tuner_sda_gpio = -1,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 2,
+               .demod_i2c_master = 1,
+               .ir_i2c_master = 2,
+               .rc_map_name = RC_MAP_PIXELVIEW_002T,
+               .has_dvb = 1,
+               .demod_addr = 0x10,
+               .norm = V4L2_STD_PAL_M,
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+       [CX231XX_BOARD_PV_XCAPTURE_USB] = {
+               .name = "Pixelview Xcapture USB",
+               .tuner_type = TUNER_ABSENT,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .norm = V4L2_STD_NTSC,
+               .no_alt_vanc = 1,
+               .external_av = 1,
+               .dont_use_port_3 = 1,
+ 
+               .input = {{
+                               .type = CX231XX_VMUX_COMPOSITE1,
+                               .vmux = CX231XX_VIN_2_1,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }, {
+                               .type = CX231XX_VMUX_SVIDEO,
+                               .vmux = CX231XX_VIN_1_1 |
+                                       (CX231XX_VIN_1_2 << 8) |
+                                       CX25840_SVIDEO_ON,
+                               .amux = CX231XX_AMUX_LINE_IN,
+                               .gpio = NULL,
+                       }
+               },
+       },
+ 
+       [CX231XX_BOARD_ICONBIT_U100] = {
+               .name = "Iconbit Analog Stick U100 FM",
+               .tuner_type = TUNER_ABSENT,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1C,
+               .gpio_pin_status_mask = 0x4001000,
+ 
+               .input = {{
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+       [CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL] = {
+               .name = "Hauppauge WinTV USB2 FM (PAL)",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .norm = V4L2_STD_PAL,
+ 
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+       [CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC] = {
+               .name = "Hauppauge WinTV USB2 FM (NTSC)",
+               .tuner_type = TUNER_NXP_TDA18271,
+               .tuner_addr = 0x60,
+               .tuner_gpio = RDE250_XCV_TUNER,
+               .tuner_sif_gpio = 0x05,
+               .tuner_scl_gpio = 0x1a,
+               .tuner_sda_gpio = 0x1b,
+               .decoder = CX231XX_AVDECODER,
+               .output_mode = OUT_MODE_VIP11,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x0c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 1,
+               .norm = V4L2_STD_NTSC,
+ 
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = NULL,
+               } },
+       },
+ };
+ const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
+ 
+ /* table of devices that work with this driver */
+ struct usb_device_id cx231xx_id_table[] = {
+       {USB_DEVICE(0x0572, 0x5A3C),
+        .driver_info = CX231XX_BOARD_UNKNOWN},
+       {USB_DEVICE(0x0572, 0x58A2),
+        .driver_info = CX231XX_BOARD_CNXT_CARRAERA},
+       {USB_DEVICE(0x0572, 0x58A1),
+        .driver_info = CX231XX_BOARD_CNXT_SHELBY},
+       {USB_DEVICE(0x0572, 0x58A4),
+        .driver_info = CX231XX_BOARD_CNXT_RDE_253S},
+       {USB_DEVICE(0x0572, 0x58A5),
+        .driver_info = CX231XX_BOARD_CNXT_RDU_253S},
+       {USB_DEVICE(0x0572, 0x58A6),
+        .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER},
+       {USB_DEVICE(0x0572, 0x589E),
+        .driver_info = CX231XX_BOARD_CNXT_RDE_250},
+       {USB_DEVICE(0x0572, 0x58A0),
+        .driver_info = CX231XX_BOARD_CNXT_RDU_250},
+       {USB_DEVICE(0x2040, 0xb110),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL},
+       {USB_DEVICE(0x2040, 0xb111),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC},
+       {USB_DEVICE(0x2040, 0xb120),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+       {USB_DEVICE(0x2040, 0xb140),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_EXETER},
+       {USB_DEVICE(0x2040, 0xc200),
+        .driver_info = CX231XX_BOARD_HAUPPAUGE_USBLIVE2},
+       {USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x4000, 0x4001),
+        .driver_info = CX231XX_BOARD_PV_PLAYTV_USB_HYBRID},
+       {USB_DEVICE(USB_VID_PIXELVIEW, 0x5014),
+        .driver_info = CX231XX_BOARD_PV_XCAPTURE_USB},
+       {USB_DEVICE(0x1b80, 0xe424),
+        .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID},
+       {USB_DEVICE(0x1f4d, 0x0237),
+        .driver_info = CX231XX_BOARD_ICONBIT_U100},
+       {},
+ };
+ 
+ MODULE_DEVICE_TABLE(usb, cx231xx_id_table);
+ 
+ /* cx231xx_tuner_callback
+  * will be used to reset XC5000 tuner using GPIO pin
+  */
+ 
+ int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
+ {
+       int rc = 0;
+       struct cx231xx *dev = ptr;
+ 
+       if (dev->tuner_type == TUNER_XC5000) {
+               if (command == XC5000_TUNER_RESET) {
+                       cx231xx_info
+                               ("Tuner CB: RESET: cmd %d : tuner type %d \n",
+                                command, dev->tuner_type);
+                       cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
+                                              1);
+                       msleep(10);
+                       cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
+                                              0);
+                       msleep(330);
+                       cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
+                                              1);
+                       msleep(10);
+               }
+       } else if (dev->tuner_type == TUNER_NXP_TDA18271) {
+               switch (command) {
+               case TDA18271_CALLBACK_CMD_AGC_ENABLE:
+                       if (dev->model == CX231XX_BOARD_PV_PLAYTV_USB_HYBRID)
+                               rc = cx231xx_set_agc_analog_digital_mux_select(dev, arg);
+                       break;
+               default:
+                       rc = -EINVAL;
+                       break;
+               }
+       }
+       return rc;
+ }
+ EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
+ 
+ void cx231xx_reset_out(struct cx231xx *dev)
+ {
+       cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
+       msleep(200);
+       cx231xx_set_gpio_value(dev, CX23417_RESET, 0);
+       msleep(200);
+       cx231xx_set_gpio_value(dev, CX23417_RESET, 1);
+ }
+ void cx231xx_enable_OSC(struct cx231xx *dev)
+ {
+       cx231xx_set_gpio_value(dev, CX23417_OSC_EN, 1);
+ }
+ void cx231xx_sleep_s5h1432(struct cx231xx *dev)
+ {
+       cx231xx_set_gpio_value(dev, SLEEP_S5H1432, 0);
+ }
+ 
+ static inline void cx231xx_set_model(struct cx231xx *dev)
+ {
+       memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
+ }
+ 
+ /* Since cx231xx_pre_card_setup() requires a proper dev->model,
+  * this won't work for boards with generic PCI IDs
+  */
+ void cx231xx_pre_card_setup(struct cx231xx *dev)
+ {
+ 
+       cx231xx_set_model(dev);
+ 
+       cx231xx_info("Identified as %s (card=%d)\n",
+                    dev->board.name, dev->model);
+ 
+       /* set the direction for GPIO pins */
+       if (dev->board.tuner_gpio) {
+               cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
+               cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
+       }
+       if (dev->board.tuner_sif_gpio >= 0)
+               cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
+ 
+       /* request some modules if any required */
+ 
+       /* set the mode to Analog mode initially */
+       cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
+ 
+       /* Unlock device */
+       /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */
+ 
+ }
+ 
+ static void cx231xx_config_tuner(struct cx231xx *dev)
+ {
+       struct tuner_setup tun_setup;
+       struct v4l2_frequency f;
+ 
+       if (dev->tuner_type == TUNER_ABSENT)
+               return;
+ 
+       tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+       tun_setup.type = dev->tuner_type;
+       tun_setup.addr = dev->tuner_addr;
+       tun_setup.tuner_callback = cx231xx_tuner_callback;
+ 
+       tuner_call(dev, tuner, s_type_addr, &tun_setup);
+ 
+ #if 0
+       if (tun_setup.type == TUNER_XC5000) {
+               static struct xc2028_ctrl ctrl = {
+                       .fname = XC5000_DEFAULT_FIRMWARE,
+                       .max_len = 64,
+                       .demod = 0;
+               };
+               struct v4l2_priv_tun_config cfg = {
+                       .tuner = dev->tuner_type,
+                       .priv = &ctrl,
+               };
+               tuner_call(dev, tuner, s_config, &cfg);
+       }
+ #endif
+       /* configure tuner */
+       f.tuner = 0;
+       f.type = V4L2_TUNER_ANALOG_TV;
+       f.frequency = 9076;     /* just a magic number */
+       dev->ctl_freq = f.frequency;
+       call_all(dev, tuner, s_frequency, &f);
+ 
+ }
+ 
+ void cx231xx_card_setup(struct cx231xx *dev)
+ {
+ 
+       cx231xx_set_model(dev);
+ 
+       dev->tuner_type = cx231xx_boards[dev->model].tuner_type;
+       if (cx231xx_boards[dev->model].tuner_addr)
+               dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr;
+ 
+       /* request some modules */
+       if (dev->board.decoder == CX231XX_AVDECODER) {
+               dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                                       &dev->i2c_bus[0].i2c_adap,
+                                       "cx25840", 0x88 >> 1, NULL);
+               if (dev->sd_cx25840 == NULL)
+                       cx231xx_info("cx25840 subdev registration failure\n");
+               cx25840_call(dev, core, load_fw);
+ 
+       }
+ 
+       /* Initialize the tuner */
+       if (dev->board.tuner_type != TUNER_ABSENT) {
+               dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                                                   &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+                                                   "tuner",
+                                                   dev->tuner_addr, NULL);
+               if (dev->sd_tuner == NULL)
+                       cx231xx_info("tuner subdev registration failure\n");
+               else
+                       cx231xx_config_tuner(dev);
+       }
+ }
+ 
+ /*
+  * cx231xx_config()
+  * inits registers with sane defaults
+  */
+ int cx231xx_config(struct cx231xx *dev)
+ {
+       /* TBD need to add cx231xx specific code */
+       dev->mute = 1;          /* maybe not the right place... */
+       dev->volume = 0x1f;
+ 
+       return 0;
+ }
+ 
+ /*
+  * cx231xx_config_i2c()
+  * configure i2c attached devices
+  */
+ void cx231xx_config_i2c(struct cx231xx *dev)
+ {
+       /* u32 input = INPUT(dev->video_input)->vmux; */
+ 
+       call_all(dev, video, s_stream, 1);
+ }
+ 
+ /*
+  * cx231xx_realease_resources()
+  * unregisters the v4l2,i2c and usb devices
+  * called when the device gets disconected or at module unload
+ */
+ void cx231xx_release_resources(struct cx231xx *dev)
+ {
+       cx231xx_release_analog_resources(dev);
+ 
+       cx231xx_remove_from_devlist(dev);
+ 
+       cx231xx_ir_exit(dev);
+ 
+       /* Release I2C buses */
+       cx231xx_dev_uninit(dev);
+ 
+       /* delete v4l2 device */
+       v4l2_device_unregister(&dev->v4l2_dev);
+ 
+       usb_put_dev(dev->udev);
+ 
+       /* Mark device as unused */
+       clear_bit(dev->devno, &cx231xx_devused);
+ 
+       kfree(dev->video_mode.alt_max_pkt_size);
+       kfree(dev->vbi_mode.alt_max_pkt_size);
+       kfree(dev->sliced_cc_mode.alt_max_pkt_size);
+       kfree(dev->ts1_mode.alt_max_pkt_size);
+       kfree(dev);
+ }
+ 
+ /*
+  * cx231xx_init_dev()
+  * allocates and inits the device structs, registers i2c bus and v4l device
+  */
+ static int cx231xx_init_dev(struct cx231xx *dev, struct usb_device *udev,
+                           int minor)
+ {
+       int retval = -ENOMEM;
+       int errCode;
+       unsigned int maxh, maxw;
+ 
+       dev->udev = udev;
+       mutex_init(&dev->lock);
+       mutex_init(&dev->ctrl_urb_lock);
+       mutex_init(&dev->gpio_i2c_lock);
+       mutex_init(&dev->i2c_lock);
+ 
+       spin_lock_init(&dev->video_mode.slock);
+       spin_lock_init(&dev->vbi_mode.slock);
+       spin_lock_init(&dev->sliced_cc_mode.slock);
+ 
+       init_waitqueue_head(&dev->open);
+       init_waitqueue_head(&dev->wait_frame);
+       init_waitqueue_head(&dev->wait_stream);
+ 
+       dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg;
+       dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg;
+       dev->cx231xx_send_usb_command = cx231xx_send_usb_command;
+       dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read;
+       dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write;
+ 
+       /* Query cx231xx to find what pcb config it is related to */
+       initialize_cx231xx(dev);
+ 
+       /*To workaround error number=-71 on EP0 for VideoGrabber,
+                need set alt here.*/
+       if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER ||
+           dev->model == CX231XX_BOARD_HAUPPAUGE_USBLIVE2) {
+               cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3);
+               cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
+       }
+       /* Cx231xx pre card setup */
+       cx231xx_pre_card_setup(dev);
+ 
+       errCode = cx231xx_config(dev);
+       if (errCode) {
+               cx231xx_errdev("error configuring device\n");
+               return -ENOMEM;
+       }
+ 
+       /* set default norm */
+       dev->norm = dev->board.norm;
+ 
+       /* register i2c bus */
+       errCode = cx231xx_dev_init(dev);
+       if (errCode < 0) {
+               cx231xx_dev_uninit(dev);
+               cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
+                              __func__, errCode);
+               return errCode;
+       }
+ 
+       /* Do board specific init */
+       cx231xx_card_setup(dev);
+ 
+       /* configure the device */
+       cx231xx_config_i2c(dev);
+ 
+       maxw = norm_maxw(dev);
+       maxh = norm_maxh(dev);
+ 
+       /* set default image size */
+       dev->width = maxw;
+       dev->height = maxh;
+       dev->interlaced = 0;
+       dev->video_input = 0;
+ 
+       errCode = cx231xx_config(dev);
+       if (errCode < 0) {
+               cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
+                              __func__, errCode);
+               return errCode;
+       }
+ 
+       /* init video dma queues */
+       INIT_LIST_HEAD(&dev->video_mode.vidq.active);
+       INIT_LIST_HEAD(&dev->video_mode.vidq.queued);
+ 
+       /* init vbi dma queues */
+       INIT_LIST_HEAD(&dev->vbi_mode.vidq.active);
+       INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
+ 
+       /* Reset other chips required if they are tied up with GPIO pins */
+       cx231xx_add_into_devlist(dev);
+ 
+       if (dev->board.has_417) {
+               printk(KERN_INFO "attach 417 %d\n", dev->model);
+               if (cx231xx_417_register(dev) < 0) {
+                       printk(KERN_ERR
+                               "%s() Failed to register 417 on VID_B\n",
+                              __func__);
+               }
+       }
+ 
+       retval = cx231xx_register_analog_devices(dev);
+       if (retval < 0) {
+               cx231xx_release_resources(dev);
+               return retval;
+       }
+ 
+       cx231xx_ir_init(dev);
+ 
+       cx231xx_init_extension(dev);
+ 
+       return 0;
+ }
+ 
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       struct cx231xx *dev = container_of(work,
+                                          struct cx231xx, request_module_wk);
+ 
+       if (dev->has_alsa_audio)
+               request_module("cx231xx-alsa");
+ 
+       if (dev->board.has_dvb)
+               request_module("cx231xx-dvb");
+ 
+ }
+ 
+ static void request_modules(struct cx231xx *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_modules(struct cx231xx *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ 
+ /*
+  * cx231xx_usb_probe()
+  * checks for supported devices
+  */
+ static int cx231xx_usb_probe(struct usb_interface *interface,
+                            const struct usb_device_id *id)
+ {
+       struct usb_device *udev;
+       struct usb_interface *uif;
+       struct cx231xx *dev = NULL;
+       int retval = -ENODEV;
+       int nr = 0, ifnum;
+       int i, isoc_pipe = 0;
+       char *speed;
+       struct usb_interface_assoc_descriptor *assoc_desc;
+ 
+       udev = usb_get_dev(interface_to_usbdev(interface));
+       ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+ 
+       /*
+        * Interface number 0 - IR interface (handled by mceusb driver)
+        * Interface number 1 - AV interface (handled by this driver)
+        */
+       if (ifnum != 1)
+               return -ENODEV;
+ 
+       /* Check to see next free device and mark as used */
+       do {
+               nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
+               if (nr >= CX231XX_MAXBOARDS) {
+                       /* No free device slots */
+                       cx231xx_err(DRIVER_NAME ": Supports only %i devices.\n",
+                                       CX231XX_MAXBOARDS);
+                       return -ENOMEM;
+               }
+       } while (test_and_set_bit(nr, &cx231xx_devused));
+ 
+       /* allocate memory for our device state and initialize it */
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL) {
+               cx231xx_err(DRIVER_NAME ": out of memory!\n");
+               clear_bit(nr, &cx231xx_devused);
+               return -ENOMEM;
+       }
+ 
+       snprintf(dev->name, 29, "cx231xx #%d", nr);
+       dev->devno = nr;
+       dev->model = id->driver_info;
+       dev->video_mode.alt = -1;
+ 
+       dev->interface_count++;
+       /* reset gpio dir and value */
+       dev->gpio_dir = 0;
+       dev->gpio_val = 0;
+       dev->xc_fw_load_done = 0;
+       dev->has_alsa_audio = 1;
+       dev->power_mode = -1;
+       atomic_set(&dev->devlist_count, 0);
+ 
+       /* 0 - vbi ; 1 -sliced cc mode */
+       dev->vbi_or_sliced_cc_mode = 0;
+ 
+       /* get maximum no.of IAD interfaces */
+       assoc_desc = udev->actconfig->intf_assoc[0];
+       dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
+ 
+       /* init CIR module TBD */
+ 
+       /*mode_tv: digital=1 or analog=0*/
+       dev->mode_tv = 0;
+ 
+       dev->USE_ISO = transfer_mode;
+ 
+       switch (udev->speed) {
+       case USB_SPEED_LOW:
+               speed = "1.5";
+               break;
+       case USB_SPEED_UNKNOWN:
+       case USB_SPEED_FULL:
+               speed = "12";
+               break;
+       case USB_SPEED_HIGH:
+               speed = "480";
+               break;
+       default:
+               speed = "unknown";
+       }
+ 
+       cx231xx_info("New device %s %s @ %s Mbps "
+            "(%04x:%04x) with %d interfaces\n",
+            udev->manufacturer ? udev->manufacturer : "",
+            udev->product ? udev->product : "",
+            speed,
+            le16_to_cpu(udev->descriptor.idVendor),
+            le16_to_cpu(udev->descriptor.idProduct),
+            dev->max_iad_interface_count);
+ 
+       /* increment interface count */
+       dev->interface_count++;
+ 
+       /* get device number */
+       nr = dev->devno;
+ 
+       assoc_desc = udev->actconfig->intf_assoc[0];
+       if (assoc_desc->bFirstInterface != ifnum) {
+               cx231xx_err(DRIVER_NAME ": Not found "
+                           "matching IAD interface\n");
+               clear_bit(dev->devno, &cx231xx_devused);
+               kfree(dev);
+               dev = NULL;
+               return -ENODEV;
+       }
+ 
+       cx231xx_info("registering interface %d\n", ifnum);
+ 
+       /* save our data pointer in this interface device */
+       usb_set_intfdata(interface, dev);
+ 
+       /*
+        * AV device initialization - only done at the last interface
+        */
+ 
+       /* Create v4l2 device */
+       retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+       if (retval) {
+               cx231xx_errdev("v4l2_device_register failed\n");
+               clear_bit(dev->devno, &cx231xx_devused);
+               kfree(dev);
+               dev = NULL;
+               return -EIO;
+       }
+       /* allocate device struct */
+       retval = cx231xx_init_dev(dev, udev, nr);
+       if (retval) {
+               clear_bit(dev->devno, &cx231xx_devused);
+               v4l2_device_unregister(&dev->v4l2_dev);
+               kfree(dev);
+               dev = NULL;
+               usb_set_intfdata(interface, NULL);
+ 
+               return retval;
+       }
+ 
+       /* compute alternate max packet sizes for video */
+       uif = udev->actconfig->interface[dev->current_pcb_config.
+                      hs_config_info[0].interface_info.video_index + 1];
+ 
+       dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0].
+                       endpoint[isoc_pipe].desc.bEndpointAddress);
+ 
+       dev->video_mode.num_alt = uif->num_altsetting;
+       cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+                    dev->video_mode.end_point_addr,
+                    dev->video_mode.num_alt);
+       dev->video_mode.alt_max_pkt_size =
+               kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL);
+ 
+       if (dev->video_mode.alt_max_pkt_size == NULL) {
+               cx231xx_errdev("out of memory!\n");
+               clear_bit(dev->devno, &cx231xx_devused);
+               v4l2_device_unregister(&dev->v4l2_dev);
+               kfree(dev);
+               dev = NULL;
+               return -ENOMEM;
+       }
+ 
+       for (i = 0; i < dev->video_mode.num_alt; i++) {
+               u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+                               desc.wMaxPacketSize);
+               dev->video_mode.alt_max_pkt_size[i] =
+                   (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+               cx231xx_info("Alternate setting %i, max size= %i\n", i,
+                            dev->video_mode.alt_max_pkt_size[i]);
+       }
+ 
+       /* compute alternate max packet sizes for vbi */
+       uif = udev->actconfig->interface[dev->current_pcb_config.
+                                      hs_config_info[0].interface_info.
+                                      vanc_index + 1];
+ 
+       dev->vbi_mode.end_point_addr =
+           le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
+                       bEndpointAddress);
+ 
+       dev->vbi_mode.num_alt = uif->num_altsetting;
+       cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+                    dev->vbi_mode.end_point_addr,
+                    dev->vbi_mode.num_alt);
+       dev->vbi_mode.alt_max_pkt_size =
+           kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL);
+ 
+       if (dev->vbi_mode.alt_max_pkt_size == NULL) {
+               cx231xx_errdev("out of memory!\n");
+               clear_bit(dev->devno, &cx231xx_devused);
+               v4l2_device_unregister(&dev->v4l2_dev);
+               kfree(dev);
+               dev = NULL;
+               return -ENOMEM;
+       }
+ 
+       for (i = 0; i < dev->vbi_mode.num_alt; i++) {
+               u16 tmp =
+                   le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+                               desc.wMaxPacketSize);
+               dev->vbi_mode.alt_max_pkt_size[i] =
+                   (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+               cx231xx_info("Alternate setting %i, max size= %i\n", i,
+                            dev->vbi_mode.alt_max_pkt_size[i]);
+       }
+ 
+       /* compute alternate max packet sizes for sliced CC */
+       uif = udev->actconfig->interface[dev->current_pcb_config.
+                                      hs_config_info[0].interface_info.
+                                      hanc_index + 1];
+ 
+       dev->sliced_cc_mode.end_point_addr =
+           le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
+                       bEndpointAddress);
+ 
+       dev->sliced_cc_mode.num_alt = uif->num_altsetting;
+       cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+                    dev->sliced_cc_mode.end_point_addr,
+                    dev->sliced_cc_mode.num_alt);
+       dev->sliced_cc_mode.alt_max_pkt_size =
+               kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL);
+ 
+       if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
+               cx231xx_errdev("out of memory!\n");
+               clear_bit(dev->devno, &cx231xx_devused);
+               v4l2_device_unregister(&dev->v4l2_dev);
+               kfree(dev);
+               dev = NULL;
+               return -ENOMEM;
+       }
+ 
+       for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
+               u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
+                               desc.wMaxPacketSize);
+               dev->sliced_cc_mode.alt_max_pkt_size[i] =
+                   (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+               cx231xx_info("Alternate setting %i, max size= %i\n", i,
+                            dev->sliced_cc_mode.alt_max_pkt_size[i]);
+       }
+ 
+       if (dev->current_pcb_config.ts1_source != 0xff) {
+               /* compute alternate max packet sizes for TS1 */
+               uif = udev->actconfig->interface[dev->current_pcb_config.
+                                              hs_config_info[0].
+                                              interface_info.
+                                              ts1_index + 1];
+ 
+               dev->ts1_mode.end_point_addr =
+                   le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].
+                               desc.bEndpointAddress);
+ 
+               dev->ts1_mode.num_alt = uif->num_altsetting;
+               cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
+                            dev->ts1_mode.end_point_addr,
+                            dev->ts1_mode.num_alt);
+               dev->ts1_mode.alt_max_pkt_size =
+                       kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL);
+ 
+               if (dev->ts1_mode.alt_max_pkt_size == NULL) {
+                       cx231xx_errdev("out of memory!\n");
+                       clear_bit(dev->devno, &cx231xx_devused);
+                       v4l2_device_unregister(&dev->v4l2_dev);
+                       kfree(dev);
+                       dev = NULL;
+                       return -ENOMEM;
+               }
+ 
+               for (i = 0; i < dev->ts1_mode.num_alt; i++) {
+                       u16 tmp = le16_to_cpu(uif->altsetting[i].
+                                               endpoint[isoc_pipe].desc.
+                                               wMaxPacketSize);
+                       dev->ts1_mode.alt_max_pkt_size[i] =
+                           (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+                       cx231xx_info("Alternate setting %i, max size= %i\n", i,
+                                    dev->ts1_mode.alt_max_pkt_size[i]);
+               }
+       }
+ 
+       if (dev->model == CX231XX_BOARD_CNXT_VIDEO_GRABBER) {
+               cx231xx_enable_OSC(dev);
+               cx231xx_reset_out(dev);
+               cx231xx_set_alt_setting(dev, INDEX_VIDEO, 3);
+       }
+ 
+       if (dev->model == CX231XX_BOARD_CNXT_RDE_253S)
+               cx231xx_sleep_s5h1432(dev);
+ 
+       /* load other modules required */
+       request_modules(dev);
+ 
+       return 0;
+ }
+ 
+ /*
+  * cx231xx_usb_disconnect()
+  * called when the device gets diconencted
+  * video device will be unregistered on v4l2_close in case it is still open
+  */
+ static void cx231xx_usb_disconnect(struct usb_interface *interface)
+ {
+       struct cx231xx *dev;
+ 
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+ 
+       if (!dev)
+               return;
+ 
+       if (!dev->udev)
+               return;
+ 
+       dev->state |= DEV_DISCONNECTED;
+ 
+       flush_request_modules(dev);
+ 
+       /* wait until all current v4l2 io is finished then deallocate
+          resources */
+       mutex_lock(&dev->lock);
+ 
+       wake_up_interruptible_all(&dev->open);
+ 
+       if (dev->users) {
+               cx231xx_warn
+                   ("device %s is open! Deregistration and memory "
+                    "deallocation are deferred on close.\n",
+                    video_device_node_name(dev->vdev));
+ 
+               /* Even having users, it is safe to remove the RC i2c driver */
+               cx231xx_ir_exit(dev);
+ 
+               if (dev->USE_ISO)
+                       cx231xx_uninit_isoc(dev);
+               else
+                       cx231xx_uninit_bulk(dev);
+               wake_up_interruptible(&dev->wait_frame);
+               wake_up_interruptible(&dev->wait_stream);
+       } else {
+       }
+ 
+       cx231xx_close_extension(dev);
+ 
+       mutex_unlock(&dev->lock);
+ 
+       if (!dev->users)
+               cx231xx_release_resources(dev);
+ }
+ 
+ static struct usb_driver cx231xx_usb_driver = {
+       .name = "cx231xx",
+       .probe = cx231xx_usb_probe,
+       .disconnect = cx231xx_usb_disconnect,
+       .id_table = cx231xx_id_table,
+ };
+ 
+ module_usb_driver(cx231xx_usb_driver);
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+    em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+                   video capture devices
+ 
+    Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+                     Markus Rechberger <mrechberger@gmail.com>
+                     Mauro Carvalho Chehab <mchehab@infradead.org>
+                     Sascha Sommer <saschasommer@freenet.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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/i2c.h>
+ #include <linux/usb.h>
+ #include <media/tuner.h>
+ #include <media/msp3400.h>
+ #include <media/saa7115.h>
+ #include <media/tvp5150.h>
+ #include <media/tvaudio.h>
+ #include <media/mt9v011.h>
+ #include <media/i2c-addr.h>
+ #include <media/tveeprom.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+ 
+ #include "em28xx.h"
+ 
+ #define DRIVER_NAME         "em28xx"
+ 
+ static int tuner = -1;
+ module_param(tuner, int, 0444);
+ MODULE_PARM_DESC(tuner, "tuner type");
+ 
+ static unsigned int disable_ir;
+ module_param(disable_ir, int, 0444);
+ MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+ 
+ static unsigned int disable_usb_speed_check;
+ module_param(disable_usb_speed_check, int, 0444);
+ MODULE_PARM_DESC(disable_usb_speed_check,
+                "override min bandwidth requirement of 480M bps");
+ 
+ static unsigned int card[]     = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+ module_param_array(card,  int, NULL, 0444);
+ MODULE_PARM_DESC(card,     "card type");
+ 
+ /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
+ static unsigned long em28xx_devused;
+ 
+ struct em28xx_hash_table {
+       unsigned long hash;
+       unsigned int  model;
+       unsigned int  tuner;
+ };
+ 
+ static void em28xx_pre_card_setup(struct em28xx *dev);
+ 
+ /*
+  *  Reset sequences for analog/digital modes
+  */
+ 
+ /* Reset for the most [analog] boards */
+ static struct em28xx_reg_seq default_analog[] = {
+       {EM28XX_R08_GPIO,       0x6d,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ /* Reset for the most [digital] boards */
+ static struct em28xx_reg_seq default_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ /* Board Hauppauge WinTV HVR 900 analog */
+ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+       {EM28XX_R08_GPIO,       0x2d,   ~EM_GPIO_4,     10},
+       {0x05,                  0xff,   0x10,           10},
+       {  -1,                  -1,     -1,             -1},
+ };
+ 
+ /* Board Hauppauge WinTV HVR 900 digital */
+ static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+       {EM28XX_R08_GPIO,       0x2e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x04,   0x0f,           10},
+       {EM2880_R04_GPO,        0x0c,   0x0f,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ /* Board Hauppauge WinTV HVR 900 (R2) digital */
+ static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
+       {EM28XX_R08_GPIO,       0x2e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x0c,   0x0f,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+ static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+       {EM28XX_R08_GPIO,       0x69,   ~EM_GPIO_4,      10},
+       {       -1,             -1,     -1,              -1},
+ };
+ 
+ /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+ 
+ /* Board  - EM2870 Kworld 355u
+    Analog - No input analog */
+ 
+ /* Board - EM2882 Kworld 315U digital */
+ static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xfe,   0xff,           10},
+       {EM2880_R04_GPO,        0x04,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x7e,   0xff,           10},
+       {  -1,                  -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,           10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,           10},
+       {  -1,                  -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq kworld_330u_analog[] = {
+       {EM28XX_R08_GPIO,       0x6d,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x00,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq kworld_330u_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ /* Evga inDtube
+    GPIO0 - Enable digital power (s5h1409) - low to enable
+    GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
+    GPIO4 - xc3028 reset
+    GOP3  - s5h1409 reset
+  */
+ static struct em28xx_reg_seq evga_indtube_analog[] = {
+       {EM28XX_R08_GPIO,       0x79,   0xff,           60},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq evga_indtube_digital[] = {
+       {EM28XX_R08_GPIO,       0x7a,   0xff,            1},
+       {EM2880_R04_GPO,        0x04,   0xff,           10},
+       {EM2880_R04_GPO,        0x0c,   0xff,            1},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ /*
+  * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
+  * EM_GPIO_0 - currently unknown
+  * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
+  * EM_GPIO_2 - currently unknown
+  * EM_GPIO_3 - currently unknown
+  * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
+  * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
+  * EM_GPIO_6 - currently unknown
+  * EM_GPIO_7 - currently unknown
+  */
+ static struct em28xx_reg_seq kworld_a340_digital[] = {
+       {EM28XX_R08_GPIO,       0x6d,           ~EM_GPIO_4,     10},
+       { -1,                   -1,             -1,             -1},
+ };
+ 
+ /* Pinnacle Hybrid Pro eb1a:2881 */
+ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+       {EM28XX_R08_GPIO,       0xfd,   ~EM_GPIO_4,     10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x04,   0xff,          100},/* zl10353 reset */
+       {EM2880_R04_GPO,        0x0c,   0xff,            1},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
+       {EM28XX_R08_GPIO,       0x6d,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x00,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ /* eb1a:2868 Reddo DVB-C USB TV Box
+    GPIO4 - CU1216L NIM
+    Other GPIOs seems to be don't care. */
+ static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
+       {EM28XX_R08_GPIO,       0xfe,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xde,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xfe,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x7f,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x6f,   0xff,           10},
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {-1,                    -1,     -1,             -1},
+ };
+ 
+ /* Callback for the most boards */
+ static struct em28xx_reg_seq default_tuner_gpio[] = {
+       {EM28XX_R08_GPIO,       EM_GPIO_4,      EM_GPIO_4,      10},
+       {EM28XX_R08_GPIO,       0,              EM_GPIO_4,      10},
+       {EM28XX_R08_GPIO,       EM_GPIO_4,      EM_GPIO_4,      10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ 
+ /* Mute/unmute */
+ static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
+       {EM28XX_R08_GPIO,       5,              7,              10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
+       {EM28XX_R08_GPIO,       4,              7,              10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq compro_mute_gpio[] = {
+       {EM28XX_R08_GPIO,       6,              7,              10},
+       {  -1,                  -1,             -1,             -1},
+ };
+ 
+ /* Terratec AV350 */
+ static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
+       {EM28XX_R08_GPIO,       0xff,   0x7f,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,           10},
+       {EM28XX_R08_GPIO,       0x01,   0xf7,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq vc211a_enable[] = {
+       {EM28XX_R08_GPIO,       0xff,   0x07,           10},
+       {EM28XX_R08_GPIO,       0xff,   0x0f,           10},
+       {EM28XX_R08_GPIO,       0xff,   0x0b,           10},
+       {       -1,             -1,     -1,             -1},
+ };
+ 
+ static struct em28xx_reg_seq dikom_dk300_digital[] = {
+       {EM28XX_R08_GPIO,       0x6e,   ~EM_GPIO_4,     10},
+       {EM2880_R04_GPO,        0x08,   0xff,           10},
+       { -1,                   -1,     -1,             -1},
+ };
+ 
+ 
+ /* Reset for the most [digital] boards */
+ static struct em28xx_reg_seq leadership_digital[] = {
+       {EM2874_R80_GPIO,       0x70,   0xff,   10},
+       {       -1,             -1,     -1,     -1},
+ };
+ 
+ static struct em28xx_reg_seq leadership_reset[] = {
+       {EM2874_R80_GPIO,       0xf0,   0xff,   10},
+       {EM2874_R80_GPIO,       0xb0,   0xff,   10},
+       {EM2874_R80_GPIO,       0xf0,   0xff,   10},
+       {       -1,             -1,     -1,     -1},
+ };
+ 
+ /* 2013:024f PCTV nanoStick T2 290e
+  * GPIO_6 - demod reset
+  * GPIO_7 - LED
+  */
+ static struct em28xx_reg_seq pctv_290e[] = {
+       {EM2874_R80_GPIO,       0x00,   0xff,           80},
+       {EM2874_R80_GPIO,       0x40,   0xff,           80}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO,       0xc0,   0xff,           80}, /* GPIO_7 = 1 */
+       {-1,                    -1,     -1,             -1},
+ };
+ 
+ #if 0
+ static struct em28xx_reg_seq terratec_h5_gpio[] = {
+       {EM28XX_R08_GPIO,       0xff,   0xff,   10},
+       {EM2874_R80_GPIO,       0xf6,   0xff,   100},
+       {EM2874_R80_GPIO,       0xf2,   0xff,   50},
+       {EM2874_R80_GPIO,       0xf6,   0xff,   50},
+       { -1,                   -1,     -1,     -1},
+ };
+ 
+ static struct em28xx_reg_seq terratec_h5_digital[] = {
+       {EM2874_R80_GPIO,       0xf6,   0xff,   10},
+       {EM2874_R80_GPIO,       0xe6,   0xff,   100},
+       {EM2874_R80_GPIO,       0xa6,   0xff,   10},
+       { -1,                   -1,     -1,     -1},
+ };
+ #endif
+ 
+ /* 2013:024f PCTV DVB-S2 Stick 460e
+  * GPIO_0 - POWER_ON
+  * GPIO_1 - BOOST
+  * GPIO_2 - VUV_LNB (red LED)
+  * GPIO_3 - EXT_12V
+  * GPIO_4 - INT_DEM (DEMOD GPIO_0)
+  * GPIO_5 - INT_LNB
+  * GPIO_6 - RESET_DEM
+  * GPIO_7 - LED (green LED)
+  */
+ static struct em28xx_reg_seq pctv_460e[] = {
+       {EM2874_R80_GPIO, 0x01, 0xff,  50},
+       {0x0d,            0xff, 0xff,  50},
+       {EM2874_R80_GPIO, 0x41, 0xff,  50}, /* GPIO_6=1 */
+       {0x0d,            0x42, 0xff,  50},
+       {EM2874_R80_GPIO, 0x61, 0xff,  50}, /* GPIO_5=1 */
+       {             -1,   -1,   -1,  -1},
+ };
+ 
+ #if 0
+ static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
+       {EM2874_R80_GPIO,       0x6f,   0xff,   10},
+       {EM2874_R80_GPIO,       0x4f,   0xff,   10}, /* xc5000 reset */
+       {EM2874_R80_GPIO,       0x6f,   0xff,   10},
+       {EM2874_R80_GPIO,       0x4f,   0xff,   10},
+       { -1,                   -1,     -1,     -1},
+ };
+ 
+ static struct em28xx_reg_seq hauppauge_930c_digital[] = {
+       {EM2874_R80_GPIO,       0xf6,   0xff,   10},
+       {EM2874_R80_GPIO,       0xe6,   0xff,   100},
+       {EM2874_R80_GPIO,       0xa6,   0xff,   10},
+       { -1,                   -1,     -1,     -1},
+ };
+ #endif
+ 
+ /* 1b80:e425 MaxMedia UB425-TC
+  * GPIO_6 - demod reset, 0=active
+  * GPIO_7 - LED, 0=active
+  */
+ static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
+       {EM2874_R80_GPIO,  0x83,  0xff,  100},
+       {EM2874_R80_GPIO,  0xc3,  0xff,  100}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO,  0x43,  0xff,  000}, /* GPIO_7 = 0 */
+       {-1,                 -1,    -1,   -1},
+ };
+ 
+ /* 2304:0242 PCTV QuatroStick (510e)
+  * GPIO_2: decoder reset, 0=active
+  * GPIO_4: decoder suspend, 0=active
+  * GPIO_6: demod reset, 0=active
+  * GPIO_7: LED, 1=active
+  */
+ static struct em28xx_reg_seq pctv_510e[] = {
+       {EM2874_R80_GPIO, 0x10, 0xff, 100},
+       {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+       {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+       {             -1,   -1,   -1,  -1},
+ };
+ 
+ /* 2013:0251 PCTV QuatroStick nano (520e)
+  * GPIO_2: decoder reset, 0=active
+  * GPIO_4: decoder suspend, 0=active
+  * GPIO_6: demod reset, 0=active
+  * GPIO_7: LED, 1=active
+  */
+ static struct em28xx_reg_seq pctv_520e[] = {
+       {EM2874_R80_GPIO, 0x10, 0xff, 100},
+       {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+       {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+       {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
+       {             -1,   -1,   -1,  -1},
+ };
+ 
+ /*
+  *  Board definitions
+  */
+ struct em28xx_board em28xx_boards[] = {
+       [EM2750_BOARD_UNKNOWN] = {
+               .name          = "EM2710/EM2750/EM2751 webcam grabber",
+               .xclk          = EM28XX_XCLK_FREQUENCY_20MHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .is_webcam     = 1,
+               .input         = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = silvercrest_reg_seq,
+               } },
+       },
+       [EM2800_BOARD_UNKNOWN] = {
+               .name         = "Unknown EM2800 video grabber",
+               .is_em2800    = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .tuner_type   = TUNER_ABSENT,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_UNKNOWN] = {
+               .name          = "Unknown EM2750/28xx video grabber",
+               .tuner_type    = TUNER_ABSENT,
+               .is_webcam     = 1,     /* To enable sensor probe */
+       },
+       [EM2750_BOARD_DLCW_130] = {
+               /* Beijing Huaqi Information Digital Technology Co., Ltd */
+               .name          = "Huaqi DLCW-130",
+               .valid         = EM28XX_BOARD_NOT_VALIDATED,
+               .xclk          = EM28XX_XCLK_FREQUENCY_48MHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .is_webcam     = 1,
+               .input         = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2820_BOARD_KWORLD_PVRTV2800RF] = {
+               .name         = "Kworld PVR TV 2800 RF",
+               .tuner_type   = TUNER_TEMIC_PAL,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_GADMEI_TVR200] = {
+               .name         = "Gadmei TVR200",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_TERRATEC_CINERGY_250] = {
+               .name         = "Terratec Cinergy 250 USB",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .has_ir_i2c   = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PINNACLE_USB_2] = {
+               .name         = "Pinnacle PCTV USB 2",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .has_ir_i2c   = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
+               .name         = "Hauppauge WinTV USB 2",
+               .tuner_type   = TUNER_PHILIPS_FM1236_MK3,
+               .tda9887_conf = TDA9887_PRESENT |
+                               TDA9887_PORT1_ACTIVE |
+                               TDA9887_PORT2_ACTIVE,
+               .decoder      = EM28XX_TVP5150,
+               .has_msp34xx  = 1,
+               .has_ir_i2c   = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = MSP_INPUT_DEFAULT,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+                                       MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
+               } },
+       },
+       [EM2820_BOARD_DLINK_USB_TV] = {
+               .name         = "D-Link DUB-T210 TV Tuner",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
+               .name         = "Hercules Smart TV USB 2.0",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
+               .name         = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_PHILIPS_FM1216ME_MK3,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_GADMEI_UTV310] = {
+               .name         = "Gadmei UTV310",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_TNF_5335MF,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
+               .name         = "Leadtek Winfast USB II Deluxe",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_PHILIPS_FM1216ME_MK3,
+               .has_ir_i2c   = 1,
+               .tvaudio_addr = 0x58,
+               .tda9887_conf = TDA9887_PRESENT |
+                               TDA9887_PORT2_ACTIVE |
+                               TDA9887_QSS,
+               .decoder      = EM28XX_SAA711X,
+               .adecoder     = EM28XX_TVAUDIO,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE4,
+                       .amux     = EM28XX_AMUX_AUX,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE5,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+                       .radio    = {
+                       .type     = EM28XX_RADIO,
+                       .amux     = EM28XX_AMUX_AUX,
+                       }
+       },
+       [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
+               .name         = "Videology 20K14XUSB USB2.0",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,
+               .is_webcam    = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2820_BOARD_SILVERCREST_WEBCAM] = {
+               .name         = "Silvercrest Webcam 1.3mpix",
+               .tuner_type   = TUNER_ABSENT,
+               .is_webcam    = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = silvercrest_reg_seq,
+               } },
+       },
+       [EM2821_BOARD_SUPERCOMP_USB_2] = {
+               .name         = "Supercomp USB 2.0 TV",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_PHILIPS_FM1236_MK3,
+               .tda9887_conf = TDA9887_PRESENT |
+                               TDA9887_PORT1_ACTIVE |
+                               TDA9887_PORT2_ACTIVE,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2821_BOARD_USBGEAR_VD204] = {
+               .name         = "Usbgear VD204v9",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,   /* Capture only device */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type  = EM28XX_VMUX_COMPOSITE1,
+                       .vmux  = SAA7115_COMPOSITE0,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type  = EM28XX_VMUX_SVIDEO,
+                       .vmux  = SAA7115_SVIDEO3,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_NETGMBH_CAM] = {
+               /* Beijing Huaqi Information Digital Technology Co., Ltd */
+               .name         = "NetGMBH Cam",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,
+               .is_webcam    = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = 0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2860_BOARD_TYPHOON_DVD_MAKER] = {
+               .name         = "Typhoon DVD Maker",
+               .decoder      = EM28XX_SAA711X,
+               .tuner_type   = TUNER_ABSENT,   /* Capture only device */
+               .input        = { {
+                       .type  = EM28XX_VMUX_COMPOSITE1,
+                       .vmux  = SAA7115_COMPOSITE0,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type  = EM28XX_VMUX_SVIDEO,
+                       .vmux  = SAA7115_SVIDEO3,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_GADMEI_UTV330] = {
+               .name         = "Gadmei UTV330",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_TNF_5335MF,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2861_BOARD_GADMEI_UTV330PLUS] = {
+               .name         = "Gadmei UTV330+",
+               .tuner_type   = TUNER_TNF_5335MF,
+               .tda9887_conf = TDA9887_PRESENT,
+               .ir_codes     = RC_MAP_GADMEI_RM008Z,
+               .decoder      = EM28XX_SAA711X,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_TERRATEC_HYBRID_XS] = {
+               .name         = "Terratec Cinergy A Hybrid XS",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+ 
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2861_BOARD_KWORLD_PVRTV_300U] = {
+               .name         = "KWorld PVRTV 300U",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
+               .name          = "Yakumo MovieMixer",
+               .tuner_type    = TUNER_ABSENT,  /* Capture only device */
+               .decoder       = EM28XX_TVP5150,
+               .input         = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = {
+               .name          = "EM2860/TVP5150 Reference Design",
+               .tuner_type    = TUNER_ABSENT,  /* Capture only device */
+               .decoder       = EM28XX_TVP5150,
+               .input         = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2861_BOARD_PLEXTOR_PX_TV100U] = {
+               .name         = "Plextor ConvertX PX-TV100U",
+               .tuner_type   = TUNER_TNF_5335MF,
+               .xclk         = EM28XX_XCLK_I2S_MSB_TIMING |
+                               EM28XX_XCLK_FREQUENCY_12MHZ,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_TVP5150,
+               .has_msp34xx  = 1,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               } },
+       },
+ 
+       /* Those boards with em2870 are DVB Only*/
+ 
+       [EM2870_BOARD_TERRATEC_XS] = {
+               .name         = "Terratec Cinergy T XS",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+       },
+       [EM2870_BOARD_TERRATEC_XS_MT2060] = {
+               .name         = "Terratec Cinergy T XS (MT2060)",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT, /* MT2060 */
+       },
+       [EM2870_BOARD_KWORLD_350U] = {
+               .name         = "Kworld 350 U DVB-T",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+       },
+       [EM2870_BOARD_KWORLD_355U] = {
+               .name         = "Kworld 355 U DVB-T",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT,
+               .tuner_gpio   = default_tuner_gpio,
+               .has_dvb      = 1,
+               .dvb_gpio     = default_digital,
+       },
+       [EM2870_BOARD_PINNACLE_PCTV_DVB] = {
+               .name         = "Pinnacle PCTV DVB-T",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT, /* MT2060 */
+               /* djh - I have serious doubts this is right... */
+               .xclk         = EM28XX_XCLK_IR_RC5_MODE |
+                               EM28XX_XCLK_FREQUENCY_10MHZ,
+       },
+       [EM2870_BOARD_COMPRO_VIDEOMATE] = {
+               .name         = "Compro, VideoMate U3",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_ABSENT, /* MT2060 */
+       },
+ 
+       [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
+               .name         = "Terratec Hybrid XS Secam",
+               .has_msp34xx  = 1,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = terratec_cinergy_USB_XS_FR_digital,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = terratec_cinergy_USB_XS_FR_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = terratec_cinergy_USB_XS_FR_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = terratec_cinergy_USB_XS_FR_analog,
+               } },
+       },
+       [EM2884_BOARD_TERRATEC_H5] = {
+               .name         = "Terratec Cinergy H5",
+               .has_dvb      = 1,
+ #if 0
+               .tuner_type   = TUNER_PHILIPS_TDA8290,
+               .tuner_addr   = 0x41,
+               .dvb_gpio     = terratec_h5_digital, /* FIXME: probably wrong */
+               .tuner_gpio   = terratec_h5_gpio,
+ #else
+               .tuner_type   = TUNER_ABSENT,
+ #endif
+               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
+               .name         = "Hauppauge WinTV HVR 930C",
+               .has_dvb      = 1,
+ #if 0 /* FIXME: Add analog support */
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0x41,
+               .dvb_gpio     = hauppauge_930c_digital,
+               .tuner_gpio   = hauppauge_930c_gpio,
+ #else
+               .tuner_type   = TUNER_ABSENT,
+ #endif
+               .ir_codes     = RC_MAP_HAUPPAUGE,
+               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       [EM2884_BOARD_CINERGY_HTC_STICK] = {
+               .name         = "Terratec Cinergy HTC Stick",
+               .has_dvb      = 1,
+               .ir_codes     = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+               .tuner_type   = TUNER_ABSENT,
+               .i2c_speed    = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
+               .name         = "Hauppauge WinTV HVR 900",
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900_digital,
+               .ir_codes     = RC_MAP_HAUPPAUGE,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
+               .name         = "Hauppauge WinTV HVR 900 (R2)",
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900R2_digital,
+               .ir_codes     = RC_MAP_HAUPPAUGE,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
+               .name           = "Hauppauge WinTV HVR 850",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_HAUPPAUGE,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
+               .name           = "Hauppauge WinTV HVR 950",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_HAUPPAUGE,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
+               .name           = "Pinnacle PCTV HD Pro Stick",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_PINNACLE_PCTV_HD,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
+               .name           = "AMD ATI TV Wonder HD 600",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .mts_firmware   = 1,
+               .has_dvb        = 1,
+               .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
+               .ir_codes       = RC_MAP_ATI_TV_WONDER_HD_600,
+               .decoder        = EM28XX_TVP5150,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
+               .name           = "Terratec Hybrid XS",
+               .tuner_type     = TUNER_XC2028,
+               .tuner_gpio     = default_tuner_gpio,
+               .decoder        = EM28XX_TVP5150,
+               .has_dvb        = 1,
+               .dvb_gpio       = default_digital,
+               .ir_codes       = RC_MAP_TERRATEC_CINERGY_XS,
+               .xclk           = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               } },
+       },
+       /* maybe there's a reason behind it why Terratec sells the Hybrid XS
+          as Prodigy XS with a different PID, let's keep it separated for now
+          maybe we'll need it lateron */
+       [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
+               .name         = "Terratec Prodigy XS",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2820_BOARD_MSI_VOX_USB_2] = {
+               .name              = "MSI VOX USB 2.0",
+               .tuner_type        = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf      = TDA9887_PRESENT      |
+                                    TDA9887_PORT1_ACTIVE |
+                                    TDA9887_PORT2_ACTIVE,
+               .max_range_640_480 = 1,
+               .decoder           = EM28XX_SAA711X,
+               .input             = { {
+                       .type      = EM28XX_VMUX_TELEVISION,
+                       .vmux      = SAA7115_COMPOSITE4,
+                       .amux      = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type      = EM28XX_VMUX_COMPOSITE1,
+                       .vmux      = SAA7115_COMPOSITE0,
+                       .amux      = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type      = EM28XX_VMUX_SVIDEO,
+                       .vmux      = SAA7115_SVIDEO3,
+                       .amux      = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_TERRATEC_CINERGY_200] = {
+               .name         = "Terratec Cinergy 200 USB",
+               .is_em2800    = 1,
+               .has_ir_i2c   = 1,
+               .tuner_type   = TUNER_LG_TALN,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_GRABBEEX_USB2800] = {
+               .name       = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
+               .is_em2800  = 1,
+               .decoder    = EM28XX_SAA711X,
+               .tuner_type = TUNER_ABSENT, /* capture only board */
+               .input      = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_VC211A] = {
+               .name         = "Actionmaster/LinXcel/Digitus VC211A",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_ABSENT,   /* Capture-only board */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = vc211a_enable,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = vc211a_enable,
+               } },
+       },
+       [EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
+               .name         = "Leadtek Winfast USB II",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_KWORLD_USB2800] = {
+               .name         = "Kworld USB2800",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_PHILIPS_FCV1236D,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PINNACLE_DVC_90] = {
+               .name         = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker "
+                              "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U",
+               .tuner_type   = TUNER_ABSENT, /* capture only board */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2800_BOARD_VGEAR_POCKETTV] = {
+               .name         = "V-Gear PocketTV",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+               .name         = "Pixelview PlayTV Box 4 USB 2.0",
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_YMEC_TVF_5533MF,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .aout     = EM28XX_AOUT_MONO |  /* I2S */
+                                   EM28XX_AOUT_MASTER, /* Line out pin */
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+               .name         = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
+               .has_snapshot_button = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_YMEC_TVF_5533MF,
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = SAA7115_COMPOSITE2,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .aout     = EM28XX_AOUT_MONO |  /* I2S */
+                                   EM28XX_AOUT_MASTER, /* Line out pin */
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = {
+               .name                = "EM2860/SAA711X Reference Design",
+               .has_snapshot_button = 1,
+               .tuner_type          = TUNER_ABSENT,
+               .decoder             = EM28XX_SAA711X,
+               .input               = { {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+               } },
+       },
+ 
+       [EM2874_BOARD_LEADERSHIP_ISDBT] = {
+               .i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
+                                 EM28XX_I2C_CLK_WAIT_ENABLE |
+                                 EM28XX_I2C_FREQ_100_KHZ,
+               .xclk           = EM28XX_XCLK_FREQUENCY_10MHZ,
+               .name           = "EM2874 Leadership ISDBT",
+               .tuner_type     = TUNER_ABSENT,
+               .tuner_gpio     = leadership_reset,
+               .dvb_gpio       = leadership_digital,
+               .has_dvb        = 1,
+       },
+ 
+       [EM2880_BOARD_MSI_DIGIVOX_AD] = {
+               .name         = "MSI DigiVox A/D",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               } },
+       },
+       [EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
+               .name         = "MSI DigiVox A/D II",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = em2880_msi_digivox_ad_analog,
+               } },
+       },
+       [EM2880_BOARD_KWORLD_DVB_305U] = {
+               .name         = "KWorld DVB-T 305U",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2880_BOARD_KWORLD_DVB_310U] = {
+               .name         = "KWorld DVB-T 310U",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .has_dvb      = 1,
+               .dvb_gpio     = default_digital,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               }, {    /* S-video has not been tested yet */
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               } },
+       },
+       [EM2882_BOARD_KWORLD_ATSC_315U] = {
+               .name           = "KWorld ATSC 315U HDTV TV Box",
+               .valid          = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type     = TUNER_THOMSON_DTT761X,
+               .tuner_gpio     = em2882_kworld_315u_tuner_gpio,
+               .tda9887_conf   = TDA9887_PRESENT,
+               .decoder        = EM28XX_SAA711X,
+               .has_dvb        = 1,
+               .dvb_gpio       = em2882_kworld_315u_digital,
+               .ir_codes       = RC_MAP_KWORLD_315U,
+               .xclk           = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .i2c_speed      = EM28XX_I2C_CLK_WAIT_ENABLE,
+               /* Analog mode - still not ready */
+               /*.input        = { {
+                       .type = EM28XX_VMUX_TELEVISION,
+                       .vmux = SAA7115_COMPOSITE2,
+                       .amux = EM28XX_AMUX_VIDEO,
+                       .gpio = em2882_kworld_315u_analog,
+                       .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type = EM28XX_VMUX_COMPOSITE1,
+                       .vmux = SAA7115_COMPOSITE0,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = em2882_kworld_315u_analog1,
+                       .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type = EM28XX_VMUX_SVIDEO,
+                       .vmux = SAA7115_SVIDEO3,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = em2882_kworld_315u_analog1,
+                       .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               } }, */
+       },
+       [EM2880_BOARD_EMPIRE_DUAL_TV] = {
+               .name = "Empire dual TV",
+               .tuner_type = TUNER_XC2028,
+               .tuner_gpio = default_tuner_gpio,
+               .has_dvb = 1,
+               .dvb_gpio = default_digital,
+               .mts_firmware = 1,
+               .decoder = EM28XX_TVP5150,
+               .input = { {
+                       .type = EM28XX_VMUX_TELEVISION,
+                       .vmux = TVP5150_COMPOSITE0,
+                       .amux = EM28XX_AMUX_VIDEO,
+                       .gpio = default_analog,
+               }, {
+                       .type = EM28XX_VMUX_COMPOSITE1,
+                       .vmux = TVP5150_COMPOSITE1,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = default_analog,
+               }, {
+                       .type = EM28XX_VMUX_SVIDEO,
+                       .vmux = TVP5150_SVIDEO,
+                       .amux = EM28XX_AMUX_LINE_IN,
+                       .gpio = default_analog,
+               } },
+       },
+       [EM2881_BOARD_DNT_DA2_HYBRID] = {
+               .name         = "DNT DA2 Hybrid",
+               .valid        = EM28XX_BOARD_NOT_VALIDATED,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = default_analog,
+               } },
+       },
+       [EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
+               .name         = "Pinnacle Hybrid Pro",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = pinnacle_hybrid_pro_digital,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = pinnacle_hybrid_pro_analog,
+               } },
+       },
+       [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = {
+               .name         = "Pinnacle Hybrid Pro (330e)",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900R2_digital,
+               .ir_codes     = RC_MAP_PINNACLE_PCTV_HD,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2882_BOARD_KWORLD_VS_DVBT] = {
+               .name         = "Kworld VS-DVB-T 323UR",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = kworld_330u_digital,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+               .ir_codes     = RC_MAP_KWORLD_315U,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2882_BOARD_TERRATEC_HYBRID_XS] = {
+               .name         = "Terratec Cinnergy Hybrid T USB XS (em2882)",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900_digital,
+               .ir_codes     = RC_MAP_TERRATEC_CINERGY_XS,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = hauppauge_wintv_hvr_900_analog,
+               } },
+       },
+       [EM2882_BOARD_DIKOM_DK300] = {
+               .name         = "Dikom DK300",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = dikom_dk300_digital,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = default_analog,
+               } },
+       },
+       [EM2883_BOARD_KWORLD_HYBRID_330U] = {
+               .name         = "Kworld PlusTV HD Hybrid 330",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = kworld_330u_digital,
+               .xclk             = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .i2c_speed        = EM28XX_I2C_CLK_WAIT_ENABLE |
+                                   EM28XX_I2C_EEPROM_ON_BOARD |
+                                   EM28XX_I2C_EEPROM_KEY_VALID,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = kworld_330u_analog,
+                       .aout     = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = kworld_330u_analog,
+                       .aout     = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = kworld_330u_analog,
+               } },
+       },
+       [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
+               .name         = "Compro VideoMate ForYou/Stereo",
+               .tuner_type   = TUNER_LG_PAL_NEW_TAPC,
+               .tvaudio_addr = 0xb0,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_TVP5150,
+               .adecoder     = EM28XX_TVAUDIO,
+               .mute_gpio    = compro_mute_gpio,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = compro_unmute_tv_gpio,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = compro_unmute_svid_gpio,
+               } },
+       },
+       [EM2860_BOARD_KAIOMY_TVNPC_U2] = {
+               .name         = "Kaiomy TVnPC U2",
+               .vchannels    = 3,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0x61,
+               .mts_firmware = 1,
+               .decoder      = EM28XX_TVP5150,
+               .tuner_gpio   = default_tuner_gpio,
+               .ir_codes     = RC_MAP_KAIOMY,
+               .input          = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+ 
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+               .radio          = {
+                       .type     = EM28XX_RADIO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }
+       },
+       [EM2860_BOARD_EASYCAP] = {
+               .name         = "Easy Cap Capture DC-60",
+               .vchannels    = 2,
+               .tuner_type   = TUNER_ABSENT,
+               .decoder      = EM28XX_SAA711X,
+               .input           = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2820_BOARD_IODATA_GVMVP_SZ] = {
+               .name       = "IO-DATA GV-MVP/SZ",
+               .tuner_type   = TUNER_PHILIPS_FM1236_MK3,
+               .tuner_gpio   = default_tuner_gpio,
+               .tda9887_conf = TDA9887_PRESENT,
+               .decoder      = EM28XX_TVP5150,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, { /* Composite has not been tested yet */
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               }, { /* S-video has not been tested yet */
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_VIDEO,
+               } },
+       },
+       [EM2860_BOARD_TERRATEC_GRABBY] = {
+               .name            = "Terratec Grabby",
+               .vchannels       = 2,
+               .tuner_type      = TUNER_ABSENT,
+               .decoder         = EM28XX_SAA711X,
+               .xclk            = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .input           = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       [EM2860_BOARD_TERRATEC_AV350] = {
+               .name            = "Terratec AV350",
+               .vchannels       = 2,
+               .tuner_type      = TUNER_ABSENT,
+               .decoder         = EM28XX_TVP5150,
+               .xclk            = EM28XX_XCLK_FREQUENCY_12MHZ,
+               .mute_gpio       = terratec_av350_mute_gpio,
+               .input           = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AUDIO_SRC_LINE,
+                       .gpio     = terratec_av350_unmute_gpio,
+ 
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AUDIO_SRC_LINE,
+                       .gpio     = terratec_av350_unmute_gpio,
+               } },
+       },
+ 
+       [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = {
+               .name         = "Elgato Video Capture",
+               .decoder      = EM28XX_SAA711X,
+               .tuner_type   = TUNER_ABSENT,   /* Capture only device */
+               .input        = { {
+                       .type  = EM28XX_VMUX_COMPOSITE1,
+                       .vmux  = SAA7115_COMPOSITE0,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type  = EM28XX_VMUX_SVIDEO,
+                       .vmux  = SAA7115_SVIDEO3,
+                       .amux  = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+ 
+       [EM2882_BOARD_EVGA_INDTUBE] = {
+               .name         = "Evga inDtube",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_gpio   = default_tuner_gpio,
+               .decoder      = EM28XX_TVP5150,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+               .mts_firmware = 1,
+               .has_dvb      = 1,
+               .dvb_gpio     = evga_indtube_digital,
+               .ir_codes     = RC_MAP_EVGA_INDTUBE,
+               .input        = { {
+                       .type     = EM28XX_VMUX_TELEVISION,
+                       .vmux     = TVP5150_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_VIDEO,
+                       .gpio     = evga_indtube_analog,
+               }, {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = TVP5150_COMPOSITE1,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = evga_indtube_analog,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = TVP5150_SVIDEO,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = evga_indtube_analog,
+               } },
+       },
+       /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
+          Infineon TUA6034) */
+       [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
+               .name          = "Reddo DVB-C USB TV Box",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = reddo_dvb_c_usb_box,
+               .has_dvb       = 1,
+       },
+       /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
+        * initially as the KWorld PlusTV 340U, then as the UB435-Q.
+        * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
+       [EM2870_BOARD_KWORLD_A340] = {
+               .name       = "KWorld PlusTV 340U or UB435-Q (ATSC)",
+               .tuner_type = TUNER_ABSENT,     /* Digital-only TDA18271HD */
+               .has_dvb    = 1,
+               .dvb_gpio   = kworld_a340_digital,
+               .tuner_gpio = default_tuner_gpio,
+       },
+       /* 2013:024f PCTV nanoStick T2 290e.
+        * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
+       [EM28174_BOARD_PCTV_290E] = {
+               .name          = "PCTV nanoStick T2 290e",
+               .i2c_speed      = EM2874_I2C_SECONDARY_BUS_SELECT |
+                       EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_290e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+       },
+       /* 2013:024f PCTV DVB-S2 Stick 460e
+        * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+       [EM28174_BOARD_PCTV_460E] = {
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                       EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+               .name          = "PCTV DVB-S2 Stick (460e)",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_460e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+       },
+       /* eb1a:5006 Honestech VIDBOX NW03
+        * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */
+       [EM2860_BOARD_HT_VIDBOX_NW03] = {
+               .name                = "Honestech Vidbox NW03",
+               .tuner_type          = TUNER_ABSENT,
+               .decoder             = EM28XX_SAA711X,
+               .input               = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,  /* S-VIDEO needs confirming */
+                       .amux     = EM28XX_AMUX_LINE_IN,
+               } },
+       },
+       /* 1b80:e425 MaxMedia UB425-TC
+        * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
+       [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
+               .name          = "MaxMedia UB425-TC",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = maxmedia_ub425_tc,
+               .has_dvb       = 1,
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       /* 2304:0242 PCTV QuatroStick (510e)
+        * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+       [EM2884_BOARD_PCTV_510E] = {
+               .name          = "PCTV QuatroStick (510e)",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_510e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+       /* 2013:0251 PCTV QuatroStick nano (520e)
+        * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+       [EM2884_BOARD_PCTV_520E] = {
+               .name          = "PCTV QuatroStick nano (520e)",
+               .tuner_type    = TUNER_ABSENT,
+               .tuner_gpio    = pctv_520e,
+               .has_dvb       = 1,
+               .ir_codes      = RC_MAP_PINNACLE_PCTV_HD,
+               .i2c_speed     = EM2874_I2C_SECONDARY_BUS_SELECT |
+                               EM28XX_I2C_CLK_WAIT_ENABLE |
+                               EM28XX_I2C_FREQ_400_KHZ,
+       },
+ };
+ const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+ 
+ /* table of devices that work with this driver */
+ struct usb_device_id em28xx_id_table[] = {
+       { USB_DEVICE(0xeb1a, 0x2750),
+                       .driver_info = EM2750_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2751),
+                       .driver_info = EM2750_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2800),
+                       .driver_info = EM2800_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2710),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2820),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2821),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2860),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2861),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2862),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2863),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2870),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2881),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2883),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2868),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2875),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0xe300),
+                       .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
+       { USB_DEVICE(0xeb1a, 0xe303),
+                       .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 },
+       { USB_DEVICE(0xeb1a, 0xe305),
+                       .driver_info = EM2880_BOARD_KWORLD_DVB_305U },
+       { USB_DEVICE(0xeb1a, 0xe310),
+                       .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD },
+       { USB_DEVICE(0xeb1a, 0xa313),
+               .driver_info = EM2882_BOARD_KWORLD_ATSC_315U },
+       { USB_DEVICE(0xeb1a, 0xa316),
+                       .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U },
+       { USB_DEVICE(0xeb1a, 0xe320),
+                       .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II },
+       { USB_DEVICE(0xeb1a, 0xe323),
+                       .driver_info = EM2882_BOARD_KWORLD_VS_DVBT },
+       { USB_DEVICE(0xeb1a, 0xe350),
+                       .driver_info = EM2870_BOARD_KWORLD_350U },
+       { USB_DEVICE(0xeb1a, 0xe355),
+                       .driver_info = EM2870_BOARD_KWORLD_355U },
+       { USB_DEVICE(0xeb1a, 0x2801),
+                       .driver_info = EM2800_BOARD_GRABBEEX_USB2800 },
+       { USB_DEVICE(0xeb1a, 0xe357),
+                       .driver_info = EM2870_BOARD_KWORLD_355U },
+       { USB_DEVICE(0xeb1a, 0xe359),
+                       .driver_info = EM2870_BOARD_KWORLD_355U },
+       { USB_DEVICE(0x1b80, 0xe302),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
+       { USB_DEVICE(0x1b80, 0xe304),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */
+       { USB_DEVICE(0x0ccd, 0x0036),
+                       .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+       { USB_DEVICE(0x0ccd, 0x004c),
+                       .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR },
+       { USB_DEVICE(0x0ccd, 0x004f),
+                       .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS },
+       { USB_DEVICE(0x0ccd, 0x005e),
+                       .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+       { USB_DEVICE(0x0ccd, 0x0042),
+                       .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+       { USB_DEVICE(0x0ccd, 0x0043),
+                       .driver_info = EM2870_BOARD_TERRATEC_XS },
+       { USB_DEVICE(0x0ccd, 0x008e),   /* Cinergy HTC USB XS Rev. 1 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x00ac),   /* Cinergy HTC USB XS Rev. 2 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x10a2),   /* H5 Rev. 1 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x10ad),   /* H5 Rev. 2 */
+                       .driver_info = EM2884_BOARD_TERRATEC_H5 },
+       { USB_DEVICE(0x0ccd, 0x0084),
+                       .driver_info = EM2860_BOARD_TERRATEC_AV350 },
+       { USB_DEVICE(0x0ccd, 0x0096),
+                       .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+       { USB_DEVICE(0x0ccd, 0x10AF),
+                       .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+       { USB_DEVICE(0x0ccd, 0x00b2),
+                       .driver_info = EM2884_BOARD_CINERGY_HTC_STICK },
+       { USB_DEVICE(0x0fd9, 0x0033),
+                       .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
+       { USB_DEVICE(0x185b, 0x2870),
+                       .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
+       { USB_DEVICE(0x185b, 0x2041),
+                       .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU },
+       { USB_DEVICE(0x2040, 0x4200),
+                       .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+       { USB_DEVICE(0x2040, 0x4201),
+                       .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+       { USB_DEVICE(0x2040, 0x6500),
+                       .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+       { USB_DEVICE(0x2040, 0x6502),
+                       .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
+       { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+       { USB_DEVICE(0x2040, 0x6517), /* HP  HVR-950 */
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+       { USB_DEVICE(0x2040, 0x651b), /* RP  HVR-950 */
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+       { USB_DEVICE(0x2040, 0x651f),
+                       .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
+       { USB_DEVICE(0x0438, 0xb002),
+                       .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
+       { USB_DEVICE(0x2001, 0xf112),
+                       .driver_info = EM2820_BOARD_DLINK_USB_TV },
+       { USB_DEVICE(0x2304, 0x0207),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x2304, 0x0208),
+                       .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
+       { USB_DEVICE(0x2304, 0x021a),
+                       .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x2304, 0x0226),
+                       .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E },
+       { USB_DEVICE(0x2304, 0x0227),
+                       .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
+       { USB_DEVICE(0x0413, 0x6023),
+                       .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
+       { USB_DEVICE(0x093b, 0xa003),
+                      .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+       { USB_DEVICE(0x093b, 0xa005),
+                       .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
+       { USB_DEVICE(0x04bb, 0x0515),
+                       .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+       { USB_DEVICE(0xeb1a, 0x50a6),
+                       .driver_info = EM2860_BOARD_GADMEI_UTV330 },
+       { USB_DEVICE(0x1b80, 0xa340),
+                       .driver_info = EM2870_BOARD_KWORLD_A340 },
+       { USB_DEVICE(0x2013, 0x024f),
+                       .driver_info = EM28174_BOARD_PCTV_290E },
+       { USB_DEVICE(0x2013, 0x024c),
+                       .driver_info = EM28174_BOARD_PCTV_460E },
+       { USB_DEVICE(0x2040, 0x1605),
+                       .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C },
+       { USB_DEVICE(0xeb1a, 0x5006),
+                       .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
+       { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
+                       .driver_info = EM2860_BOARD_EASYCAP },
+       { USB_DEVICE(0x1b80, 0xe425),
+                       .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
+       { USB_DEVICE(0x2304, 0x0242),
+                       .driver_info = EM2884_BOARD_PCTV_510E },
+       { USB_DEVICE(0x2013, 0x0251),
+                       .driver_info = EM2884_BOARD_PCTV_520E },
+       { },
+ };
+ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+ 
+ /*
+  * EEPROM hash table for devices with generic USB IDs
+  */
+ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
+       /* P/N: SA 60002070465 Tuner: TVF7533-MF */
+       {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+       {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
+       {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
+       {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
+       {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
+       {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
+       {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
+       {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
+ };
+ 
+ /* I2C devicelist hash table for devices with generic USB IDs */
+ static struct em28xx_hash_table em28xx_i2c_hash[] = {
+       {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
+       {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+       {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT},
+       {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
+       {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
+       {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
+       {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
+ };
+ 
+ /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
+ static unsigned short saa711x_addrs[] = {
+       0x4a >> 1, 0x48 >> 1,   /* SAA7111, SAA7111A and SAA7113 */
+       0x42 >> 1, 0x40 >> 1,   /* SAA7114, SAA7115 and SAA7118 */
+       I2C_CLIENT_END };
+ 
+ static unsigned short tvp5150_addrs[] = {
+       0xb8 >> 1,
+       0xba >> 1,
+       I2C_CLIENT_END
+ };
+ 
+ static unsigned short msp3400_addrs[] = {
+       0x80 >> 1,
+       0x88 >> 1,
+       I2C_CLIENT_END
+ };
+ 
+ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
+ {
+       int rc = 0;
+       struct em28xx *dev = ptr;
+ 
+       if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
+               return 0;
+ 
+       if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET)
+               return 0;
+ 
+       rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ 
+       return rc;
+ }
+ EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
+ 
+ static inline void em28xx_set_model(struct em28xx *dev)
+ {
+       memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+ 
+       /* Those are the default values for the majority of boards
+          Use those values if not specified otherwise at boards entry
+        */
+       if (!dev->board.xclk)
+               dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
+                                 EM28XX_XCLK_FREQUENCY_12MHZ;
+ 
+       if (!dev->board.i2c_speed)
+               dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+                                      EM28XX_I2C_FREQ_100_KHZ;
+ }
+ 
+ 
+ /* FIXME: Should be replaced by a proper mt9m111 driver */
+ static int em28xx_initialize_mt9m111(struct em28xx *dev)
+ {
+       int i;
+       unsigned char regs[][3] = {
+               { 0x0d, 0x00, 0x01, },  /* reset and use defaults */
+               { 0x0d, 0x00, 0x00, },
+               { 0x0a, 0x00, 0x21, },
+               { 0x21, 0x04, 0x00, },  /* full readout speed, no row/col skipping */
+       };
+ 
+       for (i = 0; i < ARRAY_SIZE(regs); i++)
+               i2c_master_send(&dev->i2c_client, ®s[i][0], 3);
+ 
+       return 0;
+ }
+ 
+ 
+ /* FIXME: Should be replaced by a proper mt9m001 driver */
+ static int em28xx_initialize_mt9m001(struct em28xx *dev)
+ {
+       int i;
+       unsigned char regs[][3] = {
+               { 0x0d, 0x00, 0x01, },
+               { 0x0d, 0x00, 0x00, },
+               { 0x04, 0x05, 0x00, },  /* hres = 1280 */
+               { 0x03, 0x04, 0x00, },  /* vres = 1024 */
+               { 0x20, 0x11, 0x00, },
+               { 0x06, 0x00, 0x10, },
+               { 0x2b, 0x00, 0x24, },
+               { 0x2e, 0x00, 0x24, },
+               { 0x35, 0x00, 0x24, },
+               { 0x2d, 0x00, 0x20, },
+               { 0x2c, 0x00, 0x20, },
+               { 0x09, 0x0a, 0xd4, },
+               { 0x35, 0x00, 0x57, },
+       };
+ 
+       for (i = 0; i < ARRAY_SIZE(regs); i++)
+               i2c_master_send(&dev->i2c_client, ®s[i][0], 3);
+ 
+       return 0;
+ }
+ 
+ /* HINT method: webcam I2C chips
+  *
+  * This method works for webcams with Micron sensors
+  */
+ static int em28xx_hint_sensor(struct em28xx *dev)
+ {
+       int rc;
+       char *sensor_name;
+       unsigned char cmd;
+       __be16 version_be;
+       u16 version;
+ 
+       /* Micron sensor detection */
+       dev->i2c_client.addr = 0xba >> 1;
+       cmd = 0;
+       i2c_master_send(&dev->i2c_client, &cmd, 1);
+       rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
+       if (rc != 2)
+               return -EINVAL;
+ 
+       version = be16_to_cpu(version_be);
+       switch (version) {
+       case 0x8232:            /* mt9v011 640x480 1.3 Mpix sensor */
+       case 0x8243:            /* mt9v011 rev B 640x480 1.3 Mpix sensor */
+               dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+               em28xx_set_model(dev);
+ 
+               sensor_name = "mt9v011";
+               dev->em28xx_sensor = EM28XX_MT9V011;
+               dev->sensor_xres = 640;
+               dev->sensor_yres = 480;
+               /*
+                * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+                * the Silvercrest cam I have here for testing - for higher
+                * resolutions, a high clock cause horizontal artifacts, so we
+                * need to use a lower xclk frequency.
+                * Yet, it would be possible to adjust xclk depending on the
+                * desired resolution, since this affects directly the
+                * frame rate.
+                */
+               dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+               dev->sensor_xtal = 4300000;
+ 
+               /* probably means GRGB 16 bit bayer */
+               dev->vinmode = 0x0d;
+               dev->vinctl = 0x00;
+ 
+               break;
+ 
+       case 0x143a:    /* MT9M111 as found in the ECS G200 */
+               dev->model = EM2750_BOARD_UNKNOWN;
+               em28xx_set_model(dev);
+ 
+               sensor_name = "mt9m111";
+               dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+               dev->em28xx_sensor = EM28XX_MT9M111;
+               em28xx_initialize_mt9m111(dev);
+               dev->sensor_xres = 640;
+               dev->sensor_yres = 512;
+ 
+               dev->vinmode = 0x0a;
+               dev->vinctl = 0x00;
+ 
+               break;
+ 
+       case 0x8431:
+               dev->model = EM2750_BOARD_UNKNOWN;
+               em28xx_set_model(dev);
+ 
+               sensor_name = "mt9m001";
+               dev->em28xx_sensor = EM28XX_MT9M001;
+               em28xx_initialize_mt9m001(dev);
+               dev->sensor_xres = 1280;
+               dev->sensor_yres = 1024;
+ 
+               /* probably means BGGR 16 bit bayer */
+               dev->vinmode = 0x0c;
+               dev->vinctl = 0x00;
+ 
+               break;
+       default:
+               printk("Unknown Micron Sensor 0x%04x\n", version);
+               return -EINVAL;
+       }
+ 
+       /* Setup webcam defaults */
+       em28xx_pre_card_setup(dev);
+ 
+       em28xx_errdev("Sensor is %s, using model %s entry.\n",
+                     sensor_name, em28xx_boards[dev->model].name);
+ 
+       return 0;
+ }
+ 
+ /* Since em28xx_pre_card_setup() requires a proper dev->model,
+  * this won't work for boards with generic PCI IDs
+  */
+ static void em28xx_pre_card_setup(struct em28xx *dev)
+ {
+       /* Set the initial XCLK and I2C clock values based on the board
+          definition */
+       em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
+       if (!dev->board.is_em2800)
+               em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+       msleep(50);
+ 
+       /* request some modules */
+       switch (dev->model) {
+       case EM2861_BOARD_PLEXTOR_PX_TV100U:
+               /* Sets the msp34xx I2S speed */
+               dev->i2s_speed = 2048000;
+               break;
+       case EM2861_BOARD_KWORLD_PVRTV_300U:
+       case EM2880_BOARD_KWORLD_DVB_305U:
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
+               msleep(10);
+               break;
+       case EM2870_BOARD_COMPRO_VIDEOMATE:
+               /* TODO: someone can do some cleanup here...
+                        not everything's needed */
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+               msleep(10);
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+               mdelay(70);
+               break;
+       case EM2870_BOARD_TERRATEC_XS_MT2060:
+               /* this device needs some gpio writes to get the DVB-T
+                  demod work */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               break;
+       case EM2870_BOARD_PINNACLE_PCTV_DVB:
+               /* this device needs some gpio writes to get the
+                  DVB-T demod work */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+               mdelay(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               mdelay(70);
+               break;
+       case EM2820_BOARD_GADMEI_UTV310:
+       case EM2820_BOARD_MSI_VOX_USB_2:
+               /* enables audio for that devices */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               break;
+ 
+       case EM2882_BOARD_KWORLD_ATSC_315U:
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               msleep(10);
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+               msleep(10);
+               em28xx_write_reg(dev, EM2880_R04_GPO, 0x08);
+               msleep(10);
+               break;
+ 
+       case EM2860_BOARD_KAIOMY_TVNPC_U2:
+               em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
+               em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
+               em28xx_write_regs(dev, 0x0d, "\x42", 1);
+               em28xx_write_regs(dev, 0x08, "\xfd", 1);
+               msleep(10);
+               em28xx_write_regs(dev, 0x08, "\xff", 1);
+               msleep(10);
+               em28xx_write_regs(dev, 0x08, "\x7f", 1);
+               msleep(10);
+               em28xx_write_regs(dev, 0x08, "\x6b", 1);
+ 
+               break;
+       case EM2860_BOARD_EASYCAP:
+               em28xx_write_regs(dev, 0x08, "\xf8", 1);
+               break;
+ 
+       case EM2820_BOARD_IODATA_GVMVP_SZ:
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+               msleep(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+               msleep(70);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               msleep(70);
+               break;
+       }
+ 
+       em28xx_gpio_set(dev, dev->board.tuner_gpio);
+       em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ 
+       /* Unlock device */
+       em28xx_set_mode(dev, EM28XX_SUSPEND);
+ }
+ 
+ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
+ {
+       memset(ctl, 0, sizeof(*ctl));
+ 
+       ctl->fname   = XC2028_DEFAULT_FIRMWARE;
+       ctl->max_len = 64;
+       ctl->mts = em28xx_boards[dev->model].mts_firmware;
+ 
+       switch (dev->model) {
+       case EM2880_BOARD_EMPIRE_DUAL_TV:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2882_BOARD_TERRATEC_HYBRID_XS:
+               ctl->demod = XC3028_FE_ZARLINK456;
+               break;
+       case EM2880_BOARD_TERRATEC_HYBRID_XS:
+       case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
+       case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+               ctl->demod = XC3028_FE_ZARLINK456;
+               break;
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+       case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
+               ctl->demod = XC3028_FE_DEFAULT;
+               break;
+       case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+               ctl->demod = XC3028_FE_DEFAULT;
+               ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+               break;
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+               /* FIXME: Better to specify the needed IF */
+               ctl->demod = XC3028_FE_DEFAULT;
+               break;
+       case EM2883_BOARD_KWORLD_HYBRID_330U:
+       case EM2882_BOARD_DIKOM_DK300:
+       case EM2882_BOARD_KWORLD_VS_DVBT:
+               ctl->demod = XC3028_FE_CHINA;
+               ctl->fname = XC2028_DEFAULT_FIRMWARE;
+               break;
+       case EM2882_BOARD_EVGA_INDTUBE:
+               ctl->demod = XC3028_FE_CHINA;
+               ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+               break;
+       default:
+               ctl->demod = XC3028_FE_OREN538;
+       }
+ }
+ 
+ static void em28xx_tuner_setup(struct em28xx *dev)
+ {
+       struct tuner_setup           tun_setup;
+       struct v4l2_frequency        f;
+ 
+       if (dev->tuner_type == TUNER_ABSENT)
+               return;
+ 
+       memset(&tun_setup, 0, sizeof(tun_setup));
+ 
+       tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+       tun_setup.tuner_callback = em28xx_tuner_callback;
+ 
+       if (dev->board.radio.type) {
+               tun_setup.type = dev->board.radio.type;
+               tun_setup.addr = dev->board.radio_addr;
+ 
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+       }
+ 
+       if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
+               tun_setup.type   = dev->tuner_type;
+               tun_setup.addr   = dev->tuner_addr;
+ 
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+       }
+ 
+       if (dev->tda9887_conf) {
+               struct v4l2_priv_tun_config tda9887_cfg;
+ 
+               tda9887_cfg.tuner = TUNER_TDA9887;
+               tda9887_cfg.priv = &dev->tda9887_conf;
+ 
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
+       }
+ 
+       if (dev->tuner_type == TUNER_XC2028) {
+               struct v4l2_priv_tun_config  xc2028_cfg;
+               struct xc2028_ctrl           ctl;
+ 
+               memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+               memset(&ctl, 0, sizeof(ctl));
+ 
+               em28xx_setup_xc3028(dev, &ctl);
+ 
+               xc2028_cfg.tuner = TUNER_XC2028;
+               xc2028_cfg.priv  = &ctl;
+ 
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
+       }
+ 
+       /* configure tuner */
+       f.tuner = 0;
+       f.type = V4L2_TUNER_ANALOG_TV;
+       f.frequency = 9076;     /* just a magic number */
+       dev->ctl_freq = f.frequency;
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+ }
+ 
+ static int em28xx_hint_board(struct em28xx *dev)
+ {
+       int i;
+ 
+       /* HINT method: EEPROM
+        *
+        * This method works only for boards with eeprom.
+        * Uses a hash of all eeprom bytes. The hash should be
+        * unique for a vendor/tuner pair.
+        * There are a high chance that tuners for different
+        * video standards produce different hashes.
+        */
+       for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
+               if (dev->hash == em28xx_eeprom_hash[i].hash) {
+                       dev->model = em28xx_eeprom_hash[i].model;
+                       dev->tuner_type = em28xx_eeprom_hash[i].tuner;
+ 
+                       em28xx_errdev("Your board has no unique USB ID.\n");
+                       em28xx_errdev("A hint were successfully done, "
+                                     "based on eeprom hash.\n");
+                       em28xx_errdev("This method is not 100%% failproof.\n");
+                       em28xx_errdev("If the board were missdetected, "
+                                     "please email this log to:\n");
+                       em28xx_errdev("\tV4L Mailing List "
+                                     " <linux-media@vger.kernel.org>\n");
+                       em28xx_errdev("Board detected as %s\n",
+                                     em28xx_boards[dev->model].name);
+ 
+                       return 0;
+               }
+       }
+ 
+       /* HINT method: I2C attached devices
+        *
+        * This method works for all boards.
+        * Uses a hash of i2c scanned devices.
+        * Devices with the same i2c attached chips will
+        * be considered equal.
+        * This method is less precise than the eeprom one.
+        */
+ 
+       /* user did not request i2c scanning => do it now */
+       if (!dev->i2c_hash)
+               em28xx_do_i2c_scan(dev);
+ 
+       for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
+               if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
+                       dev->model = em28xx_i2c_hash[i].model;
+                       dev->tuner_type = em28xx_i2c_hash[i].tuner;
+                       em28xx_errdev("Your board has no unique USB ID.\n");
+                       em28xx_errdev("A hint were successfully done, "
+                                     "based on i2c devicelist hash.\n");
+                       em28xx_errdev("This method is not 100%% failproof.\n");
+                       em28xx_errdev("If the board were missdetected, "
+                                     "please email this log to:\n");
+                       em28xx_errdev("\tV4L Mailing List "
+                                     " <linux-media@vger.kernel.org>\n");
+                       em28xx_errdev("Board detected as %s\n",
+                                     em28xx_boards[dev->model].name);
+ 
+                       return 0;
+               }
+       }
+ 
+       em28xx_errdev("Your board has no unique USB ID and thus need a "
+                     "hint to be detected.\n");
+       em28xx_errdev("You may try to use card=<n> insmod option to "
+                     "workaround that.\n");
+       em28xx_errdev("Please send an email with this log to:\n");
+       em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\n");
+       em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
+       em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
+ 
+       em28xx_errdev("Here is a list of valid choices for the card=<n>"
+                     " insmod option:\n");
+       for (i = 0; i < em28xx_bcount; i++) {
+               em28xx_errdev("    card=%d -> %s\n",
+                               i, em28xx_boards[i].name);
+       }
+       return -1;
+ }
+ 
+ static void em28xx_card_setup(struct em28xx *dev)
+ {
+       /*
+        * If the device can be a webcam, seek for a sensor.
+        * If sensor is not found, then it isn't a webcam.
+        */
+       if (dev->board.is_webcam) {
+               if (em28xx_hint_sensor(dev) < 0)
+                       dev->board.is_webcam = 0;
+               else
+                       dev->progressive = 1;
+       }
+ 
+       if (!dev->board.is_webcam) {
+               switch (dev->model) {
+               case EM2820_BOARD_UNKNOWN:
+               case EM2800_BOARD_UNKNOWN:
+               /*
+                * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+                *
+                * This occurs because they share identical USB vendor and
+                * product IDs.
+                *
+                * What we do here is look up the EEPROM hash of the K-WORLD
+                * and if it is found then we decide that we do not have
+                * a DIGIVOX and reset the device to the K-WORLD instead.
+                *
+                * This solution is only valid if they do not share eeprom
+                * hash identities which has not been determined as yet.
+                */
+               if (em28xx_hint_board(dev) < 0)
+                       em28xx_errdev("Board not discovered\n");
+               else {
+                       em28xx_set_model(dev);
+                       em28xx_pre_card_setup(dev);
+               }
+               break;
+               default:
+                       em28xx_set_model(dev);
+               }
+       }
+ 
+       em28xx_info("Identified as %s (card=%d)\n",
+                   dev->board.name, dev->model);
+ 
+       dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+       if (em28xx_boards[dev->model].tuner_addr)
+               dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
+ 
+       if (em28xx_boards[dev->model].tda9887_conf)
+               dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
+ 
+       /* request some modules */
+       switch (dev->model) {
+       case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+       case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+       {
+               struct tveeprom tv;
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+               request_module("tveeprom");
+ #endif
+               /* Call first TVeeprom */
+ 
+               dev->i2c_client.addr = 0xa0 >> 1;
+               tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+ 
+               dev->tuner_type = tv.tuner_type;
+ 
+               if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
+                       dev->i2s_speed = 2048000;
+                       dev->board.has_msp34xx = 1;
+               }
+               break;
+       }
+       case EM2882_BOARD_KWORLD_ATSC_315U:
+               em28xx_write_reg(dev, 0x0d, 0x42);
+               msleep(10);
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+               msleep(10);
+               break;
+       case EM2820_BOARD_KWORLD_PVRTV2800RF:
+               /* GPIO enables sound on KWORLD PVR TV 2800RF */
+               em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
+               break;
+       case EM2820_BOARD_UNKNOWN:
+       case EM2800_BOARD_UNKNOWN:
+               /*
+                * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+                *
+                * This occurs because they share identical USB vendor and
+                * product IDs.
+                *
+                * What we do here is look up the EEPROM hash of the K-WORLD
+                * and if it is found then we decide that we do not have
+                * a DIGIVOX and reset the device to the K-WORLD instead.
+                *
+                * This solution is only valid if they do not share eeprom
+                * hash identities which has not been determined as yet.
+                */
+       case EM2880_BOARD_MSI_DIGIVOX_AD:
+               if (!em28xx_hint_board(dev))
+                       em28xx_set_model(dev);
+ 
+               /* In cases where we had to use a board hint, the call to
+                  em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+                  so make the call now so the analog GPIOs are set properly
+                  before probing the i2c bus. */
+               em28xx_gpio_set(dev, dev->board.tuner_gpio);
+               em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+               break;
+ 
+ /*
+                * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
+                *
+                * This occurs because they share identical USB vendor and
+                * product IDs.
+                *
+                * What we do here is look up the EEPROM hash of the Dikom
+                * and if it is found then we decide that we do not have
+                * a Kworld and reset the device to the Dikom instead.
+                *
+                * This solution is only valid if they do not share eeprom
+                * hash identities which has not been determined as yet.
+                */
+       case EM2882_BOARD_KWORLD_VS_DVBT:
+               if (!em28xx_hint_board(dev))
+                       em28xx_set_model(dev);
+ 
+               /* In cases where we had to use a board hint, the call to
+                  em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+                  so make the call now so the analog GPIOs are set properly
+                  before probing the i2c bus. */
+               em28xx_gpio_set(dev, dev->board.tuner_gpio);
+               em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+               break;
+       }
+ 
+       if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
+               em28xx_errdev("\n\n");
+               em28xx_errdev("The support for this board weren't "
+                             "valid yet.\n");
+               em28xx_errdev("Please send a report of having this working\n");
+               em28xx_errdev("not to V4L mailing list (and/or to other "
+                               "addresses)\n\n");
+       }
+ 
+       /* Allow override tuner type by a module parameter */
+       if (tuner >= 0)
+               dev->tuner_type = tuner;
+ 
+       /* request some modules */
+       if (dev->board.has_msp34xx)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "msp3400", 0, msp3400_addrs);
+ 
+       if (dev->board.decoder == EM28XX_SAA711X)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "saa7115_auto", 0, saa711x_addrs);
+ 
+       if (dev->board.decoder == EM28XX_TVP5150)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "tvp5150", 0, tvp5150_addrs);
+ 
+       if (dev->em28xx_sensor == EM28XX_MT9V011) {
+               struct mt9v011_platform_data pdata;
+               struct i2c_board_info mt9v011_info = {
+                       .type = "mt9v011",
+                       .addr = 0xba >> 1,
+                       .platform_data = &pdata,
+               };
+ 
+               pdata.xtal = dev->sensor_xtal;
+               v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
+                               &mt9v011_info, NULL);
+       }
+ 
+ 
+       if (dev->board.adecoder == EM28XX_TVAUDIO)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "tvaudio", dev->board.tvaudio_addr, NULL);
+ 
+       if (dev->board.tuner_type != TUNER_ABSENT) {
+               int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+ 
+               if (dev->board.radio.type)
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                               "tuner", dev->board.radio_addr, NULL);
+ 
+               if (has_demod)
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap, "tuner",
+                               0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+               if (dev->tuner_addr == 0) {
+                       enum v4l2_i2c_tuner_type type =
+                               has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+                       struct v4l2_subdev *sd;
+ 
+                       sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+                               &dev->i2c_adap, "tuner",
+                               0, v4l2_i2c_tuner_addrs(type));
+ 
+                       if (sd)
+                               dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
+               } else {
+                       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                               "tuner", dev->tuner_addr, NULL);
+               }
+       }
+ 
+       em28xx_tuner_setup(dev);
+ }
+ 
+ 
+ static void request_module_async(struct work_struct *work)
+ {
+       struct em28xx *dev = container_of(work,
+                            struct em28xx, request_module_wk);
+ 
+       /*
+        * The em28xx extensions can be modules or builtin. If the
+        * modules are already loaded or are built in, those extensions
+        * can be initialised right now. Otherwise, the module init
+        * code will do it.
+        */
+       em28xx_init_extension(dev);
+ 
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+       if (dev->has_audio_class)
+               request_module("snd-usb-audio");
+       else if (dev->has_alsa_audio)
+               request_module("em28xx-alsa");
+ 
+       if (dev->board.has_dvb)
+               request_module("em28xx-dvb");
+       if (dev->board.ir_codes && !disable_ir)
+               request_module("em28xx-rc");
+ #endif /* CONFIG_MODULES */
+ }
+ 
+ static void request_modules(struct em28xx *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_modules(struct em28xx *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ 
+ /*
+  * em28xx_release_resources()
+  * unregisters the v4l2,i2c and usb devices
+  * called when the device gets disconnected or at module unload
+ */
+ void em28xx_release_resources(struct em28xx *dev)
+ {
+       /*FIXME: I2C IR should be disconnected */
+ 
+       em28xx_release_analog_resources(dev);
+ 
+       em28xx_i2c_unregister(dev);
+ 
+       v4l2_device_unregister(&dev->v4l2_dev);
+ 
+       usb_put_dev(dev->udev);
+ 
+       /* Mark device as unused */
+       clear_bit(dev->devno, &em28xx_devused);
+ };
+ 
+ /*
+  * em28xx_init_dev()
+  * allocates and inits the device structs, registers i2c bus and v4l device
+  */
+ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
+                          struct usb_interface *interface,
+                          int minor)
+ {
+       int retval;
+ 
+       dev->udev = udev;
+       mutex_init(&dev->ctrl_urb_lock);
+       spin_lock_init(&dev->slock);
+ 
+       dev->em28xx_write_regs = em28xx_write_regs;
+       dev->em28xx_read_reg = em28xx_read_reg;
+       dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+       dev->em28xx_write_regs_req = em28xx_write_regs_req;
+       dev->em28xx_read_reg_req = em28xx_read_reg_req;
+       dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+ 
+       em28xx_set_model(dev);
+ 
+       /* Set the default GPO/GPIO for legacy devices */
+       dev->reg_gpo_num = EM2880_R04_GPO;
+       dev->reg_gpio_num = EM28XX_R08_GPIO;
+ 
+       dev->wait_after_write = 5;
+ 
+       /* Based on the Chip ID, set the device configuration */
+       retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+       if (retval > 0) {
+               dev->chip_id = retval;
+ 
+               switch (dev->chip_id) {
+               case CHIP_ID_EM2800:
+                       em28xx_info("chip ID is em2800\n");
+                       break;
+               case CHIP_ID_EM2710:
+                       em28xx_info("chip ID is em2710\n");
+                       break;
+               case CHIP_ID_EM2750:
+                       em28xx_info("chip ID is em2750\n");
+                       break;
+               case CHIP_ID_EM2820:
+                       em28xx_info("chip ID is em2820 (or em2710)\n");
+                       break;
+               case CHIP_ID_EM2840:
+                       em28xx_info("chip ID is em2840\n");
+                       break;
+               case CHIP_ID_EM2860:
+                       em28xx_info("chip ID is em2860\n");
+                       break;
+               case CHIP_ID_EM2870:
+                       em28xx_info("chip ID is em2870\n");
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM2874:
+                       em28xx_info("chip ID is em2874\n");
+                       dev->reg_gpio_num = EM2874_R80_GPIO;
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM28174:
+                       em28xx_info("chip ID is em28174\n");
+                       dev->reg_gpio_num = EM2874_R80_GPIO;
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM2883:
+                       em28xx_info("chip ID is em2882/em2883\n");
+                       dev->wait_after_write = 0;
+                       break;
+               case CHIP_ID_EM2884:
+                       em28xx_info("chip ID is em2884\n");
+                       dev->reg_gpio_num = EM2874_R80_GPIO;
+                       dev->wait_after_write = 0;
+                       break;
+               default:
+                       em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+               }
+       }
+ 
+       if (dev->is_audio_only) {
+               retval = em28xx_audio_setup(dev);
+               if (retval)
+                       return -ENODEV;
+               em28xx_init_extension(dev);
+ 
+               return 0;
+       }
+ 
+       /* Prepopulate cached GPO register content */
+       retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+       if (retval >= 0)
+               dev->reg_gpo = retval;
+ 
+       em28xx_pre_card_setup(dev);
+ 
+       if (!dev->board.is_em2800) {
+               /* Resets I2C speed */
+               retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg failed!"
+                                     " retval [%d]\n",
+                                     __func__, retval);
+                       return retval;
+               }
+       }
+ 
+       retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+       if (retval < 0) {
+               em28xx_errdev("Call to v4l2_device_register() failed!\n");
+               return retval;
+       }
+ 
+       /* register i2c bus */
+       retval = em28xx_i2c_register(dev);
+       if (retval < 0) {
+               em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+                       __func__, retval);
+               goto unregister_dev;
+       }
+ 
+       /*
+        * Default format, used for tvp5150 or saa711x output formats
+        */
+       dev->vinmode = 0x10;
+       dev->vinctl  = EM28XX_VINCTRL_INTERLACED |
+                      EM28XX_VINCTRL_CCIR656_ENABLE;
+ 
+       /* Do board specific init and eeprom reading */
+       em28xx_card_setup(dev);
+ 
+       /* Configure audio */
+       retval = em28xx_audio_setup(dev);
+       if (retval < 0) {
+               em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+                       __func__, retval);
+               goto fail;
+       }
+ 
+       /* wake i2c devices */
+       em28xx_wake_i2c(dev);
+ 
+       /* init video dma queues */
+       INIT_LIST_HEAD(&dev->vidq.active);
+       INIT_LIST_HEAD(&dev->vbiq.active);
+ 
+       if (dev->board.has_msp34xx) {
+               /* Send a reset to other chips via gpio */
+               retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg - "
+                                     "msp34xx(1) failed! error [%d]\n",
+                                     __func__, retval);
+                       goto fail;
+               }
+               msleep(3);
+ 
+               retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+               if (retval < 0) {
+                       em28xx_errdev("%s: em28xx_write_reg - "
+                                     "msp34xx(2) failed! error [%d]\n",
+                                     __func__, retval);
+                       goto fail;
+               }
+               msleep(3);
+       }
+ 
+       retval = em28xx_register_analog_devices(dev);
+       if (retval < 0) {
+               goto fail;
+       }
+ 
+       /* Save some power by putting tuner to sleep */
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+ 
+       return 0;
+ 
+ fail:
+       em28xx_i2c_unregister(dev);
+ 
+ unregister_dev:
+       v4l2_device_unregister(&dev->v4l2_dev);
+ 
+       return retval;
+ }
+ 
+ /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+ 
+ /*
+  * em28xx_usb_probe()
+  * checks for supported devices
+  */
+ static int em28xx_usb_probe(struct usb_interface *interface,
+                           const struct usb_device_id *id)
+ {
+       struct usb_device *udev;
+       struct em28xx *dev = NULL;
+       int retval;
+       bool has_audio = false, has_video = false, has_dvb = false;
+       int i, nr;
+       const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+       char *speed;
+ 
+       udev = usb_get_dev(interface_to_usbdev(interface));
+ 
+       /* Check to see next free device and mark as used */
+       do {
+               nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+               if (nr >= EM28XX_MAXBOARDS) {
+                       /* No free device slots */
+                       printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+                                       EM28XX_MAXBOARDS);
+                       retval = -ENOMEM;
+                       goto err_no_slot;
+               }
+       } while (test_and_set_bit(nr, &em28xx_devused));
+ 
+       /* Don't register audio interfaces */
+       if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+               em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+                       "interface %i, class %i\n",
+                       le16_to_cpu(udev->descriptor.idVendor),
+                       le16_to_cpu(udev->descriptor.idProduct),
+                       ifnum,
+                       interface->altsetting[0].desc.bInterfaceClass);
+ 
+               retval = -ENODEV;
+               goto err;
+       }
+ 
+       /* allocate memory for our device state and initialize it */
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL) {
+               em28xx_err(DRIVER_NAME ": out of memory!\n");
+               retval = -ENOMEM;
+               goto err;
+       }
+ 
+       /* compute alternate max packet sizes */
+       dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) *
+                                       interface->num_altsetting, GFP_KERNEL);
+       if (dev->alt_max_pkt_size == NULL) {
+               em28xx_errdev("out of memory!\n");
+               kfree(dev);
+               retval = -ENOMEM;
+               goto err;
+       }
+ 
+       /* Get endpoints */
+       for (i = 0; i < interface->num_altsetting; i++) {
+               int ep;
+ 
+               for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+                       const struct usb_endpoint_descriptor *e;
+                       int sizedescr, size;
+ 
+                       e = &interface->altsetting[i].endpoint[ep].desc;
+ 
+                       sizedescr = le16_to_cpu(e->wMaxPacketSize);
+                       size = sizedescr & 0x7ff;
+ 
+                       if (udev->speed == USB_SPEED_HIGH)
+                               size = size * hb_mult(sizedescr);
+ 
+                       if (usb_endpoint_xfer_isoc(e) &&
+                           usb_endpoint_dir_in(e)) {
+                               switch (e->bEndpointAddress) {
+                               case EM28XX_EP_AUDIO:
+                                       has_audio = true;
+                                       break;
+                               case EM28XX_EP_ANALOG:
+                                       has_video = true;
+                                       dev->alt_max_pkt_size[i] = size;
+                                       break;
+                               case EM28XX_EP_DIGITAL:
+                                       has_dvb = true;
+                                       if (size > dev->dvb_max_pkt_size) {
+                                               dev->dvb_max_pkt_size = size;
+                                               dev->dvb_alt = i;
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+ 
+       if (!(has_audio || has_video || has_dvb)) {
+               retval = -ENODEV;
+               goto err_free;
+       }
+ 
+       switch (udev->speed) {
+       case USB_SPEED_LOW:
+               speed = "1.5";
+               break;
+       case USB_SPEED_UNKNOWN:
+       case USB_SPEED_FULL:
+               speed = "12";
+               break;
+       case USB_SPEED_HIGH:
+               speed = "480";
+               break;
+       default:
+               speed = "unknown";
+       }
+ 
+       printk(KERN_INFO DRIVER_NAME
+               ": New device %s %s @ %s Mbps "
+               "(%04x:%04x, interface %d, class %d)\n",
+               udev->manufacturer ? udev->manufacturer : "",
+               udev->product ? udev->product : "",
+               speed,
+               le16_to_cpu(udev->descriptor.idVendor),
+               le16_to_cpu(udev->descriptor.idProduct),
+               ifnum,
+               interface->altsetting->desc.bInterfaceNumber);
+ 
+       if (has_audio)
+               printk(KERN_INFO DRIVER_NAME
+                      ": Audio Vendor Class interface %i found\n",
+                      ifnum);
+       if (has_video)
+               printk(KERN_INFO DRIVER_NAME
+                      ": Video interface %i found\n",
+                      ifnum);
+       if (has_dvb)
+               printk(KERN_INFO DRIVER_NAME
+                      ": DVB interface %i found\n",
+                      ifnum);
+ 
+       /*
+        * Make sure we have 480 Mbps of bandwidth, otherwise things like
+        * video stream wouldn't likely work, since 12 Mbps is generally
+        * not enough even for most Digital TV streams.
+        */
+       if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
+               printk(DRIVER_NAME ": Device initialization failed.\n");
+               printk(DRIVER_NAME ": Device must be connected to a high-speed"
+                      " USB 2.0 port.\n");
+               retval = -ENODEV;
+               goto err_free;
+       }
+ 
+       snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
+       dev->devno = nr;
+       dev->model = id->driver_info;
+       dev->alt   = -1;
+       dev->is_audio_only = has_audio && !(has_video || has_dvb);
+       dev->has_alsa_audio = has_audio;
+       dev->audio_ifnum = ifnum;
+ 
+       /* Checks if audio is provided by some interface */
+       for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+               struct usb_interface *uif = udev->config->interface[i];
+               if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+                       dev->has_audio_class = 1;
+                       break;
+               }
+       }
+ 
+       dev->num_alt = interface->num_altsetting;
+ 
+       if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+               dev->model = card[nr];
+ 
+       /* save our data pointer in this interface device */
+       usb_set_intfdata(interface, dev);
+ 
+       /* allocate device struct */
+       mutex_init(&dev->lock);
+       mutex_lock(&dev->lock);
+       retval = em28xx_init_dev(dev, udev, interface, nr);
+       if (retval) {
+               goto unlock_and_free;
+       }
+ 
+       if (has_dvb) {
+               /* pre-allocate DVB isoc transfer buffers */
+               retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
+                                          EM28XX_DVB_MAX_PACKETS,
+                                          EM28XX_DVB_NUM_BUFS,
+                                          dev->dvb_max_pkt_size);
+               if (retval) {
+                       goto unlock_and_free;
+               }
+       }
+ 
+       request_modules(dev);
+ 
+       /* Should be the last thing to do, to avoid newer udev's to
+          open the device before fully initializing it
+        */
+       mutex_unlock(&dev->lock);
+ 
+       return 0;
+ 
+ unlock_and_free:
+       mutex_unlock(&dev->lock);
+ 
+ err_free:
+       kfree(dev->alt_max_pkt_size);
+       kfree(dev);
+ 
+ err:
+       clear_bit(nr, &em28xx_devused);
+ 
+ err_no_slot:
+       usb_put_dev(udev);
+       return retval;
+ }
+ 
+ /*
+  * em28xx_usb_disconnect()
+  * called when the device gets disconnected
+  * video device will be unregistered on v4l2_close in case it is still open
+  */
+ static void em28xx_usb_disconnect(struct usb_interface *interface)
+ {
+       struct em28xx *dev;
+ 
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+ 
+       if (!dev)
+               return;
+ 
+       if (dev->is_audio_only) {
+               mutex_lock(&dev->lock);
+               em28xx_close_extension(dev);
+               mutex_unlock(&dev->lock);
+               return;
+       }
+ 
+       em28xx_info("disconnecting %s\n", dev->vdev->name);
+ 
+       flush_request_modules(dev);
+ 
+       /* wait until all current v4l2 io is finished then deallocate
+          resources */
+       mutex_lock(&dev->lock);
+ 
+       v4l2_device_disconnect(&dev->v4l2_dev);
+ 
+       if (dev->users) {
+               em28xx_warn
+                   ("device %s is open! Deregistration and memory "
+                    "deallocation are deferred on close.\n",
+                    video_device_node_name(dev->vdev));
+ 
+               dev->state |= DEV_MISCONFIGURED;
+               em28xx_uninit_isoc(dev, dev->mode);
+               dev->state |= DEV_DISCONNECTED;
+       } else {
+               dev->state |= DEV_DISCONNECTED;
+               em28xx_release_resources(dev);
+       }
+ 
+       /* free DVB isoc buffers */
+       em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
+ 
+       mutex_unlock(&dev->lock);
+ 
+       em28xx_close_extension(dev);
+ 
+       if (!dev->users) {
+               kfree(dev->alt_max_pkt_size);
+               kfree(dev);
+       }
+ }
+ 
+ static struct usb_driver em28xx_usb_driver = {
+       .name = "em28xx",
+       .probe = em28xx_usb_probe,
+       .disconnect = em28xx_usb_disconnect,
+       .id_table = em28xx_id_table,
+ };
+ 
+ module_usb_driver(em28xx_usb_driver);
 
--- /dev/null
 -      flush_work_sync(&dev->request_module_wk);
+ /*
+  *  tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+  *
+  *  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+  *
+  *  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 version 2
+  *
+  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+  */
+ 
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/i2c.h>
+ #include <linux/usb.h>
+ #include <linux/slab.h>
+ #include <media/v4l2-common.h>
+ #include <media/tuner.h>
+ #include <media/tvaudio.h>
+ #include <media/i2c-addr.h>
+ #include <media/rc-map.h>
+ 
+ #include "tm6000.h"
+ #include "tm6000-regs.h"
+ #include "tuner-xc2028.h"
+ #include "xc5000.h"
+ 
+ #define TM6000_BOARD_UNKNOWN                  0
+ #define TM5600_BOARD_GENERIC                  1
+ #define TM6000_BOARD_GENERIC                  2
+ #define TM6010_BOARD_GENERIC                  3
+ #define TM5600_BOARD_10MOONS_UT821            4
+ #define TM5600_BOARD_10MOONS_UT330            5
+ #define TM6000_BOARD_ADSTECH_DUAL_TV          6
+ #define TM6000_BOARD_FREECOM_AND_SIMILAR      7
+ #define TM6000_BOARD_ADSTECH_MINI_DUAL_TV     8
+ #define TM6010_BOARD_HAUPPAUGE_900H           9
+ #define TM6010_BOARD_BEHOLD_WANDER            10
+ #define TM6010_BOARD_BEHOLD_VOYAGER           11
+ #define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE       12
+ #define TM6010_BOARD_TWINHAN_TU501            13
+ #define TM6010_BOARD_BEHOLD_WANDER_LITE               14
+ #define TM6010_BOARD_BEHOLD_VOYAGER_LITE      15
+ #define TM5600_BOARD_TERRATEC_GRABSTER                16
+ 
+ #define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \
+                          (model == TM5600_BOARD_GENERIC) || \
+                          (model == TM6000_BOARD_GENERIC) || \
+                          (model == TM6010_BOARD_GENERIC))
+ 
+ #define TM6000_MAXBOARDS        16
+ static unsigned int card[]     = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
+ 
+ module_param_array(card,  int, NULL, 0444);
+ 
+ static unsigned long tm6000_devused;
+ 
+ 
+ struct tm6000_board {
+       char            *name;
+       char            eename[16];             /* EEPROM name */
+       unsigned        eename_size;            /* size of EEPROM name */
+       unsigned        eename_pos;             /* Position where it appears at ROM */
+ 
+       struct tm6000_capabilities caps;
+ 
+       enum            tm6000_devtype type;    /* variant of the chipset */
+       int             tuner_type;     /* type of the tuner */
+       int             tuner_addr;     /* tuner address */
+       int             demod_addr;     /* demodulator address */
+ 
+       struct tm6000_gpio gpio;
+ 
+       struct tm6000_input     vinput[3];
+       struct tm6000_input     rinput;
+ 
+       char            *ir_codes;
+ };
+ 
+ static struct tm6000_board tm6000_boards[] = {
+       [TM6000_BOARD_UNKNOWN] = {
+               .name         = "Unknown tm6000 video grabber",
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_eeprom     = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6000_GPIO_1,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM5600_BOARD_GENERIC] = {
+               .name         = "Generic tm5600 board",
+               .type         = TM5600,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0xc2 >> 1,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_eeprom     = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6000_GPIO_1,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6000_BOARD_GENERIC] = {
+               .name         = "Generic tm6000 board",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0xc2 >> 1,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_eeprom     = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6000_GPIO_1,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6010_BOARD_GENERIC] = {
+               .name         = "Generic tm6010 board",
+               .type         = TM6010,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_dvb        = 1,
+                       .has_zl10353    = 1,
+                       .has_eeprom     = 1,
+                       .has_remote     = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_2,
+                       .tuner_on       = TM6010_GPIO_3,
+                       .demod_reset    = TM6010_GPIO_1,
+                       .demod_on       = TM6010_GPIO_4,
+                       .power_led      = TM6010_GPIO_7,
+                       .dvb_led        = TM6010_GPIO_5,
+                       .ir             = TM6010_GPIO_0,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM5600_BOARD_10MOONS_UT821] = {
+               .name         = "10Moons UT 821",
+               .tuner_type   = TUNER_XC2028,
+               .eename       = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b},
+               .eename_size  = 14,
+               .eename_pos   = 0x14,
+               .type         = TM5600,
+               .tuner_addr   = 0xc2 >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_eeprom   = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6000_GPIO_1,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM5600_BOARD_10MOONS_UT330] = {
+               .name         = "10Moons UT 330",
+               .tuner_type   = TUNER_PHILIPS_FQ1216AME_MK4,
+               .tuner_addr   = 0xc8 >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 0,
+                       .has_zl10353  = 0,
+                       .has_eeprom   = 1,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6000_BOARD_ADSTECH_DUAL_TV] = {
+               .name         = "ADSTECH Dual TV USB",
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0xc8 >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_tda9874  = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
+               .name         = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 0,
+                       .has_remote   = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6000_GPIO_4,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
+               .name         = "ADSTECH Mini Dual TV USB",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc8 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 0,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6000_GPIO_4,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6010_BOARD_HAUPPAUGE_900H] = {
+               .name         = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick",
+               .eename       = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 },
+               .eename_size  = 14,
+               .eename_pos   = 0x42,
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .ir_codes = RC_MAP_HAUPPAUGE,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+                       .has_remote   = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_2,
+                       .tuner_on       = TM6010_GPIO_3,
+                       .demod_reset    = TM6010_GPIO_1,
+                       .demod_on       = TM6010_GPIO_4,
+                       .power_led      = TM6010_GPIO_7,
+                       .dvb_led        = TM6010_GPIO_5,
+                       .ir             = TM6010_GPIO_0,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6010_BOARD_BEHOLD_WANDER] = {
+               .name         = "Beholder Wander DVB-T/TV/FM USB2.0",
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_dvb        = 1,
+                       .has_zl10353    = 1,
+                       .has_eeprom     = 1,
+                       .has_remote     = 1,
+                       .has_radio      = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_0,
+                       .demod_reset    = TM6010_GPIO_1,
+                       .power_led      = TM6010_GPIO_6,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+               .rinput = {
+                       .type   = TM6000_INPUT_RADIO,
+                       .amux   = TM6000_AMUX_ADC1,
+               },
+       },
+       [TM6010_BOARD_BEHOLD_VOYAGER] = {
+               .name         = "Beholder Voyager TV/FM USB2.0",
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0xc2 >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_dvb        = 0,
+                       .has_zl10353    = 0,
+                       .has_eeprom     = 1,
+                       .has_remote     = 1,
+                       .has_radio      = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_0,
+                       .power_led      = TM6010_GPIO_6,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+               .rinput = {
+                       .type   = TM6000_INPUT_RADIO,
+                       .amux   = TM6000_AMUX_ADC1,
+               },
+       },
+       [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
+               .name         = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+                       .has_remote   = 1,
+                       .has_radio    = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_2,
+                       .tuner_on       = TM6010_GPIO_3,
+                       .demod_reset    = TM6010_GPIO_1,
+                       .demod_on       = TM6010_GPIO_4,
+                       .power_led      = TM6010_GPIO_7,
+                       .dvb_led        = TM6010_GPIO_5,
+                       .ir             = TM6010_GPIO_0,
+               },
+               .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+               .rinput = {
+                       .type = TM6000_INPUT_RADIO,
+                       .amux = TM6000_AMUX_SIF1,
+               },
+       },
+       [TM5600_BOARD_TERRATEC_GRABSTER] = {
+               .name         = "Terratec Grabster AV 150/250 MX",
+               .type         = TM5600,
+               .tuner_type   = TUNER_ABSENT,
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_ADC1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6010_BOARD_TWINHAN_TU501] = {
+               .name         = "Twinhan TU501(704D1)",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+                       .has_remote   = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_2,
+                       .tuner_on       = TM6010_GPIO_3,
+                       .demod_reset    = TM6010_GPIO_1,
+                       .demod_on       = TM6010_GPIO_4,
+                       .power_led      = TM6010_GPIO_7,
+                       .dvb_led        = TM6010_GPIO_5,
+                       .ir             = TM6010_GPIO_0,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       }, {
+                       .type   = TM6000_INPUT_COMPOSITE1,
+                       .vmux   = TM6000_VMUX_VIDEO_A,
+                       .amux   = TM6000_AMUX_ADC2,
+                       }, {
+                       .type   = TM6000_INPUT_SVIDEO,
+                       .vmux   = TM6000_VMUX_VIDEO_AB,
+                       .amux   = TM6000_AMUX_ADC2,
+                       },
+               },
+       },
+       [TM6010_BOARD_BEHOLD_WANDER_LITE] = {
+               .name         = "Beholder Wander Lite DVB-T/TV/FM USB2.0",
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_dvb        = 1,
+                       .has_zl10353    = 1,
+                       .has_eeprom     = 1,
+                       .has_remote     = 0,
+                       .has_radio      = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_0,
+                       .demod_reset    = TM6010_GPIO_1,
+                       .power_led      = TM6010_GPIO_6,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       },
+               },
+               .rinput = {
+                       .type   = TM6000_INPUT_RADIO,
+                       .amux   = TM6000_AMUX_ADC1,
+               },
+       },
+       [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = {
+               .name         = "Beholder Voyager Lite TV/FM USB2.0",
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0xc2 >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_dvb        = 0,
+                       .has_zl10353    = 0,
+                       .has_eeprom     = 1,
+                       .has_remote     = 0,
+                       .has_radio      = 1,
+               },
+               .gpio = {
+                       .tuner_reset    = TM6010_GPIO_0,
+                       .power_led      = TM6010_GPIO_6,
+               },
+               .vinput = { {
+                       .type   = TM6000_INPUT_TV,
+                       .vmux   = TM6000_VMUX_VIDEO_B,
+                       .amux   = TM6000_AMUX_SIF1,
+                       },
+               },
+               .rinput = {
+                       .type   = TM6000_INPUT_RADIO,
+                       .amux   = TM6000_AMUX_ADC1,
+               },
+       },
+ };
+ 
+ /* table of devices that work with this driver */
+ static struct usb_device_id tm6000_id_table[] = {
+       { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC },
+       { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
+       { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
+       { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
+       { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
+       { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+       { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+       { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+       { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+       { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
+       { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
+       { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
+       { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
+       { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER },
+       { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+       { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+       { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+       { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 },
+       { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE },
+       { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
+       { }
+ };
+ MODULE_DEVICE_TABLE(usb, tm6000_id_table);
+ 
+ /* Control power led for show some activity */
+ void tm6000_flash_led(struct tm6000_core *dev, u8 state)
+ {
+       /* Power LED unconfigured */
+       if (!dev->gpio.power_led)
+               return;
+ 
+       /* ON Power LED */
+       if (state) {
+               switch (dev->model) {
+               case TM6010_BOARD_HAUPPAUGE_900H:
+               case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+               case TM6010_BOARD_TWINHAN_TU501:
+                       tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                               dev->gpio.power_led, 0x00);
+                       break;
+               case TM6010_BOARD_BEHOLD_WANDER:
+               case TM6010_BOARD_BEHOLD_VOYAGER:
+               case TM6010_BOARD_BEHOLD_WANDER_LITE:
+               case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+                       tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                               dev->gpio.power_led, 0x01);
+                       break;
+               }
+       }
+       /* OFF Power LED */
+       else {
+               switch (dev->model) {
+               case TM6010_BOARD_HAUPPAUGE_900H:
+               case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+               case TM6010_BOARD_TWINHAN_TU501:
+                       tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                               dev->gpio.power_led, 0x01);
+                       break;
+               case TM6010_BOARD_BEHOLD_WANDER:
+               case TM6010_BOARD_BEHOLD_VOYAGER:
+               case TM6010_BOARD_BEHOLD_WANDER_LITE:
+               case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+                       tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                               dev->gpio.power_led, 0x00);
+                       break;
+               }
+       }
+ }
+ 
+ /* Tuner callback to provide the proper gpio changes needed for xc5000 */
+ int tm6000_xc5000_callback(void *ptr, int component, int command, int arg)
+ {
+       int rc = 0;
+       struct tm6000_core *dev = ptr;
+ 
+       if (dev->tuner_type != TUNER_XC5000)
+               return 0;
+ 
+       switch (command) {
+       case XC5000_TUNER_RESET:
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                              dev->gpio.tuner_reset, 0x01);
+               msleep(15);
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                              dev->gpio.tuner_reset, 0x00);
+               msleep(15);
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                              dev->gpio.tuner_reset, 0x01);
+               break;
+       }
+       return rc;
+ }
+ EXPORT_SYMBOL_GPL(tm6000_xc5000_callback);
+ 
+ /* Tuner callback to provide the proper gpio changes needed for xc2028 */
+ 
+ int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
+ {
+       int rc = 0;
+       struct tm6000_core *dev = ptr;
+ 
+       if (dev->tuner_type != TUNER_XC2028)
+               return 0;
+ 
+       switch (command) {
+       case XC2028_RESET_CLK:
+               tm6000_ir_wait(dev, 0);
+ 
+               tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
+                                       0x02, arg);
+               msleep(10);
+               rc = tm6000_i2c_reset(dev, 10);
+               break;
+       case XC2028_TUNER_RESET:
+               /* Reset codes during load firmware */
+               switch (arg) {
+               case 0:
+                       /* newer tuner can faster reset */
+                       switch (dev->model) {
+                       case TM5600_BOARD_10MOONS_UT821:
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x01);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              0x300, 0x01);
+                               msleep(10);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x00);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              0x300, 0x00);
+                               msleep(10);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x01);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              0x300, 0x01);
+                               break;
+                       case TM6010_BOARD_HAUPPAUGE_900H:
+                       case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+                       case TM6010_BOARD_TWINHAN_TU501:
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x01);
+                               msleep(60);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x00);
+                               msleep(75);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x01);
+                               msleep(60);
+                               break;
+                       default:
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x00);
+                               msleep(130);
+                               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                              dev->gpio.tuner_reset, 0x01);
+                               msleep(130);
+                               break;
+                       }
+ 
+                       tm6000_ir_wait(dev, 1);
+                       break;
+               case 1:
+                       tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT,
+                                               0x02, 0x01);
+                       msleep(10);
+                       break;
+               case 2:
+                       rc = tm6000_i2c_reset(dev, 100);
+                       break;
+               }
+               break;
+       case XC2028_I2C_FLUSH:
+               tm6000_set_reg(dev, REQ_50_SET_START, 0, 0);
+               tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0);
+               break;
+       }
+       return rc;
+ }
+ EXPORT_SYMBOL_GPL(tm6000_tuner_callback);
+ 
+ int tm6000_cards_setup(struct tm6000_core *dev)
+ {
+       /*
+        * Board-specific initialization sequence. Handles all GPIO
+        * initialization sequences that are board-specific.
+        * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
+        * Probably, they're all based on some reference device. Due to that,
+        * there's a common routine at the end to handle those GPIO's. Devices
+        * that use different pinups or init sequences can just return at
+        * the board-specific session.
+        */
+       switch (dev->model) {
+       case TM6010_BOARD_HAUPPAUGE_900H:
+       case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+       case TM6010_BOARD_TWINHAN_TU501:
+       case TM6010_BOARD_GENERIC:
+               /* Turn xceive 3028 on */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01);
+               msleep(15);
+               /* Turn zarlink zl10353 on */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
+               msleep(15);
+               /* Reset zarlink zl10353 */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
+               msleep(50);
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
+               msleep(15);
+               /* Turn zarlink zl10353 off */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01);
+               msleep(15);
+               /* ir ? */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01);
+               msleep(15);
+               /* Power led on (blue) */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00);
+               msleep(15);
+               /* DVB led off (orange) */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01);
+               msleep(15);
+               /* Turn zarlink zl10353 on */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00);
+               msleep(15);
+               break;
+       case TM6010_BOARD_BEHOLD_WANDER:
+       case TM6010_BOARD_BEHOLD_WANDER_LITE:
+               /* Power led on (blue) */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
+               msleep(15);
+               /* Reset zarlink zl10353 */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00);
+               msleep(50);
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01);
+               msleep(15);
+               break;
+       case TM6010_BOARD_BEHOLD_VOYAGER:
+       case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+               /* Power led on (blue) */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01);
+               msleep(15);
+               break;
+       default:
+               break;
+       }
+ 
+       /*
+        * Default initialization. Most of the devices seem to use GPIO1
+        * and GPIO4.on the same way, so, this handles the common sequence
+        * used by most devices.
+        * If a device uses a different sequence or different GPIO pins for
+        * reset, just add the code at the board-specific part
+        */
+ 
+       if (dev->gpio.tuner_reset) {
+               int rc;
+               int i;
+ 
+               for (i = 0; i < 2; i++) {
+                       rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                               dev->gpio.tuner_reset, 0x00);
+                       if (rc < 0) {
+                               printk(KERN_ERR "Error %i doing tuner reset\n", rc);
+                               return rc;
+                       }
+ 
+                       msleep(10); /* Just to be conservative */
+                       rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                               dev->gpio.tuner_reset, 0x01);
+                       if (rc < 0) {
+                               printk(KERN_ERR "Error %i doing tuner reset\n", rc);
+                               return rc;
+                       }
+               }
+       } else {
+               printk(KERN_ERR "Tuner reset is not configured\n");
+               return -1;
+       }
+ 
+       msleep(50);
+ 
+       return 0;
+ };
+ 
+ static void tm6000_config_tuner(struct tm6000_core *dev)
+ {
+       struct tuner_setup tun_setup;
+ 
+       /* Load tuner module */
+       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+               "tuner", dev->tuner_addr, NULL);
+ 
+       memset(&tun_setup, 0, sizeof(tun_setup));
+       tun_setup.type = dev->tuner_type;
+       tun_setup.addr = dev->tuner_addr;
+ 
+       tun_setup.mode_mask = 0;
+       if (dev->caps.has_tuner)
+               tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO);
+ 
+       switch (dev->tuner_type) {
+       case TUNER_XC2028:
+               tun_setup.tuner_callback = tm6000_tuner_callback;
+               break;
+       case TUNER_XC5000:
+               tun_setup.tuner_callback = tm6000_xc5000_callback;
+               break;
+       }
+ 
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+ 
+       switch (dev->tuner_type) {
+       case TUNER_XC2028: {
+               struct v4l2_priv_tun_config xc2028_cfg;
+               struct xc2028_ctrl ctl;
+ 
+               memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+               memset(&ctl, 0, sizeof(ctl));
+ 
+               ctl.demod = XC3028_FE_ZARLINK456;
+ 
+               xc2028_cfg.tuner = TUNER_XC2028;
+               xc2028_cfg.priv  = &ctl;
+ 
+               switch (dev->model) {
+               case TM6010_BOARD_HAUPPAUGE_900H:
+               case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+               case TM6010_BOARD_TWINHAN_TU501:
+                       ctl.max_len = 80;
+                       ctl.fname = "xc3028L-v36.fw";
+                       break;
+               default:
+                       if (dev->dev_type == TM6010)
+                               ctl.fname = "xc3028-v27.fw";
+                       else
+                               ctl.fname = "xc3028-v24.fw";
+               }
+ 
+               printk(KERN_INFO "Setting firmware parameters for xc2028\n");
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
+                                    &xc2028_cfg);
+ 
+               }
+               break;
+       case TUNER_XC5000:
+               {
+               struct v4l2_priv_tun_config  xc5000_cfg;
+               struct xc5000_config ctl = {
+                       .i2c_address = dev->tuner_addr,
+                       .if_khz      = 4570,
+                       .radio_input = XC5000_RADIO_FM1_MONO,
+                       };
+ 
+               xc5000_cfg.tuner = TUNER_XC5000;
+               xc5000_cfg.priv  = &ctl;
+ 
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
+                                    &xc5000_cfg);
+               }
+               break;
+       default:
+               printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n");
+               break;
+       }
+ }
+ 
+ static int fill_board_specific_data(struct tm6000_core *dev)
+ {
+       int rc;
+ 
+       dev->dev_type   = tm6000_boards[dev->model].type;
+       dev->tuner_type = tm6000_boards[dev->model].tuner_type;
+       dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
+ 
+       dev->gpio = tm6000_boards[dev->model].gpio;
+ 
+       dev->ir_codes = tm6000_boards[dev->model].ir_codes;
+ 
+       dev->demod_addr = tm6000_boards[dev->model].demod_addr;
+ 
+       dev->caps = tm6000_boards[dev->model].caps;
+ 
+       dev->vinput[0] = tm6000_boards[dev->model].vinput[0];
+       dev->vinput[1] = tm6000_boards[dev->model].vinput[1];
+       dev->vinput[2] = tm6000_boards[dev->model].vinput[2];
+       dev->rinput = tm6000_boards[dev->model].rinput;
+ 
+       /* setup per-model quirks */
+       switch (dev->model) {
+       case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+       case TM6010_BOARD_HAUPPAUGE_900H:
+               dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
+               break;
+ 
+       default:
+               break;
+       }
+ 
+       /* initialize hardware */
+       rc = tm6000_init(dev);
+       if (rc < 0)
+               return rc;
+ 
+       return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
+ }
+ 
+ 
+ static void use_alternative_detection_method(struct tm6000_core *dev)
+ {
+       int i, model = -1;
+ 
+       if (!dev->eedata_size)
+               return;
+ 
+       for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) {
+               if (!tm6000_boards[i].eename_size)
+                       continue;
+               if (dev->eedata_size < tm6000_boards[i].eename_pos +
+                                      tm6000_boards[i].eename_size)
+                       continue;
+ 
+               if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos],
+                           tm6000_boards[i].eename,
+                           tm6000_boards[i].eename_size)) {
+                       model = i;
+                       break;
+               }
+       }
+       if (model < 0) {
+               printk(KERN_INFO "Device has eeprom but is currently unknown\n");
+               return;
+       }
+ 
+       dev->model = model;
+ 
+       printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n",
+              tm6000_boards[model].name, model);
+ }
+ 
+ #if defined(CONFIG_MODULES) && defined(MODULE)
+ static void request_module_async(struct work_struct *work)
+ {
+       struct tm6000_core *dev = container_of(work, struct tm6000_core,
+                                              request_module_wk);
+ 
+       request_module("tm6000-alsa");
+ 
+       if (dev->caps.has_dvb)
+               request_module("tm6000-dvb");
+ }
+ 
+ static void request_modules(struct tm6000_core *dev)
+ {
+       INIT_WORK(&dev->request_module_wk, request_module_async);
+       schedule_work(&dev->request_module_wk);
+ }
+ 
+ static void flush_request_modules(struct tm6000_core *dev)
+ {
++      flush_work(&dev->request_module_wk);
+ }
+ #else
+ #define request_modules(dev)
+ #define flush_request_modules(dev)
+ #endif /* CONFIG_MODULES */
+ 
+ static int tm6000_init_dev(struct tm6000_core *dev)
+ {
+       struct v4l2_frequency f;
+       int rc = 0;
+ 
+       mutex_init(&dev->lock);
+       mutex_lock(&dev->lock);
+ 
+       if (!is_generic(dev->model)) {
+               rc = fill_board_specific_data(dev);
+               if (rc < 0)
+                       goto err;
+ 
+               /* register i2c bus */
+               rc = tm6000_i2c_register(dev);
+               if (rc < 0)
+                       goto err;
+       } else {
+               /* register i2c bus */
+               rc = tm6000_i2c_register(dev);
+               if (rc < 0)
+                       goto err;
+ 
+               use_alternative_detection_method(dev);
+ 
+               rc = fill_board_specific_data(dev);
+               if (rc < 0)
+                       goto err;
+       }
+ 
+       /* Default values for STD and resolutions */
+       dev->width = 720;
+       dev->height = 480;
+       dev->norm = V4L2_STD_PAL_M;
+ 
+       /* Configure tuner */
+       tm6000_config_tuner(dev);
+ 
+       /* Set video standard */
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+ 
+       /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
+       f.tuner = 0;
+       f.type = V4L2_TUNER_ANALOG_TV;
+       f.frequency = 3092;     /* 193.25 MHz */
+       dev->freq = f.frequency;
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+ 
+       if (dev->caps.has_tda9874)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "tvaudio", I2C_ADDR_TDA9874, NULL);
+ 
+       /* register and initialize V4L2 */
+       rc = tm6000_v4l2_register(dev);
+       if (rc < 0)
+               goto err;
+ 
+       tm6000_add_into_devlist(dev);
+       tm6000_init_extension(dev);
+ 
+       tm6000_ir_init(dev);
+ 
+       request_modules(dev);
+ 
+       mutex_unlock(&dev->lock);
+       return 0;
+ 
+ err:
+       mutex_unlock(&dev->lock);
+       return rc;
+ }
+ 
+ /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+ 
+ static void get_max_endpoint(struct usb_device *udev,
+                            struct usb_host_interface *alt,
+                            char *msgtype,
+                            struct usb_host_endpoint *curr_e,
+                            struct tm6000_endpoint *tm_ep)
+ {
+       u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
+       unsigned int size = tmp & 0x7ff;
+ 
+       if (udev->speed == USB_SPEED_HIGH)
+               size = size * hb_mult(tmp);
+ 
+       if (size > tm_ep->maxsize) {
+               tm_ep->endp = curr_e;
+               tm_ep->maxsize = size;
+               tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber;
+               tm_ep->bAlternateSetting = alt->desc.bAlternateSetting;
+ 
+               printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
+                                       msgtype, curr_e->desc.bEndpointAddress,
+                                       size);
+       }
+ }
+ 
+ /*
+  * tm6000_usb_probe()
+  * checks for supported devices
+  */
+ static int tm6000_usb_probe(struct usb_interface *interface,
+                           const struct usb_device_id *id)
+ {
+       struct usb_device *usbdev;
+       struct tm6000_core *dev = NULL;
+       int i, rc = 0;
+       int nr = 0;
+       char *speed;
+ 
+       usbdev = usb_get_dev(interface_to_usbdev(interface));
+ 
+       /* Selects the proper interface */
+       rc = usb_set_interface(usbdev, 0, 1);
+       if (rc < 0)
+               goto err;
+ 
+       /* Check to see next free device and mark as used */
+       nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS);
+       if (nr >= TM6000_MAXBOARDS) {
+               printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS);
+               usb_put_dev(usbdev);
+               return -ENOMEM;
+       }
+ 
+       /* Create and initialize dev struct */
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL) {
+               printk(KERN_ERR "tm6000" ": out of memory!\n");
+               usb_put_dev(usbdev);
+               return -ENOMEM;
+       }
+       spin_lock_init(&dev->slock);
+       mutex_init(&dev->usb_lock);
+ 
+       /* Increment usage count */
+       set_bit(nr, &tm6000_devused);
+       snprintf(dev->name, 29, "tm6000 #%d", nr);
+ 
+       dev->model = id->driver_info;
+       if (card[nr] < ARRAY_SIZE(tm6000_boards))
+               dev->model = card[nr];
+ 
+       dev->udev = usbdev;
+       dev->devno = nr;
+ 
+       switch (usbdev->speed) {
+       case USB_SPEED_LOW:
+               speed = "1.5";
+               break;
+       case USB_SPEED_UNKNOWN:
+       case USB_SPEED_FULL:
+               speed = "12";
+               break;
+       case USB_SPEED_HIGH:
+               speed = "480";
+               break;
+       default:
+               speed = "unknown";
+       }
+ 
+       /* Get endpoints */
+       for (i = 0; i < interface->num_altsetting; i++) {
+               int ep;
+ 
+               for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+                       struct usb_host_endpoint        *e;
+                       int dir_out;
+ 
+                       e = &interface->altsetting[i].endpoint[ep];
+ 
+                       dir_out = ((e->desc.bEndpointAddress &
+                                       USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+ 
+                       printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n",
+                              i,
+                              interface->altsetting[i].desc.bInterfaceNumber,
+                              interface->altsetting[i].desc.bInterfaceClass);
+ 
+                       switch (e->desc.bmAttributes) {
+                       case USB_ENDPOINT_XFER_BULK:
+                               if (!dir_out) {
+                                       get_max_endpoint(usbdev,
+                                                        &interface->altsetting[i],
+                                                        "Bulk IN", e,
+                                                        &dev->bulk_in);
+                               } else {
+                                       get_max_endpoint(usbdev,
+                                                        &interface->altsetting[i],
+                                                        "Bulk OUT", e,
+                                                        &dev->bulk_out);
+                               }
+                               break;
+                       case USB_ENDPOINT_XFER_ISOC:
+                               if (!dir_out) {
+                                       get_max_endpoint(usbdev,
+                                                        &interface->altsetting[i],
+                                                        "ISOC IN", e,
+                                                        &dev->isoc_in);
+                               } else {
+                                       get_max_endpoint(usbdev,
+                                                        &interface->altsetting[i],
+                                                        "ISOC OUT", e,
+                                                        &dev->isoc_out);
+                               }
+                               break;
+                       case USB_ENDPOINT_XFER_INT:
+                               if (!dir_out) {
+                                       get_max_endpoint(usbdev,
+                                                       &interface->altsetting[i],
+                                                       "INT IN", e,
+                                                       &dev->int_in);
+                               } else {
+                                       get_max_endpoint(usbdev,
+                                                       &interface->altsetting[i],
+                                                       "INT OUT", e,
+                                                       &dev->int_out);
+                               }
+                               break;
+                       }
+               }
+       }
+ 
+ 
+       printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
+               speed,
+               le16_to_cpu(dev->udev->descriptor.idVendor),
+               le16_to_cpu(dev->udev->descriptor.idProduct),
+               interface->altsetting->desc.bInterfaceNumber);
+ 
+ /* check if the the device has the iso in endpoint at the correct place */
+       if (!dev->isoc_in.endp) {
+               printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n");
+               rc = -ENODEV;
+ 
+               goto err;
+       }
+ 
+       /* save our data pointer in this interface device */
+       usb_set_intfdata(interface, dev);
+ 
+       printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name);
+ 
+       rc = tm6000_init_dev(dev);
+       if (rc < 0)
+               goto err;
+ 
+       return 0;
+ 
+ err:
+       printk(KERN_ERR "tm6000: Error %d while registering\n", rc);
+ 
+       clear_bit(nr, &tm6000_devused);
+       usb_put_dev(usbdev);
+ 
+       kfree(dev);
+       return rc;
+ }
+ 
+ /*
+  * tm6000_usb_disconnect()
+  * called when the device gets diconencted
+  * video device will be unregistered on v4l2_close in case it is still open
+  */
+ static void tm6000_usb_disconnect(struct usb_interface *interface)
+ {
+       struct tm6000_core *dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+ 
+       if (!dev)
+               return;
+ 
+       printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
+ 
+       flush_request_modules(dev);
+ 
+       tm6000_ir_fini(dev);
+ 
+       if (dev->gpio.power_led) {
+               switch (dev->model) {
+               case TM6010_BOARD_HAUPPAUGE_900H:
+               case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+               case TM6010_BOARD_TWINHAN_TU501:
+                       /* Power led off */
+                       tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                               dev->gpio.power_led, 0x01);
+                       msleep(15);
+                       break;
+               case TM6010_BOARD_BEHOLD_WANDER:
+               case TM6010_BOARD_BEHOLD_VOYAGER:
+               case TM6010_BOARD_BEHOLD_WANDER_LITE:
+               case TM6010_BOARD_BEHOLD_VOYAGER_LITE:
+                       /* Power led off */
+                       tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                               dev->gpio.power_led, 0x00);
+                       msleep(15);
+                       break;
+               }
+       }
+       tm6000_v4l2_unregister(dev);
+ 
+       tm6000_i2c_unregister(dev);
+ 
+       v4l2_device_unregister(&dev->v4l2_dev);
+ 
+       dev->state |= DEV_DISCONNECTED;
+ 
+       usb_put_dev(dev->udev);
+ 
+       tm6000_close_extension(dev);
+       tm6000_remove_from_devlist(dev);
+ 
+       clear_bit(dev->devno, &tm6000_devused);
+       kfree(dev);
+ }
+ 
+ static struct usb_driver tm6000_usb_driver = {
+               .name = "tm6000",
+               .probe = tm6000_usb_probe,
+               .disconnect = tm6000_usb_disconnect,
+               .id_table = tm6000_id_table,
+ };
+ 
+ module_usb_driver(tm6000_usb_driver);
+ 
+ MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
+ MODULE_AUTHOR("Mauro Carvalho Chehab");
+ MODULE_LICENSE("GPL");