diff options
author | Stefan Schmidt <stefan@datenfreihafen.org> | 2009-03-23 11:45:40 +0100 |
---|---|---|
committer | Stefan Schmidt <stefan@datenfreihafen.org> | 2009-03-23 11:45:40 +0100 |
commit | 451b1c687105655a4f2c9c477b05535041e25060 (patch) | |
tree | 3db315590172cd6244107a97a6603add934d7e32 /recipes/linux/linux-davinci-2.6.28/davinci-sffsdr/0011-Add-lyrvpss-example-driver-for-the-SFFSDR-board.patch | |
parent | 6767ca50430e37cdad0a8992b73c3f82ead134bf (diff) | |
parent | e2b99b79f516a7466dc050902cee62f39869bf9d (diff) |
Merge branch 'org.openembedded.dev' of git@git.openembedded.net:openembedded into org.openembedded.dev
Diffstat (limited to 'recipes/linux/linux-davinci-2.6.28/davinci-sffsdr/0011-Add-lyrvpss-example-driver-for-the-SFFSDR-board.patch')
-rw-r--r-- | recipes/linux/linux-davinci-2.6.28/davinci-sffsdr/0011-Add-lyrvpss-example-driver-for-the-SFFSDR-board.patch | 919 |
1 files changed, 919 insertions, 0 deletions
diff --git a/recipes/linux/linux-davinci-2.6.28/davinci-sffsdr/0011-Add-lyrvpss-example-driver-for-the-SFFSDR-board.patch b/recipes/linux/linux-davinci-2.6.28/davinci-sffsdr/0011-Add-lyrvpss-example-driver-for-the-SFFSDR-board.patch new file mode 100644 index 0000000000..893cbd3d22 --- /dev/null +++ b/recipes/linux/linux-davinci-2.6.28/davinci-sffsdr/0011-Add-lyrvpss-example-driver-for-the-SFFSDR-board.patch @@ -0,0 +1,919 @@ +From 25a91bba1bcc8d9f120e8b85b0ec53a18ccec244 Mon Sep 17 00:00:00 2001 +From: Hugo Villeneuve <hugo@hugovil.com> +Date: Thu, 5 Mar 2009 16:04:23 -0500 +Subject: [PATCH 11/12] Add lyrvpss example driver for the SFFSDR board + +Currently there is only a VPFE driver in lyrvpss, and it is called luyrvpfe. +It works with a FPGA bitstream that generates a ramp and sends it over the +VPFE interface. The lyrvpfe driver receives an interrupt each time the HSYNC +line is pulsed (even if the VDINT0 interrupt line is used), and stores and +checks the data to make sure that it is valid. The driver will request a new +frame from the FPGA each time there is a read from /proc/lyrvpfe. For example, +to receive a new frame, issue the following: + + $> cat /proc/lyrvpfe + +This will send a request to the FPGA (using the GPIO line) to send a new frame, +wait one second then display the contents of the PING and PONG reception buffers. + +Signed-off-by: Hugo Villeneuve <hugo@hugovil.com> +--- + .../arm/mach-davinci/include/mach/sffsdr-lyrvpfe.h | 32 + + drivers/char/Kconfig | 2 + + drivers/char/Makefile | 2 + + drivers/char/lyrvpss/Kconfig | 42 ++ + drivers/char/lyrvpss/Makefile | 8 + + drivers/char/lyrvpss/vpfe.c | 753 ++++++++++++++++++++ + 6 files changed, 839 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/mach-davinci/include/mach/sffsdr-lyrvpfe.h + create mode 100644 drivers/char/lyrvpss/Kconfig + create mode 100644 drivers/char/lyrvpss/Makefile + create mode 100644 drivers/char/lyrvpss/vpfe.c + +diff --git a/arch/arm/mach-davinci/include/mach/sffsdr-lyrvpfe.h b/arch/arm/mach-davinci/include/mach/sffsdr-lyrvpfe.h +new file mode 100644 +index 0000000..fb47851 +--- /dev/null ++++ b/arch/arm/mach-davinci/include/mach/sffsdr-lyrvpfe.h +@@ -0,0 +1,32 @@ ++/* ++ * lyrvpfe.h ++ * ++ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * 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. ++ */ ++ ++#ifndef __LYRVPFE_H ++#define __LYRVPFE_H ++ ++struct lyrvpfe_platform_data { ++ unsigned ready_gpio; ++}; ++ ++#endif /* __LYRVPFE_H */ +diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig +index 43d6ba8..b98a8e2 100644 +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -1073,5 +1073,7 @@ config DEVPORT + + source "drivers/s390/char/Kconfig" + ++source "drivers/char/lyrvpss/Kconfig" ++ + endmenu + +diff --git a/drivers/char/Makefile b/drivers/char/Makefile +index 438f713..8800b3f 100644 +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -109,6 +109,8 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o + obj-$(CONFIG_JS_RTC) += js-rtc.o + js-rtc-y = rtc.o + ++obj-$(CONFIG_LYRTECH_VPSS) += lyrvpss/ ++ + # Files generated that shall be removed upon make clean + clean-files := consolemap_deftbl.c defkeymap.c + +diff --git a/drivers/char/lyrvpss/Kconfig b/drivers/char/lyrvpss/Kconfig +new file mode 100644 +index 0000000..80b1487 +--- /dev/null ++++ b/drivers/char/lyrvpss/Kconfig +@@ -0,0 +1,42 @@ ++# ++# Lyrtech VPSS drivers ++# ++ ++menuconfig LYRTECH_VPSS ++ bool 'Lyrtech SFFSDR VPSS drivers' ++ depends on ARCH_DAVINCI && MACH_SFFSDR ++ help ++ This enables support for Lyrtech SFFSDR VPSS drivers. ++ ++ If unsure, say N. ++ ++if LYRTECH_VPSS ++ ++config LYRTECH_VPFE ++ tristate "Lyrtech VPFE Driver Support" ++ help ++ This option enables support for the Lyrtech VPFE driver ++ for FPGA to DaVinci data transfers. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called lyrvpfe. ++ ++ If unsure, say N. ++ ++config LYRTECH_VPBE ++ tristate "Lyrtech VPBE Driver Support" ++ help ++ This option enables support for the Lyrtech VPBE driver ++ for DaVinci to FPGA data transfers. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called lyrvpbe. ++ ++ If unsure, say N. ++ ++config LYRVPSS_DEBUG ++ boolean "Debug support for LYRVPSSS drivers" ++ help ++ Say "yes" to enable verbose debug messaging. ++ ++endif # LYRTECH_VPSS +diff --git a/drivers/char/lyrvpss/Makefile b/drivers/char/lyrvpss/Makefile +new file mode 100644 +index 0000000..ac36807 +--- /dev/null ++++ b/drivers/char/lyrvpss/Makefile +@@ -0,0 +1,8 @@ ++# ++# Makefile for the Lyrtech SFFSDR VPSS driver ++# ++ ++obj-$(CONFIG_LYRTECH_VPFE) += lyrvpfe.o ++obj-$(CONFIG_LYRTECH_VPBE) += lyrvpbe.o ++lyrvpfe-objs := vpfe.o ++lyrvpbe-objs := vpbe.o +diff --git a/drivers/char/lyrvpss/vpfe.c b/drivers/char/lyrvpss/vpfe.c +new file mode 100644 +index 0000000..45e2853 +--- /dev/null ++++ b/drivers/char/lyrvpss/vpfe.c +@@ -0,0 +1,753 @@ ++/* ++ * lyrvpfe driver ++ * ++ * Copyright (C) 2008 Lyrtech <www.lyrtech.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. ++ * ++ * 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/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/string.h> ++#include <linux/delay.h> ++#include <linux/firmware.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/err.h> ++#include <linux/fs.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++ ++#ifdef CONFIG_PROC_FS ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++#include <asm/uaccess.h> ++#endif /* CONFIG_PROC_FS */ ++ ++#include <asm/gpio.h> ++ ++#include <mach/sffsdr-fpga.h> ++#include <mach/sffsdr-lyrvpfe.h> ++#include <mach/mux.h> ++#include <mach/irqs.h> ++ ++#define MODULE_NAME "lyrvpfe" ++ ++#ifdef CONFIG_LYRVPSS_DEBUG ++#define DBGMSG(fmt, args...) \ ++ printk(KERN_INFO "%s: "fmt"\n" , MODULE_NAME, ## args) ++#else ++#define DBGMSG(fmt, args...) ++#endif ++ ++#define FAILMSG(fmt, args...) \ ++ printk(KERN_ERR "%s: "fmt"\n" , MODULE_NAME, ## args) ++ ++#define DAVINCI_CCDC_REGS_OFFSET 0x400 ++ ++/* This word is written at index 0 to mark a buffer as invalid. */ ++#define INVALIDATE_BUFFER_CODE 0x11222211 ++ ++/* Default values for our driver. */ ++#define LYRVPFE_LINES_PER_FRAME 2 ++#define LYRVPFE_WORDS_PER_LINE 8 /* Minimum is 8 words */ ++ ++/* SFFSDR VPSS limits */ ++#define LYRVPFE_MAX_WORDS_PER_LINE 1024 ++#define LYRVPFE_MAX_LINES_PER_FRAME 10 ++#define LYRVPFE_MAX_BUFFER_SIZE 65536 ++ ++#define BUFFER_PING 0 ++#define BUFFER_PONG 1 ++ ++struct ccdc_regs { ++ u32 pid; ++ u32 pcr; ++ u32 syn_mode; ++ u32 hd_vd_wid; ++ u32 pix_lines; ++ u32 horz_info; ++ u32 vert_start; ++ u32 vert_lines; ++ u32 culling; ++ u32 hsize_off; ++ u32 sdofst; ++ u32 sdr_addr; ++ u32 clamp; ++ u32 dcsub; ++ u32 colptn; ++ u32 blkcmp; ++ u32 fpc; ++ u32 fpc_addr; ++ u32 vdint; ++ u32 alaw; ++ u32 rec656if; ++ u32 ccdcfg; ++ u32 fmtcfg; ++ u32 fmt_horz; ++ u32 fmt_vert; ++ u32 unused[48]; ++ u32 vp_out; ++}; ++ ++#define CCDC_REGS_COUNT 38 ++ ++#define CCDC_WEN_BIT (1 << 17) ++#define CCDC_VDHDEN_BIT (1 << 16) ++#define CCDC_VDPOL_NEG (1 << 2) ++ ++/* Structure containing driver informations. */ ++struct lyrvpfe_private { ++ enum { ++ LYRVPFE_INIT_START, ++ LYRVPFE_INIT_HAVE_REGS, ++ LYRVPFE_INIT_HAVE_IRQ, ++ LYRVPFE_INIT_HAVE_GPIO, ++ LYRVPFE_INIT_VPFE, ++ LYRVPFE_INIT_HAVE_PING_BUFFER, ++ LYRVPFE_INIT_HAVE_PONG_BUFFER, ++ LYRVPFE_INIT_HAVE_PROC ++ } init_state; ++ u32 id; ++ unsigned ready_gpio; ++ unsigned int irq; ++ void *regs; ++ volatile struct ccdc_regs *ccdc_regs; ++ u32 ramp_index; ++ u32 lines_per_frame; ++ u32 words_per_line; ++ int line_size; ++ int bufsize; ++ int wrid; /* 0 (ping) or 1 (pong) */ ++ u32 *data_buffers[2]; ++ struct device dev; ++}; ++ ++static struct lyrvpfe_private lyrvpfe; ++ ++/* Informs the FPGA that the DaVinci can receive a new frame. */ ++static void lyrvpfe_set_ready(void) ++{ ++ int value; ++ ++ /* Read current pin state */ ++ value = gpio_get_value(lyrvpfe.ready_gpio); ++ ++ /* Toggle state. */ ++ value ^= 1; ++ ++ /* Toggle pin. */ ++ gpio_set_value(lyrvpfe.ready_gpio, value); ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++#define LYRVPFE_PROC_NAME "lyrvpfe" ++ ++static void *lyrvpfe_start(struct seq_file *m, loff_t *pos) ++{ ++ return *pos < 1 ? (void *)1 : NULL; ++} ++ ++static void *lyrvpfe_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ ++*pos; ++ return NULL; ++} ++ ++static void lyrvpfe_stop(struct seq_file *m, void *v) ++{ ++} ++ ++static void lyrvpfe_display_regs(char *msg, u32 *regs, int size, ++ struct seq_file *m) ++{ ++ int k; ++ ++ seq_printf(m, "%s:", msg); ++ for (k = 0; k < size; k++) { ++ if ((k % 4) == 0) ++ seq_printf(m, "\n"); ++ ++ seq_printf(m, " [$%02X] $%08X", k * 4, regs[k]); ++ } ++ seq_printf(m, "\n"); ++} ++ ++static int lyrvpfe_show(struct seq_file *m, void *v) ++{ ++ u32 *regs; ++ unsigned long jtarget, jcurrent; ++ ++ /* Toggle pin to receive next frame */ ++ lyrvpfe_set_ready(); ++ ++ jtarget = jiffies + (1 * HZ); ++ ++ /* Wait 1 second for data to arrive. */ ++ do { ++ jcurrent = jiffies; ++ cpu_relax(); ++ } while (time_before(jcurrent, jtarget)); ++ ++ regs = (u32 *) lyrvpfe.ccdc_regs; ++ lyrvpfe_display_regs("CCDC registers", regs, CCDC_REGS_COUNT, m); ++ ++ seq_printf(m, "FPGA registers:\n"); ++ ++ seq_printf(m, " [$%04X] $%04X [$%04X] $%04X" \ ++ " [$%04X] $%04X [$%04X] $%04X\n", ++ SFFSDR_FPGA_REVISION, ++ sffsdr_fpga_regread(SFFSDR_FPGA_REVISION), ++ SFFSDR_FPGA_VPSS_CONTROL, ++ sffsdr_fpga_regread(SFFSDR_FPGA_VPSS_CONTROL), ++ SFFSDR_FPGA_VPSS_TO_DSP_FIFO, ++ sffsdr_fpga_regread(SFFSDR_FPGA_VPSS_TO_DSP_FIFO), ++ SFFSDR_FPGA_VPSS_LINES_PER_FRAME, ++ sffsdr_fpga_regread(SFFSDR_FPGA_VPSS_LINES_PER_FRAME)); ++ ++ regs = lyrvpfe.data_buffers[BUFFER_PING]; ++ lyrvpfe_display_regs("PING buffer", regs, 64, m); ++ ++ regs = lyrvpfe.data_buffers[BUFFER_PONG]; ++ lyrvpfe_display_regs("PONG buffer", regs, 64, m); ++ ++ return 0; ++} ++ ++static const struct seq_operations lyrvpfe_op = { ++ .start = lyrvpfe_start, ++ .next = lyrvpfe_next, ++ .stop = lyrvpfe_stop, ++ .show = lyrvpfe_show ++}; ++ ++static int lyrvpfe_open(struct inode *inode, struct file *file) ++{ ++ struct seq_file *m; ++ int ret; ++ ++ DBGMSG("lyrvpfe_open"); ++ ++ ret = seq_open(file, &lyrvpfe_op); ++ if (ret < 0) ++ return ret; ++ ++ m = file->private_data; ++ ++ return 0; ++} ++ ++static const struct file_operations proc_lyrvpfe_operations = { ++ .open = lyrvpfe_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int lyrvpfe_proc_init(void) ++{ ++ struct proc_dir_entry *entry; ++ ++ entry = create_proc_entry(LYRVPFE_PROC_NAME, 0, NULL); ++ if (!entry) { ++ FAILMSG("Error creating proc entry"); ++ return -EFAULT; ++ } ++ ++ entry->proc_fops = &proc_lyrvpfe_operations; ++ entry->data = &lyrvpfe; ++ ++ return 0; ++} ++ ++#endif /* CONFIG_PROC_FS */ ++ ++static int lyrvpfe_validate_buffer(u32 *buffer) ++{ ++ u8 xor, xnor; ++ u8 *cksum_data = (u8 *) buffer; ++ ++ /* Compute XOR of bytes 4 and 5 */ ++ xnor = ~(cksum_data[4] ^ cksum_data[5]); ++ xor = cksum_data[4] ^ cksum_data[5]; ++ ++ if ((xor != cksum_data[1]) || (xnor != cksum_data[0])) ++ return -1; ++ else ++ return 0; ++} ++ ++static inline void lyrvpfe_invalidate_buffer(u32 *buffer) ++{ ++ int line; ++ int offset; ++ ++ for (line = 0; line < lyrvpfe.lines_per_frame; line++) { ++ /* Get offset of next line. */ ++ offset = (line * lyrvpfe.ccdc_regs->hsize_off) / 4; ++ ++ /* Mark buffer as invalid. */ ++ buffer[offset] = INVALIDATE_BUFFER_CODE; ++ } ++} ++ ++/* ++ * Lyrtech SFFSDR custom VPFE format: ++ * ++ * Length is in u32 units ++ * ++ * Format for each line: ++ * ++ * | u32 | bits | ++ * |offset| 31..24 | 23..16 | 15..08 | 07..00 | ++ * ============================================ ++ * 0 | dummy dummy dummy dummy ++ * 1 | dummy dummy dummy dummy ++ * 2 | length length cksum cksum ++ * 3 | data0 data0 data0 data0 ++ * 4 | data1 data1 data1 data1 ++ * ... ... ++ */ ++static int vpfe_check_buffer(u32 *buffer) ++{ ++ int k; ++ int line; ++ u16 length; ++ int offset; ++ int ret; ++ ++ for (line = 0; line < lyrvpfe.lines_per_frame; line++) { ++ /* Get offset of next line. */ ++ offset = (line * lyrvpfe.ccdc_regs->hsize_off) / 4; ++ ++ if (buffer[offset] == INVALIDATE_BUFFER_CODE) { ++ /* No error. Means that HD pulses generated the VDINT0 ++ * interruption, but VD was not asserted. */ ++ return -1; ++ } ++ ++ /* First two words contain empty/dummy data. */ ++ offset += 2; ++ ++ if (line == 0) { ++ if (lyrvpfe.wrid == BUFFER_PING) ++ DBGMSG("VDINT: PING buffer"); ++ else ++ DBGMSG("VDINT: PONG buffer"); ++ } ++ ++ ret = lyrvpfe_validate_buffer(&buffer[offset]); ++ if (ret < 0) { ++ /* This may mean a checksum error, or that ++ * the FPGA sent fewer lines than the maximum ++ * configured. */ ++ FAILMSG(" Checksum error line %d", line); ++ return -1; ++ } ++ ++ length = buffer[offset] >> 16; ++ ++ /* Points to first data word. */ ++ offset++; ++ ++ for (k = 0; k < length; k++) { ++ if (buffer[offset + k] != (lyrvpfe.ramp_index + k)) { ++ FAILMSG(" Ramp error at index %d, line %d", ++ lyrvpfe.ramp_index, line); ++ FAILMSG(" read: $%08X", ++ buffer[offset + k]); ++ FAILMSG(" expected: $%08X", ++ lyrvpfe.ramp_index + k); ++ ++ lyrvpfe_invalidate_buffer(buffer); ++ return -1; ++ } ++ } ++ ++ lyrvpfe.ramp_index += length; ++ } ++ ++ return 0; ++} ++ ++static void lyrvpfe_set_ccdc_buffer(u32 *virt_address) ++{ ++ lyrvpfe.ccdc_regs->sdr_addr = (u32) virt_to_phys(virt_address); ++} ++ ++/* ++ * The CCDC VDINT0 and VDINT1 HD counters begin counting HD pulses from the ++ * rising edge of the external VD. The Lyrtech FPGA VPFE design only drives VD ++ * when the ARM request data by toggling the SET_VPFE_READY GPIO. Unfortunately, ++ * the FPGA never disable the HD line, and the ISR will be called all the time ++ * with invalid data when VD is not driven. This is why we need to invalidate a ++ * buffer once it has been read. ++ */ ++static irqreturn_t lyrvpfe_isr(int irq, void *dev_id) ++{ ++ int ret; ++ int buffer_read_id; ++ ++ /* Buffer index for reading data */ ++ buffer_read_id = lyrvpfe.wrid; ++ ++ ret = vpfe_check_buffer(lyrvpfe.data_buffers[buffer_read_id]); ++ if (ret) { ++ /* This could mean a real error or simply that we received a ++ * dummy HD interrupt. */ ++ lyrvpfe_invalidate_buffer(lyrvpfe.data_buffers[buffer_read_id]); ++ } else { ++ /* Valid data was received. We can now switch the pong-pong ++ * buffers. */ ++ ++ /* Switch ping-pong buffers for writing. */ ++ lyrvpfe.wrid ^= 1; ++ lyrvpfe_set_ccdc_buffer(lyrvpfe.data_buffers[lyrvpfe.wrid]); ++ ++ /* Make sure to invalidate the new buffer */ ++ lyrvpfe_invalidate_buffer(lyrvpfe.data_buffers[lyrvpfe.wrid]); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Configures the VPFE interface to receive data from the FPGA. ++ * ++ * lines_per_frame: Lines per frame (within the VSYNC period). ++ * words_per_line: 32-bits data words per line (within the HSYNC period). ++ */ ++static int lyrvpfe_init_vpfe(u16 lines_per_frame, u16 words_per_line) ++{ ++ int bytes_per_buffer; ++ ++ lyrvpfe.ramp_index = 0; ++ lyrvpfe.wrid = BUFFER_PING; ++ ++ if (words_per_line > LYRVPFE_MAX_WORDS_PER_LINE) { ++ FAILMSG("VPFE init: invalid words_per_line (%d)", ++ words_per_line); ++ return -1; ++ } ++ ++ if (lines_per_frame > LYRVPFE_MAX_LINES_PER_FRAME) { ++ FAILMSG("VPFE init: invalid lines_per_frame (%d)", ++ lines_per_frame); ++ return -1; ++ } ++ ++ bytes_per_buffer = (words_per_line + 3) * 4 * lines_per_frame; ++ if (bytes_per_buffer > LYRVPFE_MAX_BUFFER_SIZE) { ++ FAILMSG("VPFE init: wrong bytes_per_buffer (%d)", ++ bytes_per_buffer); ++ return -1; ++ } ++ ++ DBGMSG(" words_per_line = $%04X", words_per_line); ++ DBGMSG(" lines_per_frame = $%04X", lines_per_frame); ++ ++ /* Setup FPGA parameters */ ++ sffsdr_fpga_regwrite(SFFSDR_FPGA_VPSS_TO_DSP_FIFO, ++ (words_per_line & 0x3ff) | ++ (lines_per_frame << 10)); ++ ++ /* 2 additional for blanking and 1 for header (length and checksum). */ ++ words_per_line = words_per_line + 3; ++ ++ lyrvpfe.words_per_line = words_per_line; ++ lyrvpfe.lines_per_frame = lines_per_frame; ++ ++ /************************************************/ ++ /* Setup Fix VPFE parameter */ ++ /************************************************/ ++ /* Setup VPFE Hardware */ ++ lyrvpfe.ccdc_regs->syn_mode = CCDC_WEN_BIT | CCDC_VDHDEN_BIT | ++ CCDC_VDPOL_NEG; ++ ++ /* Start at Line 0 */ ++ lyrvpfe.ccdc_regs->vert_start = 0; ++ ++ /* Disable culling */ ++ lyrvpfe.ccdc_regs->culling = 0xFFFF00FF; ++ ++ lyrvpfe.ccdc_regs->sdofst = 0; ++ lyrvpfe.ccdc_regs->clamp = 0; ++ lyrvpfe.ccdc_regs->dcsub = 0; ++ lyrvpfe.ccdc_regs->colptn = 0; ++ lyrvpfe.ccdc_regs->blkcmp = 0; ++ lyrvpfe.ccdc_regs->fpc = 0; ++ lyrvpfe.ccdc_regs->vdint = 0; ++ lyrvpfe.ccdc_regs->alaw = 0; ++ lyrvpfe.ccdc_regs->rec656if = 0; ++ ++ /* Disable shadowing as recommended in silicon errata. Very important, ++ * if not set, a lot of problems may occur. */ ++ /* VDLC: Not latched on VSYNC. */ ++ lyrvpfe.ccdc_regs->ccdcfg = (1 << 15); ++ ++ /************************************************/ ++ /* Setup variable VPFE parameter */ ++ /************************************************/ ++ /* Max. length of a line */ ++ lyrvpfe.ccdc_regs->horz_info = words_per_line * 4; ++ ++ /* Max. number of lines per frame - 1 */ ++ lyrvpfe.ccdc_regs->vert_lines = lines_per_frame - 1; ++ ++ /* Offset of a line in memory (in bytes). ++ * Must be on 32 bytes boundary */ ++ lyrvpfe.line_size = ((words_per_line * sizeof(u32)) + 31) & ~31; ++ lyrvpfe.ccdc_regs->hsize_off = lyrvpfe.line_size; ++ ++ /* Enable CCDC */ ++ lyrvpfe.ccdc_regs->pcr = 0x1; ++ ++ return 0; ++} ++ ++static void lyrvpfe_disable_vpfe(void) ++{ ++ /* Disable CCDC */ ++ lyrvpfe.ccdc_regs->pcr = 0; ++} ++ ++static void lyrvpfe_dev_cleanup(void) ++{ ++ DBGMSG("lyrvpfe_dev_cleanup()"); ++ ++ switch (lyrvpfe.init_state) { ++ case LYRVPFE_INIT_HAVE_PROC: ++#ifdef CONFIG_PROC_FS ++ remove_proc_entry(LYRVPFE_PROC_NAME, NULL); ++#endif ++ case LYRVPFE_INIT_HAVE_IRQ: ++ free_irq(lyrvpfe.irq, &lyrvpfe); ++ case LYRVPFE_INIT_HAVE_PONG_BUFFER: ++ kfree(lyrvpfe.data_buffers[BUFFER_PONG]); ++ case LYRVPFE_INIT_HAVE_PING_BUFFER: ++ kfree(lyrvpfe.data_buffers[BUFFER_PING]); ++ case LYRVPFE_INIT_VPFE: ++ lyrvpfe_disable_vpfe(); ++ case LYRVPFE_INIT_HAVE_GPIO: ++ gpio_free(lyrvpfe.ready_gpio); ++ case LYRVPFE_INIT_HAVE_REGS: ++ iounmap(lyrvpfe.regs); ++ case LYRVPFE_INIT_START: ++ break; ++ } ++} ++ ++struct bus_type lyrvpfe_bus_type = { ++ .name = "lyrvpfe", ++}; ++EXPORT_SYMBOL(lyrvpfe_bus_type); ++ ++static int lyrvpfe_probe(struct platform_device *pdev) ++{ ++ struct lyrvpfe_platform_data *pdata; ++ struct resource *regs_res; ++ struct resource *irq_res; ++ int result; ++ void *buf; ++ ++ DBGMSG("lyrvpfe_probe()"); ++ ++ /* We Should enable the VPFE with the PSC controller and PINMUX0. */ ++ ++ lyrvpfe.id = pdev->id; ++ lyrvpfe.dev.bus = &lyrvpfe_bus_type; ++ lyrvpfe.dev.parent = &pdev->dev; ++ snprintf(lyrvpfe.dev.bus_id, BUS_ID_SIZE, "lyrvpfe%d", lyrvpfe.id); ++ lyrvpfe.dev.bus_id[BUS_ID_SIZE - 1] = 0; ++ lyrvpfe.init_state = LYRVPFE_INIT_START; ++ ++ regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); ++ if (!regs_res) { ++ FAILMSG("Error getting REGS ressource"); ++ result = -ENODEV; ++ goto error; ++ } ++ ++ lyrvpfe.regs = ioremap(regs_res->start, ++ regs_res->end - regs_res->start); ++ if (!lyrvpfe.regs) { ++ FAILMSG("Can't remap CCDC registers"); ++ result = -ENXIO; ++ goto error; ++ } ++ lyrvpfe.ccdc_regs = (struct ccdc_regs *) ++ (lyrvpfe.regs + DAVINCI_CCDC_REGS_OFFSET); ++ ++ lyrvpfe.init_state = LYRVPFE_INIT_HAVE_REGS; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ FAILMSG("Error getting platform data"); ++ result = -ENODEV; ++ goto error; ++ } ++ lyrvpfe.dev.platform_data = pdata; ++ ++ /* Configure VPFE SET READY GPIO. */ ++ lyrvpfe.ready_gpio = pdata->ready_gpio; ++ ++ result = gpio_request(lyrvpfe.ready_gpio, "vpfe_ready"); ++ if (result == 0) { ++ /* Must start at 1, if not gives errors. */ ++ result = gpio_direction_output(lyrvpfe.ready_gpio, 1); ++ } ++ if (result != 0) ++ goto error; ++ ++ lyrvpfe.init_state = LYRVPFE_INIT_HAVE_GPIO; ++ ++ result = lyrvpfe_init_vpfe(LYRVPFE_LINES_PER_FRAME, ++ LYRVPFE_WORDS_PER_LINE); ++ if (result < 0) { ++ FAILMSG("lyrvpfe_init_vpfe() failed (%d)", result); ++ goto error; ++ } ++ lyrvpfe.init_state = LYRVPFE_INIT_VPFE; ++ ++ /* Adding 256 to compensate for 256 bytes alignment */ ++ lyrvpfe.bufsize = lyrvpfe.line_size * lyrvpfe.lines_per_frame + 256; ++ ++ buf = kmalloc(lyrvpfe.bufsize /*LYRVPFE_BUFFER_SIZE*/, GFP_KERNEL); ++ if (!buf) { ++ result = -ENOMEM; ++ goto error; ++ } ++ /* Buffer must be 32 bytes aligned for the hardware but must be ++ * 256 bytes aligned to cope with cache line size. */ ++ lyrvpfe.data_buffers[BUFFER_PING] = ++ (u32 *) (((u32) buf + 255) & 0xFFFFFF00); ++ lyrvpfe.data_buffers[BUFFER_PING][0] = 0x11111111; ++ lyrvpfe.data_buffers[BUFFER_PING][1] = 0x22222222; ++ lyrvpfe_invalidate_buffer(lyrvpfe.data_buffers[BUFFER_PING]); ++ lyrvpfe.init_state = LYRVPFE_INIT_HAVE_PING_BUFFER; ++ ++ buf = kmalloc(lyrvpfe.bufsize /*LYRVPFE_BUFFER_SIZE*/, GFP_KERNEL); ++ if (!buf) { ++ result = -ENOMEM; ++ goto error; ++ } ++ /* Buffer must be 32 bytes aligned for the hardware but must be ++ * 256 bytes aligned to cope with cache line size. */ ++ lyrvpfe.data_buffers[BUFFER_PONG] = ++ (u32 *) (((u32) buf + 255) & 0xFFFFFF00); ++ lyrvpfe.data_buffers[BUFFER_PONG][0] = 0x33333333; ++ lyrvpfe.data_buffers[BUFFER_PONG][1] = 0x44444444; ++ lyrvpfe_invalidate_buffer(lyrvpfe.data_buffers[BUFFER_PONG]); ++ lyrvpfe.init_state = LYRVPFE_INIT_HAVE_PONG_BUFFER; ++ ++ lyrvpfe_set_ccdc_buffer(lyrvpfe.data_buffers[lyrvpfe.wrid]); ++ ++ /* setup interrupt handling */ ++ irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); ++ if (!irq_res) { ++ FAILMSG("Error getting IRQ ressource"); ++ result = -ENODEV; ++ goto error; ++ } ++ ++ lyrvpfe.irq = irq_res->start; ++ result = request_irq(lyrvpfe.irq, lyrvpfe_isr, ++ IRQF_SHARED /*IRQF_DISABLED*/, ++ MODULE_NAME, &lyrvpfe); ++ if (result) { ++ FAILMSG("Error requesting IRQ ressource"); ++ result = -ENODEV; /* To check */ ++ goto error; ++ } ++ ++ lyrvpfe.init_state = LYRVPFE_INIT_HAVE_IRQ; ++ ++#ifdef CONFIG_PROC_FS ++ result = lyrvpfe_proc_init(); ++ if (result < 0) { ++ FAILMSG("Error creating proc entry"); ++ goto error; ++ } ++#endif ++ ++ lyrvpfe.init_state = LYRVPFE_INIT_HAVE_PROC; ++ ++ return 0; ++ ++error: ++ lyrvpfe_dev_cleanup(); ++ return result; ++} ++ ++static int __devexit lyrvpfe_remove(struct platform_device *pdev) ++{ ++ DBGMSG("lyrvpfe_remove()"); ++ ++ lyrvpfe_dev_cleanup(); ++ return 0; ++} ++ ++static struct platform_driver lyrvpfe_pdriver = { ++ .driver = { ++ .name = MODULE_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .remove = lyrvpfe_remove, ++}; ++ ++static int __init lyrvpfe_init(void) ++{ ++ int res = 0; ++ ++ DBGMSG("lyrvpfe_init()"); ++ ++ res = bus_register(&lyrvpfe_bus_type); ++ if (res) { ++ FAILMSG("bus_register() failed"); ++ goto fail_bus; ++ } ++ ++ res = platform_driver_probe(&lyrvpfe_pdriver, lyrvpfe_probe); ++ if (res) { ++ FAILMSG("platform_driver_probe() failed"); ++ goto fail_platform; ++ } ++ ++ return 0; ++ ++fail_platform: ++ bus_unregister(&lyrvpfe_bus_type); ++fail_bus: ++ return res; ++} ++module_init(lyrvpfe_init); ++ ++static void __exit lyrvpfe_exit(void) ++{ ++ DBGMSG("lyrvpfe_exit()"); ++ ++ platform_driver_unregister(&lyrvpfe_pdriver); ++ bus_unregister(&lyrvpfe_bus_type); ++} ++module_exit(lyrvpfe_exit); ++ ++MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@lyrtech.com>"); ++MODULE_DESCRIPTION("Lyrtech SFFSDR VPFE driver"); ++MODULE_LICENSE("GPL"); +-- +1.5.4.5 + |