Path: blob/master/drivers/media/common/saa7146_core.c
15112 views
/*1saa7146.o - driver for generic saa7146-based hardware23Copyright (C) 1998-2003 Michael Hunold <[email protected]>45This program is free software; you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation; either version 2 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program; if not, write to the Free Software17Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.18*/1920#include <media/saa7146.h>2122LIST_HEAD(saa7146_devices);23DEFINE_MUTEX(saa7146_devices_lock);2425static int saa7146_num;2627unsigned int saa7146_debug;2829module_param(saa7146_debug, uint, 0644);30MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");3132#if 033static void dump_registers(struct saa7146_dev* dev)34{35int i = 0;3637INFO((" @ %li jiffies:\n",jiffies));38for(i = 0; i <= 0x148; i+=4) {39printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));40}41}42#endif4344/****************************************************************************45* gpio and debi helper functions46****************************************************************************/4748void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)49{50u32 value = 0;5152BUG_ON(port > 3);5354value = saa7146_read(dev, GPIO_CTRL);55value &= ~(0xff << (8*port));56value |= (data << (8*port));57saa7146_write(dev, GPIO_CTRL, value);58}5960/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */61static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,62unsigned long us1, unsigned long us2)63{64unsigned long timeout;65int err;6667/* wait for registers to be programmed */68timeout = jiffies + usecs_to_jiffies(us1);69while (1) {70err = time_after(jiffies, timeout);71if (saa7146_read(dev, MC2) & 2)72break;73if (err) {74printk(KERN_ERR "%s: %s timed out while waiting for "75"registers getting programmed\n",76dev->name, __func__);77return -ETIMEDOUT;78}79msleep(1);80}8182/* wait for transfer to complete */83timeout = jiffies + usecs_to_jiffies(us2);84while (1) {85err = time_after(jiffies, timeout);86if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))87break;88saa7146_read(dev, MC2);89if (err) {90DEB_S(("%s: %s timed out while waiting for transfer "91"completion\n", dev->name, __func__));92return -ETIMEDOUT;93}94msleep(1);95}9697return 0;98}99100static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,101unsigned long us1, unsigned long us2)102{103unsigned long loops;104105/* wait for registers to be programmed */106loops = us1;107while (1) {108if (saa7146_read(dev, MC2) & 2)109break;110if (!loops--) {111printk(KERN_ERR "%s: %s timed out while waiting for "112"registers getting programmed\n",113dev->name, __func__);114return -ETIMEDOUT;115}116udelay(1);117}118119/* wait for transfer to complete */120loops = us2 / 5;121while (1) {122if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))123break;124saa7146_read(dev, MC2);125if (!loops--) {126DEB_S(("%s: %s timed out while waiting for transfer "127"completion\n", dev->name, __func__));128return -ETIMEDOUT;129}130udelay(5);131}132133return 0;134}135136int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)137{138if (nobusyloop)139return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);140else141return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);142}143144/****************************************************************************145* general helper functions146****************************************************************************/147148/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c149make sure virt has been allocated with vmalloc_32(), otherwise the BUG()150may be triggered on highmem machines */151static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)152{153struct scatterlist *sglist;154struct page *pg;155int i;156157sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);158if (NULL == sglist)159return NULL;160sg_init_table(sglist, nr_pages);161for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {162pg = vmalloc_to_page(virt);163if (NULL == pg)164goto err;165BUG_ON(PageHighMem(pg));166sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);167}168return sglist;169170err:171kfree(sglist);172return NULL;173}174175/********************************************************************************/176/* common page table functions */177178void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)179{180int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;181void *mem = vmalloc_32(length);182int slen = 0;183184if (NULL == mem)185goto err_null;186187if (!(pt->slist = vmalloc_to_sg(mem, pages)))188goto err_free_mem;189190if (saa7146_pgtable_alloc(pci, pt))191goto err_free_slist;192193pt->nents = pages;194slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);195if (0 == slen)196goto err_free_pgtable;197198if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))199goto err_unmap_sg;200201return mem;202203err_unmap_sg:204pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);205err_free_pgtable:206saa7146_pgtable_free(pci, pt);207err_free_slist:208kfree(pt->slist);209pt->slist = NULL;210err_free_mem:211vfree(mem);212err_null:213return NULL;214}215216void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)217{218pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);219saa7146_pgtable_free(pci, pt);220kfree(pt->slist);221pt->slist = NULL;222vfree(mem);223}224225void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)226{227if (NULL == pt->cpu)228return;229pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);230pt->cpu = NULL;231}232233int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)234{235__le32 *cpu;236dma_addr_t dma_addr = 0;237238cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);239if (NULL == cpu) {240return -ENOMEM;241}242pt->size = PAGE_SIZE;243pt->cpu = cpu;244pt->dma = dma_addr;245246return 0;247}248249int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,250struct scatterlist *list, int sglen )251{252__le32 *ptr, fill;253int nr_pages = 0;254int i,p;255256BUG_ON(0 == sglen);257BUG_ON(list->offset > PAGE_SIZE);258259/* if we have a user buffer, the first page may not be260aligned to a page boundary. */261pt->offset = list->offset;262263ptr = pt->cpu;264for (i = 0; i < sglen; i++, list++) {265/*266printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);267*/268for (p = 0; p * 4096 < list->length; p++, ptr++) {269*ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);270nr_pages++;271}272}273274275/* safety; fill the page table up with the last valid page */276fill = *(ptr-1);277for(i=nr_pages;i<1024;i++) {278*ptr++ = fill;279}280281/*282ptr = pt->cpu;283printk("offset: %d\n",pt->offset);284for(i=0;i<5;i++) {285printk("ptr1 %d: 0x%08x\n",i,ptr[i]);286}287*/288return 0;289}290291/********************************************************************************/292/* interrupt handler */293static irqreturn_t interrupt_hw(int irq, void *dev_id)294{295struct saa7146_dev *dev = dev_id;296u32 isr;297u32 ack_isr;298299/* read out the interrupt status register */300ack_isr = isr = saa7146_read(dev, ISR);301302/* is this our interrupt? */303if ( 0 == isr ) {304/* nope, some other device */305return IRQ_NONE;306}307308if (dev->ext) {309if (dev->ext->irq_mask & isr) {310if (dev->ext->irq_func)311dev->ext->irq_func(dev, &isr);312isr &= ~dev->ext->irq_mask;313}314}315if (0 != (isr & (MASK_27))) {316DEB_INT(("irq: RPS0 (0x%08x).\n",isr));317if (dev->vv_data && dev->vv_callback)318dev->vv_callback(dev,isr);319isr &= ~MASK_27;320}321if (0 != (isr & (MASK_28))) {322if (dev->vv_data && dev->vv_callback)323dev->vv_callback(dev,isr);324isr &= ~MASK_28;325}326if (0 != (isr & (MASK_16|MASK_17))) {327SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);328/* only wake up if we expect something */329if (0 != dev->i2c_op) {330dev->i2c_op = 0;331wake_up(&dev->i2c_wq);332} else {333u32 psr = saa7146_read(dev, PSR);334u32 ssr = saa7146_read(dev, SSR);335printk(KERN_WARNING "%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",336dev->name, isr, psr, ssr);337}338isr &= ~(MASK_16|MASK_17);339}340if( 0 != isr ) {341ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));342ERR(("disabling interrupt source(s)!\n"));343SAA7146_IER_DISABLE(dev,isr);344}345saa7146_write(dev, ISR, ack_isr);346return IRQ_HANDLED;347}348349/*********************************************************************************/350/* configuration-functions */351352static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)353{354struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;355struct saa7146_extension *ext = pci_ext->ext;356struct saa7146_dev *dev;357int err = -ENOMEM;358359/* clear out mem for sure */360dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);361if (!dev) {362ERR(("out of memory.\n"));363goto out;364}365366DEB_EE(("pci:%p\n",pci));367368err = pci_enable_device(pci);369if (err < 0) {370ERR(("pci_enable_device() failed.\n"));371goto err_free;372}373374/* enable bus-mastering */375pci_set_master(pci);376377dev->pci = pci;378379/* get chip-revision; this is needed to enable bug-fixes */380dev->revision = pci->revision;381382/* remap the memory from virtual to physical address */383384err = pci_request_region(pci, 0, "saa7146");385if (err < 0)386goto err_disable;387388dev->mem = ioremap(pci_resource_start(pci, 0),389pci_resource_len(pci, 0));390if (!dev->mem) {391ERR(("ioremap() failed.\n"));392err = -ENODEV;393goto err_release;394}395396/* we don't do a master reset here anymore, it screws up397some boards that don't have an i2c-eeprom for configuration398values */399/*400saa7146_write(dev, MC1, MASK_31);401*/402403/* disable all irqs */404saa7146_write(dev, IER, 0);405406/* shut down all dma transfers and rps tasks */407saa7146_write(dev, MC1, 0x30ff0000);408409/* clear out any rps-signals pending */410saa7146_write(dev, MC2, 0xf8000000);411412/* request an interrupt for the saa7146 */413err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,414dev->name, dev);415if (err < 0) {416ERR(("request_irq() failed.\n"));417goto err_unmap;418}419420err = -ENOMEM;421422/* get memory for various stuff */423dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,424&dev->d_rps0.dma_handle);425if (!dev->d_rps0.cpu_addr)426goto err_free_irq;427memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);428429dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,430&dev->d_rps1.dma_handle);431if (!dev->d_rps1.cpu_addr)432goto err_free_rps0;433memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);434435dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,436&dev->d_i2c.dma_handle);437if (!dev->d_i2c.cpu_addr)438goto err_free_rps1;439memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);440441/* the rest + print status message */442443/* create a nice device name */444sprintf(dev->name, "saa7146 (%d)", saa7146_num);445446INFO(("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));447dev->ext = ext;448449mutex_init(&dev->v4l2_lock);450spin_lock_init(&dev->int_slock);451spin_lock_init(&dev->slock);452453mutex_init(&dev->i2c_lock);454455dev->module = THIS_MODULE;456init_waitqueue_head(&dev->i2c_wq);457458/* set some sane pci arbitrition values */459saa7146_write(dev, PCI_BT_V1, 0x1c00101f);460461/* TODO: use the status code of the callback */462463err = -ENODEV;464465if (ext->probe && ext->probe(dev)) {466DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));467goto err_free_i2c;468}469470if (ext->attach(dev, pci_ext)) {471DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));472goto err_free_i2c;473}474/* V4L extensions will set the pci drvdata to the v4l2_device in the475attach() above. So for those cards that do not use V4L we have to476set it explicitly. */477pci_set_drvdata(pci, &dev->v4l2_dev);478479INIT_LIST_HEAD(&dev->item);480list_add_tail(&dev->item,&saa7146_devices);481saa7146_num++;482483err = 0;484out:485return err;486487err_free_i2c:488pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,489dev->d_i2c.dma_handle);490err_free_rps1:491pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,492dev->d_rps1.dma_handle);493err_free_rps0:494pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,495dev->d_rps0.dma_handle);496err_free_irq:497free_irq(pci->irq, (void *)dev);498err_unmap:499iounmap(dev->mem);500err_release:501pci_release_region(pci, 0);502err_disable:503pci_disable_device(pci);504err_free:505kfree(dev);506goto out;507}508509static void saa7146_remove_one(struct pci_dev *pdev)510{511struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);512struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);513struct {514void *addr;515dma_addr_t dma;516} dev_map[] = {517{ dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },518{ dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },519{ dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },520{ NULL, 0 }521}, *p;522523DEB_EE(("dev:%p\n",dev));524525dev->ext->detach(dev);526/* Zero the PCI drvdata after use. */527pci_set_drvdata(pdev, NULL);528529/* shut down all video dma transfers */530saa7146_write(dev, MC1, 0x00ff0000);531532/* disable all irqs, release irq-routine */533saa7146_write(dev, IER, 0);534535free_irq(pdev->irq, dev);536537for (p = dev_map; p->addr; p++)538pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);539540iounmap(dev->mem);541pci_release_region(pdev, 0);542list_del(&dev->item);543pci_disable_device(pdev);544kfree(dev);545546saa7146_num--;547}548549/*********************************************************************************/550/* extension handling functions */551552int saa7146_register_extension(struct saa7146_extension* ext)553{554DEB_EE(("ext:%p\n",ext));555556ext->driver.name = ext->name;557ext->driver.id_table = ext->pci_tbl;558ext->driver.probe = saa7146_init_one;559ext->driver.remove = saa7146_remove_one;560561printk("saa7146: register extension '%s'.\n",ext->name);562return pci_register_driver(&ext->driver);563}564565int saa7146_unregister_extension(struct saa7146_extension* ext)566{567DEB_EE(("ext:%p\n",ext));568printk("saa7146: unregister extension '%s'.\n",ext->name);569pci_unregister_driver(&ext->driver);570return 0;571}572573EXPORT_SYMBOL_GPL(saa7146_register_extension);574EXPORT_SYMBOL_GPL(saa7146_unregister_extension);575576/* misc functions used by extension modules */577EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);578EXPORT_SYMBOL_GPL(saa7146_pgtable_free);579EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);580EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);581EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);582EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);583584EXPORT_SYMBOL_GPL(saa7146_setgpio);585586EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);587588EXPORT_SYMBOL_GPL(saa7146_debug);589EXPORT_SYMBOL_GPL(saa7146_devices);590EXPORT_SYMBOL_GPL(saa7146_devices_lock);591592MODULE_AUTHOR("Michael Hunold <[email protected]>");593MODULE_DESCRIPTION("driver for generic saa7146-based hardware");594MODULE_LICENSE("GPL");595596597