📄 arv.c
字号:
/* * Colour AR M64278(VGA) driver for Video4Linux * * Copyright (C) 2003 Takeo Takahashi <takahashi.takeo@renesas.com> * * 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. * * Some code is taken from AR driver sample program for M3T-M32700UT. * * AR driver sample (M32R SDK): * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION * AND RENESAS SOLUTIONS CORPORATION * All Rights Reserved. * * 2003-09-01: Support w3cam by Takeo Takahashi */#include <linux/config.h>#include <linux/init.h>#include <linux/devfs_fs_kernel.h>#include <linux/module.h>#include <linux/version.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/videodev.h>#include <asm/semaphore.h>#include <asm/uaccess.h>#include <asm/m32r.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#if 0#define DEBUG(n, args...) printk(args)#define CHECK_LOST 1#else#define DEBUG(n, args...)#define CHECK_LOST 0#endif/* * USE_INT is always 0, interrupt mode is not available * on linux due to lack of speed */#define USE_INT 0 /* Don't modify */#define VERSION "0.03"#define ar_inl(addr) inl((unsigned long)(addr))#define ar_outl(val, addr) outl((unsigned long)(val),(unsigned long)(addr))extern struct cpuinfo_m32r boot_cpu_data;/* * CCD pixel size * Note that M32700UT does not support CIF mode, but QVGA is * supported by M32700UT hardware using VGA mode of AR LSI. * * Supported: VGA (Normal mode, Interlace mode) * QVGA (Always Interlace mode of VGA) * */#define AR_WIDTH_VGA 640#define AR_HEIGHT_VGA 480#define AR_WIDTH_QVGA 320#define AR_HEIGHT_QVGA 240#define MIN_AR_WIDTH AR_WIDTH_QVGA#define MIN_AR_HEIGHT AR_HEIGHT_QVGA#define MAX_AR_WIDTH AR_WIDTH_VGA#define MAX_AR_HEIGHT AR_HEIGHT_VGA/* bits & bytes per pixel */#define AR_BITS_PER_PIXEL 16#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL/8)/* line buffer size */#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL)#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL)#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA/* frame size & type */#define AR_FRAME_BYTES_VGA \ (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL)#define AR_FRAME_BYTES_QVGA \ (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL)#define MAX_AR_FRAME_BYTES \ (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL)#define AR_MAX_FRAME 15/* capture size */#define AR_SIZE_VGA 0#define AR_SIZE_QVGA 1/* capture mode */#define AR_MODE_INTERLACE 0#define AR_MODE_NORMAL 1struct ar_device { struct video_device *vdev; unsigned int start_capture; /* duaring capture in INT. mode. */#if USE_INT unsigned char *line_buff; /* DMA line buffer */#endif unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */ short size; /* capture size */ short mode; /* capture mode */ int width, height; int frame_bytes, line_bytes; wait_queue_head_t wait; struct semaphore lock;};static int video_nr = -1; /* video device number (first free) */static unsigned char yuv[MAX_AR_FRAME_BYTES];/* module parameters *//* default frequency */#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */static int vga = 0; /* default mode(0:QVGA mode, other:VGA mode) */static int vga_interlace = 0; /* 0 is normal mode for, else interlace mode */MODULE_PARM(freq, "i");MODULE_PARM(vga, "i");MODULE_PARM(vga_interlace, "i");static int ar_initialize(struct video_device *dev);static inline void wait_for_vsync(void){ while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */ cpu_relax(); while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */ cpu_relax();}static inline void wait_acknowledge(void){ int i; for (i = 0; i < 1000; i++) cpu_relax(); while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK) cpu_relax();}/******************************************************************* * I2C functions *******************************************************************/void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, unsigned long data3){ int i; /* Slave Address */ ar_outl(addr, PLDI2CDATA); wait_for_vsync(); /* Start */ ar_outl(1, PLDI2CCND); wait_acknowledge(); /* Transfer data 1 */ ar_outl(data1, PLDI2CDATA); wait_for_vsync(); ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); wait_acknowledge(); /* Transfer data 2 */ ar_outl(data2, PLDI2CDATA); wait_for_vsync(); ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); wait_acknowledge(); if (n == 3) { /* Transfer data 3 */ ar_outl(data3, PLDI2CDATA); wait_for_vsync(); ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); wait_acknowledge(); } /* Stop */ for (i = 0; i < 100; i++) cpu_relax(); ar_outl(2, PLDI2CCND); ar_outl(2, PLDI2CCND); while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB) cpu_relax();}void init_iic(void){ DEBUG(1, "init_iic:\n"); /* * ICU Setting (iic) */ /* I2C Setting */ ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */ ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */ ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */ /* I2C CLK */ /* 50MH-100k */ if (freq == 75) { ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */ } else if (freq == 50) { ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */ } else { ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */ } ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */}/************************************************************************** * * Video4Linux Interface functions * **************************************************************************/static inline void disable_dma(void){ ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */}static inline void enable_dma(void){ ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */}static inline void clear_dma_status(void){ ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */}static inline void wait_for_vertical_sync(int exp_line){#if CHECK_LOST int tmout = 10000; /* FIXME */ int l; /* * check HCOUNT because we cannot check vertical sync. */ for (; tmout >= 0; tmout--) { l = ar_inl(ARVHCOUNT); if (l == exp_line) break; } if (tmout < 0) printk("arv: lost %d -> %d\n", exp_line, l);#else while (ar_inl(ARVHCOUNT) != exp_line) cpu_relax();#endif}static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos){ struct video_device *v = video_devdata(file); struct ar_device *ar = v->priv; long ret = ar->frame_bytes; /* return read bytes */ unsigned long arvcr1 = 0; unsigned long flags; unsigned char *p; int h, w; unsigned char *py, *pu, *pv;#if ! USE_INT int l;#endif DEBUG(1, "ar_read()\n"); if (ar->size == AR_SIZE_QVGA) arvcr1 |= ARVCR1_QVGA; if (ar->mode == AR_MODE_NORMAL) arvcr1 |= ARVCR1_NORMAL; down(&ar->lock);#if USE_INT local_irq_save(flags); disable_dma(); ar_outl(0xa1871300, M32R_DMA0CR0_PORTL); ar_outl(0x01000000, M32R_DMA0CR1_PORTL); /* set AR FIFO address as source(BSEL5) */ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */ ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */ /* * Okey , kicks AR LSI to invoke an interrupt */ ar->start_capture = 0; ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1); local_irq_restore(flags); /* .... AR interrupts .... */ interruptible_sleep_on(&ar->wait); if (signal_pending(current)) { printk("arv: interrupted while get frame data.\n"); ret = -EINTR; goto out_up; }#else /* ! USE_INT */ /* polling */ ar_outl(arvcr1, ARVCR1); disable_dma(); ar_outl(0x8000, M32R_DMAEDET_PORTL); ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); ar_outl(0x01000000, M32R_DMA0CR1_PORTL); ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); local_irq_save(flags); while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */ cpu_relax(); if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { for (h = 0; h < ar->height; h++) { wait_for_vertical_sync(h); if (h < (AR_HEIGHT_VGA/2)) l = h << 1; else l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1); ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL); enable_dma(); while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) cpu_relax(); disable_dma(); clear_dma_status(); ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); } } else { for (h = 0; h < ar->height; h++) { wait_for_vertical_sync(h); ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL); enable_dma(); while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) cpu_relax(); disable_dma(); clear_dma_status(); ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); } } local_irq_restore(flags);#endif /* ! USE_INT */ /* * convert YUV422 to YUV422P * +--------------------+ * | Y0,Y1,... | * | ..............Yn | * +--------------------+ * | U0,U1,........Un | * +--------------------+ * | V0,V1,........Vn | * +--------------------+ */ py = yuv; pu = py + (ar->frame_bytes / 2); pv = pu + (ar->frame_bytes / 4); for (h = 0; h < ar->height; h++) { p = ar->frame[h]; for (w = 0; w < ar->line_bytes; w += 4) { *py++ = *p++; *pu++ = *p++; *py++ = *p++; *pv++ = *p++; } } if (copy_to_user(buf, yuv, ar->frame_bytes)) { printk("arv: failed while copy_to_user yuv.\n"); ret = -EFAULT; goto out_up; } DEBUG(1, "ret = %d\n", ret);out_up: up(&ar->lock); return ret;}static int ar_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct video_device *dev = video_devdata(file); struct ar_device *ar = dev->priv; DEBUG(1, "ar_ioctl()\n"); switch(cmd) { case VIDIOCGCAP: { struct video_capability *b = arg; DEBUG(1, "VIDIOCGCAP:\n"); strcpy(b->name, ar->vdev->name); b->type = VID_TYPE_CAPTURE; b->channels = 0; b->audios = 0; b->maxwidth = MAX_AR_WIDTH; b->maxheight = MAX_AR_HEIGHT; b->minwidth = MIN_AR_WIDTH; b->minheight = MIN_AR_HEIGHT; return 0; } case VIDIOCGCHAN: DEBUG(1, "VIDIOCGCHAN:\n"); return 0; case VIDIOCSCHAN: DEBUG(1, "VIDIOCSCHAN:\n"); return 0; case VIDIOCGTUNER: DEBUG(1, "VIDIOCGTUNER:\n"); return 0; case VIDIOCSTUNER: DEBUG(1, "VIDIOCSTUNER:\n"); return 0; case VIDIOCGPICT: DEBUG(1, "VIDIOCGPICT:\n"); return 0; case VIDIOCSPICT: DEBUG(1, "VIDIOCSPICT:\n"); return 0; case VIDIOCCAPTURE: DEBUG(1, "VIDIOCCAPTURE:\n"); return -EINVAL; case VIDIOCGWIN: { struct video_window *w = arg; DEBUG(1, "VIDIOCGWIN:\n"); memset(w, 0, sizeof(w)); w->width = ar->width; w->height = ar->height; return 0; } case VIDIOCSWIN: { struct video_window *w = arg; DEBUG(1, "VIDIOCSWIN:\n"); if ((w->width != AR_WIDTH_VGA || w->height != AR_HEIGHT_VGA) && (w->width != AR_WIDTH_QVGA || w->height != AR_HEIGHT_QVGA)) return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -