📄 planb.c
字号:
/* planb - PlanB frame grabber driver PlanB is used in the 7x00/8x00 series of PowerMacintosh Computers as video input DMA controller. Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu) 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.*//* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */#include <linux/init.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/vmalloc.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/videodev.h>#include <linux/wait.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/dbdma.h>#include <asm/pgtable.h>#include <asm/page.h>#include <asm/irq.h>#include <asm/semaphore.h>#include "planb.h"#include "saa7196.h"/* Would you mind for some ugly debugging? */#if 0#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */#else#define DEBUG(x...) /* Don't debug driver */#endif#if 0#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */#else#define IDEBUG(x...) /* Don't debug interrupt part */#endif/* Ever seen a Mac with more than 1 of these? */#define PLANB_MAX 1static int planb_num;static struct planb planbs[PLANB_MAX];static volatile struct planb_registers *planb_regs;static int def_norm = PLANB_DEF_NORM; /* default norm */static int video_nr = -1;MODULE_PARM(def_norm, "i");MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)");MODULE_PARM(video_nr,"i");MODULE_LICENSE("GPL");/* ------------------ PlanB Exported Functions ------------------ */static long planb_write(struct video_device *, const char *, unsigned long, int);static long planb_read(struct video_device *, char *, unsigned long, int);static int planb_open(struct video_device *, int);static void planb_close(struct video_device *);static int planb_ioctl(struct video_device *, unsigned int, void *);static int planb_init_done(struct video_device *);static int planb_mmap(struct video_device *, const char *, unsigned long);static void planb_irq(int, void *, struct pt_regs *);static void release_planb(void);int init_planbs(struct video_init *);/* ------------------ PlanB Internal Functions ------------------ */static int planb_prepare_open(struct planb *);static void planb_prepare_close(struct planb *);static void saa_write_reg(unsigned char, unsigned char);static unsigned char saa_status(int, struct planb *);static void saa_set(unsigned char, unsigned char, struct planb *);static void saa_init_regs(struct planb *);static int grabbuf_alloc(struct planb *);static int vgrab(struct planb *, struct video_mmap *);static void add_clip(struct planb *, struct video_clip *);static void fill_cmd_buff(struct planb *);static void cmd_buff(struct planb *);static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *);static void overlay_start(struct planb *);static void overlay_stop(struct planb *);static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short, unsigned int);static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int, unsigned int);static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short, unsigned short, unsigned int, unsigned int);static int init_planb(struct planb *);static int find_planb(void);static void planb_pre_capture(int, int, struct planb *);static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *, int, int, int, int, int, struct planb *);static inline void planb_dbdma_stop(volatile struct dbdma_regs *);static unsigned int saa_geo_setup(int, int, int, int, struct planb *);static inline int overlay_is_active(struct planb *);/*******************************//* Memory management functions *//*******************************/static int grabbuf_alloc(struct planb *pb){ int i, npage; npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1)#ifndef PLANB_GSCANLINE + MAX_LNUM#endif /* PLANB_GSCANLINE */ ); if ((pb->rawbuf = (unsigned char**) kmalloc (npage * sizeof(unsigned long), GFP_KERNEL)) == 0) return -ENOMEM; for (i = 0; i < npage; i++) { pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL |GFP_DMA, 0); if (!pb->rawbuf[i]) break; SetPageReserved(virt_to_page(pb->rawbuf[i])); } if (i-- < npage) { printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n"); for (; i > 0; i--) { ClearPageReserved(virt_to_page(pb->rawbuf[i])); free_pages((unsigned long)pb->rawbuf[i], 0); } kfree(pb->rawbuf); return -ENOBUFS; } pb->rawbuf_size = npage; return 0;}/*****************************//* Hardware access functions *//*****************************/static void saa_write_reg(unsigned char addr, unsigned char val){ planb_regs->saa_addr = addr; eieio(); planb_regs->saa_regval = val; eieio(); return;}/* return status byte 0 or 1: */static unsigned char saa_status(int byte, struct planb *pb){ saa_regs[pb->win.norm][SAA7196_STDC] = (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1); saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]); /* Let's wait 30msec for this one */ msleep_interruptible(30); return (unsigned char)in_8 (&planb_regs->saa_status);}static void saa_set(unsigned char addr, unsigned char val, struct planb *pb){ if(saa_regs[pb->win.norm][addr] != val) { saa_regs[pb->win.norm][addr] = val; saa_write_reg (addr, val); } return;}static void saa_init_regs(struct planb *pb){ int i; for (i = 0; i < SAA7196_NUMREGS; i++) saa_write_reg (i, saa_regs[pb->win.norm][i]);}static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp, struct planb *pb){ int ht, norm = pb->win.norm; switch(bpp) { case 2: /* RGB555+a 1x16-bit + 16-bit transparent */ saa_regs[norm][SAA7196_FMTS] &= ~0x3; break; case 1: case 4: /* RGB888 1x24-bit + 8-bit transparent */ saa_regs[norm][SAA7196_FMTS] &= ~0x1; saa_regs[norm][SAA7196_FMTS] |= 0x2; break; default: return -EINVAL; } ht = (interlace ? height / 2 : height); saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff); saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3) | (width >> 8 & 0x3); saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff); saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3) | (ht >> 8 & 0x3); /* feed both fields if interlaced, or else feed only even fields */ saa_regs[norm][SAA7196_FMTS] = (interlace) ? (saa_regs[norm][SAA7196_FMTS] & ~0x60) : (saa_regs[norm][SAA7196_FMTS] | 0x60); /* transparent mode; extended format enabled */ saa_regs[norm][SAA7196_DPATH] |= 0x3; return 0;}/***************************//* DBDMA support functions *//***************************/static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch){ out_le32(&ch->control, PLANB_CLR(RUN)); out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE));}static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch){ int i = 0; out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH)); while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) { IDEBUG("PlanB: waiting for DMA to stop\n"); i++; }}static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch, unsigned short command, unsigned int cmd_dep){ st_le16(&ch->command, command); st_le32(&ch->cmd_dep, cmd_dep);}static inline void tab_cmd_store(volatile struct dbdma_cmd *ch, unsigned int phy_addr, unsigned int cmd_dep){ st_le16(&ch->command, STORE_WORD | KEY_SYSTEM); st_le16(&ch->req_count, 4); st_le32(&ch->phy_addr, phy_addr); st_le32(&ch->cmd_dep, cmd_dep);}static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch, unsigned short command, unsigned short req_count, unsigned int phy_addr, unsigned int cmd_dep){ st_le16(&ch->command, command); st_le16(&ch->req_count, req_count); st_le32(&ch->phy_addr, phy_addr); st_le32(&ch->cmd_dep, cmd_dep);}static volatile struct dbdma_cmd *cmd_geo_setup( volatile struct dbdma_cmd *c1, int width, int height, int interlace, int bpp, int clip, struct planb *pb){ int norm = pb->win.norm; if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0) return (volatile struct dbdma_cmd *)NULL; tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), SAA7196_FMTS); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), saa_regs[norm][SAA7196_FMTS]); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), SAA7196_DPATH); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), saa_regs[norm][SAA7196_DPATH]); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even), bpp | ((clip)? PLANB_CLIPMASK: 0)); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd), bpp | ((clip)? PLANB_CLIPMASK: 0)); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), SAA7196_OUTPIX); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), saa_regs[norm][SAA7196_OUTPIX]); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), SAA7196_HFILT); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), saa_regs[norm][SAA7196_HFILT]); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), SAA7196_OUTLINE); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), saa_regs[norm][SAA7196_OUTLINE]); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), SAA7196_VYP); tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), saa_regs[norm][SAA7196_VYP]); return c1;}/******************************//* misc. supporting functions *//******************************/static inline void planb_lock(struct planb *pb){ down(&pb->lock);}static inline void planb_unlock(struct planb *pb){ up(&pb->lock);}/***************//* Driver Core *//***************/static int planb_prepare_open(struct planb *pb){ int i, size; /* allocate memory for two plus alpha command buffers (size: max lines, plus 40 commands handling, plus 1 alignment), plus dummy command buf, plus clipmask buffer, plus frame grabbing status */ size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS * PLANB_DUMMY)*sizeof(struct dbdma_cmd) +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8 +MAX_GBUFFERS*sizeof(unsigned int); if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0) return -ENOMEM; memset ((void *) pb->priv_space, 0, size); pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *) DBDMA_ALIGN (pb->priv_space); pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size; pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd); pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size; pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR; for (i = 1; i < MAX_GBUFFERS; i++) { pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY; pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR; } pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1] + PLANB_DUMMY); pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS); pb->rawbuf = NULL; pb->rawbuf_size = 0; pb->grabbing = 0; for (i = 0; i < MAX_GBUFFERS; i++) { pb->frame_stat[i] = GBUFFER_UNUSED; pb->gwidth[i] = 0; pb->gheight[i] = 0; pb->gfmt[i] = 0; pb->gnorm_switch[i] = 0;#ifndef PLANB_GSCANLINE pb->lsize[i] = 0; pb->lnum[i] = 0;#endif /* PLANB_GSCANLINE */ } pb->gcount = 0; pb->suspend = 0; pb->last_fr = -999; pb->prev_last_fr = -999; /* Reset DMA controllers */ planb_dbdma_stop(&pb->planb_base->ch2); planb_dbdma_stop(&pb->planb_base->ch1); return 0;}static void planb_prepare_close(struct planb *pb){ int i; /* make sure the dma's are idle */ planb_dbdma_stop(&pb->planb_base->ch2); planb_dbdma_stop(&pb->planb_base->ch1); /* free kernel memory of command buffers */ if(pb->priv_space != 0) { kfree (pb->priv_space); pb->priv_space = 0; pb->cmd_buff_inited = 0; } if(pb->rawbuf) { for (i = 0; i < pb->rawbuf_size; i++) { ClearPageReserved(virt_to_page(pb->rawbuf[i])); free_pages((unsigned long)pb->rawbuf[i], 0); } kfree(pb->rawbuf); } pb->rawbuf = NULL;}/*****************************//* overlay support functions *//*****************************/static inline int overlay_is_active(struct planb *pb){ unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd); unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr); return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys) && (caddr < (pb->ch1_cmd_phys + size)) && (caddr >= (unsigned)pb->ch1_cmd_phys);}static void overlay_start(struct planb *pb){ DEBUG("PlanB: overlay_start()\n"); if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { DEBUG("PlanB: presumably, grabbing is in progress...\n"); planb_dbdma_stop(&pb->planb_base->ch2); out_le32 (&pb->planb_base->ch2.cmdptr, virt_to_bus(pb->ch2_cmd)); planb_dbdma_restart(&pb->planb_base->ch2); st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); tab_cmd_dbdma(pb->last_cmd[pb->last_fr], DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd)); eieio(); pb->prev_last_fr = pb->last_fr; pb->last_fr = -2; if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { IDEBUG("PlanB: became inactive " "in the mean time... reactivating\n"); planb_dbdma_stop(&pb->planb_base->ch1); out_le32 (&pb->planb_base->ch1.cmdptr, virt_to_bus(pb->ch1_cmd)); planb_dbdma_restart(&pb->planb_base->ch1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -