139d08ab9SHans Verkuil // SPDX-License-Identifier: GPL-2.0-or-later
239d08ab9SHans Verkuil /*
339d08ab9SHans Verkuil     saa7146.o - driver for generic saa7146-based hardware
439d08ab9SHans Verkuil 
539d08ab9SHans Verkuil     Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
639d08ab9SHans Verkuil 
739d08ab9SHans Verkuil */
839d08ab9SHans Verkuil 
939d08ab9SHans Verkuil #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1039d08ab9SHans Verkuil 
1139d08ab9SHans Verkuil #include <media/drv-intf/saa7146.h>
1239d08ab9SHans Verkuil #include <linux/module.h>
1339d08ab9SHans Verkuil 
1439d08ab9SHans Verkuil static int saa7146_num;
1539d08ab9SHans Verkuil 
1639d08ab9SHans Verkuil unsigned int saa7146_debug;
1739d08ab9SHans Verkuil 
1839d08ab9SHans Verkuil module_param(saa7146_debug, uint, 0644);
1939d08ab9SHans Verkuil MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
2039d08ab9SHans Verkuil 
2139d08ab9SHans Verkuil #if 0
2239d08ab9SHans Verkuil static void dump_registers(struct saa7146_dev* dev)
2339d08ab9SHans Verkuil {
2439d08ab9SHans Verkuil 	int i = 0;
2539d08ab9SHans Verkuil 
2639d08ab9SHans Verkuil 	pr_info(" @ %li jiffies:\n", jiffies);
2739d08ab9SHans Verkuil 	for (i = 0; i <= 0x148; i += 4)
2839d08ab9SHans Verkuil 		pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
2939d08ab9SHans Verkuil }
3039d08ab9SHans Verkuil #endif
3139d08ab9SHans Verkuil 
3239d08ab9SHans Verkuil /****************************************************************************
3339d08ab9SHans Verkuil  * gpio and debi helper functions
3439d08ab9SHans Verkuil  ****************************************************************************/
3539d08ab9SHans Verkuil 
saa7146_setgpio(struct saa7146_dev * dev,int port,u32 data)3639d08ab9SHans Verkuil void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
3739d08ab9SHans Verkuil {
3839d08ab9SHans Verkuil 	u32 value = 0;
3939d08ab9SHans Verkuil 
4040e986c9SHans Verkuil 	if (WARN_ON(port > 3))
4140e986c9SHans Verkuil 		return;
4239d08ab9SHans Verkuil 
4339d08ab9SHans Verkuil 	value = saa7146_read(dev, GPIO_CTRL);
4439d08ab9SHans Verkuil 	value &= ~(0xff << (8*port));
4539d08ab9SHans Verkuil 	value |= (data << (8*port));
4639d08ab9SHans Verkuil 	saa7146_write(dev, GPIO_CTRL, value);
4739d08ab9SHans Verkuil }
4839d08ab9SHans Verkuil 
4939d08ab9SHans Verkuil /* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
saa7146_wait_for_debi_done_sleep(struct saa7146_dev * dev,unsigned long us1,unsigned long us2)5039d08ab9SHans Verkuil static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
5139d08ab9SHans Verkuil 				unsigned long us1, unsigned long us2)
5239d08ab9SHans Verkuil {
5339d08ab9SHans Verkuil 	unsigned long timeout;
5439d08ab9SHans Verkuil 	int err;
5539d08ab9SHans Verkuil 
5639d08ab9SHans Verkuil 	/* wait for registers to be programmed */
5739d08ab9SHans Verkuil 	timeout = jiffies + usecs_to_jiffies(us1);
5839d08ab9SHans Verkuil 	while (1) {
5939d08ab9SHans Verkuil 		err = time_after(jiffies, timeout);
6039d08ab9SHans Verkuil 		if (saa7146_read(dev, MC2) & 2)
6139d08ab9SHans Verkuil 			break;
6239d08ab9SHans Verkuil 		if (err) {
6339d08ab9SHans Verkuil 			pr_debug("%s: %s timed out while waiting for registers getting programmed\n",
6439d08ab9SHans Verkuil 			       dev->name, __func__);
6539d08ab9SHans Verkuil 			return -ETIMEDOUT;
6639d08ab9SHans Verkuil 		}
6739d08ab9SHans Verkuil 		msleep(1);
6839d08ab9SHans Verkuil 	}
6939d08ab9SHans Verkuil 
7039d08ab9SHans Verkuil 	/* wait for transfer to complete */
7139d08ab9SHans Verkuil 	timeout = jiffies + usecs_to_jiffies(us2);
7239d08ab9SHans Verkuil 	while (1) {
7339d08ab9SHans Verkuil 		err = time_after(jiffies, timeout);
7439d08ab9SHans Verkuil 		if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
7539d08ab9SHans Verkuil 			break;
7639d08ab9SHans Verkuil 		saa7146_read(dev, MC2);
7739d08ab9SHans Verkuil 		if (err) {
7839d08ab9SHans Verkuil 			DEB_S("%s: %s timed out while waiting for transfer completion\n",
7939d08ab9SHans Verkuil 			      dev->name, __func__);
8039d08ab9SHans Verkuil 			return -ETIMEDOUT;
8139d08ab9SHans Verkuil 		}
8239d08ab9SHans Verkuil 		msleep(1);
8339d08ab9SHans Verkuil 	}
8439d08ab9SHans Verkuil 
8539d08ab9SHans Verkuil 	return 0;
8639d08ab9SHans Verkuil }
8739d08ab9SHans Verkuil 
saa7146_wait_for_debi_done_busyloop(struct saa7146_dev * dev,unsigned long us1,unsigned long us2)8839d08ab9SHans Verkuil static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
8939d08ab9SHans Verkuil 				unsigned long us1, unsigned long us2)
9039d08ab9SHans Verkuil {
9139d08ab9SHans Verkuil 	unsigned long loops;
9239d08ab9SHans Verkuil 
9339d08ab9SHans Verkuil 	/* wait for registers to be programmed */
9439d08ab9SHans Verkuil 	loops = us1;
9539d08ab9SHans Verkuil 	while (1) {
9639d08ab9SHans Verkuil 		if (saa7146_read(dev, MC2) & 2)
9739d08ab9SHans Verkuil 			break;
9839d08ab9SHans Verkuil 		if (!loops--) {
9939d08ab9SHans Verkuil 			pr_err("%s: %s timed out while waiting for registers getting programmed\n",
10039d08ab9SHans Verkuil 			       dev->name, __func__);
10139d08ab9SHans Verkuil 			return -ETIMEDOUT;
10239d08ab9SHans Verkuil 		}
10339d08ab9SHans Verkuil 		udelay(1);
10439d08ab9SHans Verkuil 	}
10539d08ab9SHans Verkuil 
10639d08ab9SHans Verkuil 	/* wait for transfer to complete */
10739d08ab9SHans Verkuil 	loops = us2 / 5;
10839d08ab9SHans Verkuil 	while (1) {
10939d08ab9SHans Verkuil 		if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
11039d08ab9SHans Verkuil 			break;
11139d08ab9SHans Verkuil 		saa7146_read(dev, MC2);
11239d08ab9SHans Verkuil 		if (!loops--) {
11339d08ab9SHans Verkuil 			DEB_S("%s: %s timed out while waiting for transfer completion\n",
11439d08ab9SHans Verkuil 			      dev->name, __func__);
11539d08ab9SHans Verkuil 			return -ETIMEDOUT;
11639d08ab9SHans Verkuil 		}
11739d08ab9SHans Verkuil 		udelay(5);
11839d08ab9SHans Verkuil 	}
11939d08ab9SHans Verkuil 
12039d08ab9SHans Verkuil 	return 0;
12139d08ab9SHans Verkuil }
12239d08ab9SHans Verkuil 
saa7146_wait_for_debi_done(struct saa7146_dev * dev,int nobusyloop)12339d08ab9SHans Verkuil int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
12439d08ab9SHans Verkuil {
12539d08ab9SHans Verkuil 	if (nobusyloop)
12639d08ab9SHans Verkuil 		return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
12739d08ab9SHans Verkuil 	else
12839d08ab9SHans Verkuil 		return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
12939d08ab9SHans Verkuil }
13039d08ab9SHans Verkuil 
13139d08ab9SHans Verkuil /****************************************************************************
13239d08ab9SHans Verkuil  * general helper functions
13339d08ab9SHans Verkuil  ****************************************************************************/
13439d08ab9SHans Verkuil 
13539d08ab9SHans Verkuil /* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
136*b2aa8ac6SChristophe JAILLET    make sure virt has been allocated with vmalloc_32(), otherwise return NULL
137*b2aa8ac6SChristophe JAILLET    on highmem machines */
vmalloc_to_sg(unsigned char * virt,int nr_pages)13839d08ab9SHans Verkuil static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
13939d08ab9SHans Verkuil {
14039d08ab9SHans Verkuil 	struct scatterlist *sglist;
14139d08ab9SHans Verkuil 	struct page *pg;
14239d08ab9SHans Verkuil 	int i;
14339d08ab9SHans Verkuil 
14439d08ab9SHans Verkuil 	sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
14539d08ab9SHans Verkuil 	if (NULL == sglist)
14639d08ab9SHans Verkuil 		return NULL;
14739d08ab9SHans Verkuil 	sg_init_table(sglist, nr_pages);
14839d08ab9SHans Verkuil 	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
14939d08ab9SHans Verkuil 		pg = vmalloc_to_page(virt);
15039d08ab9SHans Verkuil 		if (NULL == pg)
15139d08ab9SHans Verkuil 			goto err;
15240e986c9SHans Verkuil 		if (WARN_ON(PageHighMem(pg)))
153*b2aa8ac6SChristophe JAILLET 			goto err;
15439d08ab9SHans Verkuil 		sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
15539d08ab9SHans Verkuil 	}
15639d08ab9SHans Verkuil 	return sglist;
15739d08ab9SHans Verkuil 
15839d08ab9SHans Verkuil  err:
15939d08ab9SHans Verkuil 	kfree(sglist);
16039d08ab9SHans Verkuil 	return NULL;
16139d08ab9SHans Verkuil }
16239d08ab9SHans Verkuil 
16339d08ab9SHans Verkuil /********************************************************************************/
16439d08ab9SHans Verkuil /* common page table functions */
16539d08ab9SHans Verkuil 
saa7146_vmalloc_build_pgtable(struct pci_dev * pci,long length,struct saa7146_pgtable * pt)16639d08ab9SHans Verkuil void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
16739d08ab9SHans Verkuil {
16839d08ab9SHans Verkuil 	int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
16939d08ab9SHans Verkuil 	void *mem = vmalloc_32(length);
17039d08ab9SHans Verkuil 	int slen = 0;
17139d08ab9SHans Verkuil 
17239d08ab9SHans Verkuil 	if (NULL == mem)
17339d08ab9SHans Verkuil 		goto err_null;
17439d08ab9SHans Verkuil 
17539d08ab9SHans Verkuil 	if (!(pt->slist = vmalloc_to_sg(mem, pages)))
17639d08ab9SHans Verkuil 		goto err_free_mem;
17739d08ab9SHans Verkuil 
17839d08ab9SHans Verkuil 	if (saa7146_pgtable_alloc(pci, pt))
17939d08ab9SHans Verkuil 		goto err_free_slist;
18039d08ab9SHans Verkuil 
18139d08ab9SHans Verkuil 	pt->nents = pages;
18239d08ab9SHans Verkuil 	slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
18339d08ab9SHans Verkuil 	if (0 == slen)
18439d08ab9SHans Verkuil 		goto err_free_pgtable;
18539d08ab9SHans Verkuil 
18639d08ab9SHans Verkuil 	if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
18739d08ab9SHans Verkuil 		goto err_unmap_sg;
18839d08ab9SHans Verkuil 
18939d08ab9SHans Verkuil 	return mem;
19039d08ab9SHans Verkuil 
19139d08ab9SHans Verkuil err_unmap_sg:
19239d08ab9SHans Verkuil 	dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
19339d08ab9SHans Verkuil err_free_pgtable:
19439d08ab9SHans Verkuil 	saa7146_pgtable_free(pci, pt);
19539d08ab9SHans Verkuil err_free_slist:
19639d08ab9SHans Verkuil 	kfree(pt->slist);
19739d08ab9SHans Verkuil 	pt->slist = NULL;
19839d08ab9SHans Verkuil err_free_mem:
19939d08ab9SHans Verkuil 	vfree(mem);
20039d08ab9SHans Verkuil err_null:
20139d08ab9SHans Verkuil 	return NULL;
20239d08ab9SHans Verkuil }
20339d08ab9SHans Verkuil 
saa7146_vfree_destroy_pgtable(struct pci_dev * pci,void * mem,struct saa7146_pgtable * pt)20439d08ab9SHans Verkuil void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
20539d08ab9SHans Verkuil {
20639d08ab9SHans Verkuil 	dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE);
20739d08ab9SHans Verkuil 	saa7146_pgtable_free(pci, pt);
20839d08ab9SHans Verkuil 	kfree(pt->slist);
20939d08ab9SHans Verkuil 	pt->slist = NULL;
21039d08ab9SHans Verkuil 	vfree(mem);
21139d08ab9SHans Verkuil }
21239d08ab9SHans Verkuil 
saa7146_pgtable_free(struct pci_dev * pci,struct saa7146_pgtable * pt)21339d08ab9SHans Verkuil void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
21439d08ab9SHans Verkuil {
21539d08ab9SHans Verkuil 	if (NULL == pt->cpu)
21639d08ab9SHans Verkuil 		return;
21739d08ab9SHans Verkuil 	dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma);
21839d08ab9SHans Verkuil 	pt->cpu = NULL;
21939d08ab9SHans Verkuil }
22039d08ab9SHans Verkuil 
saa7146_pgtable_alloc(struct pci_dev * pci,struct saa7146_pgtable * pt)22139d08ab9SHans Verkuil int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
22239d08ab9SHans Verkuil {
22339d08ab9SHans Verkuil 	__le32       *cpu;
22439d08ab9SHans Verkuil 	dma_addr_t   dma_addr = 0;
22539d08ab9SHans Verkuil 
22639d08ab9SHans Verkuil 	cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL);
22739d08ab9SHans Verkuil 	if (NULL == cpu) {
22839d08ab9SHans Verkuil 		return -ENOMEM;
22939d08ab9SHans Verkuil 	}
23039d08ab9SHans Verkuil 	pt->size = PAGE_SIZE;
23139d08ab9SHans Verkuil 	pt->cpu  = cpu;
23239d08ab9SHans Verkuil 	pt->dma  = dma_addr;
23339d08ab9SHans Verkuil 
23439d08ab9SHans Verkuil 	return 0;
23539d08ab9SHans Verkuil }
23639d08ab9SHans Verkuil 
saa7146_pgtable_build_single(struct pci_dev * pci,struct saa7146_pgtable * pt,struct scatterlist * list,int sglen)23739d08ab9SHans Verkuil int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
23839d08ab9SHans Verkuil 				 struct scatterlist *list, int sglen)
23939d08ab9SHans Verkuil {
240b3b2dd37SHans Verkuil 	struct sg_dma_page_iter dma_iter;
24139d08ab9SHans Verkuil 	__le32 *ptr, fill;
24239d08ab9SHans Verkuil 	int nr_pages = 0;
243b3b2dd37SHans Verkuil 	int i;
24439d08ab9SHans Verkuil 
24540e986c9SHans Verkuil 	if (WARN_ON(!sglen) ||
24640e986c9SHans Verkuil 	    WARN_ON(list->offset > PAGE_SIZE))
24740e986c9SHans Verkuil 		return -EIO;
24839d08ab9SHans Verkuil 
24939d08ab9SHans Verkuil 	/* if we have a user buffer, the first page may not be
25039d08ab9SHans Verkuil 	   aligned to a page boundary. */
25139d08ab9SHans Verkuil 	pt->offset = list->offset;
25239d08ab9SHans Verkuil 
25339d08ab9SHans Verkuil 	ptr = pt->cpu;
254b3b2dd37SHans Verkuil 	for_each_sg_dma_page(list, &dma_iter, sglen, 0) {
255b3b2dd37SHans Verkuil 		*ptr++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter));
25639d08ab9SHans Verkuil 		nr_pages++;
25739d08ab9SHans Verkuil 	}
25839d08ab9SHans Verkuil 
25939d08ab9SHans Verkuil 
26039d08ab9SHans Verkuil 	/* safety; fill the page table up with the last valid page */
26139d08ab9SHans Verkuil 	fill = *(ptr-1);
262b3b2dd37SHans Verkuil 	for (i = nr_pages; i < 1024; i++)
26339d08ab9SHans Verkuil 		*ptr++ = fill;
26439d08ab9SHans Verkuil 	return 0;
26539d08ab9SHans Verkuil }
26639d08ab9SHans Verkuil 
26739d08ab9SHans Verkuil /********************************************************************************/
26839d08ab9SHans Verkuil /* interrupt handler */
interrupt_hw(int irq,void * dev_id)26939d08ab9SHans Verkuil static irqreturn_t interrupt_hw(int irq, void *dev_id)
27039d08ab9SHans Verkuil {
27139d08ab9SHans Verkuil 	struct saa7146_dev *dev = dev_id;
27239d08ab9SHans Verkuil 	u32 isr;
27339d08ab9SHans Verkuil 	u32 ack_isr;
27439d08ab9SHans Verkuil 
27539d08ab9SHans Verkuil 	/* read out the interrupt status register */
27639d08ab9SHans Verkuil 	ack_isr = isr = saa7146_read(dev, ISR);
27739d08ab9SHans Verkuil 
27839d08ab9SHans Verkuil 	/* is this our interrupt? */
27939d08ab9SHans Verkuil 	if ( 0 == isr ) {
28039d08ab9SHans Verkuil 		/* nope, some other device */
28139d08ab9SHans Verkuil 		return IRQ_NONE;
28239d08ab9SHans Verkuil 	}
28339d08ab9SHans Verkuil 
28439d08ab9SHans Verkuil 	if (dev->ext) {
28539d08ab9SHans Verkuil 		if (dev->ext->irq_mask & isr) {
28639d08ab9SHans Verkuil 			if (dev->ext->irq_func)
28739d08ab9SHans Verkuil 				dev->ext->irq_func(dev, &isr);
28839d08ab9SHans Verkuil 			isr &= ~dev->ext->irq_mask;
28939d08ab9SHans Verkuil 		}
29039d08ab9SHans Verkuil 	}
29139d08ab9SHans Verkuil 	if (0 != (isr & (MASK_27))) {
29239d08ab9SHans Verkuil 		DEB_INT("irq: RPS0 (0x%08x)\n", isr);
29339d08ab9SHans Verkuil 		if (dev->vv_data && dev->vv_callback)
29439d08ab9SHans Verkuil 			dev->vv_callback(dev,isr);
29539d08ab9SHans Verkuil 		isr &= ~MASK_27;
29639d08ab9SHans Verkuil 	}
29739d08ab9SHans Verkuil 	if (0 != (isr & (MASK_28))) {
29839d08ab9SHans Verkuil 		if (dev->vv_data && dev->vv_callback)
29939d08ab9SHans Verkuil 			dev->vv_callback(dev,isr);
30039d08ab9SHans Verkuil 		isr &= ~MASK_28;
30139d08ab9SHans Verkuil 	}
30239d08ab9SHans Verkuil 	if (0 != (isr & (MASK_16|MASK_17))) {
30339d08ab9SHans Verkuil 		SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
30439d08ab9SHans Verkuil 		/* only wake up if we expect something */
30539d08ab9SHans Verkuil 		if (0 != dev->i2c_op) {
30639d08ab9SHans Verkuil 			dev->i2c_op = 0;
30739d08ab9SHans Verkuil 			wake_up(&dev->i2c_wq);
30839d08ab9SHans Verkuil 		} else {
30939d08ab9SHans Verkuil 			u32 psr = saa7146_read(dev, PSR);
31039d08ab9SHans Verkuil 			u32 ssr = saa7146_read(dev, SSR);
31139d08ab9SHans Verkuil 			pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
31239d08ab9SHans Verkuil 				dev->name, isr, psr, ssr);
31339d08ab9SHans Verkuil 		}
31439d08ab9SHans Verkuil 		isr &= ~(MASK_16|MASK_17);
31539d08ab9SHans Verkuil 	}
31639d08ab9SHans Verkuil 	if( 0 != isr ) {
31739d08ab9SHans Verkuil 		ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
31839d08ab9SHans Verkuil 		    isr);
31939d08ab9SHans Verkuil 		ERR("disabling interrupt source(s)!\n");
32039d08ab9SHans Verkuil 		SAA7146_IER_DISABLE(dev,isr);
32139d08ab9SHans Verkuil 	}
32239d08ab9SHans Verkuil 	saa7146_write(dev, ISR, ack_isr);
32339d08ab9SHans Verkuil 	return IRQ_HANDLED;
32439d08ab9SHans Verkuil }
32539d08ab9SHans Verkuil 
32639d08ab9SHans Verkuil /*********************************************************************************/
32739d08ab9SHans Verkuil /* configuration-functions                                                       */
32839d08ab9SHans Verkuil 
saa7146_init_one(struct pci_dev * pci,const struct pci_device_id * ent)32939d08ab9SHans Verkuil static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
33039d08ab9SHans Verkuil {
33139d08ab9SHans Verkuil 	struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
33239d08ab9SHans Verkuil 	struct saa7146_extension *ext = pci_ext->ext;
33339d08ab9SHans Verkuil 	struct saa7146_dev *dev;
33439d08ab9SHans Verkuil 	int err = -ENOMEM;
33539d08ab9SHans Verkuil 
33639d08ab9SHans Verkuil 	/* clear out mem for sure */
33739d08ab9SHans Verkuil 	dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
33839d08ab9SHans Verkuil 	if (!dev) {
33939d08ab9SHans Verkuil 		ERR("out of memory\n");
34039d08ab9SHans Verkuil 		goto out;
34139d08ab9SHans Verkuil 	}
34239d08ab9SHans Verkuil 
34339d08ab9SHans Verkuil 	/* create a nice device name */
34439d08ab9SHans Verkuil 	sprintf(dev->name, "saa7146 (%d)", saa7146_num);
34539d08ab9SHans Verkuil 
34639d08ab9SHans Verkuil 	DEB_EE("pci:%p\n", pci);
34739d08ab9SHans Verkuil 
34839d08ab9SHans Verkuil 	err = pci_enable_device(pci);
34939d08ab9SHans Verkuil 	if (err < 0) {
35039d08ab9SHans Verkuil 		ERR("pci_enable_device() failed\n");
35139d08ab9SHans Verkuil 		goto err_free;
35239d08ab9SHans Verkuil 	}
35339d08ab9SHans Verkuil 
35439d08ab9SHans Verkuil 	/* enable bus-mastering */
35539d08ab9SHans Verkuil 	pci_set_master(pci);
35639d08ab9SHans Verkuil 
35739d08ab9SHans Verkuil 	dev->pci = pci;
35839d08ab9SHans Verkuil 
35939d08ab9SHans Verkuil 	/* get chip-revision; this is needed to enable bug-fixes */
36039d08ab9SHans Verkuil 	dev->revision = pci->revision;
36139d08ab9SHans Verkuil 
36239d08ab9SHans Verkuil 	/* remap the memory from virtual to physical address */
36339d08ab9SHans Verkuil 
36439d08ab9SHans Verkuil 	err = pci_request_region(pci, 0, "saa7146");
36539d08ab9SHans Verkuil 	if (err < 0)
36639d08ab9SHans Verkuil 		goto err_disable;
36739d08ab9SHans Verkuil 
36839d08ab9SHans Verkuil 	dev->mem = ioremap(pci_resource_start(pci, 0),
36939d08ab9SHans Verkuil 			   pci_resource_len(pci, 0));
37039d08ab9SHans Verkuil 	if (!dev->mem) {
37139d08ab9SHans Verkuil 		ERR("ioremap() failed\n");
37239d08ab9SHans Verkuil 		err = -ENODEV;
37339d08ab9SHans Verkuil 		goto err_release;
37439d08ab9SHans Verkuil 	}
37539d08ab9SHans Verkuil 
37639d08ab9SHans Verkuil 	/* we don't do a master reset here anymore, it screws up
37739d08ab9SHans Verkuil 	   some boards that don't have an i2c-eeprom for configuration
37839d08ab9SHans Verkuil 	   values */
37939d08ab9SHans Verkuil /*
38039d08ab9SHans Verkuil 	saa7146_write(dev, MC1, MASK_31);
38139d08ab9SHans Verkuil */
38239d08ab9SHans Verkuil 
38339d08ab9SHans Verkuil 	/* disable all irqs */
38439d08ab9SHans Verkuil 	saa7146_write(dev, IER, 0);
38539d08ab9SHans Verkuil 
38639d08ab9SHans Verkuil 	/* shut down all dma transfers and rps tasks */
38739d08ab9SHans Verkuil 	saa7146_write(dev, MC1, 0x30ff0000);
38839d08ab9SHans Verkuil 
38939d08ab9SHans Verkuil 	/* clear out any rps-signals pending */
39039d08ab9SHans Verkuil 	saa7146_write(dev, MC2, 0xf8000000);
39139d08ab9SHans Verkuil 
39239d08ab9SHans Verkuil 	/* request an interrupt for the saa7146 */
39339d08ab9SHans Verkuil 	err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED,
39439d08ab9SHans Verkuil 			  dev->name, dev);
39539d08ab9SHans Verkuil 	if (err < 0) {
39639d08ab9SHans Verkuil 		ERR("request_irq() failed\n");
39739d08ab9SHans Verkuil 		goto err_unmap;
39839d08ab9SHans Verkuil 	}
39939d08ab9SHans Verkuil 
40039d08ab9SHans Verkuil 	err = -ENOMEM;
40139d08ab9SHans Verkuil 
40239d08ab9SHans Verkuil 	/* get memory for various stuff */
40339d08ab9SHans Verkuil 	dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
40439d08ab9SHans Verkuil 						  &dev->d_rps0.dma_handle,
40539d08ab9SHans Verkuil 						  GFP_KERNEL);
40639d08ab9SHans Verkuil 	if (!dev->d_rps0.cpu_addr)
40739d08ab9SHans Verkuil 		goto err_free_irq;
40839d08ab9SHans Verkuil 
40939d08ab9SHans Verkuil 	dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
41039d08ab9SHans Verkuil 						  &dev->d_rps1.dma_handle,
41139d08ab9SHans Verkuil 						  GFP_KERNEL);
41239d08ab9SHans Verkuil 	if (!dev->d_rps1.cpu_addr)
41339d08ab9SHans Verkuil 		goto err_free_rps0;
41439d08ab9SHans Verkuil 
41539d08ab9SHans Verkuil 	dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM,
41639d08ab9SHans Verkuil 						 &dev->d_i2c.dma_handle, GFP_KERNEL);
41739d08ab9SHans Verkuil 	if (!dev->d_i2c.cpu_addr)
41839d08ab9SHans Verkuil 		goto err_free_rps1;
41939d08ab9SHans Verkuil 
42039d08ab9SHans Verkuil 	/* the rest + print status message */
42139d08ab9SHans Verkuil 
42239d08ab9SHans Verkuil 	pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
42339d08ab9SHans Verkuil 		dev->mem, dev->revision, pci->irq,
42439d08ab9SHans Verkuil 		pci->subsystem_vendor, pci->subsystem_device);
42539d08ab9SHans Verkuil 	dev->ext = ext;
42639d08ab9SHans Verkuil 
42739d08ab9SHans Verkuil 	mutex_init(&dev->v4l2_lock);
42839d08ab9SHans Verkuil 	spin_lock_init(&dev->int_slock);
42939d08ab9SHans Verkuil 	spin_lock_init(&dev->slock);
43039d08ab9SHans Verkuil 
43139d08ab9SHans Verkuil 	mutex_init(&dev->i2c_lock);
43239d08ab9SHans Verkuil 
43339d08ab9SHans Verkuil 	dev->module = THIS_MODULE;
43439d08ab9SHans Verkuil 	init_waitqueue_head(&dev->i2c_wq);
43539d08ab9SHans Verkuil 
43639d08ab9SHans Verkuil 	/* set some sane pci arbitrition values */
43739d08ab9SHans Verkuil 	saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
43839d08ab9SHans Verkuil 
43939d08ab9SHans Verkuil 	/* TODO: use the status code of the callback */
44039d08ab9SHans Verkuil 
44139d08ab9SHans Verkuil 	err = -ENODEV;
44239d08ab9SHans Verkuil 
44339d08ab9SHans Verkuil 	if (ext->probe && ext->probe(dev)) {
44439d08ab9SHans Verkuil 		DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
44539d08ab9SHans Verkuil 		goto err_free_i2c;
44639d08ab9SHans Verkuil 	}
44739d08ab9SHans Verkuil 
44839d08ab9SHans Verkuil 	if (ext->attach(dev, pci_ext)) {
44939d08ab9SHans Verkuil 		DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
45039d08ab9SHans Verkuil 		goto err_free_i2c;
45139d08ab9SHans Verkuil 	}
45239d08ab9SHans Verkuil 	/* V4L extensions will set the pci drvdata to the v4l2_device in the
45339d08ab9SHans Verkuil 	   attach() above. So for those cards that do not use V4L we have to
45439d08ab9SHans Verkuil 	   set it explicitly. */
45539d08ab9SHans Verkuil 	pci_set_drvdata(pci, &dev->v4l2_dev);
45639d08ab9SHans Verkuil 
45739d08ab9SHans Verkuil 	saa7146_num++;
45839d08ab9SHans Verkuil 
45939d08ab9SHans Verkuil 	err = 0;
46039d08ab9SHans Verkuil out:
46139d08ab9SHans Verkuil 	return err;
46239d08ab9SHans Verkuil 
46339d08ab9SHans Verkuil err_free_i2c:
46439d08ab9SHans Verkuil 	dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
46539d08ab9SHans Verkuil 			  dev->d_i2c.dma_handle);
46639d08ab9SHans Verkuil err_free_rps1:
46739d08ab9SHans Verkuil 	dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
46839d08ab9SHans Verkuil 			  dev->d_rps1.dma_handle);
46939d08ab9SHans Verkuil err_free_rps0:
47039d08ab9SHans Verkuil 	dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
47139d08ab9SHans Verkuil 			  dev->d_rps0.dma_handle);
47239d08ab9SHans Verkuil err_free_irq:
47339d08ab9SHans Verkuil 	free_irq(pci->irq, (void *)dev);
47439d08ab9SHans Verkuil err_unmap:
47539d08ab9SHans Verkuil 	iounmap(dev->mem);
47639d08ab9SHans Verkuil err_release:
47739d08ab9SHans Verkuil 	pci_release_region(pci, 0);
47839d08ab9SHans Verkuil err_disable:
47939d08ab9SHans Verkuil 	pci_disable_device(pci);
48039d08ab9SHans Verkuil err_free:
48139d08ab9SHans Verkuil 	kfree(dev);
48239d08ab9SHans Verkuil 	goto out;
48339d08ab9SHans Verkuil }
48439d08ab9SHans Verkuil 
saa7146_remove_one(struct pci_dev * pdev)48539d08ab9SHans Verkuil static void saa7146_remove_one(struct pci_dev *pdev)
48639d08ab9SHans Verkuil {
48739d08ab9SHans Verkuil 	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
48839d08ab9SHans Verkuil 	struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
48939d08ab9SHans Verkuil 	struct {
49039d08ab9SHans Verkuil 		void *addr;
49139d08ab9SHans Verkuil 		dma_addr_t dma;
49239d08ab9SHans Verkuil 	} dev_map[] = {
49339d08ab9SHans Verkuil 		{ dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
49439d08ab9SHans Verkuil 		{ dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
49539d08ab9SHans Verkuil 		{ dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
49639d08ab9SHans Verkuil 		{ NULL, 0 }
49739d08ab9SHans Verkuil 	}, *p;
49839d08ab9SHans Verkuil 
49939d08ab9SHans Verkuil 	DEB_EE("dev:%p\n", dev);
50039d08ab9SHans Verkuil 
50139d08ab9SHans Verkuil 	dev->ext->detach(dev);
50239d08ab9SHans Verkuil 
50339d08ab9SHans Verkuil 	/* shut down all video dma transfers */
50439d08ab9SHans Verkuil 	saa7146_write(dev, MC1, 0x00ff0000);
50539d08ab9SHans Verkuil 
50639d08ab9SHans Verkuil 	/* disable all irqs, release irq-routine */
50739d08ab9SHans Verkuil 	saa7146_write(dev, IER, 0);
50839d08ab9SHans Verkuil 
50939d08ab9SHans Verkuil 	free_irq(pdev->irq, dev);
51039d08ab9SHans Verkuil 
51139d08ab9SHans Verkuil 	for (p = dev_map; p->addr; p++)
51239d08ab9SHans Verkuil 		dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr,
51339d08ab9SHans Verkuil 				  p->dma);
51439d08ab9SHans Verkuil 
51539d08ab9SHans Verkuil 	iounmap(dev->mem);
51639d08ab9SHans Verkuil 	pci_release_region(pdev, 0);
51739d08ab9SHans Verkuil 	pci_disable_device(pdev);
51839d08ab9SHans Verkuil 	kfree(dev);
51939d08ab9SHans Verkuil 
52039d08ab9SHans Verkuil 	saa7146_num--;
52139d08ab9SHans Verkuil }
52239d08ab9SHans Verkuil 
52339d08ab9SHans Verkuil /*********************************************************************************/
52439d08ab9SHans Verkuil /* extension handling functions                                                  */
52539d08ab9SHans Verkuil 
saa7146_register_extension(struct saa7146_extension * ext)52639d08ab9SHans Verkuil int saa7146_register_extension(struct saa7146_extension* ext)
52739d08ab9SHans Verkuil {
52839d08ab9SHans Verkuil 	DEB_EE("ext:%p\n", ext);
52939d08ab9SHans Verkuil 
53039d08ab9SHans Verkuil 	ext->driver.name = ext->name;
53139d08ab9SHans Verkuil 	ext->driver.id_table = ext->pci_tbl;
53239d08ab9SHans Verkuil 	ext->driver.probe = saa7146_init_one;
53339d08ab9SHans Verkuil 	ext->driver.remove = saa7146_remove_one;
53439d08ab9SHans Verkuil 
53539d08ab9SHans Verkuil 	pr_info("register extension '%s'\n", ext->name);
53639d08ab9SHans Verkuil 	return pci_register_driver(&ext->driver);
53739d08ab9SHans Verkuil }
53839d08ab9SHans Verkuil 
saa7146_unregister_extension(struct saa7146_extension * ext)53939d08ab9SHans Verkuil int saa7146_unregister_extension(struct saa7146_extension* ext)
54039d08ab9SHans Verkuil {
54139d08ab9SHans Verkuil 	DEB_EE("ext:%p\n", ext);
54239d08ab9SHans Verkuil 	pr_info("unregister extension '%s'\n", ext->name);
54339d08ab9SHans Verkuil 	pci_unregister_driver(&ext->driver);
54439d08ab9SHans Verkuil 	return 0;
54539d08ab9SHans Verkuil }
54639d08ab9SHans Verkuil 
54739d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_register_extension);
54839d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
54939d08ab9SHans Verkuil 
55039d08ab9SHans Verkuil /* misc functions used by extension modules */
55139d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
55239d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
55339d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
55439d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
55539d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);
55639d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
55739d08ab9SHans Verkuil 
55839d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_setgpio);
55939d08ab9SHans Verkuil 
56039d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
56139d08ab9SHans Verkuil 
56239d08ab9SHans Verkuil EXPORT_SYMBOL_GPL(saa7146_debug);
56339d08ab9SHans Verkuil 
56439d08ab9SHans Verkuil MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
56539d08ab9SHans Verkuil MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
56639d08ab9SHans Verkuil MODULE_LICENSE("GPL");
567