📄 zoran_device.c
字号:
/* * Zoran zr36057/zr36067 PCI controller driver, for the * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux * Media Labs LML33/LML33R10. * * This part handles device access (PCI/I2C/codec/...) * * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> * * Currently maintained by: * Ronald Bultje <rbultje@ronald.bitfreak.net> * Laurent Pinchart <laurent.pinchart@skynet.be> * Mailinglist <mjpeg-users@lists.sf.net> * * 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 <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/vmalloc.h>#include <linux/byteorder/generic.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/i2c.h>#include <linux/i2c-algo-bit.h>#include <linux/videodev.h>#include <linux/spinlock.h>#include <linux/sem.h>#include <linux/pci.h>#include <linux/video_decoder.h>#include <linux/video_encoder.h>#include <linux/delay.h>#include <linux/wait.h>#include <asm/io.h>#include "videocodec.h"#include "zoran.h"#include "zoran_device.h"#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \ ZR36057_ISR_GIRQ1 | \ ZR36057_ISR_JPEGRepIRQ )extern const struct zoran_format zoran_formats[];extern int *zr_debug;#define dprintk(num, format, args...) \ do { \ if (*zr_debug >= num) \ printk(format, ##args); \ } while (0)static int lml33dpath = 0; /* 1 will use digital path in capture * mode instead of analog. It can be * used for picture adjustments using * tool like xawtv while watching image * on TV monitor connected to the output. * However, due to absence of 75 Ohm * load on Bt819 input, there will be * some image imperfections */module_param(lml33dpath, bool, 0);MODULE_PARM_DESC(lml33dpath, "Use digital path capture mode (on LML33 cards)");static voidzr36057_init_vfe (struct zoran *zr);/* * General Purpose I/O and Guest bus access *//* * This is a bit tricky. When a board lacks a GPIO function, the corresponding * GPIO bit number in the card_info structure is set to 0. */voidGPIO (struct zoran *zr, int bit, unsigned int value){ u32 reg; u32 mask; /* Make sure the bit number is legal * A bit number of -1 (lacking) gives a mask of 0, * making it harmless */ mask = (1 << (24 + bit)) & 0xff000000; reg = btread(ZR36057_GPPGCR1) & ~mask; if (value) { reg |= mask; } btwrite(reg, ZR36057_GPPGCR1); udelay(1);}/* * Wait til post office is no longer busy */intpost_office_wait (struct zoran *zr){ u32 por;// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) { /* wait for something to happen */ } if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) { /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr), por); return -1; } return 0;}intpost_office_write (struct zoran *zr, unsigned int guest, unsigned int reg, unsigned int value){ u32 por; por = ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16) | (value & 0xFF); btwrite(por, ZR36057_POR); return post_office_wait(zr);}intpost_office_read (struct zoran *zr, unsigned int guest, unsigned int reg){ u32 por; por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); btwrite(por, ZR36057_POR); if (post_office_wait(zr) < 0) { return -1; } return btread(ZR36057_POR) & 0xFF;}/* * detect guests */static voiddump_guests (struct zoran *zr){ if (*zr_debug > 2) { int i, guest[8]; for (i = 1; i < 8; i++) { // Don't read jpeg codec here guest[i] = post_office_read(zr, i, 0); } printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr)); for (i = 1; i < 8; i++) { printk(" 0x%02x", guest[i]); } printk("\n"); }}static inline unsigned longget_time (void){ struct timeval tv; do_gettimeofday(&tv); return (1000000 * tv.tv_sec + tv.tv_usec);}voiddetect_guest_activity (struct zoran *zr){ int timeout, i, j, res, guest[8], guest0[8], change[8][3]; unsigned long t0, t1; dump_guests(zr); printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", ZR_DEVNAME(zr)); for (i = 1; i < 8; i++) { // Don't read jpeg codec here guest0[i] = guest[i] = post_office_read(zr, i, 0); } timeout = 0; j = 0; t0 = get_time(); while (timeout < 10000) { udelay(10); timeout++; for (i = 1; (i < 8) && (j < 8); i++) { res = post_office_read(zr, i, 0); if (res != guest[i]) { t1 = get_time(); change[j][0] = (t1 - t0); t0 = t1; change[j][1] = i; change[j][2] = res; j++; guest[i] = res; } } if (j >= 8) break; } printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr)); for (i = 1; i < 8; i++) { printk(" 0x%02x", guest0[i]); } printk("\n"); if (j == 0) { printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr)); return; } for (i = 0; i < j; i++) { printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr), change[i][0], change[i][1], change[i][2]); }}/* * JPEG Codec access */voidjpeg_codec_sleep (struct zoran *zr, int sleep){ GPIO(zr, zr->card.gpio[GPIO_JPEG_SLEEP], !sleep); if (!sleep) { dprintk(3, KERN_DEBUG "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n", ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); udelay(500); } else { dprintk(3, KERN_DEBUG "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n", ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); udelay(2); }}intjpeg_codec_reset (struct zoran *zr){ /* Take the codec out of sleep */ jpeg_codec_sleep(zr, 0); if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, 0); udelay(2); } else { GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 0); udelay(2); GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 1); udelay(2); } return 0;}/* * Set the registers for the size we have specified. Don't bother * trying to understand this without the ZR36057 manual in front of * you [AC]. * * PS: The manual is free for download in .pdf format from * www.zoran.com - nicely done those folks. */static voidzr36057_adjust_vfe (struct zoran *zr, enum zoran_codec_mode mode){ u32 reg; switch (mode) { case BUZ_MODE_MOTION_DECOMPRESS: btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); reg = btread(ZR36057_VFEHCR); if ((reg & (1 << 10)) && zr->card.type != LML33R10) { reg += ((1 << 10) | 1); } btwrite(reg, ZR36057_VFEHCR); break; case BUZ_MODE_MOTION_COMPRESS: case BUZ_MODE_IDLE: default: if (zr->norm == VIDEO_MODE_NTSC || (zr->card.type == LML33R10 && zr->norm == VIDEO_MODE_PAL)) btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); else btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); reg = btread(ZR36057_VFEHCR); if (!(reg & (1 << 10)) && zr->card.type != LML33R10) { reg -= ((1 << 10) | 1); } btwrite(reg, ZR36057_VFEHCR); break; }}/* * set geometry */static voidzr36057_set_vfe (struct zoran *zr, int video_width, int video_height, const struct zoran_format *format){ struct tvnorm *tvn; unsigned HStart, HEnd, VStart, VEnd; unsigned DispMode; unsigned VidWinWid, VidWinHt; unsigned hcrop1, hcrop2, vcrop1, vcrop2; unsigned Wa, We, Ha, He; unsigned X, Y, HorDcm, VerDcm; u32 reg; unsigned mask_line_size; tvn = zr->timing; Wa = tvn->Wa; Ha = tvn->Ha; dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n", ZR_DEVNAME(zr), video_width, video_height); if (zr->norm != VIDEO_MODE_PAL && zr->norm != VIDEO_MODE_NTSC && zr->norm != VIDEO_MODE_SECAM) { dprintk(1, KERN_ERR "%s: set_vfe() - norm = %d not valid\n", ZR_DEVNAME(zr), zr->norm); return; } if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT || video_width > Wa || video_height > Ha) { dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", ZR_DEVNAME(zr), video_width, video_height); return; } /**** zr36057 ****/ /* horizontal */ VidWinWid = video_width; X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; We = (VidWinWid * 64) / X; HorDcm = 64 - X; hcrop1 = 2 * ((tvn->Wa - We) / 4); hcrop2 = tvn->Wa - We - hcrop1; HStart = tvn->HStart ? tvn->HStart : 1; /* (Ronald) Original comment: * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" * this is false. It inverses chroma values on the LML33R10 (so Cr * suddenly is shown as Cb and reverse, really cool effect if you * want to see blue faces, not useful otherwise). So don't use |1. * However, the DC10 has '0' as HStart, but does need |1, so we * use a dirty check... */ HEnd = HStart + tvn->Wa - 1; HStart += hcrop1; HEnd -= hcrop2; reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); if (zr->card.vfe_pol.hsync_pol) reg |= ZR36057_VFEHCR_HSPol; btwrite(reg, ZR36057_VFEHCR); /* Vertical */ DispMode = !(video_height > BUZ_MAX_HEIGHT / 2); VidWinHt = DispMode ? video_height : video_height / 2; Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; He = (VidWinHt * 64) / Y; VerDcm = 64 - Y; vcrop1 = (tvn->Ha / 2 - He) / 2; vcrop2 = tvn->Ha / 2 - He - vcrop1; VStart = tvn->VStart; VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP VStart += vcrop1; VEnd -= vcrop2; reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); if (zr->card.vfe_pol.vsync_pol) reg |= ZR36057_VFEVCR_VSPol; btwrite(reg, ZR36057_VFEVCR); /* scaler and pixel format */ reg = 0; reg |= (HorDcm << ZR36057_VFESPFR_HorDcm); reg |= (VerDcm << ZR36057_VFESPFR_VerDcm); reg |= (DispMode << ZR36057_VFESPFR_DispMode); if (format->palette != VIDEO_PALETTE_YUV422) reg |= ZR36057_VFESPFR_LittleEndian; /* RJ: I don't know, why the following has to be the opposite * of the corresponding ZR36060 setting, but only this way * we get the correct colors when uncompressing to the screen */ //reg |= ZR36057_VFESPFR_VCLKPol; /**/ /* RJ: Don't know if that is needed for NTSC also */ if (zr->norm != VIDEO_MODE_NTSC) reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang reg |= ZR36057_VFESPFR_TopField; switch (format->palette) { case VIDEO_PALETTE_YUV422: reg |= ZR36057_VFESPFR_YUV422; break; case VIDEO_PALETTE_RGB555: reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif; break; case VIDEO_PALETTE_RGB565: reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif; break; case VIDEO_PALETTE_RGB24: reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24; break; case VIDEO_PALETTE_RGB32: reg |= ZR36057_VFESPFR_RGB888; break; default: dprintk(1, KERN_INFO "%s: set_vfe() - unknown color_fmt=%x\n", ZR_DEVNAME(zr), format->palette); return; } if (HorDcm >= 48) { reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ } else if (HorDcm >= 32) { reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ } else if (HorDcm >= 16) { reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ } btwrite(reg, ZR36057_VFESPFR); /* display configuration */ reg = (16 << ZR36057_VDCR_MinPix) | (VidWinHt << ZR36057_VDCR_VidWinHt) | (VidWinWid << ZR36057_VDCR_VidWinWid); if (pci_pci_problems & PCIPCI_TRITON) // || zr->revision < 1) // Revision 1 has also Triton support reg &= ~ZR36057_VDCR_Triton; else reg |= ZR36057_VDCR_Triton; btwrite(reg, ZR36057_VDCR); /* (Ronald) don't write this if overlay_mask = NULL */ if (zr->overlay_mask) { /* Write overlay clipping mask data, but don't enable overlay clipping */ /* RJ: since this makes only sense on the screen, we use * zr->overlay_settings.width instead of video_width */ mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; reg = virt_to_bus(zr->overlay_mask); btwrite(reg, ZR36057_MMTR); reg = virt_to_bus(zr->overlay_mask + mask_line_size); btwrite(reg, ZR36057_MMBR); reg = mask_line_size - (zr->overlay_settings.width + 31) / 32; if (DispMode == 0) reg += mask_line_size; reg <<= ZR36057_OCR_MaskStride; btwrite(reg, ZR36057_OCR); } zr36057_adjust_vfe(zr, zr->codec_mode);}/* * Switch overlay on or off */voidzr36057_overlay (struct zoran *zr, int on){ u32 reg; if (on) { /* do the necessary settings ... */ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ zr36057_set_vfe(zr, zr->overlay_settings.width, zr->overlay_settings.height, zr->overlay_settings.format); /* Start and length of each line MUST be 4-byte aligned. * This should be allready checked before the call to this routine. * All error messages are internal driver checking only! */ /* video display top and bottom registers */ reg = (u32) zr->buffer.base + zr->overlay_settings.x * ((zr->overlay_settings.format->depth + 7) / 8) + zr->overlay_settings.y * zr->buffer.bytesperline; btwrite(reg, ZR36057_VDTR); if (reg & 3) dprintk(1, KERN_ERR "%s: zr36057_overlay() - video_address not aligned\n", ZR_DEVNAME(zr)); if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) reg += zr->buffer.bytesperline; btwrite(reg, ZR36057_VDBR); /* video stride, status, and frame grab register */ reg = zr->buffer.bytesperline - zr->overlay_settings.width * ((zr->overlay_settings.format->depth + 7) / 8); if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) reg += zr->buffer.bytesperline; if (reg & 3) dprintk(1, KERN_ERR "%s: zr36057_overlay() - video_stride not aligned\n", ZR_DEVNAME(zr)); reg = (reg << ZR36057_VSSFGR_DispStride); reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ btwrite(reg, ZR36057_VSSFGR); /* Set overlay clipping */ if (zr->overlay_settings.clipcount > 0) btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); /* ... and switch it on */ btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); } else { /* Switch it off */ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); }}/* * The overlay mask has one bit for each pixel on a scan line, * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. */voidwrite_overlay_mask (struct file *file, struct video_clip *vp, int count){ struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; u32 *mask; int x, y, width, height; unsigned i, j, k;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -