saa7146_core.c 14.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
    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.
*/

21 22
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
23
#include <media/saa7146.h>
24
#include <linux/module.h>
L
Linus Torvalds 已提交
25

26
static int saa7146_num;
L
Linus Torvalds 已提交
27

28
unsigned int saa7146_debug;
L
Linus Torvalds 已提交
29

30
module_param(saa7146_debug, uint, 0644);
L
Linus Torvalds 已提交
31 32 33 34 35 36 37
MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");

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

38 39 40
	pr_info(" @ %li jiffies:\n", jiffies);
	for (i = 0; i <= 0x148; i += 4)
		pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
L
Linus Torvalds 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
}
#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 */
61 62
static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
				unsigned long us1, unsigned long us2)
L
Linus Torvalds 已提交
63
{
64
	unsigned long timeout;
65
	int err;
L
Linus Torvalds 已提交
66 67

	/* wait for registers to be programmed */
68
	timeout = jiffies + usecs_to_jiffies(us1);
L
Linus Torvalds 已提交
69
	while (1) {
70
		err = time_after(jiffies, timeout);
71 72 73
		if (saa7146_read(dev, MC2) & 2)
			break;
		if (err) {
74 75
			pr_err("%s: %s timed out while waiting for registers getting programmed\n",
			       dev->name, __func__);
L
Linus Torvalds 已提交
76 77
			return -ETIMEDOUT;
		}
78
		msleep(1);
L
Linus Torvalds 已提交
79 80 81
	}

	/* wait for transfer to complete */
82
	timeout = jiffies + usecs_to_jiffies(us2);
L
Linus Torvalds 已提交
83
	while (1) {
84
		err = time_after(jiffies, timeout);
L
Linus Torvalds 已提交
85 86 87
		if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
			break;
		saa7146_read(dev, MC2);
88
		if (err) {
89 90
			DEB_S("%s: %s timed out while waiting for transfer completion\n",
			      dev->name, __func__);
L
Linus Torvalds 已提交
91 92
			return -ETIMEDOUT;
		}
93
		msleep(1);
L
Linus Torvalds 已提交
94 95 96 97 98
	}

	return 0;
}

99 100 101 102 103 104 105 106 107 108 109
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--) {
110 111
			pr_err("%s: %s timed out while waiting for registers getting programmed\n",
			       dev->name, __func__);
112 113 114 115 116 117 118 119 120 121 122 123
			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--) {
124 125
			DEB_S("%s: %s timed out while waiting for transfer completion\n",
			      dev->name, __func__);
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
			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);
}

L
Linus Torvalds 已提交
142 143 144 145
/****************************************************************************
 * general helper functions
 ****************************************************************************/

146
/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
L
Linus Torvalds 已提交
147 148 149 150 151 152 153 154
   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;

P
 
Panagiotis Issaris 已提交
155
	sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
L
Linus Torvalds 已提交
156 157
	if (NULL == sglist)
		return NULL;
J
Jens Axboe 已提交
158
	sg_init_table(sglist, nr_pages);
L
Linus Torvalds 已提交
159 160 161 162
	for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
		pg = vmalloc_to_page(virt);
		if (NULL == pg)
			goto err;
163
		BUG_ON(PageHighMem(pg));
164
		sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
L
Linus Torvalds 已提交
165 166 167 168 169 170 171 172 173 174 175
	}
	return sglist;

 err:
	kfree(sglist);
	return NULL;
}

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

176
void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
L
Linus Torvalds 已提交
177 178
{
	int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
179
	void *mem = vmalloc_32(length);
L
Linus Torvalds 已提交
180 181
	int slen = 0;

182 183
	if (NULL == mem)
		goto err_null;
L
Linus Torvalds 已提交
184

185 186
	if (!(pt->slist = vmalloc_to_sg(mem, pages)))
		goto err_free_mem;
L
Linus Torvalds 已提交
187

188 189
	if (saa7146_pgtable_alloc(pci, pt))
		goto err_free_slist;
L
Linus Torvalds 已提交
190

191 192 193 194 195 196 197
	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;
L
Linus Torvalds 已提交
198 199

	return mem;
200 201 202 203 204 205 206 207 208 209 210 211 212 213

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;
}

214
void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
215 216 217 218 219 220
{
	pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
	saa7146_pgtable_free(pci, pt);
	kfree(pt->slist);
	pt->slist = NULL;
	vfree(mem);
L
Linus Torvalds 已提交
221 222 223 224 225 226 227 228 229 230 231 232
}

void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
	if (NULL == pt->cpu)
		return;
	pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
	pt->cpu = NULL;
}

int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
233
	__le32       *cpu;
234
	dma_addr_t   dma_addr = 0;
L
Linus Torvalds 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

	cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
	if (NULL == cpu) {
		return -ENOMEM;
	}
	pt->size = PAGE_SIZE;
	pt->cpu  = cpu;
	pt->dma  = dma_addr;

	return 0;
}

int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
	struct scatterlist *list, int sglen  )
{
250
	__le32 *ptr, fill;
L
Linus Torvalds 已提交
251 252 253 254 255 256 257 258 259 260 261 262 263
	int nr_pages = 0;
	int i,p;

	BUG_ON(0 == sglen);
	BUG_ON(list->offset > PAGE_SIZE);

	/* if we have a user buffer, the first page may not be
	   aligned to a page boundary. */
	pt->offset = list->offset;

	ptr = pt->cpu;
	for (i = 0; i < sglen; i++, list++) {
/*
264 265 266
		pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
			 i, sg_dma_address(list), sg_dma_len(list),
			 list->offset);
L
Linus Torvalds 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
*/
		for (p = 0; p * 4096 < list->length; p++, ptr++) {
			*ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
			nr_pages++;
		}
	}


	/* safety; fill the page table up with the last valid page */
	fill = *(ptr-1);
	for(i=nr_pages;i<1024;i++) {
		*ptr++ = fill;
	}

/*
	ptr = pt->cpu;
283
	pr_debug("offset: %d\n", pt->offset);
L
Linus Torvalds 已提交
284
	for(i=0;i<5;i++) {
285
		pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
L
Linus Torvalds 已提交
286 287 288 289 290 291 292
	}
*/
	return 0;
}

/********************************************************************************/
/* interrupt handler */
293
static irqreturn_t interrupt_hw(int irq, void *dev_id)
L
Linus Torvalds 已提交
294 295
{
	struct saa7146_dev *dev = dev_id;
296 297
	u32 isr;
	u32 ack_isr;
L
Linus Torvalds 已提交
298 299

	/* read out the interrupt status register */
300
	ack_isr = isr = saa7146_read(dev, ISR);
L
Linus Torvalds 已提交
301 302 303 304 305 306 307

	/* is this our interrupt? */
	if ( 0 == isr ) {
		/* nope, some other device */
		return IRQ_NONE;
	}

A
Al Viro 已提交
308 309 310
	if (dev->ext) {
		if (dev->ext->irq_mask & isr) {
			if (dev->ext->irq_func)
L
Linus Torvalds 已提交
311 312 313 314 315
				dev->ext->irq_func(dev, &isr);
			isr &= ~dev->ext->irq_mask;
		}
	}
	if (0 != (isr & (MASK_27))) {
316
		DEB_INT("irq: RPS0 (0x%08x)\n", isr);
A
Al Viro 已提交
317
		if (dev->vv_data && dev->vv_callback)
L
Linus Torvalds 已提交
318 319 320 321
			dev->vv_callback(dev,isr);
		isr &= ~MASK_27;
	}
	if (0 != (isr & (MASK_28))) {
A
Al Viro 已提交
322
		if (dev->vv_data && dev->vv_callback)
L
Linus Torvalds 已提交
323 324 325 326
			dev->vv_callback(dev,isr);
		isr &= ~MASK_28;
	}
	if (0 != (isr & (MASK_16|MASK_17))) {
327 328 329 330 331
		SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
		/* only wake up if we expect something */
		if (0 != dev->i2c_op) {
			dev->i2c_op = 0;
			wake_up(&dev->i2c_wq);
L
Linus Torvalds 已提交
332
		} else {
333 334
			u32 psr = saa7146_read(dev, PSR);
			u32 ssr = saa7146_read(dev, SSR);
335 336
			pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
				dev->name, isr, psr, ssr);
L
Linus Torvalds 已提交
337 338 339 340
		}
		isr &= ~(MASK_16|MASK_17);
	}
	if( 0 != isr ) {
341 342 343
		ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
		    isr);
		ERR("disabling interrupt source(s)!\n");
L
Linus Torvalds 已提交
344 345
		SAA7146_IER_DISABLE(dev,isr);
	}
346
	saa7146_write(dev, ISR, ack_isr);
L
Linus Torvalds 已提交
347 348 349 350 351 352 353 354 355 356 357 358 359
	return IRQ_HANDLED;
}

/*********************************************************************************/
/* configuration-functions                                                       */

static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
{
	struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
	struct saa7146_extension *ext = pci_ext->ext;
	struct saa7146_dev *dev;
	int err = -ENOMEM;

P
 
Panagiotis Issaris 已提交
360 361
	/* clear out mem for sure */
	dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
L
Linus Torvalds 已提交
362
	if (!dev) {
363
		ERR("out of memory\n");
L
Linus Torvalds 已提交
364 365 366
		goto out;
	}

367
	DEB_EE("pci:%p\n", pci);
L
Linus Torvalds 已提交
368 369 370

	err = pci_enable_device(pci);
	if (err < 0) {
371
		ERR("pci_enable_device() failed\n");
372
		goto err_free;
L
Linus Torvalds 已提交
373 374 375 376 377 378 379 380
	}

	/* enable bus-mastering */
	pci_set_master(pci);

	dev->pci = pci;

	/* get chip-revision; this is needed to enable bug-fixes */
B
Bjørn Mork 已提交
381
	dev->revision = pci->revision;
L
Linus Torvalds 已提交
382

J
Joe Perches 已提交
383
	/* remap the memory from virtual to physical address */
L
Linus Torvalds 已提交
384 385 386 387 388 389 390 391

	err = pci_request_region(pci, 0, "saa7146");
	if (err < 0)
		goto err_disable;

	dev->mem = ioremap(pci_resource_start(pci, 0),
			   pci_resource_len(pci, 0));
	if (!dev->mem) {
392
		ERR("ioremap() failed\n");
L
Linus Torvalds 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
		err = -ENODEV;
		goto err_release;
	}

	/* we don't do a master reset here anymore, it screws up
	   some boards that don't have an i2c-eeprom for configuration
	   values */
/*
	saa7146_write(dev, MC1, MASK_31);
*/

	/* disable all irqs */
	saa7146_write(dev, IER, 0);

	/* shut down all dma transfers and rps tasks */
	saa7146_write(dev, MC1, 0x30ff0000);

	/* clear out any rps-signals pending */
	saa7146_write(dev, MC2, 0xf8000000);

	/* request an interrupt for the saa7146 */
414
	err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
L
Linus Torvalds 已提交
415 416
			  dev->name, dev);
	if (err < 0) {
417
		ERR("request_irq() failed\n");
L
Linus Torvalds 已提交
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
		goto err_unmap;
	}

	err = -ENOMEM;

	/* get memory for various stuff */
	dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
						    &dev->d_rps0.dma_handle);
	if (!dev->d_rps0.cpu_addr)
		goto err_free_irq;
	memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);

	dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
						    &dev->d_rps1.dma_handle);
	if (!dev->d_rps1.cpu_addr)
		goto err_free_rps0;
	memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);

	dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
						   &dev->d_i2c.dma_handle);
	if (!dev->d_i2c.cpu_addr)
		goto err_free_rps1;
	memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);

	/* the rest + print status message */

	/* create a nice device name */
	sprintf(dev->name, "saa7146 (%d)", saa7146_num);

447 448 449
	pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
		dev->mem, dev->revision, pci->irq,
		pci->subsystem_vendor, pci->subsystem_device);
L
Linus Torvalds 已提交
450 451
	dev->ext = ext;

452
	mutex_init(&dev->v4l2_lock);
L
Linus Torvalds 已提交
453 454 455
	spin_lock_init(&dev->int_slock);
	spin_lock_init(&dev->slock);

456
	mutex_init(&dev->i2c_lock);
L
Linus Torvalds 已提交
457 458 459 460 461 462 463 464 465 466 467 468

	dev->module = THIS_MODULE;
	init_waitqueue_head(&dev->i2c_wq);

	/* set some sane pci arbitrition values */
	saa7146_write(dev, PCI_BT_V1, 0x1c00101f);

	/* TODO: use the status code of the callback */

	err = -ENODEV;

	if (ext->probe && ext->probe(dev)) {
469
		DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
L
Linus Torvalds 已提交
470 471 472 473
		goto err_free_i2c;
	}

	if (ext->attach(dev, pci_ext)) {
474
		DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
475
		goto err_free_i2c;
L
Linus Torvalds 已提交
476
	}
477 478 479 480
	/* V4L extensions will set the pci drvdata to the v4l2_device in the
	   attach() above. So for those cards that do not use V4L we have to
	   set it explicitly. */
	pci_set_drvdata(pci, &dev->v4l2_dev);
L
Linus Torvalds 已提交
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

	saa7146_num++;

	err = 0;
out:
	return err;

err_free_i2c:
	pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
			    dev->d_i2c.dma_handle);
err_free_rps1:
	pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
			    dev->d_rps1.dma_handle);
err_free_rps0:
	pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
			    dev->d_rps0.dma_handle);
err_free_irq:
	free_irq(pci->irq, (void *)dev);
err_unmap:
	iounmap(dev->mem);
err_release:
	pci_release_region(pci, 0);
err_disable:
	pci_disable_device(pci);
err_free:
	kfree(dev);
	goto out;
}

static void saa7146_remove_one(struct pci_dev *pdev)
{
512
	struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
513
	struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
L
Linus Torvalds 已提交
514 515 516 517 518 519 520 521 522 523
	struct {
		void *addr;
		dma_addr_t dma;
	} dev_map[] = {
		{ dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
		{ dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
		{ dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
		{ NULL, 0 }
	}, *p;

524
	DEB_EE("dev:%p\n", dev);
L
Linus Torvalds 已提交
525 526

	dev->ext->detach(dev);
527 528
	/* Zero the PCI drvdata after use. */
	pci_set_drvdata(pdev, NULL);
L
Linus Torvalds 已提交
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

	/* shut down all video dma transfers */
	saa7146_write(dev, MC1, 0x00ff0000);

	/* disable all irqs, release irq-routine */
	saa7146_write(dev, IER, 0);

	free_irq(pdev->irq, dev);

	for (p = dev_map; p->addr; p++)
		pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);

	iounmap(dev->mem);
	pci_release_region(pdev, 0);
	pci_disable_device(pdev);
	kfree(dev);

	saa7146_num--;
}

/*********************************************************************************/
/* extension handling functions                                                  */

int saa7146_register_extension(struct saa7146_extension* ext)
{
554
	DEB_EE("ext:%p\n", ext);
L
Linus Torvalds 已提交
555 556 557 558 559 560

	ext->driver.name = ext->name;
	ext->driver.id_table = ext->pci_tbl;
	ext->driver.probe = saa7146_init_one;
	ext->driver.remove = saa7146_remove_one;

561
	pr_info("register extension '%s'\n", ext->name);
562
	return pci_register_driver(&ext->driver);
L
Linus Torvalds 已提交
563 564 565 566
}

int saa7146_unregister_extension(struct saa7146_extension* ext)
{
567 568
	DEB_EE("ext:%p\n", ext);
	pr_info("unregister extension '%s'\n", ext->name);
L
Linus Torvalds 已提交
569 570 571 572 573 574 575 576 577 578 579 580
	pci_unregister_driver(&ext->driver);
	return 0;
}

EXPORT_SYMBOL_GPL(saa7146_register_extension);
EXPORT_SYMBOL_GPL(saa7146_unregister_extension);

/* misc functions used by extension modules */
EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
581
EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);
L
Linus Torvalds 已提交
582 583 584 585 586 587 588 589 590 591 592
EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);

EXPORT_SYMBOL_GPL(saa7146_setgpio);

EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);

EXPORT_SYMBOL_GPL(saa7146_debug);

MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
MODULE_LICENSE("GPL");