rnel.
aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/media/common/saa7146_core.c
blob: 168a8d3a5e556e889b7b44033b06531addb9faf8 (plain) (tree)






















                                                                        
                                   
 
                       
 
                           
 
                                        






























                                                                              

                                                                           
 
                              
                

                                                 
                                                  
                   
                                                   


                                               


                                                                             

                                          
                          


                                           
                                                  
                   
                                                   


                                                            
                          

                                                                             

                                          
                          




                 











































                                                                               



                                                                              
                                                          







                                                                           
                                                                           

                            
                                        



                                                           
                                        
                                                          










                                                                                  
                                                                                                 

                                                   
                                       

                     

                              
 

                                                     
 

                                           
 






                                                                        

                   













                                                                    
                                                                                              





                                                                    







                                                                          



                                                                          

                              























































                                                                                                                        
                                                      

                                         

                    

                                                    
                                               






                                             





















                                                                 




                                                          
                        



                                                                                                   







                                                                                             
                                         












                                                                                                           

                                                              




                                          




















                                                                             
                                                               





























                                                                      
                                                                              




































                                                                                                                                                                   
                               


                                        
                                   






































































































                                                                                   
                                                 

















                                                                  
                                                 



                                              








                                                                
/*
    saa7146.o - driver for generic saa7146-based hardware

    Copyright (C) 1998-2003 Michael Hunold <michael@mihu.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 <media/saa7146.h>

LIST_HEAD(saa7146_devices);
DEFINE_MUTEX(saa7146_devices_lock);

static int saa7146_num;

unsigned int saa7146_debug;

module_param(saa7146_debug, uint, 0644);
MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");

#if 0
static void dump_registers(struct saa7146_dev* dev)
{
	int i = 0;

	INFO((" @ %li jiffies:\n",jiffies));
	for(i = 0; i <= 0x148; i+=4) {
		printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
	}
}
#endif

/****************************************************************************
 * gpio and debi helper functions
 ****************************************************************************/

void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
{
	u32 value = 0;

	BUG_ON(port > 3);

	value = saa7146_read(dev, GPIO_CTRL);
	value &= ~(0xff << (8*port));
	value |= (data << (8*port));
	saa7146_write(dev, GPIO_CTRL, value);
}

/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
				unsigned long us1, unsigned long us2)
{
	unsigned long timeout;
	int err;

	/* wait for registers to be programmed */
	timeout = jiffies + usecs_to_jiffies(us1);
	while (1) {
		err = time_after(jiffies, timeout);
		if (saa7146_read(dev, MC2) & 2)
			break;
		if (err) {
			printk(KERN_ERR "%s: %s timed out while waiting for "
					"registers getting programmed\n",
					dev->name, __FUNCTION__);
			return -ETIMEDOUT;
		}
		msleep(1);
	}

	/* wait for transfer to complete */
	timeout = jiffies + usecs_to_jiffies(us2);
	while (1) {
		err = time_after(jiffies, timeout);
		if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
			break;
		saa7146_read(dev, MC2);
		if (err) {
			DEB_S(("%s: %s timed out while waiting for transfer "
				"completion\n",	dev->name, __FUNCTION__));
			return -ETIMEDOUT;
		}
		msleep(1);
	}

	return 0;
}

static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
				unsigned long us1, unsigned long us2)
{
	unsigned long loops;

	/* wait for registers to be programmed */
	loops = us1;
	while (1) {
		if (saa7146_read(dev, MC2) & 2)
			break;
		if (!loops--) {
			printk(KERN_ERR "%s: %s timed out while waiting for "
					"registers getting programmed\n",
					dev->name, __FUNCTION__);
			return -ETIMEDOUT;
		}
		udelay(1);
	}

	/* wait for transfer to complete */
	loops = us2 / 5;
	while (1) {
		if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
			break;
		saa7146_read(dev, MC2);
		if (!loops--) {
			DEB_S(("%s: %s timed out while waiting for transfer "
				"completion\n", dev->name, __FUNCTION__));
			return -ETIMEDOUT;
		}
		udelay(5);
	}

	return 0;
}

int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
{
	if (nobusyloop)
		return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
	else
		return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
}

/****************************************************************************
 * general helper functions
 ****************************************************************************/

/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
   make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
   may be triggered on highmem machines */
static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
{
	struct scatterlist *sglist;
	struct page *pg;
	int i;

	sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
	if (NULL == sglist)
		return NULL;
	sg_init_table(sglist, nr_pages);
	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
		pg = vmalloc_to_page(virt);
		if (NULL == pg)
			goto err;
		BUG_ON(PageHighMem(pg));
		sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
	}
	return sglist;

 err:
	kfree(sglist);
	return NULL;
}

/********************************************************************************/
/* common page table functions */

void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
{
	int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
	void *mem = vmalloc_32(length);
	int slen = 0;

	if (NULL == mem)
		goto err_null;

	if (!(pt->slist = vmalloc_to_sg(mem, pages)))
		goto err_free_mem;

	if (saa7146_pgtable_alloc(pci, pt))
		goto err_free_slist;

	pt->nents = pages;
	slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);
	if (0 == slen)
		goto err_free_pgtable;

	if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
		goto err_unmap_sg;

	return mem;

err_unmap_sg:
	pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
err_free_pgtable:
	saa7146_pgtable_free(pci, pt);
err_free_slist:
	kfree(pt->slist);
	pt->slist = NULL;
err_free_mem:
	vfree(mem);
err_null:
	return NULL;
}

void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
{
	pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
	saa7146_pgtable_free(pci, pt);
	kfree(pt->slist);
	pt->slist = NULL;
	vfree(mem);